/*
 * Copyright (c) 2004 Apple Computer, Inc. All Rights Reserved.
 * 
 * @APPLE_LICENSE_HEADER_START@
 * 
 * This file contains Original Code and/or Modifications of Original Code
 * as defined in and that are subject to the Apple Public Source License
 * Version 2.0 (the 'License'). You may not use this file except in
 * compliance with the License. Please obtain a copy of the License at
 * http://www.opensource.apple.com/apsl/ and read it before using this
 * file.
 * 
 * The Original Code and all software distributed under the License are
 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
 * Please see the License for the specific language governing rights and
 * limitations under the License.
 * 
 * @APPLE_LICENSE_HEADER_END@
 */

/*
 * ocspResponse.h - OCSP Response class
 */
 
#ifndef	_OCSP_RESPONSE_H_
#define _OCSP_RESPONSE_H_

#include <security_ocspd/ocspExtensions.h>
#include <Security/ocspTemplates.h>
#include <Security/certextensions.h>
#include <Security/SecAsn1Coder.h>
#include <CoreFoundation/CoreFoundation.h>

/* used to indicate "I don't know the CRLReason" */
#define CrlReason_NONE		((CE_CrlReason)-1)

/*
 * CertIDs can be represented differently by two peers even though they refer to 
 * the same cert. Client can use SHA1 hash and server can use MD5, for example. 
 * So all of our code which creates a CertID based on known, existing subject and
 * issuer certs uses one of these "smart" certIDs which can encode itself and also 
 * compare against any form of existing SecAsn1OCSPCertID.
 */
class OCSPClientCertID
{
	NOCOPY(OCSPClientCertID);
public:
	/*
	 * Basic constructor given issuer's public key and name, and subject's
	 * serial number.
	 */
	OCSPClientCertID(
		const CSSM_DATA			&issuerName,
		const CSSM_DATA			&issuerPubKey,
		const CSSM_DATA			&subjectSerial);
		
	~OCSPClientCertID();
	
	/*
	 * DER encode.
	 */
	const CSSM_DATA *encode();
		
	/*
	 * Does this object refer to the same cert as specified SecAsn1OCSPCertID?
	 * This is the main purpose of this class's existence; this function works 
	 * even if specified SecAsn1OCSPCertID uses a different hash algorithm
	 * than we do, since we keep copies of our basic components. 
	 *
	 * Returns true if compare successful.
	 */
	bool compareToExist(
		const SecAsn1OCSPCertID	&exist);
		
	/* 
	 * Convenience function, like compareToExist, with a raw encoded CertID.
	 */
	bool compareToExist( 
		const CSSM_DATA	&exist);
		
private:
	CSSM_DATA mIssuerName;
	CSSM_DATA mIssuerPubKey;
	CSSM_DATA mSubjectSerial;
	CSSM_DATA mEncoded;
};

/*
 * Object representing one SecAsn1OCSPSingleResponse, i.e., the portion of 
 * an OCSP response associated with a single CertID. These are created and
 * vended solely by an OCSPResponse object. The client which gets them from
 * an OCSPResponse (via singleResponse()) must delete the object when finished
 * with it. 
 */
class OCSPSingleResponse
{
	NOCOPY(OCSPSingleResponse);
public:
	/* only OCSPResponse creates these */
	~OCSPSingleResponse();
	friend class OCSPResponse;
protected:
	
	OCSPSingleResponse(
		SecAsn1OCSPSingleResponse	*resp);
public:
		SecAsn1OCSPCertStatusTag	certStatus()	{ return mCertStatus; }
		CFAbsoluteTime				thisUpdate()	{ return mThisUpdate; }
		CFAbsoluteTime				nextUpdate()	{ return mNextUpdate; }
		CFAbsoluteTime				revokedTime()	{ return mRevokedTime; }
		CE_CrlReason				crlReason()		{ return mCrlReason; }
		
		/* Extension accessors - all are optional */
		
		/* CRL Reference */
		const CSSM_DATA				*crlUrl();
		const CSSM_DATA				*crlNum();
		CFAbsoluteTime				crlTime();			/* may be NULL_TIME */
		
		/* archive cutoff */
		CFAbsoluteTime				archiveCutoff();
		
		/* service locator not implemented yet */
private:
		SecAsn1CoderRef				mCoder;
		SecAsn1OCSPCertStatusTag	mCertStatus;
		CFAbsoluteTime				mThisUpdate;
		CFAbsoluteTime				mNextUpdate;		/* may be NULL_TIME */
		CFAbsoluteTime				mRevokedTime;		/* != NULL_TIME for CS_Revoked */
		CE_CrlReason				mCrlReason;		
		OCSPExtensions				*mExtensions;
};

/*
 * OCSPResponse maintains its own temporal validity status based on the values of 
 * all of the enclosed SingleResponses' thisUpdate and (optional) nextUpdate
 * fields, in addition to a default time-to-live (TTL) value passed to
 * OCSPResponse's constructor.
 *
 * First, all of the thisUpdate fields are checked during OCSPResponse's constructor. 
 * if any of these are later than the current time, the entire response is considered
 * invalid and the constructor throws a CssmError(CSSMERR_APPLETP_OCSP_BAD_RESPONSE). 
 * Subsequent to construction, all thisUpdate fields are ignored. 
 *
 * The NextUpdate times are handled as follows. 
 *
 * 1. An OCSPResponse's latestNextUpdate is defined as the latest of all of the 
 *    nextUpdate fields in its SingleResponses. This is evaluated during construction. 
 *
 * 2. An OCSPResponse's latestNextUpdate is NULL_TIME if none of its SingleResponses 
 *    contain any nextUpdate (this field is in fact optional). 
 *
 * 3. The caller of OCSPResponse's constructor passes in a default time-to-live 
 *    (TTL) in seconds; call this defaultTTL. Call the time at which the 
 *    constructor is called, PLUS defaultTTL, "defaultExpire".
 * 
 * -- If the OCSPResponse's latestNextUpdate is NULL_TIME then expireTime() returns
 *    defaultExpire.
 *
 * -- Otherwise, expireTime() returns the lesser of (latestNextUpdate, 
 *    defaultExpire).
 *
 * Note that this mechanism is used by both the TP's in-core cache and ocspd's
 * on-disk cache; the two have different default TTLs values but the mechanism
 * for calcuating expireTime() is identical. 
 */
class OCSPResponse
{
	NOCOPY(OCSPResponse)
public:
	/* only constructor, from DER encoded data */
	OCSPResponse(
		const CSSM_DATA &resp,
		CFTimeInterval defaultTTL);		// default time-to-live in seconds
		
	~OCSPResponse();
	
	/* 
	 * Info obtained during decode (which is done immediately during constructor) 
	 */
	SecAsn1OCSPResponseStatus	responseStatus();
	const CSSM_DATA				*nonce();			/* NULL means not present */
	CFAbsoluteTime				producedAt();		/* should always work */
	CSSM_RETURN					sigStatus();
	uint32						numSignerCerts();
	const CSSM_DATA				*signerCert(uint32 dex);
	
	/* 
	 * Obtain a OCSPSingleResponse for a given CertID. 
	 */
	OCSPSingleResponse			*singleResponseFor(OCSPClientCertID &certID);
	OCSPSingleResponse			*singleResponseFor(const CSSM_DATA &matchCertID);
	
	CFAbsoluteTime				expireTime()		{ return mExpireTime; }

	/*
	 * Access to decoded data.
	 */
	const SecAsn1OCSPResponseData	&responseData()		{ return mResponseData; }
	const SecAsn1OCSPBasicResponse	&basicResponse()	{ return mBasicResponse; }
	const SecAsn1OCSPResponderID	&responderID()		{ return mResponderId; }
	SecAsn1OCSPResponderIDTag		responderIDTag()	{ return mResponderIdTag; }

	const CSSM_DATA					*encResponderName();
	
private:
	bool						calculateValidity(CFTimeInterval defaultTTL);
	
	SecAsn1CoderRef				mCoder;
	CFAbsoluteTime				mLatestNextUpdate;
	CFAbsoluteTime				mExpireTime;
	CSSM_DATA					mEncResponderName;	// encoded ResponderId.byName, 
													// if responder is in that format,
													// lazily evaluated
	/* 
	 * Fields we decode - all in mCoder's memory space 
	 */
	SecAsn1OCSPResponse			mTopResp;
	SecAsn1OCSPBasicResponse	mBasicResponse;
	SecAsn1OCSPResponseData		mResponseData;
	SecAsn1OCSPResponderID		mResponderId;		// we have to decode
	SecAsn1OCSPResponderIDTag	mResponderIdTag;	// IDs previous field
	OCSPExtensions				*mExtensions;
};	
#endif	/* _OCSP_RESPONSE_H_ */

