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

📄 pgp_env.c

📁 cryptlib安全工具包
💻 C
📖 第 1 页 / 共 2 页
字号:
/****************************************************************************
*																			*
*					 cryptlib PGP Enveloping Routines						*
*					 Copyright Peter Gutmann 1996-2008						*
*																			*
****************************************************************************/

#if defined( INC_ALL )
  #include "envelope.h"
  #include "pgp_rw.h"
#else
  #include "envelope/envelope.h"
  #include "misc/pgp_rw.h"
#endif /* Compiler-specific includes */

#ifdef USE_PGP

/****************************************************************************
*																			*
*								Utility Routines							*
*																			*
****************************************************************************/

/* Sanity-check the envelope state */

CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
static BOOLEAN sanityCheck( const ENVELOPE_INFO *envelopeInfoPtr )
	{
	assert( isReadPtr( envelopeInfoPtr, sizeof( ENVELOPE_INFO ) ) );

	/* Make sure that general envelope state is in order */
	if( envelopeInfoPtr->flags & ENVELOPE_ISDEENVELOPE )
		return( FALSE );
	if( envelopeInfoPtr->envState < ENVSTATE_NONE || \
		envelopeInfoPtr->envState >= ENVSTATE_LAST )
		return( FALSE );

	/* Make sure that the buffer position is within bounds */
	if( envelopeInfoPtr->buffer == NULL || \
		envelopeInfoPtr->bufPos < 0 || \
		envelopeInfoPtr->bufPos > envelopeInfoPtr->bufSize || \
		envelopeInfoPtr->bufSize < MIN_BUFFER_SIZE || \
		envelopeInfoPtr->bufSize >= MAX_INTLENGTH )
		return( FALSE );

	/* If the auxBuffer isn't being used, make sure that all values related 
	   to it are clear */
	if( envelopeInfoPtr->auxBuffer != NULL || \
		envelopeInfoPtr->auxBufPos != 0 || envelopeInfoPtr->auxBufSize != 0 )
		return( FALSE );

	return( TRUE );
	}

/* Check that a requested algorithm type is valid with PGP data */

CHECK_RETVAL_BOOL \
BOOLEAN pgpCheckAlgo( IN_ALGO const CRYPT_ALGO_TYPE cryptAlgo, 
					  IN_MODE_OPT const CRYPT_MODE_TYPE cryptMode )
	{
	int dummy;

	REQUIRES_B( cryptAlgo > CRYPT_ALGO_NONE && \
				cryptAlgo < CRYPT_ALGO_LAST );
	REQUIRES_B( ( cryptMode == CRYPT_MODE_NONE ) || \
				( cryptMode > CRYPT_MODE_NONE && \
				  cryptMode < CRYPT_MODE_LAST ) );

	if( cryptStatusError( cryptlibToPgpAlgo( cryptAlgo, &dummy ) ) )
		return( FALSE );
	if( cryptAlgo >= CRYPT_ALGO_FIRST_CONVENTIONAL && \
		cryptAlgo <= CRYPT_ALGO_LAST_CONVENTIONAL )
		{
		if( cryptMode != CRYPT_MODE_CFB )
			return( FALSE );
		}
	else
		{
		if( cryptMode != CRYPT_MODE_NONE )
			return( FALSE );
		}

	return( TRUE );
	}

/****************************************************************************
*																			*
*						Write Key Exchange/Signature Packets				*
*																			*
****************************************************************************/

/* One-pass signature info:

	byte	version = 3
	byte	sigType
	byte	hashAlgo
	byte	sigAlgo
	byte[8]	keyID
	byte	1 

   This is additional header data written at the start of a block of signed
   data rather than a standard PGP packet so we can't write it using the 
   normal PGP packet read/write routines */

CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
static int writeSignatureInfoPacket( INOUT STREAM *stream, 
									 IN_HANDLE const CRYPT_CONTEXT iSignContext,
									 IN_HANDLE const CRYPT_CONTEXT iHashContext )
	{
	CRYPT_ALGO_TYPE hashAlgo, signAlgo = DUMMY_INIT;
	BYTE keyID[ PGP_KEYID_SIZE + 8 ];
	int pgpHashAlgo, pgpCryptAlgo = DUMMY_INIT, status;

	assert( isWritePtr( stream, sizeof( STREAM ) ) );

	REQUIRES( isHandleRangeValid( iSignContext ) );
	REQUIRES( isHandleRangeValid( iHashContext ) );

	/* Get the signature information */
	status = krnlSendMessage( iHashContext, IMESSAGE_GETATTRIBUTE, 
							  &hashAlgo, CRYPT_CTXINFO_ALGO );
	if( cryptStatusOK( status ) )
		status = krnlSendMessage( iSignContext, IMESSAGE_GETATTRIBUTE, 
								  &signAlgo, CRYPT_CTXINFO_ALGO );
	if( cryptStatusOK( status ) )
		{
		MESSAGE_DATA msgData;

		setMessageData( &msgData, keyID, PGP_KEYID_SIZE );
		status = krnlSendMessage( iSignContext, IMESSAGE_GETATTRIBUTE_S, 
								  &msgData, CRYPT_IATTRIBUTE_KEYID_OPENPGP );
		}
	if( cryptStatusError( status ) )
		return( status );
	status = cryptlibToPgpAlgo( hashAlgo, &pgpHashAlgo );
	if( cryptStatusOK( status ) )
		status = cryptlibToPgpAlgo( signAlgo, &pgpCryptAlgo );
	ENSURES( cryptStatusOK( status ) );

	/* Write the signature info packet.  Note that the version 3 value is 
	   normally used to identify a legal-kludged PGP 2.0 but in this case it 
	   denotes OpenPGP, which usually has the version 4 value rather than 3 */
	pgpWritePacketHeader( stream, PGP_PACKET_SIGNATURE_ONEPASS, \
						  PGP_VERSION_SIZE + 1 + PGP_ALGOID_SIZE + \
							PGP_ALGOID_SIZE + PGP_KEYID_SIZE + 1 );
	sputc( stream, 3 );		/* Version = 3 (OpenPGP) */
	sputc( stream, 0 );		/* Binary document signature */
	sputc( stream, pgpHashAlgo );
	sputc( stream, pgpCryptAlgo );
	swrite( stream, keyID, PGP_KEYID_SIZE );
	return( sputc( stream, 1 ) );
	}

/****************************************************************************
*																			*
*								Write Header Packets						*
*																			*
****************************************************************************/

/* Write the data header packet */

CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
static int writeHeaderPacket( INOUT ENVELOPE_INFO *envelopeInfoPtr )
	{
	STREAM stream;
	int status = CRYPT_OK;

	assert( isWritePtr( envelopeInfoPtr, sizeof( ENVELOPE_INFO ) ) );

	/* If we're encrypting, set up the encryption-related information.  
	   Since PGP doesn't perform a key exchange of a session key when 
	   conventionally-encrypting data the encryption information could be 
	   coming from either an encryption action (derived from a password) or 
	   a conventional key exchange action that results in the direct 
	   creation of a session encryption key */
	if( envelopeInfoPtr->usage == ACTION_CRYPT )
		{
		status = initEnvelopeEncryption( envelopeInfoPtr,
								envelopeInfoPtr->actionList->iCryptHandle, 
								CRYPT_ALGO_NONE, CRYPT_MODE_NONE, NULL, 0, 
								FALSE );
		if( cryptStatusError( status ) )
			return( status );

		/* Prepare to start emitting the key exchange (PKC-encrypted) or 
		   session key (conventionally encrypted) actions */
		envelopeInfoPtr->lastAction = \
							findAction( envelopeInfoPtr->preActionList,
										ACTION_KEYEXCHANGE_PKC );
		if( envelopeInfoPtr->lastAction == NULL )
			{
			/* There's no key exchange action, we're using a raw session key 
			   derived from a password */
			envelopeInfoPtr->lastAction = envelopeInfoPtr->actionList;
			}
		envelopeInfoPtr->envState = ENVSTATE_KEYINFO;

		ENSURES( envelopeInfoPtr->lastAction != NULL );

		return( CRYPT_OK );
		}

	/* If we're not encrypting data (i.e. there's only a single packet 
	   present rather than a packet preceded by a pile of key exchange 
	   actions) we write the appropriate PGP header based on the envelope 
	   usage */
	sMemOpen( &stream, envelopeInfoPtr->buffer, envelopeInfoPtr->bufSize );
	switch( envelopeInfoPtr->usage )
		{
		case ACTION_SIGN:
			if( !( envelopeInfoPtr->flags & ENVELOPE_DETACHED_SIG ) )
				{
				status = writeSignatureInfoPacket( &stream, 
								envelopeInfoPtr->postActionList->iCryptHandle,
								envelopeInfoPtr->actionList->iCryptHandle );
				if( cryptStatusError( status ) )
					break;
				}

			/* Since we can only sign literal data we need to explicitly 
			   write an inner data header */
			REQUIRES( envelopeInfoPtr->contentType == CRYPT_CONTENT_DATA );
			envelopeInfoPtr->envState = ENVSTATE_DATA;
			break;

		case ACTION_NONE:
			/* Write the header followed by an indicator that we're using 
			   opaque content, a zero-length filename, and no date */
			pgpWritePacketHeader( &stream, PGP_PACKET_DATA, 
								  envelopeInfoPtr->payloadSize + \
									PGP_DATA_HEADER_SIZE );
			status = swrite( &stream, PGP_DATA_HEADER, PGP_DATA_HEADER_SIZE );
			break;

		case ACTION_COMPRESS:
			/* Compressed data packets use a special unkown-length encoding 
			   that doesn't work like any other PGP packet type so we can't 
			   use pgpWritePacketHeader() for this packet type but have to 
			   hand-assemble the header ourselves */
			sputc( &stream, PGP_CTB_COMPRESSED );
			status = sputc( &stream, PGP_ALGO_ZLIB );
			if( cryptStatusError( status ) )
				break;
			if( envelopeInfoPtr->contentType == CRYPT_CONTENT_DATA )
				{
				/* If there's no inner content type we need to explicitly 
				   write an inner data header */
				envelopeInfoPtr->envState = ENVSTATE_DATA;
				}
			break;
	
		default:
			retIntError();
		}
	if( cryptStatusOK( status ) )
		envelopeInfoPtr->bufPos = stell( &stream );
	sMemDisconnect( &stream );
	if( cryptStatusError( status ) )
		return( status );

	/* Reset the segmentation state.  Although PGP doesn't segment the 
	   payload we still have to reset the state to synchronise things like 
	   payload hashing and encryption.  We also set the block size mask to 
	   all ones if we're not encrypting since we can begin and end data 
	   segments on arbitrary boundaries */
	envelopeInfoPtr->dataFlags |= ENVDATA_SEGMENTCOMPLETE;
	if( envelopeInfoPtr->usage != ACTION_CRYPT )
		envelopeInfoPtr->blockSizeMask = -1;
	envelopeInfoPtr->lastAction = NULL;

	/* If we're not emitting any inner header, we're done */
	if( envelopeInfoPtr->envState == ENVSTATE_HEADER || \
		( envelopeInfoPtr->flags & ENVELOPE_DETACHED_SIG ) )
		envelopeInfoPtr->envState = ENVSTATE_DONE;

	return( CRYPT_OK );
	}

/****************************************************************************
*																			*
*						Header/Trailer Processing Routines					*
*																			*
****************************************************************************/

/* Write key exchange actions */

CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
static int writeKeyex( INOUT ENVELOPE_INFO *envelopeInfoPtr )
	{
	ACTION_LIST *lastActionPtr;
	int iterationCount, status = CRYPT_OK;

	assert( isWritePtr( envelopeInfoPtr, sizeof( ENVELOPE_INFO ) ) );

	/* Export the session key using each of the PKC keys, or write the 
	   derivation information needed to recreate the session key */
	for( lastActionPtr = envelopeInfoPtr->lastAction, iterationCount = 0; 
		 lastActionPtr != NULL && iterationCount < FAILSAFE_ITERATIONS_MED;
		 lastActionPtr = lastActionPtr->next, iterationCount++ )
		{
		void *bufPtr = envelopeInfoPtr->buffer + envelopeInfoPtr->bufPos;
		const int dataLeft = min( envelopeInfoPtr->bufSize - \
									envelopeInfoPtr->bufPos, 
								  MAX_INTLENGTH_SHORT - 1 );
		int keyexSize;

		/* Make sure that there's enough room to emit this key exchange 
		   action */
		if( lastActionPtr->encodedSize + 128 > dataLeft )
			{
			status = CRYPT_ERROR_OVERFLOW;
			break;
			}

		/* Emit the key exchange action */
		if( lastActionPtr->action == ACTION_KEYEXCHANGE_PKC )
			{
			status = iCryptExportKey( bufPtr, dataLeft, &keyexSize, 
									  CRYPT_FORMAT_PGP, 
									  envelopeInfoPtr->iCryptContext,
									  lastActionPtr->iCryptHandle );
			}
		else
			{
			status = iCryptExportKey( bufPtr, dataLeft, &keyexSize, 
									  CRYPT_FORMAT_PGP, CRYPT_UNUSED, 
									  envelopeInfoPtr->iCryptContext );
			}
		if( cryptStatusError( status ) )
			break;
		envelopeInfoPtr->bufPos += keyexSize;
		}
	ENSURES( iterationCount < FAILSAFE_ITERATIONS_MED )
	envelopeInfoPtr->lastAction = lastActionPtr;

	return( status );
	}

CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
static int writeEncryptedContentHeader( INOUT ENVELOPE_INFO *envelopeInfoPtr )
	{
	STREAM stream;
	BYTE ivInfoBuffer[ ( CRYPT_MAX_IVSIZE + 2 ) + 8 ];
	const int dataLeft = min( envelopeInfoPtr->bufSize - \
								envelopeInfoPtr->bufPos, 
							  MAX_INTLENGTH_SHORT - 1 );
	int ivSize, status;

	assert( isWritePtr( envelopeInfoPtr, sizeof( ENVELOPE_INFO ) ) );

	/* Get the IV size and make sure that there's enough room to emit the 
	   encrypted content header (+8 for slop space) */
	status = krnlSendMessage( envelopeInfoPtr->iCryptContext,
							  IMESSAGE_GETATTRIBUTE, &ivSize, 
							  CRYPT_CTXINFO_IVSIZE );
	if( cryptStatusError( status ) )
		return( status );
	if( dataLeft < PGP_MAX_HEADER_SIZE + ( ivSize + 2 ) + 8 )
		return( CRYPT_ERROR_OVERFLOW );

	/* Set up the PGP IV information */
	status = pgpProcessIV( envelopeInfoPtr->iCryptContext, 
						   ivInfoBuffer, ivSize + 2, ivSize, TRUE, TRUE );
	if( cryptStatusError( status ) )
		return( status );

	/* Write the encrypted content header */
	sMemOpen( &stream, envelopeInfoPtr->buffer + envelopeInfoPtr->bufPos, 
			  dataLeft );
	pgpWritePacketHeader( &stream, PGP_PACKET_ENCR, 
						  ( ivSize + 2 ) + 1 + \
						  pgpSizeofLength( PGP_DATA_HEADER_SIZE + \
										   envelopeInfoPtr->payloadSize ) + \
						  PGP_DATA_HEADER_SIZE + \
						  envelopeInfoPtr->payloadSize );
	status = swrite( &stream, ivInfoBuffer, ivSize + 2 );
	if( cryptStatusOK( status ) )
		envelopeInfoPtr->bufPos += stell( &stream );
	sMemDisconnect( &stream );

	return( status );
	}

/****************************************************************************
*																			*
*					Envelope Pre/Post-processing Functions					*
*																			*
****************************************************************************/

/* The following functions take care of pre/post-processing of envelope data
   during the enveloping process */

CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
static int createSessionKey( INOUT ENVELOPE_INFO *envelopeInfoPtr )
	{
	CRYPT_CONTEXT iSessionKeyContext;
	MESSAGE_CREATEOBJECT_INFO createInfo;
	static const CRYPT_MODE_TYPE mode = CRYPT_MODE_CFB;
	int status;

	assert( isWritePtr( envelopeInfoPtr, sizeof( ENVELOPE_INFO ) ) );

	/* Create a default encryption action */
	setMessageCreateObjectInfo( &createInfo, envelopeInfoPtr->defaultAlgo );
	status = krnlSendMessage( SYSTEM_OBJECT_HANDLE, IMESSAGE_DEV_CREATEOBJECT,
							  &createInfo, OBJECT_TYPE_CONTEXT );
	if( cryptStatusError( status ) )
		return( status );
	iSessionKeyContext = createInfo.cryptHandle;
	if( envelopeInfoPtr->defaultAlgo == CRYPT_ALGO_BLOWFISH )
		{
		static const int keySize = 16;

		/* If we're using an algorithm with a variable-length key, restrict 
		   it to a fixed length.  There shouldn't be any need for this 
		   because the key length is communicated as part of the wrapped 
		   key, but some implementations choke if it's not exactly 128 bits */
		status = krnlSendMessage( iSessionKeyContext, IMESSAGE_SETATTRIBUTE, 
								  ( void * ) &keySize, 
								  CRYPT_CTXINFO_KEYSIZE );
		if( cryptStatusError( status ) )
			{
			krnlSendNotifier( iSessionKeyContext, IMESSAGE_DECREFCOUNT );
			return( status );
			}
		}
	status = krnlSendMessage( iSessionKeyContext, IMESSAGE_SETATTRIBUTE, 
							  ( void * ) &mode, CRYPT_CTXINFO_MODE );
	if( cryptStatusOK( status ) )
		status = krnlSendNotifier( iSessionKeyContext, IMESSAGE_CTX_GENKEY );

⌨️ 快捷键说明

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