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