pkiutilities/ocsp/src/responsedecoder.cpp
author hgs
Thu, 30 Sep 2010 14:52:25 +0300
changeset 60 b99ca49838a8
parent 0 164170e6151a
permissions -rw-r--r--
201037_05

// 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
	}