1 /****************************************************************************
2 *																			*
3 *								Write CMP Messages							*
4 *						Copyright Peter Gutmann 1999-2009					*
5 *																			*
6 ****************************************************************************/
7 
8 #if defined( INC_ALL )
9   #include "crypt.h"
10   #include "asn1.h"
11   #include "asn1_ext.h"
12   #include "session.h"
13   #include "cmp.h"
14 #else
15   #include "crypt.h"
16   #include "enc_dec/asn1.h"
17   #include "enc_dec/asn1_ext.h"
18   #include "session/session.h"
19   #include "session/cmp.h"
20 #endif /* Compiler-specific includes */
21 
22 /* Enabling the following define forces the use of full headers at all times.
23    cryptlib always sends minimal headers once it detects that the other side
24    is using cryptlib, ommitting as much of the unnecessary junk as possible,
25    which significantly reduces the overall message size */
26 
27 /* #define USE_FULL_HEADERS */
28 
29 #ifdef USE_CMP
30 
31 /****************************************************************************
32 *																			*
33 *								Utility Routines							*
34 *																			*
35 ****************************************************************************/
36 
37 /* Write full certificate ID information.  This is written as an attribute
38    in the generalInfo field of the message header to allow unambiguous
39    identification of the signing certificate, which the standard CMP format
40    can't do.  Although CMP uses a gratuitously incompatible definition of
41    the standard attribute type (calling it InfoTypeAndValue), it's possible
42    to shoehorn a standard attribute type in by taking the "ANY" in "ANY
43    DEFINED BY x" to mean "SET OF AttributeValue" (for once the use of
44    obsolete ASN.1 is a help, since it's so imprecise that we can shovel in
45    anything and it's still valid):
46 
47 	SigningCertificate ::=  SEQUENCE {
48 		certs			SEQUENCE OF ESSCertID	-- Size (1)
49 		}
50 
51 	ESSCertID ::= SEQUENCE {
52 		certID			OCTET STRING
53 		}
54 
55    All that we really need to identify certificates is the certificate ID,
56    so instead of writing a full ESSCertID (which also contains an optional
57    incompatible reinvention of the CMS IssuerAndSerialNumber) we write the
58    sole mandatory field, the certificate hash, which also keeps the overall
59    size down.
60 
61    This is further complicated though by the fact that certificate attributes
62    are defined as SET OF while CMS attributes are defined as SEQUENCE (and of
63    course CMP has to gratuitously invent its own type which is neither of the
64    two).  This means that the read code would need to know whether a given
65    OID corresponds to a certificate or CMS attribute and read it
66    appropriately.  Because this is just too much of a mess, we pretend that
67    all attributes are certificate attributes (since this is a PKIX protocol)
68    and encode them as a uniform SET OF */
69 
70 CHECK_RETVAL \
sizeofCertID(IN_HANDLE const CRYPT_CONTEXT iCryptCert)71 static int sizeofCertID( IN_HANDLE const CRYPT_CONTEXT iCryptCert )
72 	{
73 	const int essCertIDSize = objSize( objSize( objSize( objSize( 20 ) ) ) );
74 					/* Infinitely-nested SHA-1 hash */
75 
76 	REQUIRES( isHandleRangeValid( iCryptCert ) );
77 
78 	return( objSize( sizeofOID( OID_ESS_CERTID ) + \
79 					 sizeofObject( essCertIDSize ) ) );
80 	}
81 
82 CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
writeCertID(INOUT STREAM * stream,IN_HANDLE const CRYPT_CONTEXT iCryptCert)83 static int writeCertID( INOUT STREAM *stream,
84 						IN_HANDLE const CRYPT_CONTEXT iCryptCert )
85 	{
86 	MESSAGE_DATA msgData;
87 	BYTE certHash[ CRYPT_MAX_HASHSIZE + 8 ];
88 	int essCertIDSize, payloadSize, status;
89 
90 	assert( isWritePtr( stream, sizeof( STREAM ) ) );
91 
92 	REQUIRES( isHandleRangeValid( iCryptCert ) );
93 
94 	/* Find out how big the payload will be */
95 	setMessageData( &msgData, certHash, CRYPT_MAX_HASHSIZE );
96 	status = krnlSendMessage( iCryptCert, IMESSAGE_GETATTRIBUTE_S,
97 							  &msgData, CRYPT_CERTINFO_FINGERPRINT_SHA1 );
98 	if( cryptStatusError( status ) )
99 		return( status );
100 	essCertIDSize = ( int ) sizeofObject( msgData.length );
101 	payloadSize = objSize( objSize( objSize( essCertIDSize ) ) );
102 
103 	/* Write the signing certificate ID information */
104 	writeSequence( stream, sizeofOID( OID_ESS_CERTID ) + \
105 						   ( int ) sizeofObject( payloadSize ) );
106 	writeOID( stream, OID_ESS_CERTID );
107 	writeSet( stream, payloadSize );
108 	writeSequence( stream, objSize( objSize( essCertIDSize ) ) );
109 	writeSequence( stream, objSize( essCertIDSize ) );
110 	writeSequence( stream, essCertIDSize );
111 	return( writeOctetString( stream, certHash, msgData.length,
112 							  DEFAULT_TAG ) );
113 	}
114 
115 /* Initialise the information needed to send client/server DNs in the PKI
116    header */
117 
118 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2, 3, 4, 5 ) ) \
initDNInfo(INOUT SESSION_INFO * sessionInfoPtr,OUT_HANDLE_OPT CRYPT_HANDLE * senderNameObject,OUT_HANDLE_OPT CRYPT_HANDLE * recipNameObject,OUT_LENGTH_SHORT_Z int * senderNameLength,OUT_LENGTH_SHORT_Z int * recipNameLength,const BOOLEAN isInitialClientMessage,const BOOLEAN isClientCryptOnlyKey)119 static int initDNInfo( INOUT SESSION_INFO *sessionInfoPtr,
120 					   OUT_HANDLE_OPT CRYPT_HANDLE *senderNameObject,
121 					   OUT_HANDLE_OPT CRYPT_HANDLE *recipNameObject,
122 					   OUT_LENGTH_SHORT_Z int *senderNameLength,
123 					   OUT_LENGTH_SHORT_Z int *recipNameLength,
124 					   const BOOLEAN isInitialClientMessage,
125 					   const BOOLEAN isClientCryptOnlyKey )
126 	{
127 	MESSAGE_DATA msgData;
128 	int status;
129 
130 	assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
131 	assert( isWritePtr( senderNameObject, sizeof( CRYPT_HANDLE ) ) );
132 	assert( isWritePtr( recipNameObject, sizeof( CRYPT_HANDLE ) ) );
133 	assert( isWritePtr( senderNameLength, sizeof( int ) ) );
134 	assert( isWritePtr( recipNameLength, sizeof( int ) ) );
135 
136 	/* Clear return values */
137 	*senderNameObject = *recipNameObject = CRYPT_ERROR;
138 	*senderNameLength = *recipNameLength = 0;
139 
140 	/* Get the objects that we'll be using for our source of DN
141 	   information */
142 	if( isServer( sessionInfoPtr ) )
143 		{
144 		*senderNameObject = sessionInfoPtr->privateKey;
145 		*recipNameObject = sessionInfoPtr->iCertResponse;
146 		}
147 	else
148 		{
149 		*senderNameObject = isClientCryptOnlyKey ? \
150 							sessionInfoPtr->iAuthOutContext : \
151 							sessionInfoPtr->iCertRequest;
152 		*recipNameObject = sessionInfoPtr->iAuthInContext;
153 		}
154 
155 	/* Get the sender DN information */
156 	setMessageData( &msgData, NULL, 0 );
157 	status = krnlSendMessage( *senderNameObject, IMESSAGE_GETATTRIBUTE_S,
158 							  &msgData, CRYPT_IATTRIBUTE_SUBJECT );
159 	if( status == CRYPT_ERROR_NOTFOUND && isInitialClientMessage )
160 		{
161 		/* If there's no subject DN present and it's the first message in a
162 		   client's ir exchange, this isn't an error because the subject
163 		   usually won't know their DN yet.  That's the theory anyway,
164 		   some X.500-obsessive servers will reject a message with no
165 		   sender name but there isn't really anything that we can do about
166 		   this, particularly since we can't tell in advance what beaviour
167 		   the server will exhibit */
168 		if( sessionInfoPtr->iCertResponse == CRYPT_ERROR )
169 			{
170 			*senderNameObject = CRYPT_ERROR;
171 			msgData.length = ( int ) sizeofObject( 0 );
172 			status = CRYPT_OK;
173 			}
174 		else
175 			{
176 			/* Try again with the response from the server, which contains
177 			   our newly-allocated DN */
178 			*senderNameObject = sessionInfoPtr->iCertResponse;
179 			status = krnlSendMessage( *senderNameObject,
180 									  IMESSAGE_GETATTRIBUTE_S, &msgData,
181 									  CRYPT_IATTRIBUTE_SUBJECT );
182 			}
183 		}
184 	if( cryptStatusError( status ) )
185 		return( status );
186 	*senderNameLength = msgData.length;
187 
188 	/* Get the recipient DN information */
189 	setMessageData( &msgData, NULL, 0 );
190 	if( *recipNameObject != CRYPT_ERROR )
191 		{
192 		status = krnlSendMessage( *recipNameObject, IMESSAGE_GETATTRIBUTE_S,
193 								  &msgData, CRYPT_IATTRIBUTE_SUBJECT );
194 		}
195 	else
196 		{
197 		/* If we're sending an error response there may not be any recipient
198 		   name information present yet if the error occurred before the
199 		   recipient information could be established, and if this is a MAC-
200 		   authenticated PKIBoot we don't have the CA's certificate yet so
201 		   we don't know its DN.  To work around this we send a zero-length
202 		   DN (this is one of those places where an optional field is
203 		   specified as being mandatory, to lend balance to the places where
204 		   mandatory fields are specified as optional) */
205 		msgData.length = ( int ) sizeofObject( 0 );
206 		}
207 	if( cryptStatusError( status ) )
208 		return( status );
209 	*recipNameLength = msgData.length;
210 
211 	return( CRYPT_OK );
212 	}
213 
214 /****************************************************************************
215 *																			*
216 *								Write a PKI Header							*
217 *																			*
218 ****************************************************************************/
219 
220 /* Write a PKI header.  Fields marked with a * are unnecessary and are only
221    sent when we're not sending minimal headers.  Fields marked with a + are
222    only sent in the first message or when not sending minimal headers:
223 
224 	header				SEQUENCE {
225 		version			INTEGER (2),
226 	   *sender		[4]	EXPLICIT DirectoryName,	-- DN of initiator
227 	   *recipient	[4]	EXPLICIT DirectoryName,	-- DN of responder
228 		protAlgo	[1]	EXPLICIT AlgorithmIdentifier,
229 	   +protKeyID	[2] EXPLICIT OCTET STRING,
230 		transID		[4] EXPLICIT OCTET STRING SIZE (16),-- Random/copied from sender
231 	   *nonce		[5] EXPLICIT OCTET STRING SIZE (16),-- Random
232 	   *nonceX		[6] EXPLICIT OCTET STRING SIZE (n),	-- Copied from sender
233 		generalInfo	[8] EXPLICIT SEQUENCE OF Info OPT	-- cryptlib-specific info
234 		}
235 
236    The handling can get a bit complex if we're writing a header in response
237    to an error in reading the other side's header.  Since CMP includes such
238    a large amount of unnecessary or redundant information, it's not really
239    possible to detect in advance if we've got enough information to send a
240    header or not, the best that we can do is to require that enough of the
241    header fields have been read (indicated by the 'headerRead' flag in the
242    protocol information) before we try and create our own header in
243    response */
244 
245 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2, 3 ) ) \
writePkiHeader(INOUT STREAM * stream,INOUT SESSION_INFO * sessionInfoPtr,INOUT CMP_PROTOCOL_INFO * protocolInfo)246 static int writePkiHeader( INOUT STREAM *stream,
247 						   INOUT SESSION_INFO *sessionInfoPtr,
248 						   INOUT CMP_PROTOCOL_INFO *protocolInfo )
249 	{
250 	CRYPT_HANDLE senderNameObject DUMMY_INIT, recipNameObject DUMMY_INIT;
251 	STREAM nullStream;
252 	MESSAGE_DATA msgData;
253 #ifdef USE_FULL_HEADERS
254 	const BOOLEAN sendFullHeader = TRUE;
255 #else
256 	BOOLEAN sendFullHeader = FALSE;
257 #endif /* USE_FULL_HEADERS */
258 	BOOLEAN sendClibID = FALSE, sendCertID = FALSE, sendMacInfo = FALSE;
259 	BOOLEAN sendUserID = FALSE;
260 	int senderNameLength, recipNameLength, attributeLength = 0;
261 	int protInfoLength DUMMY_INIT, totalLength, hashAlgo, status;
262 
263 	assert( isWritePtr( stream, sizeof( STREAM ) ) );
264 	assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
265 	assert( isWritePtr( protocolInfo, sizeof( CMP_PROTOCOL_INFO ) ) );
266 
267 	/* Determine which of the many unnecessary and inexplicable bits of the
268 	   CMP header we actually have to send:
269 
270 		sendCertID: Sent on the first message where it's required (which
271 			isn't necessarily the first message in an exchange, for example
272 			it's not used in an ir/ip), either to identify the CA's cert in
273 			a CTL sent in a PKIBoot response or to identify the signing
274 			certificate when we're using signature-based message
275 			authentication.
276 
277 		sendClibID: Sent on the first message to tell the other side that
278 			this is a cryptlib client/server.
279 
280 		sendFullHeader: Sent if the other side isn't running cryptlib, unless
281 			we're doing PKIBoot, for which we couldn't send full headers even
282 			if we wanted to
283 
284 		sendMacInfo: Sent if we're using MAC integrity protection and the
285 			the other side isn't running cryptlib, or if this is the first
286 			message.
287 
288 		sendUserID: Sent on the first message or if we're sending full
289 			headers, provided that it's actually available to send */
290 	if( !( sessionInfoPtr->protocolFlags & CMP_PFLAG_CERTIDSENT ) && \
291 		( ( isServer( sessionInfoPtr ) && \
292 			protocolInfo->operation == CTAG_PB_GENM ) || \
293 		  !protocolInfo->useMACsend ) )
294 		sendCertID = TRUE;
295 	if( !( sessionInfoPtr->protocolFlags & CMP_PFLAG_CLIBIDSENT ) )
296 		sendClibID = TRUE;
297 #ifndef USE_FULL_HEADERS
298 	if( !protocolInfo->isCryptlib && \
299 		protocolInfo->operation != CTAG_PB_GENM )
300 		sendFullHeader = TRUE;
301 #endif /* !USE_FULL_HEADERS */
302 	if( protocolInfo->useMACsend && \
303 		!( protocolInfo->isCryptlib && \
304 		   ( sessionInfoPtr->protocolFlags & CMP_PFLAG_MACINFOSENT ) ) )
305 		sendMacInfo = TRUE;
306 	if( ( sendFullHeader || \
307 		  !( sessionInfoPtr->protocolFlags & CMP_PFLAG_USERIDSENT ) ) && \
308 		( protocolInfo->userIDsize > 0 ) )
309 		sendUserID = TRUE;
310 
311 	REQUIRES( !sendFullHeader || !protocolInfo->headerRead || \
312 			  ( protocolInfo->userIDsize > 0 && \
313 				protocolInfo->userIDsize < MAX_INTLENGTH_SHORT ) );
314 	REQUIRES( protocolInfo->transIDsize > 0 && \
315 			  protocolInfo->transIDsize < MAX_INTLENGTH_SHORT );
316 
317 	/* Get any other state information that we may need */
318 	status = krnlSendMessage( sessionInfoPtr->ownerHandle,
319 							  IMESSAGE_GETATTRIBUTE, &hashAlgo,
320 							  CRYPT_OPTION_ENCR_HASH );
321 	ENSURES( cryptStatusOK( status ) );
322 	protocolInfo->hashAlgo = hashAlgo;	/* int vs.enum */
323 
324 	/* Determine how big the sender and recipient information will be.  We
325 	   shouldn't need to send a recipient name for an ir because it won't
326 	   usually be known yet, but various implementations can't handle a
327 	   zero-length GeneralName so we supply it if it's available even though
328 	   it's redundant */
329 	if( sendFullHeader )
330 		{
331 		status = initDNInfo( sessionInfoPtr, &senderNameObject,
332 							 &recipNameObject, &senderNameLength,
333 							 &recipNameLength,
334 							 ( protocolInfo->operation == CTAG_PB_IR ) ? \
335 								TRUE : FALSE,
336 							 protocolInfo->cryptOnlyKey );
337 		if( cryptStatusError( status ) )
338 			return( status );
339 		}
340 	else
341 		{
342 		/* We're not using sender or recipient information since it doesn't
343 		   serve any useful purpose, just set the fields to an empty
344 		   SEQUENCE */
345 		senderNameLength = recipNameLength = sizeofObject( 0 );
346 		}
347 
348 	/* Determine how big the remaining header data will be */
349 	sMemNullOpen( &nullStream );
350 	if( protocolInfo->useMACsend )
351 		{
352 		status = writeMacInfo( &nullStream, protocolInfo, sendMacInfo );
353 		}
354 	else
355 		{
356 		status = writeContextAlgoID( &nullStream, protocolInfo->authContext,
357 									 protocolInfo->hashAlgo );
358 		}
359 	if( cryptStatusOK( status ) )
360 		protInfoLength = stell( &nullStream );
361 	sMemClose( &nullStream );
362 	if( cryptStatusError( status ) )
363 		return( status );
364 	if( sendClibID )
365 		{
366 		attributeLength += sizeofObject( \
367 								sizeofOID( OID_CRYPTLIB_PRESENCECHECK ) + \
368 								sizeofObject( 0 ) );
369 		}
370 	if( sendCertID )
371 		{
372 		const int certIDsize = sizeofCertID( protocolInfo->authContext );
373 
374 		ENSURES( certIDsize > 0 && certIDsize < MAX_INTLENGTH_SHORT );
375 
376 		attributeLength += certIDsize;
377 		}
378 	totalLength = sizeofShortInteger( CMP_VERSION ) + \
379 				  objSize( senderNameLength ) + objSize( recipNameLength ) + \
380 				  objSize( protInfoLength ) + \
381 				  objSize( sizeofObject( protocolInfo->transIDsize ) );
382 	if( sendUserID )
383 		totalLength += objSize( sizeofObject( protocolInfo->userIDsize ) );
384 	if( sendFullHeader )
385 		{
386 		if( protocolInfo->senderNonceSize > 0 )
387 			totalLength += objSize( \
388 								sizeofObject( protocolInfo->senderNonceSize ) );
389 		if( protocolInfo->recipNonceSize > 0 )
390 			totalLength += objSize( \
391 								sizeofObject( protocolInfo->recipNonceSize ) );
392 		}
393 	if( attributeLength > 0 )
394 		totalLength += objSize( objSize( attributeLength ) );
395 
396 	/* Perform an early check for data-size problems before we go through
397 	   all of the following code */
398 	if( sizeofObject( totalLength ) <= 0 || \
399 		sizeofObject( totalLength ) > sMemDataLeft( stream ) )
400 		return( CRYPT_ERROR_OVERFLOW );
401 
402 	/* Write the PKI header wrapper, version information, and sender and
403 	   recipient names if there's name information present */
404 	writeSequence( stream, totalLength );
405 	writeShortInteger( stream, CMP_VERSION, DEFAULT_TAG );
406 	if( sendFullHeader )
407 		{
408 		writeConstructed( stream, senderNameLength, 4 );
409 		if( senderNameObject != CRYPT_ERROR )
410 			{
411 			status = exportAttributeToStream( stream, senderNameObject,
412 											  CRYPT_IATTRIBUTE_SUBJECT );
413 			if( cryptStatusError( status ) )
414 				return( status );
415 			}
416 		else
417 			writeSequence( stream, 0 );
418 		writeConstructed( stream, recipNameLength, 4 );
419 		if( recipNameObject != CRYPT_ERROR )
420 			{
421 			status = exportAttributeToStream( stream, recipNameObject,
422 											  CRYPT_IATTRIBUTE_SUBJECT );
423 			}
424 		else
425 			status = writeSequence( stream, 0 );
426 		}
427 	else
428 		{
429 		/* This is one of the portions of CMP where an optional field is
430 		   marked as mandatory, to balance out the mandatory fields that are
431 		   marked as optional.  To work around this we write the names as
432 		   zero-length DNs */
433 		writeConstructed( stream, senderNameLength, 4 );
434 		writeSequence( stream, 0 );
435 		writeConstructed( stream, recipNameLength, 4 );
436 		status = writeSequence( stream, 0 );
437 		}
438 	if( cryptStatusError( status ) )
439 		return( status );
440 
441 	/* Write the protection information, assorted nonces and IDs, and extra
442 	   information that the other side may be able to make use of */
443 	writeConstructed( stream, protInfoLength, CTAG_PH_PROTECTIONALGO );
444 	if( protocolInfo->useMACsend )
445 		{
446 		status = writeMacInfo( stream, protocolInfo, sendMacInfo );
447 		sessionInfoPtr->protocolFlags |= CMP_PFLAG_MACINFOSENT;
448 		}
449 	else
450 		{
451 		status = writeContextAlgoID( stream, protocolInfo->authContext,
452 									 protocolInfo->hashAlgo );
453 		}
454 	if( cryptStatusError( status ) )
455 		return( status );
456 	if( sendUserID )
457 		{
458 		/* We're using full headers or we're the client sending our first
459 		   message, identify the sender key */
460 		writeConstructed( stream, objSize( protocolInfo->userIDsize ),
461 						  CTAG_PH_SENDERKID );
462 		writeOctetString( stream, protocolInfo->userID,
463 						  protocolInfo->userIDsize, DEFAULT_TAG );
464 		DEBUG_PRINT(( "%s: Writing userID.\n",
465 					  protocolInfo->isServer ? "SVR" : "CLI" ));
466 		DEBUG_DUMP_HEX( protocolInfo->isServer ? "SVR" : "CLI",
467 						protocolInfo->userID, protocolInfo->userIDsize );
468 		sessionInfoPtr->protocolFlags |= CMP_PFLAG_USERIDSENT;
469 		}
470 	writeConstructed( stream, objSize( protocolInfo->transIDsize ),
471 					  CTAG_PH_TRANSACTIONID );
472 	status = writeOctetString( stream, protocolInfo->transID,
473 							   protocolInfo->transIDsize, DEFAULT_TAG );
474 	if( cryptStatusError( status ) )
475 		return( status );
476 	if( sendFullHeader )
477 		{
478 		if( protocolInfo->senderNonceSize > 0 )
479 			{
480 			/* We're using nonces, generate a new sender nonce (the initial
481 			   nonce will have been set when the protocol state was
482 			   initialised) */
483 			setMessageData( &msgData, protocolInfo->senderNonce,
484 							protocolInfo->senderNonceSize );
485 			krnlSendMessage( SYSTEM_OBJECT_HANDLE, IMESSAGE_GETATTRIBUTE_S,
486 							 &msgData, CRYPT_IATTRIBUTE_RANDOM_NONCE );
487 			writeConstructed( stream,
488 							  objSize( protocolInfo->senderNonceSize ),
489 							  CTAG_PH_SENDERNONCE );
490 			status = writeOctetString( stream, protocolInfo->senderNonce,
491 									   protocolInfo->senderNonceSize,
492 									   DEFAULT_TAG );
493 			}
494 		if( protocolInfo->recipNonceSize > 0 )
495 			{
496 			writeConstructed( stream,
497 							  objSize( protocolInfo->recipNonceSize ),
498 							  CTAG_PH_RECIPNONCE );
499 			status = writeOctetString( stream, protocolInfo->recipNonce,
500 									   protocolInfo->recipNonceSize,
501 									   DEFAULT_TAG );
502 			}
503 		}
504 	if( cryptStatusError( status ) )
505 		return( status );
506 	if( attributeLength > 0 )
507 		{
508 		ENSURES( sendClibID || sendCertID );
509 
510 		/* We haven't sent any messages yet, let the other side know that
511 		   we're running cryptlib and identify our signing certificate as
512 		   required */
513 		writeConstructed( stream, objSize( attributeLength ),
514 						  CTAG_PH_GENERALINFO );
515 		writeSequence( stream, attributeLength );
516 		if( sendClibID )
517 			{
518 			sessionInfoPtr->protocolFlags |= CMP_PFLAG_CLIBIDSENT;
519 			writeSequence( stream, sizeofOID( OID_CRYPTLIB_PRESENCECHECK ) + \
520 								   sizeofObject( 0 ) );
521 			writeOID( stream, OID_CRYPTLIB_PRESENCECHECK );
522 			status = writeSet( stream, 0 );
523 			}
524 		if( sendCertID )
525 			{
526 			sessionInfoPtr->protocolFlags |= CMP_PFLAG_CERTIDSENT;
527 			status = writeCertID( stream, protocolInfo->authContext );
528 			}
529 		}
530 	return( status );
531 	}
532 
533 /****************************************************************************
534 *																			*
535 *							Write a PKI Message								*
536 *																			*
537 ****************************************************************************/
538 
539 /* Write a PKI message:
540 
541 	PkiMessage ::= SEQUENCE {
542 		header			PKIHeader,
543 		body			CHOICE { [0]... [24]... },
544 		protection	[0]	BIT STRING
545 		} */
546 
547 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
writePkiMessage(INOUT SESSION_INFO * sessionInfoPtr,INOUT CMP_PROTOCOL_INFO * protocolInfo,IN_ENUM (CMPBODY)const CMPBODY_TYPE bodyType)548 int writePkiMessage( INOUT SESSION_INFO *sessionInfoPtr,
549 					 INOUT CMP_PROTOCOL_INFO *protocolInfo,
550 					 IN_ENUM( CMPBODY ) const CMPBODY_TYPE bodyType )
551 	{
552 	WRITEMESSAGE_FUNCTION writeMessageFunction;
553 	BYTE protInfo[ 64 + MAX_PKCENCRYPTED_SIZE + 8 ], headerBuffer[ 8 + 8 ];
554 	STREAM stream;
555 	int headerSize DUMMY_INIT, protInfoSize, status;
556 
557 	assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
558 	assert( isWritePtr( protocolInfo, sizeof( CMP_PROTOCOL_INFO ) ) );
559 
560 	REQUIRES( bodyType > CMPBODY_NONE && bodyType < CMPBODY_LAST );
561 
562 	DEBUG_PRINT(( "%s: Writing message body type %d.\n",
563 				  protocolInfo->isServer ? "SVR" : "CLI", bodyType ));
564 
565 	/* Write the header and payload so that we can MAC/sign it */
566 	sMemOpen( &stream, sessionInfoPtr->receiveBuffer,
567 			  sessionInfoPtr->receiveBufSize );
568 	status = writePkiHeader( &stream, sessionInfoPtr, protocolInfo );
569 	if( cryptStatusError( status ) )
570 		{
571 		sMemClose( &stream );
572 		return( status );
573 		}
574 
575 	/* Write the message data */
576 	writeMessageFunction = getMessageWriteFunction( bodyType,
577 										isServer( sessionInfoPtr ) );
578 	ENSURES( writeMessageFunction != NULL );
579 	status = writeMessageFunction( &stream, sessionInfoPtr, protocolInfo );
580 	if( cryptStatusError( status ) )
581 		{
582 		sMemClose( &stream );
583 		return( status );
584 		}
585 
586 	/* Generate the MAC or signature as appropriate */
587 	if( protocolInfo->useMACsend )
588 		{
589 		status = writeMacProtinfo( protocolInfo->iMacContext,
590 						sessionInfoPtr->receiveBuffer, stell( &stream ),
591 						protInfo, 64 + MAX_PKCENCRYPTED_SIZE, &protInfoSize );
592 		}
593 	else
594 		{
595 		status = writeSignedProtinfo( protocolInfo->authContext,
596 						protocolInfo->hashAlgo, protocolInfo->hashParam,
597 						sessionInfoPtr->receiveBuffer, stell( &stream ),
598 						protInfo, 64 + MAX_PKCENCRYPTED_SIZE, &protInfoSize );
599 		}
600 	if( cryptStatusError( status ) )
601 		{
602 		sMemClose( &stream );
603 		return( status );
604 		}
605 
606 	/* Attach the MAC/signature to the payload */
607 	writeConstructed( &stream, protInfoSize, CTAG_PM_PROTECTION );
608 	status = swrite( &stream, protInfo, protInfoSize );
609 	if( cryptStatusOK( status ) )
610 		sessionInfoPtr->receiveBufEnd = stell( &stream );
611 	sMemDisconnect( &stream );
612 	if( cryptStatusError( status ) )
613 		return( status );
614 
615 	/* Write the wrapper and move it onto the front of the message:
616 
617 		receiveBuffer								  receiveBufSize
618 			|												|
619 			v												v
620 			+-------+-----------------------+---------------+
621 			|		|						|				|
622 			+-------+-----------------------+---------------+
623 			|<--+-->|<--- receiveBufend --->|
624 				|
625 			headerSize
626 
627 	   Unfortunately we can't just assume a fixed-length header because a
628 	   few messages (conf and pkiConf) will be short, leading to a 1-byte
629 	   length rather than the more typical 2-byte length */
630 	sMemOpen( &stream, headerBuffer, 8 );
631 	status = writeSequence( &stream, sessionInfoPtr->receiveBufEnd );
632 	if( cryptStatusOK( status ) )
633 		headerSize = stell( &stream );
634 	sMemDisconnect( &stream );
635 	ENSURES( cryptStatusOK( status ) );
636 	REQUIRES( rangeCheck( headerSize, sessionInfoPtr->receiveBufEnd,
637 						  sessionInfoPtr->receiveBufSize ) );
638 	memmove( sessionInfoPtr->receiveBuffer + headerSize,
639 			 sessionInfoPtr->receiveBuffer,
640 			 sessionInfoPtr->receiveBufEnd );
641 	memcpy( sessionInfoPtr->receiveBuffer, headerBuffer, headerSize );
642 	sessionInfoPtr->receiveBufEnd += headerSize;
643 
644 	return( CRYPT_OK );
645 	}
646 #endif /* USE_CMP */
647