cryptlib  3.4.1
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Macros
cmp_wrmsg.c
Go to the documentation of this file.
1 /****************************************************************************
2 * *
3 * Write CMP Messages Types *
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 #ifdef USE_CMP
23 
24 /****************************************************************************
25 * *
26 * PKI Body Functions *
27 * *
28 ****************************************************************************/
29 
30 /* Write request body:
31 
32  body [n] EXPLICIT SEQUENCE { -- n designates ir/cr/kur/rr
33  ... -- CRMF request
34  } */
35 
36 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2, 3 ) ) \
37 static int writeRequestBody( INOUT STREAM *stream,
40  {
42  ( protocolInfo->operation == CTAG_PB_RR ) ? \
43  CRYPT_ICERTFORMAT_DATA : CRYPT_CERTFORMAT_CERTIFICATE;
45  int status;
46 
47  assert( isWritePtr( stream, sizeof( STREAM ) ) );
48  assert( isReadPtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
49  assert( isReadPtr( protocolInfo, sizeof( CMP_PROTOCOL_INFO ) ) );
50 
51  /* Find out how big the payload will be. Since revocation requests are
52  unsigned entities we have to vary the attribute type that we're
53  reading (via the certType specifier) based on whether we're
54  submitting a signed or unsigned object in the request */
55  setMessageData( &msgData, NULL, 0 );
56  status = krnlSendMessage( sessionInfoPtr->iCertRequest,
57  IMESSAGE_CRT_EXPORT, &msgData, certType );
58  if( cryptStatusError( status ) )
59  return( status );
60 
61  /* Write the request body */
62  writeConstructed( stream, objSize( msgData.length ),
63  protocolInfo->operation );
64  writeSequence( stream, msgData.length );
65  return( exportCertToStream( stream, sessionInfoPtr->iCertRequest,
66  certType ) );
67  }
68 
69 /* Write response body:
70 
71  body [n] EXPLICIT SEQUENCE { -- n designates ir/cr/kur
72  response SEQUENCE {
73  SEQUENCE {
74  certReqID INTEGER (0), -- Not present for rp's
75  status PKIStatusInfo,
76  certKeyPair SEQUENCE {
77 Either cert[0] EXPLICIT Certificate, -- For sig-capable key
78 or cmsEncCert -- For encr-only key
79  [2] EXPLICIT EncryptedCert
80  }
81  }
82  }
83  }
84 
85  body [n] EXPLICIT SEQUENCE { -- n designates rr
86  response SEQUENCE {
87  status PKIStatusInfo
88  }
89  }
90 
91  If we're returning an encryption-only certificate we send it as standard
92  CMS data under a new tag to avoid having to hand-assemble the garbled
93  mess that CMP uses for this */
94 
96 static int writeResponseBodyHeader( INOUT STREAM *stream,
97  IN_ENUM_OPT( CMP_MESSAGE ) \
98  const CMP_MESSAGE_TYPE operationType,
99  IN_LENGTH_SHORT_Z const int payloadSize )
100  {
101  const int respType = reqToResp( operationType );
102  const int statusInfoLength = sizeofPkiStatusInfo( CRYPT_OK, 0 );
103  int totalPayloadSize;
104 
105  assert( isWritePtr( stream, sizeof( STREAM ) ) );
106 
107  REQUIRES( operationType >= CTAG_PB_IR && operationType < CTAG_PB_LAST );
108  /* CTAG_PB_IR == 0 so this is the same as _NONE */
109  REQUIRES( payloadSize >= 0 && payloadSize < MAX_INTLENGTH_SHORT );
110  REQUIRES( respType >= CTAG_PB_IP && respType < CTAG_PB_LAST );
111 
112  /* If it's a revocation response then the only content is the
113  OK-status */
114  if( respType == CTAG_PB_RP )
115  {
116  writeConstructed( stream, objSize( objSize( statusInfoLength ) ),
117  CTAG_PB_RP );
118  writeSequence( stream, objSize( statusInfoLength ) );
119  writeSequence( stream, statusInfoLength );
120  return( writePkiStatusInfo( stream, CRYPT_OK, 0 ) );
121  }
122 
123  /* Calculate the overall size of the header and payload */
124  totalPayloadSize = payloadSize + sizeofShortInteger( 0 ) + \
125  statusInfoLength;
126 
127  /* Write the response body wrapper */
128  writeConstructed( stream, objSize( objSize( objSize( totalPayloadSize ) ) ),
129  respType );
130  writeSequence( stream, objSize( objSize( totalPayloadSize ) ) );
131 
132  /* Write the response. We always write an OK status here because an
133  error will have been communicated by sending an explicit error
134  response */
135  writeSequence( stream, objSize( totalPayloadSize ) );
136  writeSequence( stream, totalPayloadSize );
137  writeShortInteger( stream, 0, DEFAULT_TAG );
138  return( writePkiStatusInfo( stream, CRYPT_OK, 0 ) );
139  }
140 
141 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2, 3 ) ) \
142 static int writeEncryptedResponseBody( INOUT STREAM *stream,
143  const SESSION_INFO *sessionInfoPtr,
144  const CMP_PROTOCOL_INFO *protocolInfo )
145  {
147  void *srcPtr, *destPtr;
149 
150  assert( isWritePtr( stream, sizeof( STREAM ) ) );
151  assert( isReadPtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
152  assert( isReadPtr( protocolInfo, sizeof( CMP_PROTOCOL_INFO ) ) );
153 
154  /* Get a pointer into the stream buffer. To avoid having to juggle two
155  buffers we use the stream buffer some distance ahead of the write
156  position as a temporary location to store the encoded certificate for
157  encryption:
158 
159  buffer srcPtr
160  | |
161  v v
162  +-------+---+-----------+-----------------------+
163  | Hdr |-->| | |
164  +-------+---+-----------+-----------------------+
165  |<-- 100 -->|<---- dataLength ----->| */
166  status = sMemGetDataBlockRemaining( stream, &srcPtr, &dataLength );
167  if( cryptStatusError( status ) )
168  return( status );
169  srcPtr = ( BYTE * ) srcPtr + 100;
170  dataLength -= 100;
171  ENSURES( dataLength >= 1024 && dataLength < sMemDataLeft( stream ) );
172 
173  /* Extract the response data into the session buffer and wrap it using
174  the client's certificate. Since the client doesn't actually have the
175  certificate yet (only we have it, since it's only just been issued)
176  we have to use the S/MIME v3 format (keys identified by key ID rather
177  than issuerAndSerialNumber) because the client won't know its iAndS
178  until it decrypts the certificate */
179  setMessageData( &msgData, srcPtr, dataLength );
180  status = krnlSendMessage( sessionInfoPtr->iCertResponse,
181  IMESSAGE_CRT_EXPORT, &msgData,
183  if( cryptStatusError( status ) )
184  return( status );
185  status = envelopeWrap( srcPtr, msgData.length, srcPtr, dataLength,
186  &dataLength, CRYPT_FORMAT_CRYPTLIB,
188  sessionInfoPtr->iCertResponse );
189  if( cryptStatusError( status ) )
190  return( status );
191 
192  /* Write the response body header */
193  status = writeResponseBodyHeader( stream, protocolInfo->operation,
194  objSize( objSize( dataLength ) ) );
195  if( cryptStatusError( status ) )
196  return( status );
197 
198  /* Write the encrypted certificate. In theory we could use an swrite()
199  to move the data rather than a memmove() directly into the buffer but
200  this is a bit risky because the read position is only about 30-40
201  bytes ahead of the write position and it's not guaranteed that the
202  two won't interfere:
203 
204  buffer destPtr srcPtr
205  | | |
206  v v v
207  +-----------+-----------+-----------------------+
208  | Hdr | | |
209  +-----------+-----------+-----------------------+
210  | |<---- dataLength ----->|
211  |<---------- destLength ----------->| */
212  writeSequence( stream, objSize( dataLength ) );
213  writeConstructed( stream, dataLength, CTAG_CK_NEWENCRYPTEDCERT );
214  status = sMemGetDataBlockRemaining( stream, &destPtr, &destLength );
215  if( cryptStatusError( status ) )
216  return( status );
217  ENSURES( dataLength <= destLength );
218  /* Should never occur since it implies that we're overwritten
219  the start of the wrapped certificate */
220  memmove( destPtr, srcPtr, dataLength );
221  return( sSkip( stream, dataLength ) );
222  }
223 
224 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2, 3 ) ) \
225 static int writeResponseBody( INOUT STREAM *stream,
226  const SESSION_INFO *sessionInfoPtr,
227  const CMP_PROTOCOL_INFO *protocolInfo )
228  {
230  int dataLength, status;
231 
232  assert( isWritePtr( stream, sizeof( STREAM ) ) );
233  assert( isReadPtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
234  assert( isReadPtr( protocolInfo, sizeof( CMP_PROTOCOL_INFO ) ) );
235 
236  /* Revocation request responses have no body */
237  if( protocolInfo->operation == CTAG_PB_RR )
238  return( writeResponseBodyHeader( stream, protocolInfo->operation,
239  0 ) );
240 
241  /* If it's an encryption-only key we return the certificate in encrypted
242  form, the client performs POP by decrypting the returned
243  certificate */
244  if( protocolInfo->cryptOnlyKey )
245  return( writeEncryptedResponseBody( stream, sessionInfoPtr,
246  protocolInfo ) );
247 
248  /* Write the response body header */
249  setMessageData( &msgData, NULL, 0 );
250  status = krnlSendMessage( sessionInfoPtr->iCertResponse,
251  IMESSAGE_CRT_EXPORT, &msgData,
253  if( cryptStatusError( status ) )
254  return( status );
255  dataLength = msgData.length;
256  status = writeResponseBodyHeader( stream, protocolInfo->operation,
257  objSize( objSize( dataLength ) ) );
258  if( cryptStatusError( status ) )
259  return( status );
260 
261  /* Write the certificate data */
262  writeSequence( stream, objSize( dataLength ) );
263  writeConstructed( stream, dataLength, CTAG_CK_CERT );
264  return( exportCertToStream( stream, sessionInfoPtr->iCertResponse,
266  }
267 
268 /* Write conf body:
269 
270  body [19] EXPLICIT SEQUENCE {
271  SEQUENCE {
272  certHash OCTET STRING
273  certReqID INTEGER (0),
274  }
275  } */
276 
277 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2, 3 ) ) \
278 static int writeConfBody( INOUT STREAM *stream,
279  const SESSION_INFO *sessionInfoPtr,
280  const CMP_PROTOCOL_INFO *protocolInfo )
281  {
282  static const MAP_TABLE fingerprintMapTable[] = {
287  { CRYPT_ERROR, 0 }, { CRYPT_ERROR, 0 }
288  };
290  BYTE hashBuffer[ CRYPT_MAX_HASHSIZE + 8 ];
291  int length, fingerprintType, status;
292 
293  assert( isWritePtr( stream, sizeof( STREAM ) ) );
294  assert( isReadPtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
295  assert( isReadPtr( protocolInfo, sizeof( CMP_PROTOCOL_INFO ) ) );
296 
297  /* Get the certificate hash */
298  status = mapValue( protocolInfo->confHashAlgo, &fingerprintType,
299  fingerprintMapTable,
300  FAILSAFE_ARRAYSIZE( fingerprintMapTable, MAP_TABLE ) );
301  ENSURES( cryptStatusOK( status ) );
302  setMessageData( &msgData, hashBuffer, CRYPT_MAX_HASHSIZE );
303  status = krnlSendMessage( sessionInfoPtr->iCertResponse,
304  IMESSAGE_GETATTRIBUTE_S, &msgData,
305  fingerprintType );
306  if( cryptStatusError( status ) )
307  return( status );
308  length = ( int ) objSize( msgData.length ) + sizeofShortInteger( 0 );
309 
310  /* Write the confirmation body */
311  writeConstructed( stream, objSize( objSize( length ) ),
313  writeSequence( stream, objSize( length ) );
314  writeSequence( stream, length );
315  writeOctetString( stream, hashBuffer, msgData.length, DEFAULT_TAG );
316  return( writeShortInteger( stream, 0, DEFAULT_TAG ) );
317  }
318 
319 /* Write pkiConf body:
320 
321  body [24] EXPLICIT NULL */
322 
324 static int writePKIConfBody( INOUT STREAM *stream,
325  STDC_UNUSED const SESSION_INFO *sessionInfoPtr,
326  STDC_UNUSED const CMP_PROTOCOL_INFO *protocolInfo )
327  {
328  assert( isWritePtr( stream, sizeof( STREAM ) ) );
329 
330  writeConstructed( stream, sizeofNull(), CTAG_PB_PKICONF );
331  return( writeNull( stream, DEFAULT_TAG ) );
332  }
333 
334 /* Write genMsg body:
335 
336  body [21] EXPLICIT SEQUENCE OF {
337  SEQUENCE {
338  infoType OBJECT IDENTIFIER,
339  intoValue ANY DEFINED BY infoType OPTIONAL
340  }
341  } */
342 
344 static int writeGenMsgRequestBody( INOUT STREAM *stream,
345  STDC_UNUSED const SESSION_INFO *sessionInfoPtr,
346  STDC_UNUSED const CMP_PROTOCOL_INFO *protocolInfo )
347  {
348  assert( isWritePtr( stream, sizeof( STREAM ) ) );
349 
350  writeConstructed( stream, objSize( objSize( sizeofOID( OID_PKIBOOT ) ) ),
351  CTAG_PB_GENM );
352  writeSequence( stream, objSize( sizeofOID( OID_PKIBOOT ) ) );
353  writeSequence( stream, sizeofOID( OID_PKIBOOT ) );
354  return( writeOID( stream, OID_PKIBOOT ) );
355  }
356 
357 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
358 static int writeGenMsgResponseBody( INOUT STREAM *stream,
359  const SESSION_INFO *sessionInfoPtr,
360  STDC_UNUSED const CMP_PROTOCOL_INFO *protocolInfo )
361  {
362  CRYPT_CERTIFICATE iCTL;
364  int status;
365 
366  assert( isWritePtr( stream, sizeof( STREAM ) ) );
367  assert( isReadPtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
368 
369  /* Get the CTL from the CA object. We recreate this each time rather
370  than cacheing it in the session to ensure that changes in the trusted
371  certificate set while the session is active get reflected back to the
372  caller.
373 
374  In addition to the explicitly trusted certificates we also include
375  the CA certificate(s) in the CTL as implicitly-trusted certificates.
376  This is done both because users often forget to mark them as trusted
377  on the server and then wonder where their CA certificates are on the
378  client, and because these should inherently be trusted since the user
379  is about to get their certificates issued by them */
380  status = krnlSendMessage( sessionInfoPtr->ownerHandle,
381  IMESSAGE_GETATTRIBUTE, &iCTL,
382  CRYPT_IATTRIBUTE_CTL );
383  if( cryptStatusError( status ) )
384  {
385  MESSAGE_CREATEOBJECT_INFO createInfo;
386 
387  if( status != CRYPT_ERROR_NOTFOUND )
388  return( status );
389 
390  /* If there are no trusted certificates present then we won't be
391  able to assemble a CTL, so we explicitly create an empty CTL to
392  add the CA certificate to */
395  IMESSAGE_DEV_CREATEOBJECT, &createInfo,
397  if( cryptStatusError( status ) )
398  return( status );
399  iCTL = createInfo.cryptHandle;
400  }
401  status = krnlSendMessage( iCTL, IMESSAGE_SETATTRIBUTE,
402  ( MESSAGE_CAST ) &sessionInfoPtr->privateKey,
403  CRYPT_IATTRIBUTE_CERTCOLLECTION );
404  if( cryptStatusError( status ) )
405  {
407  return( status );
408  }
409  setMessageData( &msgData, NULL, 0 );
410  status = krnlSendMessage( iCTL, IMESSAGE_CRT_EXPORT, &msgData,
412  if( cryptStatusError( status ) )
413  {
415  return( status );
416  }
417 
418  /* Write the response body wrapper. As with the certificate ID, we can
419  use the imprecision of the ASN.1 that CMP is specified in to
420  interpret the InfoTypeAndValue:
421 
422  InfoTypeAndValue ::= SEQUENCE {
423  infoType OBJECT IDENTIFIER,
424  infoValue ANY DEFINED BY infoType OPTIONAL
425  }
426 
427  as:
428 
429  infoType ::= id-signedData
430  infoValue ::= [0] EXPLICIT SignedData
431 
432  which makes it standard CMS data that can be passed directly to the
433  CMS code */
434  writeConstructed( stream, objSize( msgData.length ), CTAG_PB_GENP );
435  writeSequence( stream, msgData.length );
436  status = exportCertToStream( stream, iCTL, CRYPT_CERTFORMAT_CERTCHAIN );
438  return( status );
439  }
440 
441 /* Write error body:
442 
443  body [23] EXPLICIT SEQUENCE {
444  status PKIStatusInfo
445  } */
446 
447 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 3 ) ) \
448 static int writeErrorBody( INOUT STREAM *stream,
449  STDC_UNUSED const SESSION_INFO *sessionInfoPtr,
450  const CMP_PROTOCOL_INFO *protocolInfo )
451  {
452  const int statusInfoLength = \
453  sizeofPkiStatusInfo( protocolInfo->status, protocolInfo->pkiFailInfo );
454 
455  assert( isWritePtr( stream, sizeof( STREAM ) ) );
456  assert( isReadPtr( protocolInfo, sizeof( CMP_PROTOCOL_INFO ) ) );
457 
458  REQUIRES( statusInfoLength > 0 && \
459  statusInfoLength < MAX_INTLENGTH_SHORT );
460 
461  /* Write the error body */
462  writeConstructed( stream, objSize( statusInfoLength ), CTAG_PB_ERROR );
463  writeSequence( stream, statusInfoLength );
464  return( writePkiStatusInfo( stream, protocolInfo->status,
465  protocolInfo->pkiFailInfo ) );
466  }
467 
468 /****************************************************************************
469 * *
470 * Write Function Access Information *
471 * *
472 ****************************************************************************/
473 
474 typedef struct {
475  const CMPBODY_TYPE type;
476  const WRITEMESSAGE_FUNCTION function;
477  } MESSAGEWRITE_INFO;
478 static const MESSAGEWRITE_INFO FAR_BSS messageWriteClientTable[] = {
479  { CMPBODY_NORMAL, writeRequestBody },
480  { CMPBODY_CONFIRMATION, writeConfBody },
481  { CMPBODY_GENMSG, writeGenMsgRequestBody },
482  { CMPBODY_ERROR, writeErrorBody },
483  { CMPBODY_NONE, NULL }, { CMPBODY_NONE, NULL }
484  };
485 static const MESSAGEWRITE_INFO FAR_BSS messageWriteServerTable[] = {
486  { CMPBODY_NORMAL, writeResponseBody },
487  { CMPBODY_ACK, writePKIConfBody },
488  { CMPBODY_GENMSG, writeGenMsgResponseBody },
489  { CMPBODY_ERROR, writeErrorBody },
490  { CMPBODY_NONE, NULL }, { CMPBODY_NONE, NULL }
491  };
492 
493 CHECK_RETVAL_PTR \
494 WRITEMESSAGE_FUNCTION getMessageWriteFunction( IN_ENUM( CMPBODY ) \
495  const CMPBODY_TYPE bodyType,
496  const BOOLEAN isServer )
497  {
498  int i;
499 
500  REQUIRES_N( bodyType > CMPBODY_NONE && bodyType < CMPBODY_LAST );
501 
502  if( isServer )
503  {
504  for( i = 0;
505  messageWriteServerTable[ i ].type != CTAG_PB_LAST && \
506  i < FAILSAFE_ARRAYSIZE( messageWriteServerTable, MESSAGEWRITE_INFO );
507  i++ )
508  {
509  if( messageWriteServerTable[ i ].type == bodyType )
510  return( messageWriteServerTable[ i ].function );
511  }
512  ENSURES_N( i < FAILSAFE_ARRAYSIZE( messageWriteServerTable, MESSAGEWRITE_INFO ) );
513  }
514  else
515  {
516  for( i = 0;
517  messageWriteClientTable[ i ].type != CTAG_PB_LAST && \
518  i < FAILSAFE_ARRAYSIZE( messageWriteClientTable, MESSAGEWRITE_INFO );
519  i++ )
520  {
521  if( messageWriteClientTable[ i ].type == bodyType )
522  return( messageWriteClientTable[ i ].function );
523  }
524  ENSURES_N( i < FAILSAFE_ARRAYSIZE( messageWriteClientTable, MESSAGEWRITE_INFO ) );
525  }
526 
527  return( NULL );
528  }
529 #endif /* USE_CMP */