pkiutilities/ocsp/src/responsedecoder.cpp
changeset 0 164170e6151a
--- /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 <ocsp.h>
+#include <asn1dec.h>
+#include <x509cert.h>
+#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<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); // 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(); // 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<TASN1DecGeneric>* 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<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); // 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(); // Clean up items
+	}