📄 cmp_wr.c
字号:
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 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 ) );
}
/* Write response body. If we're returning an encryption-only cert we send
it as standard CMS data under a new tag to avoid having to hand-assemble
the garbled mess that CMP uses for this */
CHECK_RETVAL_BOOL STDC_NONNULL_ARG( ( 1 ) ) \
static int writeResponseBodyHeader( INOUT STREAM *stream,
IN_RANGE( CTAG_PB_IR, CTAG_PB_CERTCONF ) \
const int operationType,
IN_LENGTH_SHORT_Z const int payloadSize )
{
int totalPayloadSize;
assert( isWritePtr( stream, sizeof( STREAM ) ) );
REQUIRES( operationType >= CTAG_PB_IR && operationType < CTAG_PB_LAST );
REQUIRES( payloadSize >= 0 && payloadSize < MAX_INTLENGTH_SHORT );
/* Calculate the overall size of the header and payload. For an empty
response there's only an integer status, for a nonempty response
there's also an integer ID */
totalPayloadSize = payloadSize + sizeofShortInteger( 0 );
if( operationType != CTAG_PB_RR )
totalPayloadSize += objSize( sizeofShortInteger( 0 ) );
/* Write the response body wrapper */
writeConstructed( stream, objSize( objSize( objSize( totalPayloadSize ) ) ),
reqToResp( operationType ) );
writeSequence( stream, objSize( objSize( totalPayloadSize ) ) );
/* Write the response. We always write an OK status here because an
error will have been communicated by sending an explicit error
response */
writeSequence( stream, objSize( totalPayloadSize ) );
writeSequence( stream, totalPayloadSize );
if( operationType != CTAG_PB_RR )
{
writeShortInteger( stream, 0, DEFAULT_TAG );
writeSequence( stream, sizeofShortInteger( 0 ) );
}
return( writeShortInteger( stream, PKISTATUS_OK, DEFAULT_TAG ) );
}
CHECK_RETVAL_BOOL STDC_NONNULL_ARG( ( 1, 2, 3 ) ) \
static int writeEncryptedResponseBody( INOUT INOUT STREAM *stream,
const SESSION_INFO *sessionInfoPtr,
const CMP_PROTOCOL_INFO *protocolInfo )
{
MESSAGE_DATA msgData;
void *srcPtr, *destPtr;
int dataLength, destLength, status;
assert( isWritePtr( stream, sizeof( STREAM ) ) );
assert( isReadPtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
assert( isReadPtr( protocolInfo, sizeof( CMP_PROTOCOL_INFO ) ) );
/* Get a pointer into the stream buffer. To avoid having to juggle two
buffers we use the stream buffer some distance ahead of the write
position as a temporary location to store the encoded certificate for
encryption */
status = sMemGetDataBlockRemaining( stream, &srcPtr, &dataLength );
if( cryptStatusError( status ) )
return( status );
srcPtr = ( BYTE * ) srcPtr + 100;
dataLength -= 100;
ENSURES( dataLength >= 1024 && dataLength < sMemDataLeft( stream ) );
/* Extract the response data into the session buffer and wrap it in the
standard format using the client's cert. Since the client doesn't
actually have the cert yet (only we have it, since it's only just
been issued), we have to use the S/MIME v3 format (keys identified by
key ID rather than issuerAndSerialNumber) because the client won't
know its iAndS until it decrypts the cert */
setMessageData( &msgData, srcPtr, dataLength );
status = krnlSendMessage( sessionInfoPtr->iCertResponse,
IMESSAGE_CRT_EXPORT, &msgData,
CRYPT_CERTFORMAT_CERTIFICATE );
if( cryptStatusError( status ) )
return( status );
status = envelopeWrap( srcPtr, msgData.length, srcPtr, dataLength,
&dataLength, CRYPT_FORMAT_CRYPTLIB,
CRYPT_CONTENT_NONE,
sessionInfoPtr->iCertResponse );
if( cryptStatusError( status ) )
return( status );
/* Write the response body header */
status = writeResponseBodyHeader( stream, protocolInfo->operation,
objSize( objSize( dataLength ) ) );
if( cryptStatusError( status ) )
return( status );
/* Write the encrypted certificate. In theory we could use an swrite()
to move the data rather than an memcpy() directly into the buffer but
this is a bit risky because the read position is only about 30-40
bytes ahead of the write position and it's not guaranteed that the
two won't interfere */
writeSequence( stream, objSize( dataLength ) );
writeConstructed( stream, dataLength, CTAG_CK_NEWENCRYPTEDCERT );
status = sMemGetDataBlockRemaining( stream, &destPtr, &destLength );
if( cryptStatusOK( status ) && dataLength > destLength )
status = CRYPT_ERROR_OVERFLOW;
if( cryptStatusError( status ) )
return( status );
memmove( destPtr, srcPtr, dataLength );
return( sSkip( stream, dataLength ) );
}
CHECK_RETVAL_BOOL STDC_NONNULL_ARG( ( 1, 2, 3 ) ) \
static int writeResponseBody( INOUT STREAM *stream,
const SESSION_INFO *sessionInfoPtr,
const CMP_PROTOCOL_INFO *protocolInfo )
{
MESSAGE_DATA msgData;
int dataLength = DUMMY_INIT, status;
assert( isWritePtr( stream, sizeof( STREAM ) ) );
assert( isReadPtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
assert( isReadPtr( protocolInfo, sizeof( CMP_PROTOCOL_INFO ) ) );
/* Revocation request responses have no body */
if( protocolInfo->operation == CTAG_PB_RR )
return( writeResponseBodyHeader( stream, protocolInfo->operation,
0 ) );
/* If it's an encryption-only key we return the certificate in encrypted
form, the client performs POP by decrypting the returned cert */
if( protocolInfo->cryptOnlyKey )
return( writeEncryptedResponseBody( stream, sessionInfoPtr,
protocolInfo ) );
/* Write the response body header */
setMessageData( &msgData, NULL, 0 );
status = krnlSendMessage( sessionInfoPtr->iCertResponse,
IMESSAGE_CRT_EXPORT, &msgData,
CRYPT_CERTFORMAT_CERTIFICATE );
if( cryptStatusOK( status ) )
{
dataLength = msgData.length;
status = writeResponseBodyHeader( stream, protocolInfo->operation,
objSize( objSize( dataLength ) ) );
}
if( cryptStatusError( status ) )
return( status );
/* Write the certificate data */
writeSequence( stream, objSize( dataLength ) );
writeConstructed( stream, dataLength, CTAG_CK_CERT );
return( exportCertToStream( stream, sessionInfoPtr->iCertResponse,
CRYPT_CERTFORMAT_CERTIFICATE ) );
}
/* Write conf body */
CHECK_RETVAL_BOOL STDC_NONNULL_ARG( ( 1, 2, 3 ) ) \
static int writeConfBody( INOUT STREAM *stream,
const SESSION_INFO *sessionInfoPtr,
const CMP_PROTOCOL_INFO *protocolInfo )
{
MESSAGE_DATA msgData;
BYTE hashBuffer[ CRYPT_MAX_HASHSIZE + 8 ];
int length, status;
assert( isWritePtr( stream, sizeof( STREAM ) ) );
assert( isReadPtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
assert( isReadPtr( protocolInfo, sizeof( CMP_PROTOCOL_INFO ) ) );
/* Get the certificate hash */
setMessageData( &msgData, hashBuffer, CRYPT_MAX_HASHSIZE );
status = krnlSendMessage( sessionInfoPtr->iCertResponse,
IMESSAGE_GETATTRIBUTE_S, &msgData,
( protocolInfo->confHashAlgo == CRYPT_ALGO_SHA1 ) ? \
CRYPT_CERTINFO_FINGERPRINT_SHA : \
CRYPT_CERTINFO_FINGERPRINT_MD5 );
if( cryptStatusError( status ) )
return( status );
length = ( int ) objSize( msgData.length ) + sizeofShortInteger( 0 );
/* Write the confirmation body */
writeConstructed( stream, objSize( objSize( length ) ),
CTAG_PB_CERTCONF );
writeSequence( stream, objSize( length ) );
writeSequence( stream, length );
writeOctetString( stream, hashBuffer, msgData.length, DEFAULT_TAG );
return( writeShortInteger( stream, 0, DEFAULT_TAG ) );
}
/* Write genMsg body */
CHECK_RETVAL_BOOL STDC_NONNULL_ARG( ( 1, 2 ) ) \
static int writeGenMsgBody( INOUT STREAM *stream,
const SESSION_INFO *sessionInfoPtr )
{
CRYPT_CERTIFICATE iCTL;
MESSAGE_DATA msgData;
int status;
assert( isWritePtr( stream, sizeof( STREAM ) ) );
assert( isReadPtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
/* Get the CTL from the CA object. We recreate this each time rather
than cacheing it in the session to ensure that changes in the trusted
cert set while the session is active get reflected back to the
caller.
In addition to the explicitly trusted certs, we also include the CA
cert(s) in the CTL as implicitly-trusted certs. This is done both
because users often forget to mark them as trusted on the server and
then wonder where their CA certs are on the client, and because these
should inherently be trusted, since the user is about to get their
certs issued by them */
status = krnlSendMessage( sessionInfoPtr->ownerHandle,
IMESSAGE_GETATTRIBUTE, &iCTL,
CRYPT_IATTRIBUTE_CTL );
if( cryptStatusError( status ) )
return( status );
status = krnlSendMessage( iCTL, IMESSAGE_SETATTRIBUTE,
( void * ) &sessionInfoPtr->privateKey,
CRYPT_IATTRIBUTE_CERTCOLLECTION );
if( cryptStatusError( status ) )
return( status );
setMessageData( &msgData, NULL, 0 );
status = krnlSendMessage( iCTL, IMESSAGE_CRT_EXPORT, &msgData,
CRYPT_CERTFORMAT_CERTCHAIN );
if( cryptStatusError( status ) )
{
krnlSendNotifier( iCTL, IMESSAGE_DECREFCOUNT );
return( status );
}
/* Write the response body wrapper. As with the cert ID, we can use the
imprecision of the ASN.1 that CMP is specified in to interpret the
InfoTypeAndValue:
InfoTypeAndValue ::= SEQUENCE {
infoType OBJECT IDENTIFIER,
infoValue ANY DEFINED BY infoType OPTIONAL
}
as:
infoType ::= id-signedData
infoValue ::= [0] EXPLICIT SignedData
which makes it standard CMS data that can be passed directly to the
CMS code */
writeConstructed( stream, objSize( msgData.length ), CTAG_PB_GENP );
writeSequence( stream, msgData.length );
status = exportCertToStream( stream, iCTL, CRYPT_CERTFORMAT_CERTCHAIN );
krnlSendNotifier( iCTL, IMESSAGE_DECREFCOUNT );
return( status );
}
/* Write error body */
CHECK_RETVAL_BOOL STDC_NONNULL_ARG( ( 1, 2 ) ) \
static int writeErrorBody( INOUT STREAM *stream,
const CMP_PROTOCOL_INFO *protocolInfo )
{
const int length = writePkiStatusInfo( NULL, protocolInfo->status,
protocolInfo->pkiFailInfo );
assert( isWritePtr( stream, sizeof( STREAM ) ) );
assert( isReadPtr( protocolInfo, sizeof( CMP_PROTOCOL_INFO ) ) );
/* Write the error body. We don't write the error text string because
it reveals too much about the internal operation of the CA, some of
which may aid an attacker */
writeConstructed( stream, objSize( length ), CTAG_PB_ERROR );
writeSequence( stream, length );
return( writePkiStatusInfo( stream, protocolInfo->status,
protocolInfo->pkiFailInfo ) );
}
/****************************************************************************
* *
* Write a PKI Header *
* *
****************************************************************************/
/* Write a PKI header. Fields marked with a * are redundant and are only
sent when we're not sending minimal headers. Fields marked with a + are
only sent in the first message or when not sending minimal headers:
header SEQUENCE {
version INTEGER (2),
*sender [4] EXPLICIT DirectoryName, -- DN of initiator
*recipient [4] EXPLICIT DirectoryName, -- DN of responder
protAlgo [1] EXPLICIT AlgorithmIdentifier,
+protKeyID [2] EXPLICIT OCTET STRING,
transID [4] EXPLICIT OCTET STRING SIZE (16),-- Random/copied from sender
*nonce [5] EXPLICIT OCTET STRING SIZE (16),-- Random
*nonceX [6] EXPLICIT OCTET STRING SIZE (n), -- Copied from sender
generalInfo [8] EXPLICIT SEQUENCE OF Info OPT -- cryptlib-specific info
}
The handling can get a bit complex if we're writing a header in response
to an error in reading the other side's header. Since CMP includes such a
large amount of unnecessary or redundant information, it's not really
possible to detect in advance if we've got enough information to send a
header or not, the best we can do is to require that enough of the header
fields have been read (indicated by the 'headerRead' flag in the protocol
info) before we try and create our own header in response */
static int writePkiHeader( STREAM *stream, SESSION_INFO *sessionInfoPtr,
CMP_PROTOCOL_INFO *protocolInfo )
{
CRYPT_HANDLE senderNameObject = isServer( sessionInfoPtr ) ? \
sessionInfoPtr->privateKey : \
protocolInfo->cryptOnlyKey ? \
sessionInfoPtr->iAuthOutContext : \
sessionInfoPtr->iCertRequest;
const CRYPT_HANDLE recipNameObject = isServer( sessionInfoPtr ) ? \
sessionInfoPtr->iCertResponse : sessionInfoPtr->iAuthInContext;
STREAM nullStream;
MESSAGE_DATA msgData;
#ifdef USE_FULL_HEADERS
const BOOLEAN useFullHeader = TRUE;
#else
const BOOLEAN useFullHeader = !( protocolInfo->isCryptlib || \
protocolInfo->operation == CTAG_PB_GENM );
/* Send a minimal header if the other side is cryptlib or if
we're doing PKIBoot, for which we couldn't send full headers
if we wanted to */
#endif /* USE_MINIMAL_HEADERS */
BOOLEAN sendClibID = FALSE, sendCertID = FALSE;
int senderNameLength, recipNameLength, attributeLength = 0;
int protInfoLength, totalLength, status;
assert( !useFullHeader || !protocolInfo->headerRead || \
( protocolInfo->userIDsize > 0 ) );
assert( protocolInfo->transIDsize > 0 );
krnlSendMessage( sessionInfoPtr->ownerHandle, IMESSAGE_GETATTRIBUTE,
&protocolInfo->hashAlgo, CRYPT_OPTION_ENCR_HASH );
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -