📄 pgp_rd.c
字号:
return( CRYPT_OK );
}
/* If it's an unknown algorithm, skip this key */
if( pgpPkcAlgo != PGP_ALGO_DSA && pgpPkcAlgo != PGP_ALGO_ELGAMAL )
return( OK_SPECIAL );
/* DSA/Elgamal: p + g + y. Note that we have to separate out the
getMPIsize() calls in order to serialise them otherwise some
optimising compilers will reorder the operations, causing the check
to fail because the parameters are different for the different MPI
values */
if( pgpPkcAlgo == 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;
}
status = getMPIsize( stream, MIN_PKCSIZE, CRYPT_MAX_PKCSIZE, &length );
if( cryptStatusOK( status ) ) /* p */
{
totalLength += length;
status = getMPIsize( stream, 1, CRYPT_MAX_PKCSIZE, &length );
} /* g */
if( cryptStatusOK( status ) )
{
totalLength += length;
status = getMPIsize( stream, MIN_PKCSIZE, CRYPT_MAX_PKCSIZE,
&length ); /* y */
}
if( cryptStatusError( status ) )
return( status );
totalLength += length;
if( pgpPkcAlgo == PGP_ALGO_DSA )
{
/* DSA has q as well */
status = getMPIsize( stream, bitsToBytes( 155 ), CRYPT_MAX_PKCSIZE,
&length ); /* q */
if( cryptStatusError( status ) )
return( status );
totalLength += length;
}
*pubKeyComponentLength = totalLength;
return( CRYPT_OK );
}
/* Read a sequence of userID packets */
CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
static int readUserID( INOUT STREAM *stream,
INOUT PGP_INFO *pgpInfo )
{
long packetLength;
int ctb, packetType = DUMMY_INIT, iterationCount, status;
assert( isWritePtr( stream, sizeof( STREAM ) ) );
assert( isWritePtr( pgpInfo, sizeof( PGP_INFO ) ) );
/* 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 signatures, 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 also 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 */
for( status = CRYPT_OK, iterationCount = 0;
cryptStatusOK( status ) && iterationCount < FAILSAFE_ITERATIONS_MED;
iterationCount++ )
{
/* See what we've got. If we've run out of input or it's a non-key-
related packet, we're done */
status = ctb = sPeek( stream );
if( cryptStatusError( status ) )
break;
packetType = pgpGetPacketType( ctb );
if( packetType != PGP_PACKET_TRUST && \
packetType != PGP_PACKET_SIGNATURE && \
packetType != PGP_PACKET_USERATTR && \
!pgpIsReservedPacket( packetType ) )
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, \
getMinPacketSize( packetType ) );
if( cryptStatusOK( status ) )
status = sSkip( stream, packetLength );
}
ENSURES( iterationCount < FAILSAFE_ITERATIONS_MED );
/* If we've reached the end of the current collection of key packets,
let the caller know that we're done. Note that running out of input
is a valid condition so we don't return a fatal error code at this
point */
if( cryptStatusError( status ) || packetType != PGP_PACKET_USERID )
return( OK_SPECIAL );
/* Record the userID. If there are more userIDs than we can record we
silently ignore them. This handles keys with weird numbers of
userIDs without rejecting them just because they have, well, a weird
number of userIDs */
status = pgpReadPacketHeader( stream, &ctb, &packetLength, \
getMinPacketSize( packetType ) );
if( cryptStatusError( status ) )
return( status );
if( pgpInfo->lastUserID < MAX_PGP_USERIDS )
{
void *dataPtr;
status = sMemGetDataBlock( stream, &dataPtr, packetLength );
if( cryptStatusError( status ) )
return( status );
pgpInfo->userID[ pgpInfo->lastUserID ] = dataPtr;
pgpInfo->userIDlen[ pgpInfo->lastUserID++ ] = ( int ) packetLength;
}
return( sSkip( stream, packetLength ) );
}
/****************************************************************************
* *
* Read a Key *
* *
****************************************************************************/
/* Read a single key in a group of key packets */
CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2, 4 ) ) \
static int readKey( INOUT STREAM *stream,
INOUT PGP_INFO *pgpInfo,
IN_INT_SHORT_Z const int keyGroupNo,
INOUT ERROR_INFO *errorInfo )
{
PGP_KEYINFO *keyInfo = &pgpInfo->key;
HASHFUNCTION hashFunction;
HASHINFO hashInfo;
BYTE hash[ CRYPT_MAX_HASHSIZE + 8 ], packetHeader[ 64 + 8 ];
BOOLEAN isPublicKey = TRUE;
void *pubKeyPayload;
long packetLength;
int pubKeyPos, pubKeyPayloadPos, endPos, pubKeyPayloadLen;
int ctb, length, value, hashSize, iterationCount, status;
assert( isWritePtr( stream, sizeof( STREAM ) ) );
assert( isWritePtr( pgpInfo, sizeof( PGP_INFO ) ) );
REQUIRES( keyGroupNo >= 0 && keyGroupNo < MAX_INTLENGTH_SHORT );
REQUIRES( errorInfo != NULL );
/* Process the CTB and packet length */
ctb = sPeek( stream );
if( cryptStatusError( ctb ) )
{
/* If there was an error reading the CTB, which is the first byte of
the packet group, it means that we've run out of data so we
return the status as a not-found error rather than the actual
stream status */
return( CRYPT_ERROR_NOTFOUND );
}
switch( pgpGetPacketType( 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:
retExt( CRYPT_ERROR_BADDATA,
( CRYPT_ERROR_BADDATA, errorInfo,
"Invalid PGP CTB %02X for key packet group %d",
ctb, keyGroupNo ) );
}
status = pgpReadPacketHeader( stream, NULL, &packetLength, 64 );
if( cryptStatusError( status ) )
{
retExt( status,
( status, errorInfo,
"Invalid PGP key packet header for key packet group %d",
keyGroupNo ) );
}
if( packetLength < 64 || packetLength > sMemDataLeft( stream ) )
{
retExt( CRYPT_ERROR_BADDATA,
( CRYPT_ERROR_BADDATA, errorInfo,
"Invalid PGP key packet length %ld for key packet group %d",
packetLength, keyGroupNo ) );
}
/* Determine which bits make up the public and the private key data. The
public-key data starts at the version number and includes the date,
validity, and public-key components. Since there's no length
information included for this data block we have to record bookmarks
and then later retroactively calculate the length based on how much
data we've read in the meantime:
pubKey pubKeyPayload privKey endPos
| | | |
v v v v
+---+---------------------------+-------------------+
|hdr| | Public key | Private key |
+---+---------------------------+-------------------+
| |<pubKeyPayloadLen->| |
|<----- pubKeyDataLen ----->|<-- privKeyDLen -->|
|<--------------- packetLength ---------------->| */
pubKeyPos = stell( stream );
endPos = pubKeyPos + packetLength;
ENSURES( endPos > pubKeyPos && endPos < MAX_INTLENGTH );
status = value = sgetc( stream );
if( cryptStatusError( status ) )
return( status );
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[2] validity time - PGP 2.x only ]
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) */
status = sread( stream, packetHeader + 4, 4 );
if( !cryptStatusError( status ) && !pgpInfo->isOpenPGP )
status = sSkip( stream, 2 );
if( cryptStatusError( status ) )
return( status );
/* Read the public key components */
pubKeyPayloadPos = stell( stream );
status = readPublicKeyComponents( stream, keyInfo, &length );
if( cryptStatusError( status ) )
{
/* If the error status is OK_SPECIAL then the problem was an
unrecognised algorithm or something similar so we just skip the
packet */
if( status == OK_SPECIAL )
{
assert( DEBUG_WARN );
return( OK_SPECIAL );
}
retExt( status,
( status, errorInfo,
"Invalid PGP public-key components for key packet group %d",
keyGroupNo ) );
}
/* Now that we know where the public key data starts and finishes, we
can set up references to it */
keyInfo->pubKeyDataLen = stell( stream ) - pubKeyPos;
status = sMemGetDataBlockAbs( stream, pubKeyPos, &keyInfo->pubKeyData,
keyInfo->pubKeyDataLen );
if( cryptStatusError( status ) )
{
assert( DEBUG_WARN );
return( status );
}
pubKeyPayloadLen = stell( stream ) - pubKeyPayloadPos;
status = sMemGetDataBlockAbs( stream, pubKeyPayloadPos, &pubKeyPayload,
pubKeyPayloadLen );
if( cryptStatusError( status ) )
{
assert( DEBUG_WARN );
return( status );
}
/* 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_SHA1, &hashFunction, &hashSize );
hashFunction( hashInfo, NULL, 0, packetHeader, 1 + 2 + 1 + 4,
HASH_STATE_START );
hashFunction( hashInfo, hash, CRYPT_MAX_HASHSIZE,
pubKeyPayload, pubKeyPayloadLen, HASH_STATE_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 information for private-key components if
necessary */
status = readPrivateKeyDecryptionInfo( stream, keyInfo );
if( cryptStatusError( status ) )
{
/* If the error status is OK_SPECIAL then the problem was an
unrecognised algorithm or something similar so we just skip
the packet */
if( status == OK_SPECIAL )
{
assert( DEBUG_WARN );
return( OK_SPECIAL );
}
retExt( status,
( status, errorInfo,
"Invalid PGP private-key decryption information for "
"key packet group %d", keyGroupNo ) );
}
/* What's left is the private-key data */
keyInfo->privKeyDataLen = endPos - stell( stream );
status = sMemGetDataBlock( stream, &keyInfo->privKeyData,
keyInfo->privKeyDataLen );
if( cryptStatusOK( status ) )
status = sSkip( stream, keyInfo->privKeyDataLen );
if( cryptStatusError( status ) )
return( status );
}
/* Read the userID packet(s) */
for( iterationCount = 0; \
cryptStatusOK( status ) && iterationCount < FAILSAFE_ITERATIONS_MED; \
iterationCount++ )
{
status = readUserID( stream, pgpInfo );
}
ENSURES( iterationCount < FAILSAFE_ITERATIONS_MED );
if( cryptStatusError( status ) && status != OK_SPECIAL )
{
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -