omadrm/drmengine/roapstorage/src/responsedecoder.cpp
changeset 0 95b198f216e5
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/omadrm/drmengine/roapstorage/src/responsedecoder.cpp	Thu Dec 17 08:52:27 2009 +0200
@@ -0,0 +1,428 @@
+/*
+* Copyright (c) 2002-2008 Nokia Corporation and/or its subsidiary(-ies).
+* All rights reserved.
+* This component and the accompanying materials are made available
+* under the terms of "Eclipse Public License v1.0"
+* which accompanies this distribution, and is available
+* at the URL "http://www.eclipse.org/legal/epl-v10.html".
+*
+* Initial Contributors:
+* Nokia Corporation - initial contribution.
+*
+* Contributors:
+*
+* Description:  OCSP response decoder for given response
+*
+*/
+
+
+#include "responsedecoder.h"
+#include "oids.h"
+#include "RoapOcsp.h"
+#include <asn1dec.h>
+#include <x509cert.h>
+
+// Enum values in DER encoding of response status
+enum
+    {
+    ESuccessfulEncoding = 0,
+    EMalformedRequestEncoding = 1,
+    EInternalErrorEncoding = 2,
+    ETryLaterEncoding = 3,
+    ESigRequiredEncoding = 5,
+    EUnauthorisedEncoding = 6
+    };
+
+
+// Tag values in DER encoded response data
+const TUint KResponseBytesTag = 0;
+const TUint KCertificatesTag = 0;
+const TUint KVersionTag = 0;
+const TUint KResponderIDNameTag = 1;
+const TUint KReponsderIDKeyHashTag = 2;
+const TUint KResponseExtensionsTag = 1;
+
+
+COCSPResponseDecoder* COCSPResponseDecoder::NewL(const TDesC8& aEncoding)
+    {
+    COCSPResponseDecoder* self = new (ELeave) COCSPResponseDecoder;
+    CleanupStack::PushL(self);
+    self->ConstructL(aEncoding);
+    CleanupStack::Pop(self);
+    return self;
+    }
+
+
+COCSPResponseDecoder::COCSPResponseDecoder()
+    {
+    }
+
+
+COCSPResponseDecoder::~COCSPResponseDecoder()
+    {
+    delete iResponse;
+    }
+
+
+void COCSPResponseDecoder::ConstructL(const TDesC8& aEncoding)
+    {
+    iResponse = new (ELeave) COCSPResponse;
+
+    // Populate CSignedObject data members
+    iResponse->iKeyFactory = new (ELeave) TX509KeyFactory; // Unconventional class name
+    iResponse->iEncoding = aEncoding.AllocL();
+
+    TRAPD(error, DecodeOCSPResponseL(*iResponse->iEncoding));
+    if (error == KErrArgument || error == KErrNotSupported)
+        {
+        // These arise from problems parsing the data in X509 or ASN1
+        error = OCSP::EMalformedResponse;
+        }
+
+    if (error != KErrNone)
+        {
+        // Errors and our status codes go back to the client
+        delete iResponse;
+        iResponse = NULL;
+        User::Leave(error);
+        }
+    }
+
+
+COCSPResponse* COCSPResponseDecoder::TakeResponse()
+    {
+    COCSPResponse* result = iResponse;
+    iResponse = NULL;
+    return result;
+    }
+
+
+CArrayPtr<TASN1DecGeneric>* COCSPResponseDecoder::DecodeSequenceLC(const TDesC8& aEncoding)
+    {
+    CArrayPtr<TASN1DecGeneric>* items = NULL;
+
+    // Check we've got a sequence
+    TASN1DecGeneric decGen(aEncoding);
+    decGen.InitL();
+    if (decGen.Tag() != EASN1Sequence)
+        {
+        User::Leave(KErrArgument);
+        }
+    else
+        {
+        // Decode the sequence
+        TASN1DecSequence decSeq;
+        items = decSeq.DecodeDERLC(decGen);
+        }
+    return items;
+    }
+
+
+CArrayPtr<TASN1DecGeneric>* COCSPResponseDecoder::DecodeSequenceLC(const TDesC8& aEncoding,
+                                                            const TInt aMinTerms,
+                                                            const TInt aMaxTerms)
+    {
+    CArrayPtr<TASN1DecGeneric>* items = DecodeSequenceLC(aEncoding);
+    TInt count = items->Count();
+    if (count < aMinTerms || count > aMaxTerms)
+        {
+        // User::Leave(KErrArgument);
+        }
+
+    return items;
+    }
+
+
+void COCSPResponseDecoder::DecodeOCSPResponseL(const TDesC8& aEncoding)
+    {
+    CArrayPtr<TASN1DecGeneric>* items = DecodeSequenceLC(aEncoding, 1, 2);
+
+    // Use integer decoding for enumerated
+    TASN1DecInteger decInt;
+    TInt status = decInt.DecodeDERShortL(*items->At(0));
+    if (status == ESuccessfulEncoding)
+        {
+        if (items->Count() != 2)
+            {
+            User::Leave(OCSP::EMalformedResponse);
+            }
+
+        // Check tag on second part is [0]
+        // We ignore any other parts in the sequence after that
+        TASN1DecGeneric& responseBytesDec = *items->At(1);
+        if (responseBytesDec.Tag() != KResponseBytesTag)
+            {
+            User::Leave(OCSP::EMalformedResponse);
+            }
+
+        // It's OK, so decode the response bytes object therein
+        DecodeResponseBytesL(responseBytesDec.GetContentDER());
+        }
+    else
+        {
+        if (items->Count() != 1)
+            {
+            User::Leave(KErrArgument);
+            }
+
+        switch (status)
+            {
+            case EMalformedRequestEncoding:
+                User::Leave(OCSP::EMalformedRequest);
+            case EInternalErrorEncoding:
+                User::Leave(OCSP::EServerInternalError);
+                break;
+            case ETryLaterEncoding:
+                User::Leave(OCSP::ETryLater);
+                break;
+            case ESigRequiredEncoding:
+                User::Leave(OCSP::ESignatureRequired);
+                break;
+            case EUnauthorisedEncoding:
+                User::Leave(OCSP::EClientUnauthorised);
+                break;
+            default:
+                User::Leave(OCSP::EMalformedResponse);
+            }
+        }
+        CleanupStack::PopAndDestroy(items);
+    }
+
+
+void COCSPResponseDecoder::DecodeResponseBytesL(const TDesC8& aEncoding)
+    {
+    CArrayPtr<TASN1DecGeneric>* items = DecodeSequenceLC(aEncoding, 2, 2);
+
+    TASN1DecObjectIdentifier decOid;
+    HBufC* oid = decOid.DecodeDERL(*items->At(0));
+    CleanupStack::PushL(oid);
+    if (*oid != KOCSPOidBasic)
+        {
+        User::Leave(OCSP::EUnknownResponseType);
+        }
+
+    TASN1DecGeneric& response = *items->At(1);
+    if (response.Tag() != EASN1OctetString)
+        {
+        User::Leave(OCSP::EMalformedResponse);
+        }
+
+    DecodeBasicOCSPResponseL(response.GetContentDER());
+
+    CleanupStack::PopAndDestroy(2, items); // oid, items
+    }
+
+
+void COCSPResponseDecoder::DecodeBasicOCSPResponseL(const TDesC8& aEncoding)
+    {
+    CArrayPtr<TASN1DecGeneric>* items = DecodeSequenceLC(aEncoding, 3, 4);
+
+    // First, the ResponseData object
+    DecodeResponseDataL(items->At(0)->Encoding());
+
+    // Continue, with the AlgorithmIdentifier
+    iResponse->iSigningAlgorithm = CX509SigningAlgorithmIdentifier::NewL(items->At(1)->Encoding());
+
+    // Now move on to the signature
+    TASN1DecBitString encBS;
+    iResponse->iSignature = encBS.ExtractOctetStringL(*items->At(2));
+
+    // And finally, the certs (if they're there)
+    if (items->Count() == 4)
+        {
+        // Check explicit tag [0]
+        TASN1DecGeneric& certsDec = *items->At(3);
+        if (certsDec.Tag() != KCertificatesTag)
+            {
+            User::Leave(OCSP::EMalformedResponse);
+            }
+
+        // It's OK, so decode the response bytes object therein
+        DecodeCertificatesL(certsDec.GetContentDER());
+        }
+
+    CleanupStack::PopAndDestroy(items);
+    }
+
+
+void COCSPResponseDecoder::DecodeCertificatesL(const TDesC8& aEncoding)
+    {
+    TASN1DecGeneric dec(aEncoding);
+    dec.InitL();
+    if (dec.Tag() != EASN1Sequence)
+        {
+        User::Leave(OCSP::EMalformedResponse);
+        }
+
+    // Just stores a reference to the encoding
+    iResponse->iSigningCerts.Set(dec.GetContentDER());
+    }
+
+
+void COCSPResponseDecoder::DecodeResponseDataL(const TDesC8& aEncoding)
+    {
+    TInt index = 0;
+
+    // This is the signed data
+    iResponse->iSignedData.Set(aEncoding);
+
+    // Exclude 5 items in sequence - defaulted version v1 shouldn't appear in DER
+    CArrayPtr<TASN1DecGeneric>* items = DecodeSequenceLC(aEncoding, 3, 4);
+
+    // First item is responderID choice, or maybe version
+    TASN1DecGeneric& item0 = *items->At(index); // index = 0
+
+    ++index;
+
+    switch (item0.Tag())
+        {
+        case KResponderIDNameTag:
+            // Set to Name DER encoding
+            iResponse->iResponderIDName.Set(item0.GetContentDER());
+            break;
+        case KReponsderIDKeyHashTag:
+            {
+            // Set to KeyHash to value within the octet string
+            TASN1DecGeneric decGen(item0.GetContentDER());
+            decGen.InitL();
+            iResponse->iResponderIDKeyHash.Set(decGen.GetContentDER());
+            break;
+            }
+        case KVersionTag:
+            {
+            // skip the version, the next one should be responderID
+            TASN1DecGeneric item1 = *items->At(index); // index = 1
+            ++index;
+            if (item1.Tag() == KResponderIDNameTag)
+                {
+                iResponse->iResponderIDName.Set(item1.GetContentDER());
+                break;
+                }
+            else if (item1.Tag() == KReponsderIDKeyHashTag)
+                {
+                TASN1DecGeneric decGen(item1.GetContentDER());
+                decGen.InitL();
+                iResponse->iResponderIDKeyHash.Set(decGen.GetContentDER());
+                break;
+                }
+            else
+                {
+                // falling through...
+                }
+            }
+        default:
+            User::Leave(OCSP::EMalformedResponse);
+        }
+
+
+    // ProducedAt is a GeneralizedTime
+    TASN1DecGeneralizedTime decGT;
+    iResponse->iProducedAt = decGT.DecodeDERL(*items->At(index)); // index = 1 | 2
+    ++index;
+
+    // Now the responses themselves
+    DecodeResponsesL(items->At(index)->Encoding()); // index = 2 | 3
+    ++index;
+
+    // Continue if extensions exist
+    if (items->Count() == index + 1) // index = 3 | 4
+        {
+        // Check tag on responseExtensions
+        TASN1DecGeneric& item3 = *items->At(index);
+        if (item3.Tag() != KResponseExtensionsTag)
+            {
+            User::Leave(OCSP::EMalformedResponse);
+            }
+
+        DecodeResponseExtensionsL(item3.GetContentDER());
+        }
+    CleanupStack::PopAndDestroy(items);
+    }
+
+
+void COCSPResponseDecoder::DecodeResponseExtensionsL(const TDesC8& aEncoding)
+    {
+    CArrayPtr<TASN1DecGeneric>* items = DecodeSequenceLC(aEncoding);
+    TInt count = items->Count();
+    for (TInt index = 0; index < count; ++index)
+        {
+        DecodeResponseExtensionL(items->At(index)->Encoding());
+        }
+
+    CleanupStack::PopAndDestroy(items);
+    }
+
+
+void COCSPResponseDecoder::DecodeResponseExtensionL(const TDesC8& aEncoding)
+    {
+    CArrayPtr<TASN1DecGeneric>* items = DecodeSequenceLC(aEncoding, 2, 3);
+
+    // Get oid
+    TASN1DecGeneric& oid = *items->At(0);
+    if (oid.Tag() != EASN1ObjectIdentifier)
+        {
+        User::Leave(OCSP::EMalformedResponse);
+        }
+
+    TASN1DecObjectIdentifier oidDec;
+    HBufC* oidVal = oidDec.DecodeDERL(oid);
+    CleanupStack::PushL(oidVal);
+
+    TBool critical = EFalse; // Default value of critical flag
+    if (items->Count() == 3)
+        {
+        // The critical flag is specified - what does it say?
+        TASN1DecBoolean decBool;
+        critical = decBool.DecodeDERL(*items->At(1));
+        }
+
+    TASN1DecGeneric& extnVal = items->Count() == 3 ? *items->At(2) : *items->At(1);
+    if (extnVal.Tag() != EASN1OctetString)
+        {
+        User::Leave(OCSP::EMalformedResponse);
+        }
+
+    // Check oid to decide what to do
+    if (*oidVal == KOCSPOidNonce)
+        {
+        iResponse->iNonce.Set(extnVal.GetContentDER());
+        }
+    else if (*oidVal == KOCSPOidArchiveCutoff)
+        {
+        TASN1DecGeneralizedTime decGT;
+        TInt pos = 0;
+        iResponse->iArchiveCutoff = new (ELeave) TTime(decGT.DecodeDERL(extnVal.GetContentDER(), pos));
+        }
+    else if (critical)
+        {
+        // Didn't understand extension, and it was critical!  Erk!
+        User::Leave(OCSP::EUnknownCriticalExtension);
+        }
+
+    CleanupStack::PopAndDestroy(2, items); // oidVal, items
+    }
+
+
+void COCSPResponseDecoder::DecodeResponsesL(const TDesC8& aEncoding)
+    {
+    CArrayPtr<TASN1DecGeneric>* items = DecodeSequenceLC(aEncoding);
+    TInt count = items->Count();
+    for (TInt index = 0; index < count; ++index)
+        {
+        DecodeSingleResponseL(items->At(index)->Encoding());
+        }
+
+    CleanupStack::PopAndDestroy(items);
+    }
+
+
+void COCSPResponseDecoder::DecodeSingleResponseL(const TDesC8& aEncoding)
+    {
+    CArrayPtr<TASN1DecGeneric>* items = DecodeSequenceLC(aEncoding, 3, 5);
+
+    COCSPResponseCertInfo* response = COCSPResponseCertInfo::NewLC(*items);
+    User::LeaveIfError(iResponse->iCertInfos.Append(response));
+    CleanupStack::Pop(response); // Now owned through iSingleResponses
+
+    CleanupStack::PopAndDestroy(items);
+    }