📄 pgp.c
字号:
/****************************************************************************
* *
* PGP Key Read Routines *
* Copyright Peter Gutmann 1992-2004 *
* *
****************************************************************************/
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#if defined( INC_ALL )
#include "crypt.h"
#include "pgp.h"
#include "keyset.h"
#include "misc_rw.h"
#elif defined( INC_CHILD )
#include "../crypt.h"
#include "../envelope/pgp.h"
#include "keyset.h"
#include "../misc/misc_rw.h"
#else
#include "crypt.h"
#include "envelope/pgp.h"
#include "keyset/keyset.h"
#include "misc/misc_rw.h"
#endif /* Compiler-specific includes */
#ifdef USE_PGPKEYS
/* A PGP private key file can contain multiple key objects, 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 */
/* Each PGP key can contain an arbitrary number of user IDs, we only track
the following maximum number. Further IDs are read and stored, but not
indexed or searched on */
#define MAX_PGP_USERIDS 16
/* When reading a PGP keyring, we implement a sliding window that reads a
certain amount of data into a lookahead buffer and then tries to identify
a key packet group in the buffer. The following value determines the size
of the lookahead. Unfortunately we have to keep this above a certain
minimum size in order to handle PGP 8.x's inclusion of photo IDs in
keyrings, which means that the smallest size we can safely use is about
8kb */
#define KEYRING_BUFSIZE 8192
/* Key-related information needed to create a cryptlib context from PGP key
data */
typedef struct {
/* Key data information */
CRYPT_ALGO_TYPE pkcAlgo; /* Key algorithm */
int usageFlags; /* Keymgmt flags permitted usage */
BYTE pgpKeyID[ PGP_KEYID_SIZE ], openPGPkeyID[ PGP_KEYID_SIZE ];
void *pubKeyData, *privKeyData; /* Pointer to encoded pub/priv key data */
int pubKeyDataLen, privKeyDataLen;
/* Key data protection information */
CRYPT_ALGO_TYPE cryptAlgo; /* Key wrap algorithm */
int aesKeySize; /* Key size if algo == AES */
BYTE iv[ CRYPT_MAX_IVSIZE ]; /* Key wrap IV */
CRYPT_ALGO_TYPE hashAlgo; /* Password hashing algo */
BYTE salt[ PGP_SALTSIZE ]; /* Password hashing salt */
int saltSize;
int keySetupIterations; /* Password hashing iterations */
} PGP_KEYINFO;
/* The following structure contains the the information for one personality,
which covers one or more of a private key, public key, and subkeys. PGP
encodes keys in a complex manner by writing them as groups of (implicitly)
connected packets that require arbitrary amounts of lookahead to parse.
To handle this we read the overall encoded key data as a single unit and
store it in a dynamically-allocated buffer, then set up pointers to
locations of relevant data (public and private keys and user IDs) within
the overall key data. To further complicate matters, there can be a key
and subkey associated with the same information, so we have to maintain
two lots of physical keying information for each logical key */
typedef struct {
void *keyData; /* Encoded key data */
int keyDataLen;
PGP_KEYINFO key, subKey; /* Key and subkey information */
char *userID[ MAX_PGP_USERIDS ];/* UserIDs */
int userIDlen[ MAX_PGP_USERIDS ];
int lastUserID; /* Last used userID */
BOOLEAN isOpenPGP; /* Whether data is PGP 2.x or OpenPGP */
} PGP_INFO;
/* When we're searching for a key, we need to compare each one against a
collection of match criteria. The following struct contains the
information that we match against */
typedef struct {
CONST_INIT CRYPT_KEYID_TYPE keyIDtype;/* Key ID type */
const void *keyID;
CONST_INIT int keyIDlength; /* Key ID */
CONST_INIT int flags; /* Key usage flags */
} KEY_MATCH_INFO;
/****************************************************************************
* *
* Utility Routines *
* *
****************************************************************************/
/* Get the size of an encoded MPI and skip the payload data */
static int getMPIsize( STREAM *stream )
{
int bitLength, length;
/* Read the MPI length and make sure that it's in order */
bitLength = readUint16( stream );
length = bitsToBytes( bitLength );
if( length < 1 || length > PGP_MAX_MPISIZE || \
length > sMemDataLeft( stream ) )
{
sSetError( stream, CRYPT_ERROR_BADDATA );
return( 0 ); /* Dummy value */
}
sSkip( stream, length );
return( 2 + length );
}
/* Scan a sequence of key packets to find the extent of the packet group. In
addition to simply scanning, this function handles over-long packets by
reporting their overall length and returning OK_SPECIAL, and will try to
resync to a packet group if it starts in the middle of an arbitrary packet
collection, for example due to skipping of an over-long packet found
earlier */
static int scanPacketGroup( const void *data, const int dataLength,
int *packetGroupLength )
{
STREAM stream;
BOOLEAN firstPacket = TRUE, skipPackets = FALSE;
int endPos = 0, status = CRYPT_OK;
/* Clear return value */
*packetGroupLength = 0;
sMemConnect( &stream, data, dataLength );
do
{
long length;
int ctb;
/* Get the next CTB. If it's the start of another packet group,
we're done */
ctb = status = sPeek( &stream );
if( cryptStatusOK( status ) )
{
assert( ctb & PGP_CTB );
if( !( ctb & PGP_CTB ) )
status = CRYPT_ERROR_BADDATA;
}
if( cryptStatusError( status ) )
{
sMemDisconnect( &stream );
return( status );
}
if( firstPacket )
{
/* If the packet group doesn't start with the expected packet
type, skip packets to try to resync */
if( getCTB( ctb ) != PGP_PACKET_PUBKEY && \
getCTB( ctb ) != PGP_PACKET_SECKEY )
skipPackets = TRUE;
firstPacket = FALSE;
}
else
if( getCTB( ctb ) == PGP_PACKET_PUBKEY || \
getCTB( ctb ) == PGP_PACKET_SECKEY )
{
/* We've found the start of a new packet group, remember
where the current group ends and exit */
sMemDisconnect( &stream );
*packetGroupLength = endPos;
return( skipPackets ? OK_SPECIAL : CRYPT_OK );
}
/* Skip the current packet in the buffer */
status = pgpReadPacketHeader( &stream, NULL, &length );
if( cryptStatusError( status ) )
{
sMemDisconnect( &stream );
return( status );
}
endPos = stell( &stream ) + length;
sSkip( &stream, length );
}
while( endPos < dataLength );
sMemDisconnect( &stream );
*packetGroupLength = endPos;
/* If we skipped packets or consumed all the input in the buffer and
there's more present beyond that, tell the caller to discard the
data and try again */
return( ( skipPackets || endPos > dataLength ) ? OK_SPECIAL : CRYPT_OK );
}
/* Free object entries */
static void pgpFreeEntry( PGP_INFO *pgpInfo )
{
if( pgpInfo->keyData != NULL )
{
zeroise( pgpInfo->keyData, pgpInfo->keyDataLen );
clFree( "pgpFreeEntry", pgpInfo->keyData );
pgpInfo->keyData = NULL;
pgpInfo->keyDataLen = 0;
}
zeroise( pgpInfo, sizeof( PGP_INFO ) );
}
/****************************************************************************
* *
* 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 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 */
static BOOLEAN matchKeyID( const PGP_KEYINFO *keyInfo, const BYTE *requiredID,
const int requiredIDlength,
const BOOLEAN isPGPkeyID )
{
MESSAGE_CREATEOBJECT_INFO createInfo;
RESOURCE_DATA msgData;
BYTE keyID[ KEYID_SIZE ];
int status;
/* 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 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 possibly happen is that a sig 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 )
{
assert( 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 ) );
}
assert( 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( cryptStatusOK( status ) )
{
setMessageData( &msgData, ( void * ) keyInfo->openPGPkeyID,
PGP_KEYID_SIZE );
status = krnlSendMessage( createInfo.cryptHandle,
IMESSAGE_SETATTRIBUTE_S, &msgData,
CRYPT_IATTRIBUTE_KEYID_OPENPGP );
if( cryptStatusOK( status ) )
{
setMessageData( &msgData, keyInfo->pubKeyData,
keyInfo->pubKeyDataLen );
status = krnlSendMessage( createInfo.cryptHandle,
IMESSAGE_SETATTRIBUTE_S, &msgData,
CRYPT_IATTRIBUTE_KEY_PGP );
}
if( cryptStatusOK( status ) )
{
setMessageData( &msgData, keyID, KEYID_SIZE );
status = krnlSendMessage( createInfo.cryptHandle,
IMESSAGE_GETATTRIBUTE_S, &msgData,
CRYPT_IATTRIBUTE_KEYID );
}
krnlSendNotifier( createInfo.cryptHandle, IMESSAGE_DECREFCOUNT );
}
if( cryptStatusError( status ) )
{
assert( NOTREACHED );
return( FALSE );
}
/* Check if it's the same as the key ID we're looking for */
return( !memcmp( requiredID, keyID, requiredIDlength ) ? TRUE : FALSE );
}
/* Match a substring of a full string as done by PGP */
static BOOLEAN matchSubstring( const char *subString,
const int subStringLength,
const char *string, const int stringLength )
{
const char firstChar = toUpper( subString[ 0 ] );
int i;
/* Perform a case-insensitive match for the required substring in the
string */
for( i = 0; i <= stringLength - subStringLength; i++ )
if( ( toUpper( string[ i ] ) == firstChar ) &&
!strCompare( subString, string + i, subStringLength ) )
return( TRUE );
return( FALSE );
}
/* Check whether a key matches the required user ID */
static BOOLEAN checkKeyMatch( const PGP_INFO *pgpInfo,
const PGP_KEYINFO *keyInfo,
const KEY_MATCH_INFO *keyMatchInfo )
{
int i;
/* 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 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 ) );
assert( 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++ )
/* Check if it's the one we want. If it's a key with subkeys and no
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -