⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 tsp.c

📁 提供了很多种加密算法和CA认证及相关服务如CMP、OCSP等的开发
💻 C
📖 第 1 页 / 共 2 页
字号:
/****************************************************************************
*																			*
*						 cryptlib TSP Session Management					*
*						Copyright Peter Gutmann 1999-2002					*
*																			*
****************************************************************************/

#include <stdlib.h>
#include <string.h>
#if defined( INC_ALL )
  #include "crypt.h"
  #include "asn1.h"
  #include "asn1objs.h"
  #include "asn1oid.h"
  #include "stream.h"
  #include "session.h"
#elif defined( INC_CHILD )
  #include "../crypt.h"
  #include "../keymgmt/asn1.h"
  #include "../keymgmt/asn1objs.h"
  #include "../keymgmt/asn1oid.h"
  #include "../keymgmt/stream.h"
  #include "../session/session.h"
#else
  #include "crypt.h"
  #include "keymgmt/asn1.h"
  #include "keymgmt/asn1objs.h"
  #include "keymgmt/asn1oid.h"
  #include "keymgmt/stream.h"
  #include "session/session.h"
#endif /* Compiler-specific includes */

/* The following macro can be used to enable dumping of PDUs to disk (as a
   safeguard, this only works in the Win32 debug version to prevent it from
   being accidentally enabled in any release version) */

#if defined( __WIN32__ ) && !defined( NDEBUG )
  #define DEBUG_DUMP( name, data, length ) \
	{ \
	FILE *filePtr; \
	char fileName[ 1024 ]; \
	\
	strcpy( fileName, "r:/tmp/" ); \
	strcat( fileName, name ); \
	strcat( fileName, ".der" ); \
	\
	filePtr = fopen( fileName, "wb" ); \
	if( filePtr != NULL ) \
		{ \
		fwrite( data, 1, length, filePtr ); \
		fclose( filePtr ); \
		} \
	}
#else
  #define DEBUG_DUMP( name, data, length )
#endif /* Win32 debug */

/* TSP constants */

#define TSP_PORT					318	/* Default port number */
#define TSP_VERSION					1	/* Version number */
#define TSP_HEADER_SIZE				5	/* 4-byte length + 1-byte type */
#define MIN_MSGIMPRINT_SIZE			20	/* Min.and max.size for message imprint */
#define MAX_MSGIMPRINT_SIZE			( 32 + CRYPT_MAX_HASHSIZE )

/* TSP socket protocol message types */

enum { TSP_MESSAGE_REQUEST, TSP_MESSAGE_POLLREP, TSP_MESSAGE_POLLREQ,
	   TSP_MESSAGE_NEGPOLLREP, TSP_MESSAGE_PARTIALMSGREP, TSP_MESSAGE_RESPONSE,
	   TSP_MESSAGE_ERROR };

/* Dummy policy OID for the TSA ('snooze policy, "Anything that arrives, we
   sign") */

#define OID_TSP_POLICY		MKOID( "\x06\x0B\x2B\x06\x01\x04\x01\x97\x55\x36\xDD\x24\x36" )

/* Information about a TSP request/response */

typedef struct {
	BYTE msgImprint[ MAX_MSGIMPRINT_SIZE ];
	int msgImprintSize;					/* Message imprint */
	BYTE nonce[ CRYPT_MAX_HASHSIZE ];
	int nonceSize;						/* Nonce (if present) */
	BOOLEAN includeSigCerts;			/* Whether to include signer certs */
	} TSPREQUEST_INFO;

/****************************************************************************
*																			*
*								Utility Functions							*
*																			*
****************************************************************************/

/* Read a TSP request */

static int readTSPRequest( STREAM *stream, TSPREQUEST_INFO *requestInfo )
	{
	BYTE *bufPtr;
	long value;
	int length, status;

	memset( requestInfo, 0, sizeof( TSPREQUEST_INFO ) );

	/* Read the request header and make sure everything is in order */
	readSequence( stream, NULL );
	status = readShortInteger( stream, &value );
	if( cryptStatusError( status ) || value != TSP_VERSION )
		return( CRYPT_ERROR_BADDATA );

	/* Read the message imprint.  We don't really care what this is so we
	   just treat it as a blob */
	bufPtr = sMemBufPtr( stream );
	status = readSequence( stream, &length );
	if( cryptStatusError( status ) || \
		length < MIN_MSGIMPRINT_SIZE || length > MAX_MSGIMPRINT_SIZE || \
		cryptStatusError( sSkip( stream, length ) ) )
		return( CRYPT_ERROR_BADDATA );
	length = ( int ) sizeofObject( length );
	memcpy( requestInfo->msgImprint, bufPtr, length );
	requestInfo->msgImprintSize = length;

	/* Check for the presence of the assorted optional fields */
	if( peekTag( stream ) == BER_OBJECT_IDENTIFIER )
		/* This could be anything since it's defined as "by prior agreement"
		   so we ignore it and give them whatever policy we happen to
		   implement, if they don't like it they're free to ignore it */
		readUniversal( stream );
	if( peekTag( stream ) == BER_INTEGER )
		{
		status = readRawObject( stream, requestInfo->nonce,
								&requestInfo->nonceSize, CRYPT_MAX_HASHSIZE,
								BER_INTEGER );
		if( cryptStatusError( status ) )
			return( status );
		}
	if( peekTag( stream ) == BER_BOOLEAN )
		{
		status = readBoolean( stream, &requestInfo->includeSigCerts );
		if( cryptStatusError( status ) )
			return( status );
		}
	if( peekTag( stream ) == MAKE_CTAG( 0 ) )
		/* The TSP RFC specifies a truly braindamaged interpretation of
		   extension handling, added at the last minute with no debate or
		   discussion.  This says that extensions are handled just like RFC 
		   2459 except when they're not.  In particular it requires that you
		   reject all extensions which you don't recognise, even if they
		   don't have the critical bit set (in violation of RFC 2459).  
		   Since "recognise" is never defined and the spec doesn't specify 
		   any particular extensions which must be handled (via MUST/SHALL/
		   SHOULD), any extension at all is regarded as unrecognised in the
		   context of the RFC.  For example if a request with a 
		   subjectAltName is submitted then although the TSA knows perfectly 
		   well what a subjectAltName, it has no idea what it's supposed to 
		   do with it when it sees it in the request.  Since the semantics of 
		   all extensions are unknown (in the context of the RFC), any 
		   request with extensions has to be rejected.

		   Along with assorted other confusing and often contradictory terms
		   added in the last-minute rewrite, cryptlib ignores this 
		   requirement and instead uses the common-sense interpretation of
		   allowing any extension which the RFC doesn't specifically provide
		   semantics for.  Since it doesn't provide semantics for any 
		   extension, we allow anything */
		readUniversal( stream );

	return( sGetStatus( stream ) );
	}

/* Write a timestamp token */

static int writeTSToken( STREAM *stream, const TSPREQUEST_INFO *requestInfo )
	{
	BYTE serialNo[ 16 ];

	getNonce( serialNo, 16 );
	writeSequence( stream, sizeofShortInteger( 1 ) + \
			sizeofOID( OID_TSP_POLICY ) + requestInfo->msgImprintSize + \
			sizeofInteger( serialNo, 16 ) + sizeofGeneralizedTime() + \
			requestInfo->nonceSize );
	writeShortInteger( stream, 1, DEFAULT_TAG );
	writeOID( stream, OID_TSP_POLICY );
	swrite( stream, requestInfo->msgImprint, requestInfo->msgImprintSize );
	writeInteger( stream, serialNo, 16, DEFAULT_TAG );
	writeGeneralizedTime( stream, time( NULL ), DEFAULT_TAG );
	if( requestInfo->nonceSize > 0 )
		swrite( stream, requestInfo->nonce, requestInfo->nonceSize );

	return( sGetStatus( stream ) );
	}

/* Sign a timestamp token */

static int signTSToken( BYTE *tsaResp, int *tsaRespLength,
						const int tsaRespMaxLength, const BYTE *tstInfo,
						const int tstInfoLength,
						const CRYPT_CONTEXT privateKey,
						const BOOLEAN includeCerts )
	{
	CRYPT_CERTIFICATE iCmsAttributes;
	MESSAGE_CREATEOBJECT_INFO createInfo;
	RESOURCE_DATA msgData;
	BYTE essCertID[ 1024 ], *essCertIDptr = essCertID;
	static const int minBufferSize = MIN_BUFFER_SIZE;
	static const int contentType = CRYPT_CONTENT_TSTINFO;
	int status;

	/* Create the signing attributes.  We don't have to set the content-type
	   attribute since it'll be set automatically based on the envelope
	   content type */
	setMessageCreateObjectInfo( &createInfo, CRYPT_CERTTYPE_CMS_ATTRIBUTES );
	status = krnlSendMessage( SYSTEM_OBJECT_HANDLE,
							  RESOURCE_IMESSAGE_DEV_CREATEOBJECT,
							  &createInfo, OBJECT_TYPE_CERTIFICATE );
	if( cryptStatusError( status ) )
		return( status );
	iCmsAttributes = createInfo.cryptHandle;
	setResourceData( &msgData, NULL, 0 );
	status = krnlSendMessage( privateKey, RESOURCE_IMESSAGE_GETATTRIBUTE_S,
							  &msgData, CRYPT_IATTRIBUTE_ESSCERTID );
	if( cryptStatusOK( status ) && msgData.length > 1024 && \
	    ( essCertIDptr = malloc( msgData.length ) ) == NULL )
		status = CRYPT_ERROR_MEMORY;
	if( cryptStatusOK( status ) )
		{
		msgData.data = essCertIDptr;
		status = krnlSendMessage( privateKey, RESOURCE_IMESSAGE_GETATTRIBUTE_S,
								  &msgData, CRYPT_IATTRIBUTE_ESSCERTID );
		}
	if( cryptStatusOK( status ) )
		status = krnlSendMessage( iCmsAttributes,
								  RESOURCE_IMESSAGE_SETATTRIBUTE_S, &msgData,
								  CRYPT_CERTINFO_CMS_SIGNINGCERT_ESSCERTID );
	if( essCertIDptr != essCertID && essCertIDptr != NULL )
		free( essCertIDptr );
	if( cryptStatusError( status ) )
		{
		krnlSendNotifier( iCmsAttributes, RESOURCE_IMESSAGE_DECREFCOUNT );
		return( status );
		}

	/* Create a cryptlib envelope to sign the data.  If we're not being
	   asked to include signer certs, we have to explicitly disable the
	   inclusion of certs in the signature since S/MIME includes them by
	   default */
	setMessageCreateObjectInfo( &createInfo, CRYPT_FORMAT_CMS );
	status = krnlSendMessage( SYSTEM_OBJECT_HANDLE,
							  RESOURCE_IMESSAGE_DEV_CREATEOBJECT,
							  &createInfo, OBJECT_TYPE_ENVELOPE );
	if( cryptStatusError( status ) )
		{
		krnlSendNotifier( iCmsAttributes, RESOURCE_IMESSAGE_DECREFCOUNT );
		return( status );
		}
	status = krnlSendMessage( createInfo.cryptHandle,
						RESOURCE_IMESSAGE_SETATTRIBUTE,
						( int * ) &minBufferSize, CRYPT_ATTRIBUTE_BUFFERSIZE );
	if( cryptStatusOK( status ) )
		status = krnlSendMessage( createInfo.cryptHandle,
						RESOURCE_IMESSAGE_SETATTRIBUTE,
						( int * ) &tstInfoLength, CRYPT_ENVINFO_DATASIZE );
	if( cryptStatusOK( status ) )
		status = krnlSendMessage( createInfo.cryptHandle,
						RESOURCE_IMESSAGE_SETATTRIBUTE,
						( int * ) &contentType, CRYPT_ENVINFO_CONTENTTYPE );
	if( cryptStatusOK( status ) )
		status = krnlSendMessage( createInfo.cryptHandle,
						RESOURCE_IMESSAGE_SETATTRIBUTE,
						( void * ) &privateKey, CRYPT_ENVINFO_SIGNATURE );
	if( cryptStatusOK( status ) )
		status = krnlSendMessage( createInfo.cryptHandle,
						RESOURCE_IMESSAGE_SETATTRIBUTE,
						&iCmsAttributes, CRYPT_ENVINFO_SIGNATURE_EXTRADATA );
	if( cryptStatusOK( status ) && !includeCerts )
		status = krnlSendMessage( createInfo.cryptHandle,
						RESOURCE_IMESSAGE_SETATTRIBUTE,
						MESSAGE_VALUE_FALSE, CRYPT_IATTRIBUTE_INCLUDESIGCERT );
	krnlSendNotifier( iCmsAttributes, RESOURCE_IMESSAGE_DECREFCOUNT );
	if( cryptStatusError( status ) )
		{
		krnlSendNotifier( createInfo.cryptHandle,
						  RESOURCE_IMESSAGE_DECREFCOUNT );
		return( status );
		}

	/* Push in the data and pop the signed result */
	setResourceData( &msgData, ( void * ) tstInfo, tstInfoLength );
	status = krnlSendMessage( createInfo.cryptHandle,
					RESOURCE_IMESSAGE_ENV_PUSHDATA, &msgData, 0 );
	if( cryptStatusOK( status ) )
		{
		setResourceData( &msgData, NULL, 0 );
		status = krnlSendMessage( createInfo.cryptHandle,
					RESOURCE_IMESSAGE_ENV_PUSHDATA, &msgData, 0 );
		}
	if( cryptStatusOK( status ) )
		{
		setResourceData( &msgData, tsaResp, tsaRespMaxLength );
		status = krnlSendMessage( createInfo.cryptHandle,
					RESOURCE_IMESSAGE_ENV_POPDATA, &msgData, 0 );
		*tsaRespLength = msgData.length;
		}
	krnlSendNotifier( createInfo.cryptHandle, RESOURCE_IMESSAGE_DECREFCOUNT );
	DEBUG_DUMP( "tsa_token", tsaResp, *tsaRespLength );

	return( status );
	}

/****************************************************************************
*																			*
*							Client-side Functions							*
*																			*
****************************************************************************/

/* Exchange a datagram with a TSP server */

static int exchangeClientDatagram( STREAM *stream, void *buffer,
								   const int bufSize, const int dataOutSize,
								   const BOOLEAN useHTTP )
	{
	BYTE *bufPtr = buffer;
	long length;
	int status;

	/* If it's an HTTP transaction, most of the work is handled by the
	   stream-level code and we just write the request and read back the
	   response */
	if( useHTTP )
		{
		/* Send the request to the server */
		sioctl( stream, STREAM_IOCTL_CONTENTTYPE,
				"application/timestamp-query", 27 );
		status = swrite( stream, buffer, dataOutSize );
		if( cryptStatusError( status ) )
			return( status );

		/* Read the response from the responder, converting the sread() byte
		   count into a CRYPT_OK */
		status = sread( stream, buffer, bufSize );
		return( cryptStatusError( status ) ? status : CRYPT_OK );
		}

	/* We're using the socket protocol, add the TSP header:
		uint32		length of type + data
		byte		type
		byte[]		data */
	memmove( bufPtr + TSP_HEADER_SIZE, bufPtr, dataOutSize );
	mputBLong( bufPtr, dataOutSize + 1 );
	*bufPtr = TSP_MESSAGE_REQUEST;
	status = swrite( stream, buffer, dataOutSize + TSP_HEADER_SIZE );
	if( cryptStatusError( status ) )
		return( status );

	/* Read back the header and make sure it's is in order.  The check for
	   a response labelled as a request is necessary because some buggy
	   implementations use the request message type for any normal
	   communication */
	status = sread( stream, buffer, TSP_HEADER_SIZE );
	if( cryptStatusError( status ) )
		return( status );
	bufPtr = buffer;
	length = mgetBLong( bufPtr );
	if( length < 16 || length > bufSize || \
		( *bufPtr != TSP_MESSAGE_REQUEST && \
		  *bufPtr != TSP_MESSAGE_RESPONSE ) )
		return( CRYPT_ERROR_BADDATA );

	/* Read the response data from the responder, converting the sread()
	   byte count into a CRYPT_OK */
	status = sread( stream, buffer, length - 1 );
	return( cryptStatusError( status ) ? status : CRYPT_OK );
	}

/****************************************************************************
*																			*
*							Server-side Functions							*
*																			*
****************************************************************************/

/* Send an error response back to the client.  Since there are only a small
   number of these, we write back a fixed blob rather than encoding each

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -