diff -r 000000000000 -r 164170e6151a pkiutilities/ocsp/src/responsedecoder.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pkiutilities/ocsp/src/responsedecoder.cpp Tue Jan 26 15:20:08 2010 +0200 @@ -0,0 +1,435 @@ +// Copyright (c) 2003-2009 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: +// requestencoder.cpp +// Implementation for OCSP response decoder object. +// See RFC2560 S4.2.1 for response specification. +// +// + +#include "responsedecoder.h" +#include "oids.h" +#include +#include +#include +#include "ocsprequestandresponse.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; + +/** Version tag number in ResponseData, RFC2560, S4.2.1. */ +const TUint KOcspResponseVersionTag = 0; + +/** + Supported OCSP response Version, RFC2560, S4.2.1. + This is the integer value in Version, which means response + format version 1. + */ +const TUint KOcspResponseVersion1 = 0; + +const TUint KResponderIDNameTag = 1; +const TUint KResponderIDKeyHashTag = 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(aEncoding)); + 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* COCSPResponseDecoder::DecodeSequenceLC(const TDesC8& aEncoding) + { + CArrayPtr* 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* COCSPResponseDecoder::DecodeSequenceLC(const TDesC8& aEncoding, + const TInt aMinTerms, + const TInt aMaxTerms) + { + CArrayPtr* items = DecodeSequenceLC(aEncoding); + TInt count = items->Count(); + if (count < aMinTerms || count > aMaxTerms) + { + User::Leave(KErrArgument); + } + + return items; + } + + +void COCSPResponseDecoder::DecodeOCSPResponseL(const TDesC8& aEncoding) + { + CArrayPtr* 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* 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); // oid, items + } + + +void COCSPResponseDecoder::DecodeBasicOCSPResponseL(const TDesC8& aEncoding) + { + CArrayPtr* 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(); // Cleans up 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) + { + // This is the signed data + iResponse->iSignedData.Set(aEncoding); + + // version and responseExtensions are optional + CArrayPtr* items = DecodeSequenceLC(aEncoding, 3, 5); + + TInt seqIndex = 0; // index of currently-parsed item + TASN1DecGeneric& item0 = *items->At(0); + + // If a version number is supplied, it must be the default version, v1, + // which is encoded as an integer with value zero. (The version should + // not be sent at all if it has the default value - S11.5, ITU-Y X.690 + // (07/2002) ASN.1 encoding rules - but some servers can still send it.) + if (item0.Class() == EContextSpecific && item0.Tag() == KOcspResponseVersionTag) + { + TASN1DecGeneric vDecGen(item0.GetContentDER()); + vDecGen.InitL(); + if (!(vDecGen.Class() == EUniversal && vDecGen.Tag() == EASN1Integer)) + { + User::Leave(OCSP::EMalformedResponse); + } + + TASN1DecInteger vDecInt; + TInt version = vDecInt.DecodeDERShortL(vDecGen); + if (version != KOcspResponseVersion1) + { + User::Leave(OCSP::EMalformedResponse); + } + ++seqIndex; + } + + TASN1DecGeneric& respIdDecGen = *items->At(seqIndex++); + switch (respIdDecGen.Tag()) + { + case KResponderIDNameTag: + // Set to Name DER encoding + iResponse->iResponderIDName.Set(respIdDecGen.GetContentDER()); + break; + case KResponderIDKeyHashTag: + { + // Set to KeyHash to value within the octet string + TASN1DecGeneric keyHashDecGen(respIdDecGen.GetContentDER()); + keyHashDecGen.InitL(); + iResponse->iResponderIDKeyHash.Set(keyHashDecGen.GetContentDER()); + break; + } + default: + User::Leave(OCSP::EMalformedResponse); + } + + + // ProducedAt is a GeneralizedTime + TASN1DecGeneralizedTime decGT; + iResponse->iProducedAt = decGT.DecodeDERL(*items->At(seqIndex++)); + + // Now the responses themselves + DecodeResponsesL(items->At(seqIndex++)->Encoding()); + + // Continue if extensions exist + if (seqIndex < items->Count()) + { + // Check tag on responseExtensions + TASN1DecGeneric& extDecGen = *items->At(seqIndex++); + if (extDecGen.Tag() != KResponseExtensionsTag) + { + User::Leave(OCSP::EMalformedResponse); + } + + DecodeResponseExtensionsL(extDecGen.GetContentDER()); + } + CleanupStack::PopAndDestroy(items); + } + + +void COCSPResponseDecoder::DecodeResponseExtensionsL(const TDesC8& aEncoding) + { + CArrayPtr* 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* 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); // oidVal, items + } + + +void COCSPResponseDecoder::DecodeResponsesL(const TDesC8& aEncoding) + { + CArrayPtr* 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* 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(); // Clean up items + }