Esempio n. 1
0
CHECK_RETVAL \
static int sizeofCertID( IN_HANDLE const CRYPT_CONTEXT iCryptCert )
{
    const int essCertIDSize = objSize( objSize( objSize( objSize( 20 ) ) ) );
    /* Infinitely-nested SHA-1 hash */

    REQUIRES( isHandleRangeValid( iCryptCert ) );

    return( objSize( sizeofOID( OID_ESS_CERTID ) + \
                     sizeofObject( essCertIDSize ) ) );
}
static int writeRequestBody( INOUT STREAM *stream,
							 const SESSION_INFO *sessionInfoPtr,
							 const CMP_PROTOCOL_INFO *protocolInfo )
	{
	const CRYPT_CERTFORMAT_TYPE certType = \
				( protocolInfo->operation == CTAG_PB_RR ) ? \
				CRYPT_ICERTFORMAT_DATA : CRYPT_CERTFORMAT_CERTIFICATE;
	MESSAGE_DATA msgData;
	int status;

	assert( isWritePtr( stream, sizeof( STREAM ) ) );
	assert( isReadPtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
	assert( isReadPtr( protocolInfo, sizeof( CMP_PROTOCOL_INFO ) ) );

	/* Find out how big the payload will be.  Since revocation requests are
	   unsigned entities we have to vary the attribute type that we're 
	   reading (via the certType specifier) based on whether we're 
	   submitting a signed or unsigned object in the request */
	setMessageData( &msgData, NULL, 0 );
	status = krnlSendMessage( sessionInfoPtr->iCertRequest,
							  IMESSAGE_CRT_EXPORT, &msgData, certType );
	if( cryptStatusError( status ) )
		return( status );

	/* Write the request body */
	writeConstructed( stream, objSize( msgData.length ),
					  protocolInfo->operation );
	writeSequence( stream, msgData.length );
	return( exportCertToStream( stream, sessionInfoPtr->iCertRequest, 
								certType ) );
	}
Esempio n. 3
0
static int writeCertID( INOUT STREAM *stream,
                        IN_HANDLE const CRYPT_CONTEXT iCryptCert )
{
    MESSAGE_DATA msgData;
    BYTE certHash[ CRYPT_MAX_HASHSIZE + 8 ];
    int essCertIDSize, payloadSize, status;

    assert( isWritePtr( stream, sizeof( STREAM ) ) );

    REQUIRES( isHandleRangeValid( iCryptCert ) );

    /* Find out how big the payload will be */
    setMessageData( &msgData, certHash, CRYPT_MAX_HASHSIZE );
    status = krnlSendMessage( iCryptCert, IMESSAGE_GETATTRIBUTE_S,
                              &msgData, CRYPT_CERTINFO_FINGERPRINT_SHA1 );
    if( cryptStatusError( status ) )
        return( status );
    essCertIDSize = ( int ) sizeofObject( msgData.length );
    payloadSize = objSize( objSize( objSize( essCertIDSize ) ) );

    /* Write the signing certificate ID information */
    writeSequence( stream, sizeofOID( OID_ESS_CERTID ) + \
                   ( int ) sizeofObject( payloadSize ) );
    writeOID( stream, OID_ESS_CERTID );
    writeSet( stream, payloadSize );
    writeSequence( stream, objSize( objSize( essCertIDSize ) ) );
    writeSequence( stream, objSize( essCertIDSize ) );
    writeSequence( stream, essCertIDSize );
    return( writeOctetString( stream, certHash, msgData.length,
                              DEFAULT_TAG ) );
}
Esempio n. 4
0
// -----------------------------------------------------------------------------
// CAsf::ParseContentEncryptionObject
// -----------------------------------------------------------------------------
//
void CAsf::ParseContentEncryptionObjectL()
    {
    LOGFN( "CAsf::ParseContentEncryptionObject" );
    TInt offset( iContentEncryptionOffset + KObjectID );
    TUint32 objSize( ReadUint64FromBlockL( *iHeaderData, offset ) );

    if ( iHeaderData->Length() < iContentEncryptionOffset + objSize ) 
        {
        User::Leave( KErrOverflow );
        }

    offset += KObjectSize;
    TInt len( ReadUint32FromBlockL( *iHeaderData, offset ) );
    offset += 4;
    if ( iHeaderData->Length() < ( offset + len ) )
        {
        User::Leave( KErrArgument );
        }
    iSecretData = iHeaderData->Mid( offset, len ).AllocL();
    offset += len;

    len = ReadUint32FromBlockL( *iHeaderData, offset );
    offset += 4;
    if ( iHeaderData->Length() < ( offset + len ) )
        {
        User::Leave( KErrArgument );
        }
    iProtectionType = iHeaderData->Mid( offset, len ).AllocL();
    offset += len;

    len = ReadUint32FromBlockL( *iHeaderData, offset );
    offset += 4;
    if ( iHeaderData->Length() < ( offset + len ) )
        {
        User::Leave( KErrArgument );
        }
    iKeyId = iHeaderData->Mid( offset, len ).AllocL();
    offset += len;

    len = ReadUint32FromBlockL( *iHeaderData, offset );
    offset += 4;
    if ( iHeaderData->Length() < ( offset + len ) )
        {
        User::Leave( KErrArgument );
        }
    iLicenseUrl = iHeaderData->Mid( offset, len ).AllocL();
    }
Esempio n. 5
0
// -----------------------------------------------------------------------------
// CAsf::ParseContentDescriptionObject
// -----------------------------------------------------------------------------
//
void CAsf::ParseContentDescriptionObjectL()
    {
    LOGFN( "CAsf::ParseContentDescriptionObjectL" );
    TInt offset( iContentDescriptionOffset + KObjectID );
    TUint32 objSize( ReadUint64FromBlockL( *iHeaderData, offset ) );

    if ( iHeaderData->Length() < iContentDescriptionOffset + objSize )
        {
        User::Leave( KErrOverflow );
        }

    offset += KObjectSize;
    iTitleLength = ReadUint16FromBlockL( *iHeaderData, offset );
    offset += 2;
    iAuthorLength = ReadUint16FromBlockL( *iHeaderData, offset );
    offset += 2;
    iCopyrightLength = ReadUint16FromBlockL( *iHeaderData, offset );
    offset += 2;
    iDescriptionLength = ReadUint16FromBlockL( *iHeaderData, offset );
    offset += 2;
    iRatingLength = ReadUint16FromBlockL( *iHeaderData, offset );
    offset += 2;

    TInt length( iTitleLength + iAuthorLength + iCopyrightLength + iDescriptionLength + iRatingLength );
    if ( length > objSize - ( KObjectID + KObjectSize + 10 ) )
        {
        User::Leave( KErrOverflow );
        }

    iTitle = HBuf16IgnoreNullL( *iHeaderData, offset, iTitleLength );
    offset += iTitleLength;
    
    iAuthor = HBuf16IgnoreNullL( *iHeaderData, offset, iAuthorLength );
    offset += iAuthorLength;
    
    iCopyright = HBuf16IgnoreNullL( *iHeaderData, offset, iCopyrightLength );
    offset += iCopyrightLength;
    
    iDescription = HBuf16IgnoreNullL( *iHeaderData, offset, iDescriptionLength );
    offset += iDescriptionLength;
    
    iRating = HBuf16IgnoreNullL( *iHeaderData, offset, iRatingLength );
    offset += iRatingLength;
    }
Esempio n. 6
0
// -----------------------------------------------------------------------------
// CAsf::ParseExtendedContentDescriptionObjectL
// -----------------------------------------------------------------------------
//
void CAsf::ParseExtendedContentDescriptionObjectL()
    {
    TInt i;
    
    LOGFN( "CAsf::ParseExtendedContentDescriptionObjectL" );
    TInt offset( iExtendedContentDescriptionOffset + KObjectID );
    TUint32 objSize( ReadUint64FromBlockL( *iHeaderData, offset ) );

    if ( iHeaderData->Length() < iExtendedContentDescriptionOffset + objSize )
        {
        User::Leave( KErrOverflow );
        }

    offset += KObjectSize;
    iExtendedContentDescriptionCount = ReadUint16FromBlockL( *iHeaderData, offset );
    offset += 2;

    for ( i = 0 ; i < iExtendedContentDescriptionCount; i++ )
        {
        TInt nameLength( ReadUint16FromBlockL( *iHeaderData, offset ) );
        offset += 2;
        HBufC16* name16( HBuf16FromBlockL( *iHeaderData, offset, nameLength ) );
        offset += nameLength;
        CleanupStack::PushL( name16 );
        if( !name16->CompareF( KWMAlbumTitle ) )
            {
            iAlbumTitle = ReadExtendedContentObjectL( offset );
            }
        else if( !name16->CompareF( KWMPicture ) )
            {
            iPicture = ReadExtendedContentObjectL( offset );
            }
        else if( !name16->CompareF( KWMText) )
            {
            iText = ReadExtendedContentObjectL( offset );
            }
        else if( !name16->CompareF( KWMComposer ) )
            {
            iComposer = ReadExtendedContentObjectL( offset );
            }
        else if( !name16->CompareF( KWMGenre ) )
            {
            iGenre = ReadExtendedContentObjectL( offset );
            }
        else if( !name16->CompareF( KWMYear ) || !name16->CompareF( KWMYear1 ) )
            {
            if ( !iYear )
                {
                iYear = ReadExtendedContentObjectL( offset );
                }
            }
        else if( !name16->CompareF( KWMOriginalArtist) )
            {
            iOriginalArtist = ReadExtendedContentObjectL( offset );
            }
        else if( !name16->CompareF( KWMTrackNumber ) )
            {
            iTrackNumber = ReadExtendedContentObjectL( offset );
            }
        else if( !name16->CompareF( KWMUniqueFileIdentifier ) )
            {
            iUniqueFileID = ReadExtendedContentObjectL( offset );
            }
        else if( !name16->CompareF( KWMAudioFileURL ) )
            {
            iAudioFileUrl = ReadExtendedContentObjectL( offset );
            }
        else if( !name16->CompareF( KWMSharedUserRating ) )
            {
            iSharedUserRating = ReadExtendedContentObjectL( offset );
            }
        else if( !name16->CompareF( KWMDate ) )
            {
            iDate = ReadExtendedContentObjectL( offset );
            }
        CleanupStack::PopAndDestroy( name16 );

        offset += 2; // data type
        TInt valueLength( ReadUint16FromBlockL( *iHeaderData, offset ) );
        offset += 2;
        offset += valueLength;
        }
    }
Esempio n. 7
0
// -----------------------------------------------------------------------------
// CAsf::ValidateL
//
// -----------------------------------------------------------------------------
//
void CAsf::ValidateL()
    {
    // ASF_Header_Object GUID 128 bits.
    TBuf8<32> header;

    LOGFN( "CAsf::ValidateL" );
    iFile.Read( 0, header, KObjectID );
    if ( header.Length() < KObjectID )
        {
        User::Leave( KErrOverflow );
        }
    FormatGUID( header );
    if ( header !=  KASFHeaderObject )
        {
        User::Leave( KErrArgument );
        }
    
    // read header object size.
    iFile.Read( header, KObjectSize );
    iHeaderSize = ReadUint64FromBlockL( header, 0 );
    if ( iHeaderSize <= 30 || iHeaderSize > KMaxTInt / 2 - 1 )
        {
        User::Leave( KErrOverflow );
        }

    // read header object
    // 2~31 = 2 GB, size of header would not be greater than this,
    // also, HBufC does not have a NewL with TInt64 as arguement.
    iHeaderData = HBufC8::NewL( iHeaderSize );
    TPtr8 headerPtr = iHeaderData->Des();
    iFile.Read( headerPtr, iHeaderSize - ( KObjectID + KObjectSize ) );

    iNbrOfObjects = ReadUint32FromBlockL( *iHeaderData, 0 );
    if ( iNbrOfObjects <= 0 )
        {
        User::Leave( KErrArgument );
        }

    TInt objOffset( 6 );
    if ( iHeaderData->Length() < ( objOffset + KObjectID ) )
        {
        User::Leave( KErrArgument );
        }
    //Read next object GUID
    TBuf8<32> objGUID = iHeaderData->Mid( objOffset, KObjectID );
    FormatGUID( objGUID );
    TBool loop( ETrue );

    //Loop until all needed headers are handled or top level header is finished
    while ( loop )
        {
        //Read current object size
        TUint32 objSize( ReadUint64FromBlockL( *iHeaderData, objOffset + KObjectID ) );
        if ( objSize < 24 )
            {
            User::Leave( KErrArgument );
            }
        
        if ( !iContentDescriptionObjectExists && objGUID == 
             KASFContentDescriptionObject )
            {
            iContentDescriptionObjectExists = ETrue;
            iContentDescriptionOffset = objOffset;
            ParseContentDescriptionObjectL();
            }
        else if ( !iFilePropertiesObjectExists && objGUID == 
             KASFFilePropertiesObject )
            {
            iFilePropertiesObjectExists = ETrue; // must exist
            iFilePropertiesOffset = objOffset;
            iFilePropertiesEndOffset = iFilePropertiesOffset + objSize;
            }
        else if ( !iExtendedContentDescriptionObjectExists && objGUID ==
             KASFExtendedContentDescriptionObject )
            {
            iExtendedContentDescriptionObjectExists = ETrue;
            iExtendedContentDescriptionOffset = objOffset;
            ParseExtendedContentDescriptionObjectL();
            }
        else if ( !iExtendedContentEncryptionObjectExists && objGUID ==
             KASFExtendedContentEncryptionObject )
            {
            iExtendedContentEncryptionObjectExists = ETrue;
            iExtendedContentEncryptionOffset = objOffset;
            iIsDrmProtected = ETrue;
            TInt eCEODataOffset( objOffset + KObjectID + KObjectSize + 4 );
            TInt eCEODataLength( objSize - ( KObjectID + KObjectSize + 4 ) );
            if ( iHeaderData->Length() < eCEODataOffset + eCEODataLength ||
                 eCEODataLength < 0)
                {
                User::Leave( KErrArgument );
                }
            iExtendedContentEncryptionObject = iHeaderData->Mid( eCEODataOffset,
                                                                 eCEODataLength ).AllocL();
            }
        else if ( !iContentEncryptionObjectExists && objGUID ==
             KASFContentEncryptionObject )
            {
            iContentEncryptionObjectExists = ETrue;
            iContentEncryptionOffset = objOffset;
            iIsDrmProtected = ETrue;
            ParseContentEncryptionObjectL();
            }
        else if ( !iDigitalSignatureObjectExists && objGUID ==
             KASFDigitalSignatureObject )
            {
            iDigitalSignatureObjectExists = ETrue;
            iDigitalSignatureOffset = objOffset;
            
            TInt dSODataOffset( objOffset + KObjectID + KObjectSize + 8 );
            TInt dSODataLength( objSize - ( KObjectID + KObjectSize + 8 ) );
            if ( iHeaderData->Length() < dSODataOffset + dSODataLength ||
                 dSODataLength < 0 )
                {
                User::Leave( KErrArgument );
                }
            iDigitalSignatureObject = iHeaderData->Mid( dSODataOffset,
                                                        dSODataLength ).AllocL();
            
            if ( iHeaderData->Length() < iFilePropertiesEndOffset + ( iDigitalSignatureOffset - iFilePropertiesEndOffset ) ||
                 iDigitalSignatureOffset - iFilePropertiesEndOffset < 0 ||
                 iFilePropertiesEndOffset < 0 )
                {
                iDigitalSignatureObjectExists = EFalse;
                iDigitalSignatureOffset = 0;
                delete iDigitalSignatureObject;
                iDigitalSignatureObject = NULL;
                }
            else
                {
                iSignedData = 
                    iHeaderData->Mid( iFilePropertiesEndOffset,
                                      iDigitalSignatureOffset - iFilePropertiesEndOffset ).AllocL();
                }
            }
        
        //Move object offset to the end of the current header object  
        objOffset += objSize;
        //End loop, if top level header is finished or all needed headers are handled
        if ( objOffset >= iHeaderSize - 30 ||
            ( iContentDescriptionObjectExists &&
              iFilePropertiesObjectExists &&
              iExtendedContentDescriptionObjectExists &&
              iExtendedContentEncryptionObjectExists &&
              iDigitalSignatureObjectExists ) )
            {
            loop = EFalse;
            }
        //Loop isn't finished, read next object GUID
        else
            {
            if ( iHeaderData->Length() < ( objOffset + KObjectID ) || objOffset < 0 )
                {
                User::Leave( KErrArgument );
                }
            objGUID = iHeaderData->Mid( objOffset, KObjectID );
            FormatGUID( objGUID );
            }
        }
    if ( iFilePropertiesObjectExists )
        {
        iIsValidated = ETrue;
        }
    }
Esempio n. 8
0
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 );
}