int getPgpPacketInfo( INOUT STREAM *stream, OUT QUERY_INFO *queryInfo ) { const long startPos = stell( stream ); long offset, length; int ctb, status; assert( isWritePtr( stream, sizeof( STREAM ) ) ); assert( isWritePtr( queryInfo, sizeof( QUERY_INFO ) ) ); /* Clear return value */ memset( queryInfo, 0, sizeof( QUERY_INFO ) ); /* Read the packet header and extract information from the CTB. Note that the assignment of version numbers is speculative only because it's possible to use PGP 2.x packet headers to wrap up OpenPGP packets */ status = pgpReadPacketHeader( stream, &ctb, &length, 8 ); if( cryptStatusError( status ) ) return( status ); queryInfo->formatType = CRYPT_FORMAT_PGP; queryInfo->version = pgpGetPacketVersion( ctb ); offset = stell( stream ); if( cryptStatusError( offset ) ) return( offset ); queryInfo->size = ( offset - startPos ) + length; switch( pgpGetPacketType( ctb ) ) { case PGP_PACKET_SKE: queryInfo->type = CRYPT_OBJECT_ENCRYPTED_KEY; break; case PGP_PACKET_PKE: queryInfo->type = CRYPT_OBJECT_PKCENCRYPTED_KEY; break; case PGP_PACKET_SIGNATURE: queryInfo->type = CRYPT_OBJECT_SIGNATURE; break; case PGP_PACKET_SIGNATURE_ONEPASS: /* First half of a one-pass signature, this is given a special type of 'none' since it's not a normal packet */ queryInfo->type = CRYPT_OBJECT_NONE; break; default: DEBUG_DIAG(( "Found unrecognised ctb %X", ctb )); assert( DEBUG_WARN ); return( CRYPT_ERROR_BADDATA ); } /* Make sure that all of the data is present without resetting the stream */ return( ( sMemDataLeft( stream ) < length ) ? \ CRYPT_ERROR_UNDERFLOW : CRYPT_OK ); }
static int getObjectInfo( INOUT STREAM *stream, OUT QUERY_INFO *queryInfo ) { const long startPos = stell( stream ); long value; int tag, length, status; assert( isWritePtr( stream, sizeof( STREAM ) ) ); assert( isWritePtr( queryInfo, sizeof( QUERY_INFO ) ) ); /* Clear return value */ memset( queryInfo, 0, sizeof( QUERY_INFO ) ); /* We always need at least MIN_CRYPT_OBJECTSIZE more bytes to do anything */ if( sMemDataLeft( stream ) < MIN_CRYPT_OBJECTSIZE ) return( CRYPT_ERROR_UNDERFLOW ); /* Get the type, length, and version information */ status = getStreamObjectLength( stream, &length ); if( cryptStatusError( status ) ) return( status ); queryInfo->formatType = CRYPT_FORMAT_CRYPTLIB; queryInfo->size = length; tag = peekTag( stream ); if( cryptStatusError( tag ) ) return( tag ); readGenericHole( stream, NULL, 16, tag ); status = readShortInteger( stream, &value ); if( cryptStatusError( status ) ) return( status ); queryInfo->version = value; switch( tag ) { case BER_SEQUENCE: /* This could be a signature or a PKC-encrypted key, see what follows */ switch( value ) { case KEYTRANS_VERSION: case KEYTRANS_EX_VERSION: queryInfo->type = CRYPT_OBJECT_PKCENCRYPTED_KEY; break; case SIGNATURE_VERSION: case SIGNATURE_EX_VERSION: queryInfo->type = CRYPT_OBJECT_SIGNATURE; break; default: return( CRYPT_ERROR_BADDATA ); } if( value == KEYTRANS_VERSION || value == SIGNATURE_VERSION ) queryInfo->formatType = CRYPT_FORMAT_CMS; break; case MAKE_CTAG( CTAG_RI_KEYAGREE ): /* It's CMS' wierd X9.42-inspired key agreement mechanism, we can't do much with this (mind you neither can anyone else) so we should probably really treat it as a CRYPT_ERROR_BADDATA if we encounter it rather than just ignoring it */ queryInfo->type = CRYPT_OBJECT_NONE; DEBUG_DIAG(( "Found keyAgreeRecipientInfo" )); assert( DEBUG_WARN ); break; case MAKE_CTAG( CTAG_RI_PWRI ): queryInfo->type = CRYPT_OBJECT_ENCRYPTED_KEY; break; default: queryInfo->type = CRYPT_OBJECT_NONE; if( tag > MAKE_CTAG( CTAG_RI_PWRI ) && \ tag <= MAKE_CTAG( CTAG_RI_MAX ) ) { /* This is probably a new RecipientInfo type, skip it */ DEBUG_DIAG(( "Found unknown RecipientInfo %X", tag )); assert( DEBUG_WARN ); break; } return( CRYPT_ERROR_BADDATA ); } /* Reset the stream and make sure that all of the data is present */ sseek( stream, startPos ); return( sMemDataLeft( stream ) < queryInfo->size ? \ CRYPT_ERROR_UNDERFLOW : CRYPT_OK ); }
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 processPacketGroup( INOUT STREAM *stream, INOUT PGP_INFO *pgpInfo, IN_OPT const KEY_MATCH_INFO *keyMatchInfo, INOUT_OPT PGP_KEYINFO **matchedKeyInfoPtrPtr, IN_INT_SHORT_Z const int keyGroupNo, INOUT ERROR_INFO *errorInfo ) { int iterationCount, status; assert( isWritePtr( stream, sizeof( STREAM ) ) ); assert( isWritePtr( pgpInfo, sizeof( PGP_INFO ) ) ); assert( ( keyMatchInfo == NULL && matchedKeyInfoPtrPtr == NULL ) || \ ( isReadPtr( keyMatchInfo, sizeof( KEY_MATCH_INFO ) ) && \ isWritePtr( matchedKeyInfoPtrPtr, sizeof( PGP_KEYINFO * ) ) ) ); REQUIRES( ( keyMatchInfo == NULL && matchedKeyInfoPtrPtr == NULL ) || \ ( keyMatchInfo != NULL && matchedKeyInfoPtrPtr != NULL && \ pgpInfo->keyData != NULL && \ pgpInfo->keyDataLen == KEYRING_BUFSIZE ) ); REQUIRES( keyGroupNo >= 0 && keyGroupNo < MAX_INTLENGTH_SHORT ); REQUIRES( errorInfo != NULL ); /* Clear the index information before we read the current key(s), since it may already have been initialised during a previous (incomplete) key read */ memset( &pgpInfo->key, 0, sizeof( PGP_KEYINFO ) ); memset( &pgpInfo->subKey, 0, sizeof( PGP_KEYINFO ) ); memset( pgpInfo->userID, 0, sizeof( char * ) * MAX_PGP_USERIDS ); memset( pgpInfo->userIDlen, 0, sizeof( int ) * MAX_PGP_USERIDS ); pgpInfo->lastUserID = 0; /* Read all the packets in this packet group */ for( status = CRYPT_OK, iterationCount = 0; cryptStatusOK( status ) && sMemDataLeft( stream ) > 0 && \ iterationCount++ < FAILSAFE_ITERATIONS_MED; iterationCount++ ) { status = readKey( stream, pgpInfo, keyGroupNo, errorInfo ); } ENSURES( iterationCount < FAILSAFE_ITERATIONS_MED ); if( cryptStatusError( status ) ) { if( status != OK_SPECIAL && status != CRYPT_ERROR_NOSECURE ) return( status ); /* There's either something in the key information that we can't handle or it's stored with no security, skip the key */ if( keyMatchInfo == NULL ) pgpFreeEntry( pgpInfo ); return( status ); } /* If we're reading all keys, we're done */ if( keyMatchInfo == NULL ) return( CRYPT_OK ); /* We're searching for a particular key, see if this is the one */ if( pgpCheckKeyMatch( pgpInfo, &pgpInfo->key, keyMatchInfo ) ) { *matchedKeyInfoPtrPtr = &pgpInfo->key; return( CRYPT_OK ); } if( pgpCheckKeyMatch( pgpInfo, &pgpInfo->subKey, keyMatchInfo ) ) { *matchedKeyInfoPtrPtr = &pgpInfo->subKey; return( CRYPT_OK ); } /* No match, tell the caller to keep looking */ return( CRYPT_ERROR_NOTFOUND ); }
static int writePkiHeader( INOUT STREAM *stream, INOUT SESSION_INFO *sessionInfoPtr, INOUT CMP_PROTOCOL_INFO *protocolInfo ) { CRYPT_HANDLE senderNameObject = DUMMY_INIT, recipNameObject = DUMMY_INIT; STREAM nullStream; MESSAGE_DATA msgData; #ifdef USE_FULL_HEADERS const BOOLEAN sendFullHeader = TRUE; #else BOOLEAN sendFullHeader = FALSE; #endif /* USE_FULL_HEADERS */ BOOLEAN sendClibID = FALSE, sendCertID = FALSE, sendMacInfo = FALSE; BOOLEAN sendUserID = FALSE; int senderNameLength, recipNameLength, attributeLength = 0; int protInfoLength = DUMMY_INIT, totalLength, hashAlgo, status; assert( isWritePtr( stream, sizeof( STREAM ) ) ); assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) ); assert( isWritePtr( protocolInfo, sizeof( CMP_PROTOCOL_INFO ) ) ); /* Determine which of the many unnecessary and inexplicable bits of the CMP header we actually have to send: sendCertID: Sent on the first message where it's required (which isn't necessarily the first message in an exchange, for example it's not used in an ir/ip), either to identify the CA's cert in a CTL sent in a PKIBoot response or to identify the signing certificate when we're using signature-based message authentication. sendClibID: Sent on the first message to tell the other side that this is a cryptlib client/server. sendFullHeader: Sent if the other side isn't running cryptlib, unless we're doing PKIBoot, for which we couldn't send full headers even if we wanted to sendMacInfo: Sent if we're using MAC integrity protection and the the other side isn't running cryptlib, or if this is the first message. sendUserID: Sent on the first message or if we're sending full headers, provided that it's actually available to send */ if( !( sessionInfoPtr->protocolFlags & CMP_PFLAG_CERTIDSENT ) && \ ( ( isServer( sessionInfoPtr ) && \ protocolInfo->operation == CTAG_PB_GENM ) || \ !protocolInfo->useMACsend ) ) sendCertID = TRUE; if( !( sessionInfoPtr->protocolFlags & CMP_PFLAG_CLIBIDSENT ) ) sendClibID = TRUE; #ifndef USE_FULL_HEADERS if( !protocolInfo->isCryptlib && \ protocolInfo->operation != CTAG_PB_GENM ) sendFullHeader = TRUE; #endif /* !USE_FULL_HEADERS */ if( protocolInfo->useMACsend && \ !( protocolInfo->isCryptlib && \ ( sessionInfoPtr->protocolFlags & CMP_PFLAG_MACINFOSENT ) ) ) sendMacInfo = TRUE; if( ( sendFullHeader || \ !( sessionInfoPtr->protocolFlags & CMP_PFLAG_USERIDSENT ) ) && \ ( protocolInfo->userIDsize > 0 ) ) sendUserID = TRUE; REQUIRES( !sendFullHeader || !protocolInfo->headerRead || \ ( protocolInfo->userIDsize > 0 && \ protocolInfo->userIDsize < MAX_INTLENGTH_SHORT ) ); REQUIRES( protocolInfo->transIDsize > 0 && \ protocolInfo->transIDsize < MAX_INTLENGTH_SHORT ); /* Get any other state information that we may need */ status = krnlSendMessage( sessionInfoPtr->ownerHandle, IMESSAGE_GETATTRIBUTE, &hashAlgo, CRYPT_OPTION_ENCR_HASH ); ENSURES( cryptStatusOK( status ) ); protocolInfo->hashAlgo = hashAlgo; /* int vs.enum */ /* Determine how big the sender and recipient information will be. We shouldn't need to send a recipient name for an ir because it won't usually be known yet, but various implementations can't handle a zero-length GeneralName so we supply it if it's available even though it's redundant */ if( sendFullHeader ) { status = initDNInfo( sessionInfoPtr, &senderNameObject, &recipNameObject, &senderNameLength, &recipNameLength, ( protocolInfo->operation == CTAG_PB_IR ) ? \ TRUE : FALSE, protocolInfo->cryptOnlyKey ); if( cryptStatusError( status ) ) return( status ); } else { /* We're not using sender or recipient information since it doesn't serve any useful purpose, just set the fields to an empty SEQUENCE */ senderNameLength = recipNameLength = sizeofObject( 0 ); } /* Determine how big the remaining header data will be */ sMemNullOpen( &nullStream ); if( protocolInfo->useMACsend ) { status = writeMacInfo( &nullStream, protocolInfo, sendMacInfo ); } else { status = writeContextAlgoID( &nullStream, protocolInfo->authContext, protocolInfo->hashAlgo ); } if( cryptStatusOK( status ) ) protInfoLength = stell( &nullStream ); sMemClose( &nullStream ); if( cryptStatusError( status ) ) return( status ); if( sendClibID ) { attributeLength += sizeofObject( \ sizeofOID( OID_CRYPTLIB_PRESENCECHECK ) + \ sizeofObject( 0 ) ); } if( sendCertID ) { const int certIDsize = sizeofCertID( protocolInfo->authContext ); ENSURES( certIDsize > 0 && certIDsize < MAX_INTLENGTH_SHORT ); attributeLength += certIDsize; } totalLength = sizeofShortInteger( CMP_VERSION ) + \ objSize( senderNameLength ) + objSize( recipNameLength ) + \ objSize( protInfoLength ) + \ objSize( sizeofObject( protocolInfo->transIDsize ) ); if( sendUserID ) totalLength += objSize( sizeofObject( protocolInfo->userIDsize ) ); if( sendFullHeader ) { if( protocolInfo->senderNonceSize > 0 ) totalLength += objSize( \ sizeofObject( protocolInfo->senderNonceSize ) ); if( protocolInfo->recipNonceSize > 0 ) totalLength += objSize( \ sizeofObject( protocolInfo->recipNonceSize ) ); } if( attributeLength > 0 ) totalLength += objSize( objSize( attributeLength ) ); /* Perform an early check for data-size problems before we go through all of the following code */ if( sizeofObject( totalLength ) <= 0 || \ sizeofObject( totalLength ) > sMemDataLeft( stream ) ) return( CRYPT_ERROR_OVERFLOW ); /* Write the PKI header wrapper, version information, and sender and recipient names if there's name information present */ writeSequence( stream, totalLength ); writeShortInteger( stream, CMP_VERSION, DEFAULT_TAG ); if( sendFullHeader ) { writeConstructed( stream, senderNameLength, 4 ); if( senderNameObject != CRYPT_ERROR ) { status = exportAttributeToStream( stream, senderNameObject, CRYPT_IATTRIBUTE_SUBJECT ); if( cryptStatusError( status ) ) return( status ); } else writeSequence( stream, 0 ); writeConstructed( stream, recipNameLength, 4 ); if( recipNameObject != CRYPT_ERROR ) { status = exportAttributeToStream( stream, recipNameObject, CRYPT_IATTRIBUTE_SUBJECT ); } else status = writeSequence( stream, 0 ); } else { /* This is one of the portions of CMP where an optional field is marked as mandatory, to balance out the mandatory fields that are marked as optional. To work around this we write the names as zero-length DNs */ writeConstructed( stream, senderNameLength, 4 ); writeSequence( stream, 0 ); writeConstructed( stream, recipNameLength, 4 ); status = writeSequence( stream, 0 ); } if( cryptStatusError( status ) ) return( status ); /* Write the protection information, assorted nonces and IDs, and extra information that the other side may be able to make use of */ writeConstructed( stream, protInfoLength, CTAG_PH_PROTECTIONALGO ); if( protocolInfo->useMACsend ) { status = writeMacInfo( stream, protocolInfo, sendMacInfo ); sessionInfoPtr->protocolFlags |= CMP_PFLAG_MACINFOSENT; } else { status = writeContextAlgoID( stream, protocolInfo->authContext, protocolInfo->hashAlgo ); } if( cryptStatusError( status ) ) return( status ); if( sendUserID ) { /* We're using full headers or we're the client sending our first message, identify the sender key */ writeConstructed( stream, objSize( protocolInfo->userIDsize ), CTAG_PH_SENDERKID ); writeOctetString( stream, protocolInfo->userID, protocolInfo->userIDsize, DEFAULT_TAG ); DEBUG_PRINT(( "%s: Writing userID.\n", protocolInfo->isServer ? "SVR" : "CLI" )); DEBUG_DUMP_HEX( protocolInfo->isServer ? "SVR" : "CLI", protocolInfo->userID, protocolInfo->userIDsize ); sessionInfoPtr->protocolFlags |= CMP_PFLAG_USERIDSENT; } writeConstructed( stream, objSize( protocolInfo->transIDsize ), CTAG_PH_TRANSACTIONID ); status = writeOctetString( stream, protocolInfo->transID, protocolInfo->transIDsize, DEFAULT_TAG ); if( cryptStatusError( status ) ) return( status ); if( sendFullHeader ) { if( protocolInfo->senderNonceSize > 0 ) { /* We're using nonces, generate a new sender nonce (the initial nonce will have been set when the protocol state was initialised) */ setMessageData( &msgData, protocolInfo->senderNonce, protocolInfo->senderNonceSize ); krnlSendMessage( SYSTEM_OBJECT_HANDLE, IMESSAGE_GETATTRIBUTE_S, &msgData, CRYPT_IATTRIBUTE_RANDOM_NONCE ); writeConstructed( stream, objSize( protocolInfo->senderNonceSize ), CTAG_PH_SENDERNONCE ); status = writeOctetString( stream, protocolInfo->senderNonce, protocolInfo->senderNonceSize, DEFAULT_TAG ); } if( protocolInfo->recipNonceSize > 0 ) { writeConstructed( stream, objSize( protocolInfo->recipNonceSize ), CTAG_PH_RECIPNONCE ); status = writeOctetString( stream, protocolInfo->recipNonce, protocolInfo->recipNonceSize, DEFAULT_TAG ); } } if( cryptStatusError( status ) ) return( status ); if( attributeLength > 0 ) { ENSURES( sendClibID || sendCertID ); /* We haven't sent any messages yet, let the other side know that we're running cryptlib and identify our signing certificate as required */ writeConstructed( stream, objSize( attributeLength ), CTAG_PH_GENERALINFO ); writeSequence( stream, attributeLength ); if( sendClibID ) { sessionInfoPtr->protocolFlags |= CMP_PFLAG_CLIBIDSENT; writeSequence( stream, sizeofOID( OID_CRYPTLIB_PRESENCECHECK ) + \ sizeofObject( 0 ) ); writeOID( stream, OID_CRYPTLIB_PRESENCECHECK ); status = writeSet( stream, 0 ); } if( sendCertID ) { sessionInfoPtr->protocolFlags |= CMP_PFLAG_CERTIDSENT; status = writeCertID( stream, protocolInfo->authContext ); } } return( status ); }