static int readRDNcomponent( INOUT STREAM *stream, INOUT DN_COMPONENT **dnComponentListPtrPtr, IN_LENGTH_SHORT const int rdnDataLeft ) { CRYPT_ERRTYPE_TYPE dummy; BYTE stringBuffer[ MAX_ATTRIBUTE_SIZE + 8 ]; void *value; const int rdnStart = stell( stream ); int type, valueLength, valueStringType, stringTag; int flags = DN_FLAG_NOCHECK, status; assert( isWritePtr( stream, sizeof( STREAM ) ) ); assert( isWritePtr( dnComponentListPtrPtr, sizeof( DN_COMPONENT * ) ) ); REQUIRES( rdnDataLeft > 0 && rdnDataLeft < MAX_INTLENGTH_SHORT ); REQUIRES( rdnStart > 0 && rdnStart < MAX_INTLENGTH_SHORT ); /* Read the type information for this AVA */ status = readAVA( stream, &type, &valueLength, &stringTag ); if( cryptStatusError( status ) ) return( status ); if( valueLength <= 0 ) { /* Skip broken AVAs with zero-length strings */ return( CRYPT_OK ); } status = sMemGetDataBlock( stream, &value, valueLength ); if( cryptStatusOK( status ) ) status = sSkip( stream, valueLength ); if( cryptStatusError( status ) ) return( status ); ANALYSER_HINT( value != NULL ); /* If there's room for another AVA, mark this one as being continued. The +10 value is the minimum length for an AVA: SEQUENCE { OID, value } (2-bytes SEQUENCE + 5 bytes OID + 2 bytes (tag + length) + 1 byte min- length data). We don't do a simple =/!= check to get around incorrectly encoded lengths */ if( rdnDataLeft >= ( stell( stream ) - rdnStart ) + 10 ) flags |= DN_FLAG_CONTINUED; /* Convert the string into the local character set */ status = copyFromAsn1String( stringBuffer, MAX_ATTRIBUTE_SIZE, &valueLength, &valueStringType, value, valueLength, stringTag ); if( cryptStatusError( status ) ) return( status ); /* Add the DN component to the DN. If we hit a non-memory related error we turn it into a generic CRYPT_ERROR_BADDATA error since the other codes are somewhat too specific for this case, something like CRYPT_ERROR_INITED or an arg error isn't too useful for the caller */ status = insertDNstring( dnComponentListPtrPtr, type, stringBuffer, valueLength, valueStringType, flags, &dummy ); return( ( cryptStatusError( status ) && status != CRYPT_ERROR_MEMORY ) ? \ CRYPT_ERROR_BADDATA : status ); }
static int readConfigOption( INOUT STREAM *stream, IN_HANDLE CRYPT_USER iCryptUser ) { CRYPT_ATTRIBUTE_TYPE attributeType; const BUILTIN_OPTION_INFO *builtinOptionInfoPtr; MESSAGE_DATA msgData; void *dataPtr DUMMY_INIT_PTR; long optionCode; int value, tag, length, status; /* Read the wrapper and option index and map it to the actual option. If we find an unknown index or one that shouldn't be writeable to persistent storage, we skip it and continue. This is done to handle new options that may have been added after this version of cryptlib was built (for unknown indices) and because the stored configuration options are an untrusted source so we have to check for attempts to feed in bogus values (for non-writeable options) */ readSequence( stream, NULL ); status = readShortInteger( stream, &optionCode ); if( cryptStatusError( status ) ) return( status ); if( optionCode < 0 || optionCode > LAST_OPTION_INDEX ) { /* Unknown option, ignore it */ return( readUniversal( stream ) ); } builtinOptionInfoPtr = getBuiltinOptionInfoByCode( optionCode ); if( builtinOptionInfoPtr == NULL || \ builtinOptionInfoPtr->index < 0 || \ builtinOptionInfoPtr->index > LAST_OPTION_INDEX || \ builtinOptionInfoPtr->index == CRYPT_UNUSED ) { /* Unknown option, ignore it */ return( readUniversal( stream ) ); } attributeType = builtinOptionInfoPtr->option; /* Read the option value and set the option. We don't treat a failure to set the option as a problem since the user probably doesn't want the entire system to fail because of a bad configuration option, and in any case we'll fall back to a safe default value */ status = tag = peekTag( stream ); if( cryptStatusError( status ) ) return( status ); if( tag == BER_BOOLEAN || tag == BER_INTEGER ) { /* It's a numeric value, read the appropriate type and try and set the option */ if( tag == BER_BOOLEAN ) status = readBoolean( stream, &value ); else { long integer; status = readShortInteger( stream, &integer ); if( cryptStatusOK( status ) ) value = ( int ) integer; } if( cryptStatusError( status ) ) return( status ); ( void ) krnlSendMessage( iCryptUser, IMESSAGE_SETATTRIBUTE, &value, attributeType ); return( CRYPT_OK ); } /* It's a string value, set the option straight from the encoded data */ status = readGenericHole( stream, &length, 1, BER_STRING_UTF8 ); if( cryptStatusOK( status ) ) status = sMemGetDataBlock( stream, &dataPtr, length ); if( cryptStatusOK( status ) ) status = sSkip( stream, length, SSKIP_MAX ); if( cryptStatusError( status ) ) return( status ); setMessageData( &msgData, dataPtr, length ); ( void ) krnlSendMessage( iCryptUser, IMESSAGE_SETATTRIBUTE_S, &msgData, attributeType ); return( CRYPT_OK ); }
static int readKeyIdentifiers( INOUT STREAM *stream, INOUT PKCS15_INFO *pkcs15infoPtr, IN_LENGTH const int endPos ) { int iterationCount, status; assert( isWritePtr( stream, sizeof( STREAM ) ) ); assert( isWritePtr( pkcs15infoPtr, sizeof( PKCS15_INFO ) ) ); REQUIRES( endPos > 0 && endPos > stell( stream ) && \ endPos < MAX_INTLENGTH ); for( status = CRYPT_OK, iterationCount = 0; cryptStatusOK( status ) && stell( stream ) < endPos && \ iterationCount < FAILSAFE_ITERATIONS_MED; iterationCount++ ) { long value; int payloadLength; /* Read each identifier type and copy the useful ones into the PKCS #15 information */ readSequence( stream, &payloadLength ); status = readShortInteger( stream, &value ); if( cryptStatusError( status ) ) return( status ); switch( value ) { case PKCS15_KEYID_ISSUERANDSERIALNUMBER: { HASHFUNCTION_ATOMIC hashFunctionAtomic; void *iAndSPtr = DUMMY_INIT_PTR; int iAndSLength, hashSize; /* If we've already got the iAndSID, use that version instead */ if( pkcs15infoPtr->iAndSIDlength > 0 ) { status = readUniversal( stream ); continue; } /* Hash the full issuerAndSerialNumber to get an iAndSID */ getHashAtomicParameters( CRYPT_ALGO_SHA1, 0, &hashFunctionAtomic, &hashSize ); status = getStreamObjectLength( stream, &iAndSLength ); if( cryptStatusOK( status ) ) status = sMemGetDataBlock( stream, &iAndSPtr, iAndSLength ); if( cryptStatusOK( status ) ) status = sSkip( stream, iAndSLength ); if( cryptStatusError( status ) ) return( status ); hashFunctionAtomic( pkcs15infoPtr->iAndSID, KEYID_SIZE, iAndSPtr, iAndSLength ); pkcs15infoPtr->iAndSIDlength = hashSize; break; } case PKCS15_KEYID_SUBJECTKEYIDENTIFIER: status = readOctetString( stream, pkcs15infoPtr->keyID, &pkcs15infoPtr->keyIDlength, 8, CRYPT_MAX_HASHSIZE ); break; case PKCS15_KEYID_ISSUERANDSERIALNUMBERHASH: /* If we've already got the iAndSID by hashing the issuerAndSerialNumber, use that version instead */ if( pkcs15infoPtr->iAndSIDlength > 0 ) { status = readUniversal( stream ); continue; } status = readOctetString( stream, pkcs15infoPtr->iAndSID, &pkcs15infoPtr->iAndSIDlength, KEYID_SIZE, KEYID_SIZE ); break; case PKCS15_KEYID_ISSUERNAMEHASH: status = readOctetString( stream, pkcs15infoPtr->issuerNameID, &pkcs15infoPtr->issuerNameIDlength, KEYID_SIZE, KEYID_SIZE ); break; case PKCS15_KEYID_SUBJECTNAMEHASH: status = readOctetString( stream, pkcs15infoPtr->subjectNameID, &pkcs15infoPtr->subjectNameIDlength, KEYID_SIZE, KEYID_SIZE ); break; case PKCS15_KEYID_PGP2: status = readOctetString( stream, pkcs15infoPtr->pgp2KeyID, &pkcs15infoPtr->pgp2KeyIDlength, PGP_KEYID_SIZE, PGP_KEYID_SIZE ); break; case PKCS15_KEYID_OPENPGP: status = readOctetString( stream, pkcs15infoPtr->openPGPKeyID, &pkcs15infoPtr->openPGPKeyIDlength, PGP_KEYID_SIZE, PGP_KEYID_SIZE ); break; default: status = readUniversal( stream ); } } if( iterationCount >= FAILSAFE_ITERATIONS_MED ) { /* This could be either an internal error or some seriously malformed data, since we can't tell without human intervention we throw a debug exception but otherwise treat it as bad data */ DEBUG_DIAG(( "Encountered more than %d key IDs", FAILSAFE_ITERATIONS_MED )); assert( DEBUG_WARN ); return( CRYPT_ERROR_BADDATA ); } return( status ); }
static int readRDNcomponent( INOUT STREAM *stream, INOUT DN_COMPONENT **dnComponentListPtrPtr, IN_LENGTH_SHORT const int rdnDataLeft ) { CRYPT_ERRTYPE_TYPE dummy; BYTE stringBuffer[ MAX_ATTRIBUTE_SIZE + 8 ]; void *value; const int rdnStart = stell( stream ); int type, valueLength, valueStringType, stringTag; int flags = DN_FLAG_NOCHECK, status; assert( isWritePtr( stream, sizeof( STREAM ) ) ); assert( isWritePtr( dnComponentListPtrPtr, sizeof( DN_COMPONENT * ) ) ); REQUIRES( rdnDataLeft > 0 && rdnDataLeft < MAX_INTLENGTH_SHORT ); REQUIRES( rdnStart > 0 && rdnStart < MAX_INTLENGTH_SHORT ); /* Read the type information for this AVA */ status = readAVA( stream, &type, &valueLength, &stringTag ); if( cryptStatusError( status ) ) { /* If this is an unrecognised AVA, don't try and process it (the content will already have been skipped in readAVA()) */ if( status == OK_SPECIAL ) return( CRYPT_OK ); return( status ); } /* Make sure that the string is a valid type for a DirectoryString. We don't allow Universal strings since no-one in their right mind uses those. Alongside DirectoryString values we also allow IA5Strings, from the practice of stuffing email addresses into DNs */ if( stringTag != BER_STRING_PRINTABLE && stringTag != BER_STRING_T61 && \ stringTag != BER_STRING_BMP && stringTag != BER_STRING_UTF8 && \ stringTag != BER_STRING_IA5 ) return( CRYPT_ERROR_BADDATA ); /* Skip broken AVAs with zero-length strings */ if( valueLength <= 0 ) return( CRYPT_OK ); /* Record the string contents, avoiding moving it into a buffer */ status = sMemGetDataBlock( stream, &value, valueLength ); if( cryptStatusOK( status ) ) status = sSkip( stream, valueLength, MAX_INTLENGTH_SHORT ); if( cryptStatusError( status ) ) return( status ); ANALYSER_HINT( value != NULL ); /* If there's room for another AVA, mark this one as being continued. The +10 value is the minimum length for an AVA: SEQUENCE { OID, value } (2-bytes SEQUENCE + 5 bytes OID + 2 bytes (tag + length) + 1 byte min- length data). We don't do a simple =/!= check to get around incorrectly encoded lengths */ if( rdnDataLeft >= ( stell( stream ) - rdnStart ) + 10 ) flags |= DN_FLAG_CONTINUED; /* Convert the string into the local character set */ status = copyFromAsn1String( stringBuffer, MAX_ATTRIBUTE_SIZE, &valueLength, &valueStringType, value, valueLength, stringTag ); if( cryptStatusError( status ) ) return( status ); /* Add the DN component to the DN. If we hit a non-memory related error we turn it into a generic CRYPT_ERROR_BADDATA error since the other codes are somewhat too specific for this case, something like CRYPT_ERROR_INITED or an arg error isn't too useful for the caller */ status = insertDNstring( dnComponentListPtrPtr, type, stringBuffer, valueLength, valueStringType, flags, &dummy ); return( ( cryptStatusError( status ) && status != CRYPT_ERROR_MEMORY ) ? \ CRYPT_ERROR_BADDATA : status ); }
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, isPrimaryKey = FALSE; 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; isPublicKey = FALSE; break; case PGP_PACKET_SECKEY: isPublicKey = FALSE; break; case PGP_PACKET_PUBKEY_SUB: keyInfo = &pgpInfo->subKey; break; case PGP_PACKET_PUBKEY: isPrimaryKey = TRUE; 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 ) ); } /* Since there can (in theory) be arbitrary numbers of subkeys and other odds and ends attached to a key and the details of what to do with these things gets a bit vague, we just skip any further subkeys that may be present */ if( keyInfo->pkcAlgo != CRYPT_ALGO_NONE ) { status = sSkip( stream, packetLength ); for( iterationCount = 0; cryptStatusOK( status ) && \ iterationCount < FAILSAFE_ITERATIONS_MED; iterationCount++ ) { status = readUserID( stream, pgpInfo, isPrimaryKey ? &hashInfo : NULL ); } ENSURES( iterationCount < FAILSAFE_ITERATIONS_MED ); if( cryptStatusError( status ) && status != OK_SPECIAL ) { retExt( status, ( status, errorInfo, "Invalid additional PGP subkey information for key " "packet group %d", keyGroupNo ) ); } /* We've skipped the current subkey, we're done */ return( CRYPT_OK ); } /* 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_BUFFER_SIZE ); 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 ) { DEBUG_DIAG(( "Encountered unrecognised algorithm while " "reading key" )); 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 ) ) { DEBUG_DIAG(( "Couldn't set up reference to key data" )); assert( DEBUG_WARN ); return( status ); } pubKeyPayloadLen = stell( stream ) - pubKeyPayloadPos; status = sMemGetDataBlockAbs( stream, pubKeyPayloadPos, &pubKeyPayload, pubKeyPayloadLen ); if( cryptStatusError( status ) ) { DEBUG_DIAG(( "Couldn't set up reference to key data" )); assert( DEBUG_WARN ); return( status ); } /* Complete the packet header that we read earlier on by adding the length information */ packetHeader[ 1 ] = intToByte( ( ( 1 + 4 + length ) >> 8 ) & 0xFF ); packetHeader[ 2 ] = intToByte( ( 1 + 4 + length ) & 0xFF ); /* Hash the data needed to generate the OpenPGP keyID */ getHashParameters( CRYPT_ALGO_SHA1, 0, &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 ) { DEBUG_DIAG(( "Encountered unrecognised algorithm while " "reading key" )); 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 ); } /* If it's the primary key, start hashing it in preparation for performing signature checks on subpackets */ if( isPrimaryKey ) { packetHeader[ 0 ] = 0x99; packetHeader[ 1 ] = intToByte( ( keyInfo->pubKeyDataLen >> 8 ) & 0xFF ); packetHeader[ 2 ] = intToByte( keyInfo->pubKeyDataLen & 0xFF ); hashFunction( hashInfo, NULL, 0, packetHeader, 1 + 2, HASH_STATE_START ); hashFunction( hashInfo, NULL, 0, keyInfo->pubKeyData, keyInfo->pubKeyDataLen, HASH_STATE_CONTINUE ); } /* Read any associated subpacket(s), of which the only ones of real interest are the userID packet(s) */ for( iterationCount = 0; cryptStatusOK( status ) && \ iterationCount < FAILSAFE_ITERATIONS_MED; iterationCount++ ) { status = readUserID( stream, pgpInfo, isPrimaryKey ? &hashInfo : NULL ); } ENSURES( iterationCount < FAILSAFE_ITERATIONS_MED ); if( cryptStatusError( status ) && status != OK_SPECIAL ) { retExt( status, ( status, errorInfo, "Invalid PGP userID information for key packet group %d", keyGroupNo ) ); } /* 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 ); }
static int readUserID( INOUT STREAM *stream, INOUT_OPT PGP_INFO *pgpInfo, INOUT_OPT HASHINFO *hashInfo ) { HASHINFO localHashInfo; long packetLength; int ctb, packetType = DUMMY_INIT, iterationCount, status; assert( isWritePtr( stream, sizeof( STREAM ) ) ); assert( pgpInfo == NULL || \ isWritePtr( pgpInfo, sizeof( PGP_INFO ) ) ); assert( hashInfo == NULL || \ isWritePtr( hashInfo, sizeof( HASHINFO ) ) ); /* Take a local copy of the hash information from the primary key packet */ if( hashInfo != NULL ) memcpy( &localHashInfo, hashInfo, sizeof( HASHINFO ) ); /* 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( cryptStatusError( status ) ) break; 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 (unless we're skipping the packet). 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 != NULL && 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 ) ); }
static int readPkiHeader( INOUT STREAM *stream, INOUT CMP_PROTOCOL_INFO *protocolInfo, INOUT ERROR_INFO *errorInfo, const BOOLEAN isServerInitialMessage ) { int length, endPos, status; assert( isWritePtr( stream, sizeof( STREAM ) ) ); assert( isWritePtr( protocolInfo, sizeof( CMP_PROTOCOL_INFO ) ) ); assert( isWritePtr( errorInfo, sizeof( ERROR_INFO ) ) ); /* Clear per-message state information */ protocolInfo->userIDchanged = protocolInfo->certIDchanged = \ protocolInfo->useMACreceive = FALSE; protocolInfo->macInfoPos = CRYPT_ERROR; protocolInfo->senderDNPtr = NULL; protocolInfo->senderDNlength = 0; protocolInfo->headerRead = FALSE; /* Read the wrapper and skip the static information, which matches what we sent and is protected by the MAC so there's little point in looking at it */ status = readSequence( stream, &length ); if( cryptStatusError( status ) ) return( status ); endPos = stell( stream ) + length; readShortInteger( stream, NULL ); /* Version */ if( !protocolInfo->isCryptlib ) { /* The ID of the key used for integrity protection (or in general the identity of the sender) can be specified either as the sender DN or the senderKID or both, or in some cases even indirectly via the transaction ID. With no real guidance as to which one to use, implementors are using any of these options to identify the key. Since we need to check that the integrity-protection key that we're using is correct so that we can report a more appropriate error than bad signature or bad data, we need to remember the sender DN for later in case this is the only form of key identification provided. Unfortunately since the sender DN can't uniquely identify a certificate, if this is the only identifier that we're given then the caller can still get a bad signature error, yet another one of CMPs many wonderful features */ status = readConstructed( stream, &protocolInfo->senderDNlength, 4 ); if( cryptStatusOK( status ) && protocolInfo->senderDNlength > 0 ) { status = sMemGetDataBlock( stream, &protocolInfo->senderDNPtr, protocolInfo->senderDNlength ); if( cryptStatusOK( status ) ) status = readUniversal( stream ); } } else { /* cryptlib includes a proper certID so the whole signer identification mess is avoided and we can ignore the sender DN */ status = readUniversal( stream ); /* Sender DN */ } if( cryptStatusOK( status ) ) status = readUniversal( stream ); /* Recipient DN */ if( peekTag( stream ) == MAKE_CTAG( CTAG_PH_MESSAGETIME ) ) status = readUniversal( stream ); /* Message time */ if( cryptStatusError( status ) ) { retExt( CRYPT_ERROR_BADDATA, ( CRYPT_ERROR_BADDATA, errorInfo, "Invalid DN information in PKI header" ) ); } if( peekTag( stream ) != MAKE_CTAG( CTAG_PH_PROTECTIONALGO ) ) { /* The message was sent without integrity protection, report it as a signature error rather than the generic bad data error that we'd get from the following read */ retExt( CRYPT_ERROR_SIGNATURE, ( CRYPT_ERROR_SIGNATURE, errorInfo, "Message was sent without integrity protection" ) ); } status = readProtectionAlgo( stream, protocolInfo ); if( cryptStatusError( status ) ) { retExt( status, ( status, errorInfo, "Invalid integrity protection information in PKI " "header" ) ); } if( peekTag( stream ) == MAKE_CTAG( CTAG_PH_SENDERKID ) ) { /* Sender protection keyID */ status = readUserID( stream, protocolInfo ); if( cryptStatusError( status ) ) { retExt( status, ( status, errorInfo, "Invalid PKI user ID in PKI header" ) ); } } else { /* If we're the server, the client must provide a PKI user ID in the first message unless we got one in an earlier transaction */ if( isServerInitialMessage && protocolInfo->userIDsize <= 0 ) { retExt( CRYPT_ERROR_BADDATA, ( CRYPT_ERROR_BADDATA, errorInfo, "Missing PKI user ID in PKI header" ) ); } } if( peekTag( stream ) == MAKE_CTAG( CTAG_PH_RECIPKID ) ) readUniversal( stream ); /* Recipient protection keyID */ /* Record the transaction ID (which is effectively the nonce) or make sure that it matches the one that we sent. There's no real need to do an explicit duplicate check since a replay attempt will be rejected as a duplicate by the certificate store and the locking performed at that level makes it a much better place to catch duplicates, but we do it anyway because it doesn't cost anything and we can catch at least some problems a bit earlier */ status = readConstructed( stream, NULL, CTAG_PH_TRANSACTIONID ); if( cryptStatusError( status ) ) { retExt( status, ( status, errorInfo, "Missing transaction ID in PKI header" ) ); } status = readTransactionID( stream, protocolInfo, isServerInitialMessage ); if( cryptStatusError( status ) ) { protocolInfo->pkiFailInfo = CMPFAILINFO_BADRECIPIENTNONCE; retExt( status, ( status, errorInfo, ( status == CRYPT_ERROR_SIGNATURE ) ? \ "Returned message transaction ID doesn't match our " "transaction ID" : \ "Invalid transaction ID in PKI header" ) ); } /* Read the sender nonce, which becomes the new recipient nonce, and skip the recipient nonce if there's one present. These values may be absent, either because the other side doesn't implement them or because they're not available, for example because it's sending a response to an error that occurred before it could read the nonce from a request. In any case we don't bother checking the nonce values since the transaction ID serves the same purpose */ if( stell( stream ) < endPos && \ peekTag( stream ) == MAKE_CTAG( CTAG_PH_SENDERNONCE ) ) { readConstructed( stream, NULL, CTAG_PH_SENDERNONCE ); status = readOctetString( stream, protocolInfo->recipNonce, &protocolInfo->recipNonceSize, 4, CRYPT_MAX_HASHSIZE ); if( cryptStatusError( status ) ) { protocolInfo->pkiFailInfo = CMPFAILINFO_BADSENDERNONCE; retExt( status, ( status, errorInfo, "Invalid sender nonce in PKI header" ) ); } } if( stell( stream ) < endPos && \ peekTag( stream ) == MAKE_CTAG( CTAG_PH_RECIPNONCE ) ) { readConstructed( stream, NULL, CTAG_PH_RECIPNONCE ); status = readUniversal( stream ); if( cryptStatusError( status ) ) { protocolInfo->pkiFailInfo = CMPFAILINFO_BADRECIPIENTNONCE; retExt( status, ( status, errorInfo, "Invalid recipient nonce in PKI header" ) ); } } /* Remember that we've successfully read enough of the header information to generate a response */ protocolInfo->headerRead = TRUE; /* Skip any further junk and process the general information if there is any */ if( stell( stream ) < endPos && \ peekTag( stream ) == MAKE_CTAG( CTAG_PH_FREETEXT ) ) { status = readUniversal( stream ); /* Junk */ if( cryptStatusError( status ) ) return( status ); } if( stell( stream ) < endPos && \ peekTag( stream ) == MAKE_CTAG( CTAG_PH_GENERALINFO ) ) { status = readGeneralInfo( stream, protocolInfo ); if( cryptStatusError( status ) ) { retExt( status, ( status, errorInfo, "Invalid generalInfo information in PKI header" ) ); } } return( CRYPT_OK ); }