📄 dbxpgp.c
字号:
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;
status = sSkip( stream, packetLength );
}
return( status );
}
/* Read an entire keyring. This function can be used in one of two ways, if
key match information is supplied each packet will be checked against it
and the read will exit when a match is found. If no key match info is
supplied, all keys will be read into memory */
static int readKeyring( KEYSET_INFO *keysetInfo,
const KEY_MATCH_INFO *keyMatchInfo,
PGP_KEYINFO **matchedKeyInfoPtrPtr )
{
PGP_INFO *pgpInfo = ( PGP_INFO * ) keysetInfo->keyData;
STREAM *stream = &keysetInfo->keysetFile->stream;
BYTE streamBuffer[ STREAM_BUFSIZE ], *buffer = NULL;
BOOLEAN moreData = TRUE;
int bufEnd = 0, keyGroupNo = 0, status;
assert( ( keyMatchInfo == NULL && matchedKeyInfoPtrPtr == NULL ) || \
( keyMatchInfo != NULL && matchedKeyInfoPtrPtr != NULL ) );
assert( keyMatchInfo == NULL || \
( pgpInfo->keyData != NULL && \
pgpInfo->keyDataLen == KEYRING_BUFSIZE ) );
/* Clear the return value */
if( matchedKeyInfoPtrPtr != NULL )
*matchedKeyInfoPtrPtr = NULL;
if( ( buffer = clAlloc( "readKeyring", KEYRING_BUFSIZE ) ) == NULL )
{
keysetInfo->shutdownFunction( keysetInfo );
return( CRYPT_ERROR_MEMORY );
}
/* Since PGP keyrings just contain an arbitrary collection of packets
concatenated together, we can't tell in advance how much data we
should be reading. Because of this we have to set the file stream to
allow partial reads without returning a read error */
sioctl( stream, STREAM_IOCTL_PARTIALREAD, NULL, 0 );
/* Scan all the objects in the file. This is implemented as a sliding
window that reads a certain amount of data into a lookahead buffer
and then tries to identify a packet group in the buffer. If we need
to skip packets (for example due to unknown algorithms), we mark the
keyset as read-only since it's no longer safe for us to write the
incompletely-processed data to disk */
sioctl( stream, STREAM_IOCTL_IOBUFFER, streamBuffer, STREAM_BUFSIZE );
while( keyGroupNo < MAX_PGP_OBJECTS && \
( moreData || bufEnd > 0 ) )
{
PGP_INFO *pgpInfoPtr = &pgpInfo[ keyGroupNo ];
STREAM keyStream;
int length;
/* Fill the lookahead buffer */
if( moreData )
{
status = length = sread( stream, buffer + bufEnd,
KEYRING_BUFSIZE - bufEnd );
if( status <= 0 )
{
/* If we read nothing and there's nothing left in the buffer,
we're done */
if( bufEnd <= 0 )
{
/* If we've previously read at least one group of key
packets, we're OK */
if( keyGroupNo > 0 )
status = CRYPT_OK;
break;
}
/* There's still data in the buffer, we can continue until
we drain it */
length = 0;
}
if( length < KEYRING_BUFSIZE - bufEnd )
/* We didn't get as much as we requested, there's nothing
left to read */
moreData = FALSE;
bufEnd += length;
}
/* Determine the size of the group of key packets in the buffer */
status = scanPacketGroup( buffer, bufEnd, &length );
if( status == OK_SPECIAL )
{
/* We couldn't process one or more packets, make the keyset
read-only to ensure that the incomplete key data isn't
written to disk */
keysetInfo->options = CRYPT_KEYOPT_READONLY;
/* If the packet group is contained within the buffer, remove
the problem packets and continue */
if( length <= bufEnd )
{
if( bufEnd - length > 0 )
memmove( buffer, buffer + length, bufEnd - length );
bufEnd -= length;
continue;
}
/* The packet group overflows the buffer, skip the remaining
contents and continue */
status = sseek( stream, stell( stream ) + ( length - bufEnd ) );
if( cryptStatusError( status ) )
break;
bufEnd = 0;
continue;
}
if( cryptStatusError( status ) || length <= 0 )
break;
/* Move the packet group from the keyring buffer to the key data */
if( keyMatchInfo == NULL )
{
/* It's a read of all packets, allocate room for the current
packet group */
if( ( pgpInfoPtr->keyData = \
clAlloc( "readKeyring", length ) ) == NULL )
{
status = CRYPT_ERROR_MEMORY;
break;
}
pgpInfoPtr->keyDataLen = length;
}
memcpy( pgpInfoPtr->keyData, buffer, length );
if( bufEnd - length > 0 )
memmove( buffer, buffer + length, bufEnd - length );
bufEnd -= length;
/* Process the information in the packet group */
if( keyMatchInfo != NULL )
{
/* We're processing multiple keys, clear the index info before
we read the current keys */
memset( &pgpInfoPtr->key, 0, sizeof( PGP_KEYINFO ) );
memset( &pgpInfoPtr->subKey, 0, sizeof( PGP_KEYINFO ) );
memset( pgpInfo->userID, 0, sizeof( char * ) * MAX_PGP_USERIDS );
memset( pgpInfo->userID, 0, sizeof( int ) * MAX_PGP_USERIDS );
pgpInfoPtr->lastUserID = 0;
}
sMemConnect( &keyStream, pgpInfoPtr->keyData, length );
do
status = readKey( &keyStream, pgpInfoPtr );
while( cryptStatusOK( status ) && sMemDataLeft( &keyStream ) > 0 );
sMemDisconnect( &keyStream );
if( cryptStatusError( status ) )
{
if( status != OK_SPECIAL )
break;
/* There's something in the key information that we can't
handle, mark the keyring as read-only and skip the key */
keysetInfo->options = CRYPT_KEYOPT_READONLY;
if( keyMatchInfo == NULL )
pgpFreeEntry( pgpInfoPtr );
status = CRYPT_OK;
}
else
/* If we're searching for a particular key, see if this is the
one */
if( keyMatchInfo != NULL )
{
if( checkKeyMatch( pgpInfo, &pgpInfo->key, keyMatchInfo ) )
{
*matchedKeyInfoPtrPtr = &pgpInfo->key;
break;
}
if( checkKeyMatch( pgpInfo, &pgpInfo->subKey, keyMatchInfo ) )
{
*matchedKeyInfoPtrPtr = &pgpInfo->subKey;
break;
}
}
else
/* We're reading all keys, move on to the next empty slot */
keyGroupNo++;
}
sioctl( stream, STREAM_IOCTL_IOBUFFER, NULL, 0 );
clFree( "readKeyring", buffer );
if( keyMatchInfo != NULL )
{
/* If we're scanning for a match and didn't find anything,
report it as an error */
if( *matchedKeyInfoPtrPtr == NULL )
status = CRYPT_ERROR_NOTFOUND;
}
else
{
/* If we're reading all keys and the read failed, the keyring
as a whole can't be used */
if( keyGroupNo == MAX_PGP_OBJECTS )
status = CRYPT_ERROR_OVERFLOW;
if( cryptStatusError( status ) )
keysetInfo->shutdownFunction( keysetInfo );
}
return( status );
}
/* Read key data from a PGP keyring */
static int getItemFunction( KEYSET_INFO *keysetInfo,
CRYPT_HANDLE *iCryptHandle,
const KEYMGMT_ITEM_TYPE itemType,
const CRYPT_KEYID_TYPE keyIDtype,
const void *keyID, const int keyIDlength,
void *auxInfo, int *auxInfoLength,
const int flags )
{
CRYPT_CONTEXT iSessionKey;
PGP_INFO *pgpInfo = ( PGP_INFO * ) keysetInfo->keyData;
PGP_KEYINFO *keyInfo;
MESSAGE_CREATEOBJECT_INFO createInfo;
MECHANISM_WRAP_INFO mechanismInfo;
RESOURCE_DATA msgData;
int status;
assert( itemType == KEYMGMT_ITEM_PUBLICKEY || \
itemType == KEYMGMT_ITEM_PRIVATEKEY );
assert( keyIDtype == CRYPT_KEYID_NAME || \
keyIDtype == CRYPT_KEYID_EMAIL || \
keyIDtype == CRYPT_IKEYID_KEYID || \
keyIDtype == CRYPT_IKEYID_PGPKEYID );
/* Find the requested item. This is complicated somewhat by the fact
that private keys are held in memory while public keys (which can
be arbitrarily numerous) are held on disk. This means that the former
(and also public keys read from a private-key keyring) are found with
a quick in-memory search while the latter require a scan of the
keyring on disk */
if( itemType == KEYMGMT_ITEM_PRIVATEKEY || \
keysetInfo->subType == KEYSET_SUBTYPE_PGP_PRIVATE )
{
/* Try and locate the appropriate object in the PGP collection */
pgpInfo = findEntry( keysetInfo->keyData, keyIDtype, keyID,
keyIDlength, flags, &keyInfo );
if( pgpInfo == NULL )
return( CRYPT_ERROR_NOTFOUND );
}
else
{
CONST_INIT KEY_MATCH_INFO keyMatchInfo = \
{ keyIDtype, keyID, keyIDlength, flags };
/* Try and find the required key in the file */
sseek( &keysetInfo->keysetFile->stream, 0 );
status = readKeyring( keysetInfo, &keyMatchInfo, &keyInfo );
if( cryptStatusError( status ) )
return( status );
}
/* If it's just a check or label read, we're done */
if( flags & ( KEYMGMT_FLAG_CHECK_ONLY | KEYMGMT_FLAG_LABEL_ONLY ) )
{
if( flags & KEYMGMT_FLAG_LABEL_ONLY )
{
const int userIDsize = min( pgpInfo->userIDlen[ 0 ],
CRYPT_MAX_TEXTSIZE );
*auxInfoLength = userIDsize;
if( auxInfo != NULL )
memcpy( auxInfo, pgpInfo->userID[ 0 ], userIDsize );
}
return( CRYPT_OK );
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -