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

📄 cryptkey.c

📁 cryptlib安全工具包
💻 C
📖 第 1 页 / 共 3 页
字号:
/****************************************************************************
*																			*
*							cryptlib Keyset Routines						*
*						Copyright Peter Gutmann 1995-2007					*
*																			*
****************************************************************************/

#include <stdio.h>
#include <stdarg.h>
#include "crypt.h"
#ifdef INC_ALL
  #include "keyset.h"
  #include "asn1.h"
  #include "asn1_ext.h"
  #include "pgp_rw.h"
#else
  #include "keyset/keyset.h"
  #include "misc/asn1.h"
  #include "misc/asn1_ext.h"
  #include "misc/pgp_rw.h"
#endif /* Compiler-specific includes */

#ifdef USE_KEYSETS

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

/* Clear the extended error information that may be present from a previous
   operation prior to beginning a new operation */

STDC_NONNULL_ARG( ( 1 ) ) \
static void resetErrorInfo( INOUT KEYSET_INFO *keysetInfoPtr )
	{
#ifdef KEYSET_HAS_ERRORINFO
	ERROR_INFO *errorInfo = &keysetInfoPtr->errorInfo;

	assert( isWritePtr( keysetInfoPtr, sizeof( KEYSET_INFO ) ) );

	memset( errorInfo, 0, sizeof( ERROR_INFO ) );
#endif /* KEYSET_HAS_ERRORINFO */
	}

/* Prepare to update a keyset, performing various access checks and pre-
   processing of information */

typedef struct {
	CRYPT_KEYID_TYPE keyIDtype;		/* KeyID type */
	BUFFER_FIXED( keyIDlength ) \
	const void *keyID;				/* KeyID value */
	int keyIDlength;
	} KEYID_INFO;

CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
static int initKeysetUpdate( INOUT KEYSET_INFO *keysetInfoPtr, 
							 INOUT_OPT KEYID_INFO *keyIDinfo, 
							 OUT_BUFFER_OPT_FIXED( keyIdMaxLength ) \
								void *keyIDbuffer,
							 IN_LENGTH_SHORT_Z const int keyIdMaxLength,
							 const BOOLEAN isRead )
	{
	assert( isWritePtr( keysetInfoPtr, sizeof( KEYSET_INFO ) ) );
	assert( ( keyIDinfo == NULL && \
			  keyIDbuffer == NULL && keyIdMaxLength == 0 ) || \
			( isWritePtr( keyIDinfo, sizeof( KEYID_INFO ) ) && \
			  isReadPtr( keyIDbuffer, keyIdMaxLength ) ) );

	REQUIRES( ( keyIDinfo == NULL && \
				keyIDbuffer == NULL && keyIdMaxLength == 0 ) || \
			  ( keyIDinfo != NULL && \
				keyIDbuffer != NULL && keyIdMaxLength == KEYID_SIZE ) );

	/* If we're in the middle of a query we can't do anything else */
	if( keysetInfoPtr->isBusyFunction != NULL && \
		keysetInfoPtr->isBusyFunction( keysetInfoPtr ) )
		return( CRYPT_ERROR_INCOMPLETE );

	/* If we've been passed a full issuerAndSerialNumber as a key ID and the 
	   keyset needs an issuerID, convert it */
	if( keyIDinfo != NULL && \
		keyIDinfo->keyIDtype == CRYPT_IKEYID_ISSUERANDSERIALNUMBER && \
		( keysetInfoPtr->type == KEYSET_DBMS || \
		  ( keysetInfoPtr->type == KEYSET_FILE && \
		    keysetInfoPtr->subType == KEYSET_SUBTYPE_PKCS15 ) ) )
		{
		HASHFUNCTION_ATOMIC hashFunctionAtomic;
		int hashSize;

		/* Get the hash algorithm information */
		getHashAtomicParameters( CRYPT_ALGO_SHA1, &hashFunctionAtomic, 
								 &hashSize );

		/* Hash the full iAndS to get an issuerID and use that for the keyID */
		hashFunctionAtomic( keyIDbuffer, keyIdMaxLength, keyIDinfo->keyID, 
							keyIDinfo->keyIDlength );
		keyIDinfo->keyIDtype = CRYPT_IKEYID_ISSUERID;
		keyIDinfo->keyID = keyIDbuffer;
		keyIDinfo->keyIDlength = hashSize;
		}

	/* If this is a read access there's nothing further to do */
	if( isRead )
		return( CRYPT_OK );

	/* This is a write update, make sure that we can write to the keyset.  
	   This covers all possibilities, both keyset types for which writing 
	   isn't supported and individual keysets that we can't write to 
	   because of things like file permissions, so once we pass this check 
	   we know that we can write to the keyset */
	if( keysetInfoPtr->options == CRYPT_KEYOPT_READONLY )
		return( CRYPT_ERROR_PERMISSION );

	return( CRYPT_OK );
	}

/****************************************************************************
*																			*
*							Flat-file Keyset Functions						*
*																			*
****************************************************************************/

/* Identify a flat-file keyset type */

CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
static int getKeysetType( INOUT STREAM *stream,
						  OUT_ENUM_OPT( KEYSET_SUBTYPE ) KEYSET_SUBTYPE *subType )
	{
	long length;
	int value, status;

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

	/* Clear return value */
	*subType = KEYSET_SUBTYPE_NONE;

	/* Try and guess the basic type */
	status = value = sPeek( stream );
	if( cryptStatusError( status ) )
		return( status );
	if( value == BER_SEQUENCE )
		{
		/* Read the length of the object, which should be between 64 and 64K 
		   bytes in size.  We have to allow for very tiny files to handle 
		   PKCS #15 files that contain only configuration data, and rather 
		   large ones to handle the existence of large numbers of trusted 
		   certificates, with a maximum of 32 objects * ~2K per object we 
		   can get close to 64K in size.  The length may also be zero if the 
		   indefinite encoding form is used.  Although PKCS #15 specifies 
		   the use of DER, it doesn't hurt to allow this at least for the 
		   outer wrapper, if Microsoft ever move to PKCS #15 they're bound 
		   to get it wrong */
		status = readLongSequence( stream, &length );
		if( cryptStatusError( status ) )
			return( status );
		if( length != CRYPT_UNUSED && ( length < 64 || length > 65535L ) )
			return( CRYPT_ERROR_BADDATA );

		/* Check for a PKCS #12/#15 file */
		if( peekTag( stream ) == BER_INTEGER )
			{
			long version;

			/* Check for a PKCS #12 version number */
			status = readShortInteger( stream, &version );
			if( cryptStatusError( status ) )
				return( status );
			if( version != 3 )
				return( CRYPT_ERROR_BADDATA );
			*subType = KEYSET_SUBTYPE_PKCS12;

			return( CRYPT_OK );
			}

		/* Check for a PKCS #15 OID */
		status = readFixedOID( stream, OID_PKCS15_CONTENTTYPE, 
							   sizeofOID( OID_PKCS15_CONTENTTYPE ) );
		if( cryptStatusError( status ) )
			return( status );
		*subType = KEYSET_SUBTYPE_PKCS15;

		return( CRYPT_OK );
		}
#ifdef USE_PGP
	value = pgpGetPacketType( value );
	if( value == PGP_PACKET_PUBKEY || value == PGP_PACKET_SECKEY )
		{
		KEYSET_SUBTYPE type;

		/* Determine the file type based on the initial CTB */
		type = ( value == PGP_PACKET_PUBKEY ) ? \
			   KEYSET_SUBTYPE_PGP_PUBLIC : KEYSET_SUBTYPE_PGP_PRIVATE;

		/* Perform a sanity check to make sure that the rest looks like a 
		   PGP keyring */
		status = pgpReadPacketHeader( stream, &value, &length, 64 );
		if( cryptStatusError( status ) )
			return( status );
		if( type == KEYSET_SUBTYPE_PGP_PUBLIC )
			{
			if( length < 64 || length > 1024  )
				return( CRYPT_ERROR_BADDATA );
			}
		else
			{
			if( length < 200 || length > 4096 )
				return( CRYPT_ERROR_BADDATA );
			}
		status = value = sgetc( stream );
		if( cryptStatusError( status ) )
			return( status );
		if( value != PGP_VERSION_2 && value != PGP_VERSION_3 && \
			value != PGP_VERSION_OPENPGP )
			return( CRYPT_ERROR_BADDATA );
		*subType = type;

		return( CRYPT_OK );
		}
#endif /* USE_PGP */

	/* "It doesn't look like anything from here" */
	return( CRYPT_ERROR_BADDATA );
	}

/* Open a flat-file keyset */

CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2, 5, 6 ) ) \
static int openKeysetStream( INOUT STREAM *stream, 
							 IN_BUFFER( nameLength ) const char *name,
							 IN_LENGTH_SHORT_MIN( MIN_NAME_LENGTH ) \
								const int nameLength,
							 IN_ENUM_OPT( CRYPT_KEYOPT ) \
								const CRYPT_KEYOPT_TYPE options,
							 OUT_BOOL BOOLEAN *isReadOnly, 
							 OUT_ENUM_OPT( KEYSET_SUBTYPE ) \
								KEYSET_SUBTYPE *keysetSubType )
	{
	KEYSET_SUBTYPE subType = KEYSET_SUBTYPE_PKCS15;
	char nameBuffer[ MAX_ATTRIBUTE_SIZE + 1 + 8 ];
	const int suffixPos = nameLength - 4;
	int openMode, status;

	assert( isWritePtr( stream, sizeof( STREAM ) ) );
	assert( isReadPtr( name, nameLength ) );
	assert( isWritePtr( isReadOnly, sizeof( BOOLEAN ) ) );
	assert( isWritePtr( keysetSubType, sizeof( KEYSET_SUBTYPE ) ) );

	REQUIRES( options >= CRYPT_KEYOPT_NONE && options < CRYPT_KEYOPT_LAST );
	REQUIRES( nameLength >= MIN_NAME_LENGTH && \
			  nameLength < MAX_ATTRIBUTE_SIZE );

	/* Clear return values */
	*isReadOnly = FALSE;
	*keysetSubType = KEYSET_SUBTYPE_NONE;

	/* Convert the keyset name into a null-terminated string */
	memcpy( nameBuffer, name, nameLength );
	nameBuffer[ nameLength ] = '\0';

	/* Get the expected subtype based on the keyset name (the default is
	   PKCS #15 if no contraindication is found in the file suffix) */
	if( suffixPos > 0 && nameBuffer[ suffixPos ] == '.' )
		{
		if( !strCompare( nameBuffer + suffixPos + 1, "pgp", 3 ) || \
			!strCompare( nameBuffer + suffixPos + 1, "gpg", 3 ) || \
			!strCompare( nameBuffer + suffixPos + 1, "pkr", 3 ) )
			subType = KEYSET_SUBTYPE_PGP_PUBLIC;
		if( !strCompare( nameBuffer + suffixPos + 1, "skr", 3 ) )
			subType = KEYSET_SUBTYPE_PGP_PRIVATE;
		if( !strCompare( nameBuffer + suffixPos + 1, "pfx", 3 ) || \
			!strCompare( nameBuffer + suffixPos + 1, "p12", 3 ) )
			subType = KEYSET_SUBTYPE_PKCS12;
		}

	/* If the file is read-only, put the keyset into read-only mode */
	if( fileReadonly( nameBuffer ) )
		{
		/* If we want to create a new file we can't do it if we don't have
		   write permission */
		if( options == CRYPT_KEYOPT_CREATE )
			return( CRYPT_ERROR_PERMISSION );

		/* Open the file in read-only mode */
		*isReadOnly = TRUE;
		openMode = FILE_FLAG_READ;
		}
	else
		{
		/* If we're creating the file, open it in write-only mode.  Since
		   we'll (presumably) be storing private keys in it we mark it as
		   both private (owner-access-only ACL) and sensitive (store in
		   secure storage if possible) */
		if( options == CRYPT_KEYOPT_CREATE )
			openMode = FILE_FLAG_WRITE | FILE_FLAG_EXCLUSIVE_ACCESS | \
					   FILE_FLAG_PRIVATE | FILE_FLAG_SENSITIVE;
		else
			{
			/* Open it for read or read/write depending on whether the
			   readonly flag is set */
			openMode = ( options == CRYPT_KEYOPT_READONLY ) ? \
					   FILE_FLAG_READ : FILE_FLAG_READ | FILE_FLAG_WRITE;
			}
		}
	if( options == CRYPT_IKEYOPT_EXCLUSIVEACCESS )
		openMode |= FILE_FLAG_EXCLUSIVE_ACCESS;

	/* Pre-open the file containing the keyset.  This initially opens it in
	   read-only mode for auto-detection of the file type so we can check for
	   various problems */
	status = sFileOpen( stream, nameBuffer, FILE_FLAG_READ );
	if( cryptStatusError( status ) )
		{
		/* The file can't be opened, if the create-new-file flag isn't set 
		   return an error.  If it is set, make sure that we're trying to 
		   create a writeable keyset type */
		if( options != CRYPT_KEYOPT_CREATE )
			return( status );
		if( !isWriteableFileKeyset( subType ) )
			return( CRYPT_ERROR_NOTAVAIL );

		/* Try and create a new file */
		status = sFileOpen( stream, nameBuffer, openMode );
		if( cryptStatusError( status ) )
			{
			/* The file isn't open at this point so we have to exit 
			   explicitly rather than falling through to the error handler
			   below */
			return( status );
			}
		}
	else
		{
		/* If we're opening an existing keyset, get its type and make sure
		   that it's valid */
		if( options != CRYPT_KEYOPT_CREATE )
			{
			BYTE buffer[ 512 + 8 ];

			sioctl( stream, STREAM_IOCTL_IOBUFFER, buffer, 512 );
			status = getKeysetType( stream, &subType );
			if( cryptStatusError( status ) )
				{
				/* "It doesn't look like anything from here" */
				sFileClose( stream );
				return( CRYPT_ERROR_BADDATA );
				}
			sseek( stream, 0 );
			sioctl( stream, STREAM_IOCTL_IOBUFFER, NULL, 0 );
			}

		/* If it's a cryptlib keyset we can open it in any mode */
		if( isWriteableFileKeyset( subType ) )
			{
			/* If we're opening it something other than read-only mode, 
			   reopen it in that mode.  Note that in theory this could make 
			   us subject to a TOCTTOU attack but the only reason that we're 
			   opening the file initially is to determine its type, so if an 
			   attacker slips in a different file on the re-open it'll 
			   either be a no-op if it's the same file type or we'll get a
			   CRYPT_ERROR_BADDATA if it's the same file type */
			if( openMode != FILE_FLAG_READ )
				{
				sFileClose( stream );
				status = sFileOpen( stream, nameBuffer, openMode );
				if( cryptStatusError( status ) )
					return( status );	/* Exit with file closed */
				}
			}
		else
			{
			/* If it's a non-cryptlib keyset we can't open it for anything 
			   other than read-only access.  We return a not-available error 
			   rather than a permission error since this isn't a problem with
			   access permissions for the file but the fact that the code to
			   write the key doesn't exist */
			if( options != CRYPT_KEYOPT_READONLY )
				status = CRYPT_ERROR_NOTAVAIL;
			}
		}
	if( cryptStatusError( status ) )
		{
		sFileClose( stream );
		return( status );
		}

	*keysetSubType = subType;
	return( CRYPT_OK );
	}

/* Complete the open of a file keyset */

CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 3 ) ) \
static int completeKeysetFileOpen( INOUT KEYSET_INFO *keysetInfoPtr,
								   IN_ENUM( KEYSET_SUBTYPE ) \
									KEYSET_SUBTYPE subType,
								   INOUT STREAM *stream,
								   IN_BUFFER( nameLength ) const char *name, 
								   IN_LENGTH_SHORT_MIN( MIN_NAME_LENGTH ) \
									const int nameLength )
	{
	BYTE buffer[ STREAM_BUFSIZE + 8 ];
	int status;

	assert( isWritePtr( keysetInfoPtr, sizeof( KEYSET_INFO ) ) );
	assert( isReadPtr( name, nameLength ) );
	assert( isWritePtr( stream, sizeof( STREAM ) ) );

	REQUIRES( subType > KEYSET_SUBTYPE_NONE && \
			  subType < KEYSET_SUBTYPE_LAST );
	REQUIRES( nameLength >= MIN_NAME_LENGTH && \
			  nameLength < MAX_ATTRIBUTE_SIZE );

	/* Remember the key file's name (as a null-terminated string for 
	   filesystem access) and I/O stream */

⌨️ 快捷键说明

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