📄 cryptkey.c
字号:
/****************************************************************************
* *
* 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 + -