📄 pgp.c
字号:
/****************************************************************************
* *
* cryptlib PGP Keyset Routines *
* Copyright Peter Gutmann 1992-2007 *
* *
****************************************************************************/
#if defined( INC_ALL )
#include "crypt.h"
#include "keyset.h"
#include "misc_rw.h"
#include "pgp.h"
#include "pgp_key.h"
#else
#include "crypt.h"
#include "keyset/keyset.h"
#include "misc/misc_rw.h"
#include "misc/pgp.h"
#include "keyset/pgp_key.h"
#endif /* Compiler-specific includes */
#ifdef USE_PGPKEYS
/* A PGP private key file can contain multiple key objects so before we do
anything with the file we scan it and build an in-memory index of what's
present. When we perform an update we just flush the in-memory
information to disk.
Each file can contain information for multiple personalities (although
for private keys it's unlikely to contain more than a small number), we
allow a maximum of MAX_PGP_OBJECTS per file. A setting of 16 objects
consumes ~4K of memory (16 x ~256) so we choose that as the limit */
#ifdef CONFIG_CONSERVE_MEMORY
#define MAX_PGP_OBJECTS 4
#else
#define MAX_PGP_OBJECTS 16
#endif /* CONFIG_CONSERVE_MEMORY */
/****************************************************************************
* *
* Utility Routines *
* *
****************************************************************************/
/* Find a free PGP keyset entry */
CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
static PGP_INFO *findFreeEntry( IN_ARRAY( noPgpObjects ) \
const PGP_INFO *pgpInfo,
IN_LENGTH_SHORT const int noPgpObjects )
{
int i;
assert( isReadPtr( pgpInfo, \
sizeof( PGP_INFO ) * noPgpObjects ) );
REQUIRES_N( noPgpObjects >= 1 && noPgpObjects < MAX_INTLENGTH_SHORT );
for( i = 0; i < noPgpObjects && i < FAILSAFE_ITERATIONS_MED; i++ )
{
if( pgpInfo[ i ].keyData == NULL )
break;
}
ENSURES_N( i < FAILSAFE_ITERATIONS_MED );
if( i >= noPgpObjects )
return( NULL );
return( ( PGP_INFO * ) &pgpInfo[ i ] );
}
/* Free object entries */
STDC_NONNULL_ARG( ( 1 ) ) \
void pgpFreeEntry( INOUT PGP_INFO *pgpInfo )
{
assert( isWritePtr( pgpInfo, sizeof( PGP_INFO ) ) );
if( pgpInfo->keyData != NULL )
{
zeroise( pgpInfo->keyData, pgpInfo->keyDataLen );
clFree( "pgpFreeEntry", pgpInfo->keyData );
pgpInfo->keyData = NULL;
pgpInfo->keyDataLen = 0;
}
zeroise( pgpInfo, sizeof( PGP_INFO ) );
}
/* Create a decryption context for the private key from a user-supplied
password */
CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2, 3 ) ) \
static int createDecryptionContext( OUT_HANDLE_OPT CRYPT_CONTEXT *iSessionKey,
const PGP_KEYINFO *keyInfo,
IN_BUFFER( passwordLength ) \
const void *password,
IN_LENGTH_NAME const int passwordLength )
{
static const CRYPT_MODE_TYPE cryptMode = CRYPT_MODE_CFB;
CRYPT_CONTEXT iLocalContext;
MESSAGE_CREATEOBJECT_INFO createInfo;
MESSAGE_DATA msgData;
int ivSize, status;
assert( isWritePtr( iSessionKey, sizeof( CRYPT_CONTEXT ) ) );
assert( isReadPtr( keyInfo, sizeof( PGP_KEYINFO ) ) );
assert( isReadPtr( password, passwordLength ) );
REQUIRES( passwordLength >= MIN_NAME_LENGTH && \
passwordLength < MAX_ATTRIBUTE_SIZE );
/* Convert the user password into an encryption context */
setMessageCreateObjectInfo( &createInfo, keyInfo->cryptAlgo );
status = krnlSendMessage( SYSTEM_OBJECT_HANDLE, IMESSAGE_DEV_CREATEOBJECT,
&createInfo, OBJECT_TYPE_CONTEXT );
if( cryptStatusError( status ) )
return( status );
iLocalContext = createInfo.cryptHandle;
status = krnlSendMessage( iLocalContext, IMESSAGE_SETATTRIBUTE,
( void * ) &cryptMode, CRYPT_CTXINFO_MODE );
if( cryptStatusOK( status ) )
{
status = pgpPasswordToKey( iLocalContext,
( keyInfo->cryptAlgo == CRYPT_ALGO_AES && \
keyInfo->aesKeySize > 0 ) ? \
keyInfo->aesKeySize : CRYPT_UNUSED,
password, passwordLength,
keyInfo->hashAlgo,
( keyInfo->saltSize > 0 ) ? \
keyInfo->salt : NULL, keyInfo->saltSize,
keyInfo->keySetupIterations );
}
if( cryptStatusError( status ) )
{
krnlSendNotifier( iLocalContext, IMESSAGE_DECREFCOUNT );
return( status );
}
/* Load the IV into the context */
status = krnlSendMessage( iLocalContext, IMESSAGE_GETATTRIBUTE,
&ivSize, CRYPT_CTXINFO_IVSIZE );
if( cryptStatusOK( status ) && ivSize > keyInfo->ivSize )
status = CRYPT_ERROR_BADDATA;
if( cryptStatusOK( status ) )
{
setMessageData( &msgData, ( void * ) keyInfo->iv, keyInfo->ivSize );
status = krnlSendMessage( iLocalContext, IMESSAGE_SETATTRIBUTE_S,
&msgData, CRYPT_CTXINFO_IV );
}
if( cryptStatusError( status ) )
{
krnlSendNotifier( iLocalContext, IMESSAGE_DECREFCOUNT );
return( status );
}
*iSessionKey = iLocalContext;
return( CRYPT_OK );
}
/****************************************************************************
* *
* Find a Key *
* *
****************************************************************************/
/* Generate a cryptlib-style key ID for a PGP key and check it against the
given key ID. This will really suck with large public keyrings since it
requires creating a context for each key that we check, but there's no
easy way around this and in any case it only occurs when using PGP keys
with non-PGP messages, which is fairly rare */
CHECK_RETVAL_BOOL STDC_NONNULL_ARG( ( 1, 2 ) ) \
static BOOLEAN matchKeyID( const PGP_KEYINFO *keyInfo,
IN_BUFFER( requiredIDlength ) const BYTE *requiredID,
IN_LENGTH_KEYID const int requiredIDlength,
const BOOLEAN isPGPkeyID )
{
CRYPT_CONTEXT iLocalContext;
MESSAGE_CREATEOBJECT_INFO createInfo;
MESSAGE_DATA msgData;
BYTE keyID[ KEYID_SIZE + 8 ];
int status;
assert( isReadPtr( keyInfo, sizeof( PGP_KEYINFO ) ) );
assert( isReadPtr( requiredID, requiredIDlength ) );
ENSURES_B( requiredIDlength == PGP_KEYID_SIZE || \
requiredIDlength == KEYID_SIZE );
/* If it's a PGP key ID we can check it directly against the two PGP
key IDs. We don't distinguish between the two ID types externally
because it's a pain for external code to have to know that there are
two ID types that look the same and are often used interchangeably
but of the two only the OpenPGP variant is valid for all keys (in
fact there are some broken PGP variants that use PGP 2.x IDs marked
as OpenPGP IDs, so checking both IDs is necessary for
interoperability). The mixing of ID types is safe because the
chances of a collision are miniscule and the worst that can happen is
that a signature check will fail (encryption keys are chosen by user
ID and not key ID so accidentally using the wrong key to encrypt
isn't an issue) */
if( isPGPkeyID )
{
ENSURES_B( requiredIDlength == PGP_KEYID_SIZE );
if( !memcmp( requiredID, keyInfo->openPGPkeyID, PGP_KEYID_SIZE ) )
return( TRUE );
return( ( keyInfo->pkcAlgo == CRYPT_ALGO_RSA ) && \
!memcmp( requiredID, keyInfo->pgpKeyID, PGP_KEYID_SIZE ) );
}
ENSURES_B( requiredIDlength == KEYID_SIZE );
/* Generate the key ID via a context. We have to set the OpenPGP key ID
before the key load to mark it as a PGP key otherwise the key check
will fail since it's not a full X9.42 key with DLP validation
parameters */
setMessageCreateObjectInfo( &createInfo, keyInfo->pkcAlgo );
status = krnlSendMessage( SYSTEM_OBJECT_HANDLE, IMESSAGE_DEV_CREATEOBJECT,
&createInfo, OBJECT_TYPE_CONTEXT );
if( cryptStatusError( status ) )
{
assert( DEBUG_WARN );
return( FALSE );
}
iLocalContext = createInfo.cryptHandle;
setMessageData( &msgData, ( void * ) keyInfo->openPGPkeyID,
PGP_KEYID_SIZE );
status = krnlSendMessage( iLocalContext, IMESSAGE_SETATTRIBUTE_S,
&msgData, CRYPT_IATTRIBUTE_KEYID_OPENPGP );
if( cryptStatusOK( status ) )
{
setMessageData( &msgData, keyInfo->pubKeyData,
keyInfo->pubKeyDataLen );
status = krnlSendMessage( iLocalContext, IMESSAGE_SETATTRIBUTE_S,
&msgData, CRYPT_IATTRIBUTE_KEY_PGP );
}
if( cryptStatusOK( status ) )
{
setMessageData( &msgData, keyID, KEYID_SIZE );
status = krnlSendMessage( iLocalContext, IMESSAGE_GETATTRIBUTE_S,
&msgData, CRYPT_IATTRIBUTE_KEYID );
}
krnlSendNotifier( iLocalContext, IMESSAGE_DECREFCOUNT );
if( cryptStatusError( status ) )
{
assert( DEBUG_WARN );
return( FALSE );
}
/* Check if it's the same as the key ID that we're looking for */
return( !memcmp( requiredID, keyID, requiredIDlength ) ? TRUE : FALSE );
}
/* Check whether a key matches the required user ID */
CHECK_RETVAL_BOOL STDC_NONNULL_ARG( ( 1, 2, 3 ) ) \
BOOLEAN pgpCheckKeyMatch( const PGP_INFO *pgpInfo,
const PGP_KEYINFO *keyInfo,
const KEY_MATCH_INFO *keyMatchInfo )
{
int i;
assert( isReadPtr( pgpInfo, sizeof( PGP_INFO ) ) );
assert( isReadPtr( keyInfo, sizeof( PGP_KEYINFO ) ) );
assert( isReadPtr( keyMatchInfo, sizeof( KEY_MATCH_INFO ) ) );
/* If there's an explicitly requested key usage type, make sure that the
key is suitable */
if( ( keyMatchInfo->flags & KEYMGMT_MASK_USAGEOPTIONS ) && \
!( keyInfo->usageFlags & keyMatchInfo->flags ) )
return( FALSE );
/* If we're searching by key ID, check whether this is the packet that
we want */
if( keyMatchInfo->keyIDtype == CRYPT_IKEYID_KEYID || \
keyMatchInfo->keyIDtype == CRYPT_IKEYID_PGPKEYID )
{
return( matchKeyID( keyInfo, keyMatchInfo->keyID,
keyMatchInfo->keyIDlength,
( keyMatchInfo->keyIDtype == CRYPT_IKEYID_PGPKEYID ) ? \
TRUE : FALSE ) );
}
REQUIRES_B( keyMatchInfo->keyIDtype == CRYPT_KEYID_NAME || \
keyMatchInfo->keyIDtype == CRYPT_KEYID_URI );
/* We're searching by user ID, walk down the list of userIDs checking
for a match */
for( i = 0; i < pgpInfo->lastUserID && i < MAX_PGP_USERIDS; i++ )
{
/* Check if it's the one that we want. If it's a key with subkeys
and no usage type is explicitly specified this will always return
the main key. This is the best solution since the main key is
always a signing key, which is more likely to be what the user
wants. Encryption keys will typically only be accessed via
envelopes and the enveloping code can specify a preference of an
encryption-capable key, while signing keys will be read directly
and pushed into the envelope */
if( strFindStr( pgpInfo->userID[ i ], pgpInfo->userIDlen[ i ],
( char * ) keyMatchInfo->keyID,
keyMatchInfo->keyIDlength ) >= 0 )
return( TRUE );
}
ENSURES_B( i < MAX_PGP_USERIDS );
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -