📄 pgp.c
字号:
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( matchSubstring( ( char * ) keyMatchInfo->keyID,
keyMatchInfo->keyIDlength, pgpInfo->userID[ i ],
pgpInfo->userIDlen[ i ] ) )
return( TRUE );
return( FALSE );
}
/* Locate a key based on an ID. This is complicated somewhat by the fact
that PGP groups multiple keys around the same textual ID, so we have to
check both keys and subkeys for a possible match */
static PGP_INFO *findEntry( const PGP_INFO *pgpInfo,
const CRYPT_KEYID_TYPE keyIDtype,
const void *keyID, const int keyIDlength,
const int requestedUsage, PGP_KEYINFO **keyInfo )
{
CONST_INIT_STRUCT_4( KEY_MATCH_INFO keyMatchInfo, \
keyIDtype, keyID, keyIDlength, requestedUsage );
int i;
CONST_SET_STRUCT( keyMatchInfo.keyIDtype = keyIDtype; \
keyMatchInfo.keyID = keyID; \
keyMatchInfo.keyIDlength = keyIDlength; \
keyMatchInfo.flags = requestedUsage );
for( i = 0; i < MAX_PGP_OBJECTS; i++ )
{
if( checkKeyMatch( &pgpInfo[ i ], &pgpInfo[ i ].key,
&keyMatchInfo ) )
{
if( keyInfo != NULL )
*keyInfo = ( PGP_KEYINFO * ) &pgpInfo[ i ].key;
return( ( PGP_INFO * ) &pgpInfo[ i ] );
}
if( checkKeyMatch( &pgpInfo[ i ], &pgpInfo[ i ].subKey,
&keyMatchInfo ) )
{
if( keyInfo != NULL )
*keyInfo = ( PGP_KEYINFO * ) &pgpInfo[ i ].subKey;
return( ( PGP_INFO * ) &pgpInfo[ i ] );
}
}
return( NULL );
}
/****************************************************************************
* *
* Read a Key *
* *
****************************************************************************/
/* Read the information needed to decrypt a secret key */
static int readSecretKeyDecryptionInfo( STREAM *stream, PGP_KEYINFO *keyInfo )
{
const int ctb = sgetc( stream );
int ivSize = PGP_IVSIZE, status;
/* Clear return value */
keyInfo->cryptAlgo = keyInfo->hashAlgo = CRYPT_ALGO_NONE;
keyInfo->saltSize = keyInfo->keySetupIterations = 0;
/* If no encryption is being used, we mark the key as unusable. This
isn't exactly the correct thing to do, but storing plaintext private
keys on disk is extremely dangerous and we probably shouldn't be
using them, and an attempt to import an unencrypted key will trigger
so many security check failures in the key unwrap code that it's not
even worth trying */
if( !ctb )
return( OK_SPECIAL );
/* If it's a direct algorithm specifier, it's a PGP 2.x packet with
raw IDEA encryption */
if( ctb == PGP_ALGO_IDEA )
{
keyInfo->cryptAlgo = CRYPT_ALGO_IDEA;
keyInfo->hashAlgo = CRYPT_ALGO_MD5;
}
else
{
int value;
/* Must be an S2K specifier */
if( ctb != PGP_S2K && ctb != PGP_S2K_HASHED )
return( CRYPT_ERROR_BADDATA );
/* Get the key wrap algorithm and S2K information */
value = sgetc( stream );
if( ( keyInfo->cryptAlgo = \
pgpToCryptlibAlgo( value,
PGP_ALGOCLASS_PWCRYPT ) ) == CRYPT_ALGO_NONE )
/* Unknown algorithm type, skip this packet */
return( OK_SPECIAL );
if( keyInfo->cryptAlgo == CRYPT_ALGO_AES )
{
/* PGP uses three different algorithm IDs to identify AES with
different key sizes (ugh), so we have to remember the key size
alongside the algorithm type for this algorithm type */
keyInfo->aesKeySize = ( value == PGP_ALGO_AES_128 ) ? 16 : \
( value == PGP_ALGO_AES_192 ) ? 24 : 32;
ivSize = 16;
}
value = sgetc( stream );
if( value != 0 && value != 1 && value != 3 )
return( cryptStatusError( value ) ? value : OK_SPECIAL );
if( ( keyInfo->hashAlgo = \
pgpToCryptlibAlgo( sgetc( stream ),
PGP_ALGOCLASS_HASH ) ) == CRYPT_ALGO_NONE )
/* Unknown algorithm type, skip this packet */
return( OK_SPECIAL );
if( value != 0 )
{
/* It's a salted hash */
status = sread( stream, keyInfo->salt, PGP_SALTSIZE );
if( cryptStatusError( status ) )
return( status );
keyInfo->saltSize = PGP_SALTSIZE;
}
if( value == 3 )
{
/* Salted iterated hash, get the iteration count, limited to a
sane value. The "iteration count" is actually a count of how
many bytes are hashed, this is because the "iterated hashing"
treats the salt + password as an infinitely-repeated sequence
of values and hashes the resulting string for PGP-iteration-
count bytes worth. The value we calculate here (to prevent
overflow on 16-bit machines) is the count without the
base * 64 scaling, this also puts the range within the value
of the standard sanity check */
value = sgetc( stream );
if( cryptStatusError( value ) )
return( value );
keyInfo->keySetupIterations = \
( 16 + ( ( long ) value & 0x0F ) ) << ( value >> 4 );
if( keyInfo->keySetupIterations <= 0 || \
keyInfo->keySetupIterations > MAX_KEYSETUP_ITERATIONS )
return( CRYPT_ERROR_BADDATA );
}
}
status = sread( stream, keyInfo->iv, ivSize );
return( cryptStatusError( status ) ? status : CRYPT_OK );
}
/* Read a single key in a group of key packets */
static int readKey( STREAM *stream, PGP_INFO *pgpInfo )
{
PGP_KEYINFO *keyInfo = &pgpInfo->key;
HASHFUNCTION hashFunction;
HASHINFO hashInfo;
BYTE hash[ CRYPT_MAX_HASHSIZE ], packetHeader[ 64 ];
BOOLEAN isPublicKey = TRUE;
void *pubKeyPayload;
long packetLength;
int startPos, endPos, ctb, length, pubKeyPayloadLen;
int value, hashSize, status;
/* Skip CTB, packet length, and version byte */
ctb = sPeek( stream );
switch( getCTB( ctb ) )
{
case PGP_PACKET_SECKEY_SUB:
keyInfo = &pgpInfo->subKey;
/* Fall through */
case PGP_PACKET_SECKEY:
isPublicKey = FALSE;
break;
case PGP_PACKET_PUBKEY_SUB:
keyInfo = &pgpInfo->subKey;
/* Fall through */
case PGP_PACKET_PUBKEY:
break;
default:
return( cryptStatusError( ctb ) ? \
CRYPT_ERROR_NOTFOUND : CRYPT_ERROR_BADDATA );
}
status = pgpReadPacketHeader( stream, NULL, &packetLength );
if( cryptStatusError( status ) )
return( status );
if( packetLength < 64 || sMemDataLeft( stream ) < packetLength )
return( CRYPT_ERROR_BADDATA );
length = ( int ) packetLength;
keyInfo->pubKeyData = sMemBufPtr( stream );
startPos = stell( stream );
endPos = startPos + length;
value = sgetc( stream );
if( value != PGP_VERSION_2 && value != PGP_VERSION_3 && \
value != PGP_VERSION_OPENPGP )
/* Unknown version number, skip this packet */
return( OK_SPECIAL );
pgpInfo->isOpenPGP = ( value == PGP_VERSION_OPENPGP ) ? TRUE : FALSE;
/* Build the packet header, which is hashed along with the key components
to get the OpenPGP keyID. This is generated anyway when the context
is created, but we need to generate it here as well in order to locate
the key in the first place:
byte ctb = 0x99
byte[2] length
byte version = 4
byte[4] key generation time
byte[] key data
We can't add the length or key data yet since we have to parse the
key data to know how long it is, so we can only build the static part
of the header at this point */
packetHeader[ 0 ] = 0x99;
packetHeader[ 3 ] = PGP_VERSION_OPENPGP;
/* Read the timestamp and validity period (for PGP 2.x keys) */
sread( stream, packetHeader + 4, 4 );
if( !pgpInfo->isOpenPGP )
sSkip( stream, 2 );
/* Read the public key components */
pubKeyPayload = sMemBufPtr( stream );
pubKeyPayloadLen = stell( stream );
value = sgetc( stream );
if( value == PGP_ALGO_RSA || value == PGP_ALGO_RSA_ENCRYPT || \
value == PGP_ALGO_RSA_SIGN )
{
/* RSA: n + e. The LSBs of n serve as the PGP 2.x key ID, so we
copy the data out before continuing */
keyInfo->pkcAlgo = CRYPT_ALGO_RSA;
if( value != PGP_ALGO_RSA_SIGN )
keyInfo->usageFlags = KEYMGMT_FLAG_USAGE_CRYPT;
if( value != PGP_ALGO_RSA_ENCRYPT )
keyInfo->usageFlags |= KEYMGMT_FLAG_USAGE_SIGN;
length = 1 + getMPIsize( stream );
if( sStatusOK( stream ) && \
stell( stream ) - startPos > PGP_KEYID_SIZE )
memcpy( keyInfo->pgpKeyID, sMemBufPtr( stream ) - PGP_KEYID_SIZE,
PGP_KEYID_SIZE );
length += getMPIsize( stream );
}
else
{
/* If it's an unknown algorithm, skip this key */
if( value != PGP_ALGO_DSA && value != PGP_ALGO_ELGAMAL )
return( cryptStatusError( value ) ? value: OK_SPECIAL );
/* DSA/Elgamal: p + g + y */
if( value == PGP_ALGO_DSA )
{
keyInfo->pkcAlgo = CRYPT_ALGO_DSA;
keyInfo->usageFlags = KEYMGMT_FLAG_USAGE_SIGN;
}
else
{
keyInfo->pkcAlgo = CRYPT_ALGO_ELGAMAL;
keyInfo->usageFlags = KEYMGMT_FLAG_USAGE_CRYPT;
}
length = 1 + getMPIsize( stream ) + getMPIsize( stream ) + \
getMPIsize( stream );
if( value == PGP_ALGO_DSA )
/* DSA has q as well */
length += getMPIsize( stream );
}
status = sGetStatus( stream );
if( cryptStatusError( status ) )
return( status );
keyInfo->pubKeyDataLen = stell( stream ) - startPos;
pubKeyPayloadLen = stell( stream ) - pubKeyPayloadLen;
/* Complete the packet header that we read earlier on by adding the
length information */
packetHeader[ 1 ] = ( ( 1 + 4 + length ) >> 8 ) & 0xFF;
packetHeader[ 2 ] = ( 1 + 4 + length ) & 0xFF;
/* Hash the data needed to generate the OpenPGP keyID */
getHashParameters( CRYPT_ALGO_SHA, &hashFunction, &hashSize );
hashFunction( hashInfo, NULL, packetHeader, 1 + 2 + 1 + 4, HASH_START );
hashFunction( hashInfo, hash, pubKeyPayload, pubKeyPayloadLen, HASH_END );
memcpy( keyInfo->openPGPkeyID, hash + hashSize - PGP_KEYID_SIZE,
PGP_KEYID_SIZE );
/* If it's a private keyring, process the private key components */
if( !isPublicKey )
{
/* Handle decryption info for secret components if necessary */
status = readSecretKeyDecryptionInfo( stream, keyInfo );
if( cryptStatusError( status ) )
return( status );
/* What's left is the private-key data */
keyInfo->privKeyData = sMemBufPtr( stream );
keyInfo->privKeyDataLen = endPos - stell( stream );
status = sSkip( stream, keyInfo->privKeyDataLen );
}
/* Read the userID packet(s) */
while( cryptStatusOK( status ) )
{
int type;
/* Skip keyring trust packets, signature packets, and any private
packets (GPG uses packet type 61, which might be a DSA self-
signature).
PGP has two ways of indicating key usage, either directly via the
key type (e.g. PGP_ALGO_RSA_ENCRYPT vs. PGP_ALGO_RSA_SIGN) or in a
rather schizophrenic manner in signature packets by allowing the
signer to specify an X.509-style key usage. Since it can appear
in both self-sigs and certification sigs, the exact usage for a
key is somewhat complex to determine as a certification signer
could indicate that they trust the key when it's used for signing
while a self-signer could indicate that the key should be used
for encryption. This appears to be a preference indication
rather than a hard limit like the X.509 keyUsage, and contains
other odds and ends as well such as key splitting indicators.
For now we don't make use of these flags as it's a bit difficult
to figure out what's what, and in any case DSA vs. Elgamal
doesn't need any further constraints since there's only one usage
possible */
while( cryptStatusOK( status ) )
{
/* See what we've got. If we've run out of input or it's a non-
key-related packet, we're done */
ctb = status = sPeek( stream );
type = getCTB( ctb );
if( cryptStatusError( status ) || \
( type != PGP_PACKET_TRUST && type != PGP_PACKET_SIGNATURE && \
type != PGP_PACKET_USERATTR && !isPrivatePacket( type ) ) )
break;
/* Skip the packet. If we get an error at this point, we don't
immediately bail out but try and return at least a partial
response */
status = pgpReadPacketHeader( stream, &ctb, &packetLength );
if( cryptStatusOK( status ) )
status = sSkip( stream, packetLength );
}
/* If we've reached the end of the current collection of key
packets, exit */
if( cryptStatusError( status ) || type != PGP_PACKET_USERID )
{
/* If there's no user ID present, set a generic label */
if( pgpInfo->lastUserID == 0 )
{
pgpInfo->userID[ 0 ] = "PGP key (no user ID found)";
pgpInfo->userIDlen[ 0 ] = 26;
pgpInfo->lastUserID = 1;
}
return( CRYPT_OK );
}
/* Record the userID */
status = pgpReadPacketHeader( stream, &ctb, &packetLength );
if( cryptStatusError( status ) )
return( status );
pgpInfo->userID[ pgpInfo->lastUserID ] = sMemBufPtr( stream );
pgpInfo->userIDlen[ pgpInfo->lastUserID++ ] = ( int ) packetLength;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -