cryptlib  3.4.1
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Macros
cmp_rd.c
Go to the documentation of this file.
1 /****************************************************************************
2 * *
3 * Read CMP Messages *
4 * Copyright Peter Gutmann 1999-2009 *
5 * *
6 ****************************************************************************/
7 
8 #include <stdio.h>
9 #if defined( INC_ALL )
10  #include "crypt.h"
11  #include "asn1.h"
12  #include "asn1_ext.h"
13  #include "session.h"
14  #include "cmp.h"
15 #else
16  #include "crypt.h"
17  #include "enc_dec/asn1.h"
18  #include "enc_dec/asn1_ext.h"
19  #include "session/session.h"
20  #include "session/cmp.h"
21 #endif /* Compiler-specific includes */
22 
23 #ifdef USE_CMP
24 
25 /****************************************************************************
26 * *
27 * Utility Routines *
28 * *
29 ****************************************************************************/
30 
31 /* Read the kitchen-sink field in the PKI header */
32 
33 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
34 static int readGeneralInfoAttribute( INOUT STREAM *stream,
36  {
37  BYTE oid[ MAX_OID_SIZE + 8 ];
38  int length, status;
39 
40  assert( isWritePtr( stream, sizeof( STREAM ) ) );
41  assert( isWritePtr( protocolInfo, sizeof( CMP_PROTOCOL_INFO ) ) );
42 
43  /* Read the attribute. Since there are only two attribute types that we
44  use, we hardcode the read in here rather than performing a general-
45  purpose attribute read */
46  readSequence( stream, NULL );
47  status = readEncodedOID( stream, oid, MAX_OID_SIZE, &length,
49  if( cryptStatusError( status ) )
50  return( status );
51 
52  /* Process the cryptlib presence-check value */
53  if( length == sizeofOID( OID_CRYPTLIB_PRESENCECHECK ) && \
54  !memcmp( oid, OID_CRYPTLIB_PRESENCECHECK, length ) )
55  {
56  /* The other side is running cryptlib, we can make some common-sense
57  assumptions about its behaviour */
58  protocolInfo->isCryptlib = TRUE;
59  return( readUniversal( stream ) ); /* Attribute */
60  }
61 
62  /* Check for the ESSCertID, which fixes CMP's broken certificate
63  identification mechanism */
64  if( length == sizeofOID( OID_ESS_CERTID ) && \
65  !memcmp( oid, OID_ESS_CERTID, length ) )
66  {
67  BYTE certID[ CRYPT_MAX_HASHSIZE + 8 ];
68  int certIDsize, endPos;
69 
70  /* Extract the certificate hash from the ESSCertID */
71  readSet( stream, NULL ); /* Attribute */
72  readSequence( stream, NULL ); /* SigningCerts */
73  readSequence( stream, NULL ); /* Certs */
74  status = readSequence( stream, &length ); /* ESSCertID */
75  if( cryptStatusError( status ) )
76  return( status );
77  endPos = stell( stream ) + length;
78  status = readOctetString( stream, certID, &certIDsize,
80  if( cryptStatusError( status ) )
81  return( status );
82  if( protocolInfo->certIDsize != KEYID_SIZE || \
83  memcmp( certID, protocolInfo->certID, KEYID_SIZE ) )
84  {
85  /* The certificate used for authentication purposes has changed,
86  remember the new certID */
87  memcpy( protocolInfo->certID, certID, KEYID_SIZE );
88  protocolInfo->certIDsize = KEYID_SIZE;
89  protocolInfo->certIDchanged = TRUE;
90  }
91  if( stell( stream ) < endPos )
92  {
93  /* Skip the issuerSerial if there's one present. We can't
94  really do much with it in this form without rewriting it into
95  the standard issuerAndSerialNumber, but in any case we don't
96  need it because we've already got the certificate ID */
97  status = readUniversal( stream );
98  }
99  return( status );
100  }
101 
102  /* It's something that we don't recognise, skip it */
103  return( readUniversal( stream ) );
104  }
105 
106 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
107 static int readGeneralInfo( INOUT STREAM *stream,
108  INOUT CMP_PROTOCOL_INFO *protocolInfo )
109  {
110  long endPos;
111  int length, iterationCount, status;
112 
113  assert( isWritePtr( stream, sizeof( STREAM ) ) );
114  assert( isWritePtr( protocolInfo, sizeof( CMP_PROTOCOL_INFO ) ) );
115 
116  /* Go through the various attributes looking for anything that we can
117  use */
118  readConstructed( stream, NULL, CTAG_PH_GENERALINFO );
119  status = readSequence( stream, &length );
120  if( cryptStatusError( status ) )
121  return( status );
122  endPos = stell( stream ) + length;
123  for( iterationCount = 0;
124  stell( stream ) < endPos && \
125  iterationCount < FAILSAFE_ITERATIONS_MED; iterationCount++ )
126  {
127  status = readGeneralInfoAttribute( stream, protocolInfo );
128  if( cryptStatusError( status ) )
129  return( status );
130  }
131  ENSURES( iterationCount < FAILSAFE_ITERATIONS_MED );
132 
133  return( status );
134  }
135 
136 /* Read the user ID in the PKI header */
137 
138 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
139 static int readUserID( INOUT STREAM *stream,
140  INOUT CMP_PROTOCOL_INFO *protocolInfo,
141  const BOOLEAN isServerInitialMessage )
142  {
143  BYTE userID[ CRYPT_MAX_HASHSIZE + 8 ];
144  int userIDsize, status;
145 
146  assert( isWritePtr( stream, sizeof( STREAM ) ) );
147  assert( isWritePtr( protocolInfo, sizeof( CMP_PROTOCOL_INFO ) ) );
148 
149 #if 0 /* 28/9/08 Why is this here? Should always check the ID */
150  /* If we're in the middle of an ongoing transaction, skip the user ID,
151  which we already know */
152  if( !isServerInitialMessage )
153  return( readUniversal( stream ) );
154 #endif /* 0 */
155 
156  /* Read the PKI user ID that we'll need to handle the integrity
157  protection on the message */
158  readConstructed( stream, NULL, CTAG_PH_SENDERKID );
159  status = readOctetString( stream, userID, &userIDsize, 8,
161  if( cryptStatusError( status ) )
162  return( status );
163  ANALYSER_HINT( userIDsize >= 8 && userIDsize <= CRYPT_MAX_HASHSIZE );
164 
165  /* If there's already been a previous transaction (which means that we
166  have PKI user information present) and the current transaction
167  matches what was used in the previous one, we don't have to update
168  the user information */
169  if( protocolInfo->userIDsize == userIDsize && \
170  !memcmp( protocolInfo->userID, userID, userIDsize ) )
171  {
172  DEBUG_PRINT(( "%s: Skipped repeated userID.\n",
173  protocolInfo->isServer ? "SVR" : "CLI" ));
174  DEBUG_DUMP_HEX( protocolInfo->isServer ? "SVR" : "CLI",
175  protocolInfo->userID, protocolInfo->userIDsize );
176  return( CRYPT_OK );
177  }
178 
179  /* Record the new or changed the PKI user information and delete the
180  MAC context associated with the previous user if necessary */
181  memcpy( protocolInfo->userID, userID, userIDsize );
182  protocolInfo->userIDsize = userIDsize;
183  protocolInfo->userIDchanged = TRUE;
184  if( protocolInfo->iMacContext != CRYPT_ERROR )
185  {
186  krnlSendNotifier( protocolInfo->iMacContext,
188  protocolInfo->iMacContext = CRYPT_ERROR;
189  }
190  DEBUG_PRINT(( "%s: Read new userID.\n",
191  protocolInfo->isServer ? "SVR" : "CLI" ));
192  DEBUG_DUMP_HEX( protocolInfo->isServer ? "SVR" : "CLI",
193  protocolInfo->userID, protocolInfo->userIDsize );
194 
195  return( CRYPT_OK );
196  }
197 
198 /* Read the transaction ID (effectively the nonce) in the PKI header */
199 
200 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
201 static int readTransactionID( INOUT STREAM *stream,
202  INOUT CMP_PROTOCOL_INFO *protocolInfo,
203  const BOOLEAN isServerInitialMessage )
204  {
206  int length, status;
207 
208  assert( isWritePtr( stream, sizeof( STREAM ) ) );
209  assert( isWritePtr( protocolInfo, sizeof( CMP_PROTOCOL_INFO ) ) );
210 
211  /* If this is the first message and we're the server, record the
212  transaction ID for later */
213  if( isServerInitialMessage )
214  {
215  status = readOctetString( stream, protocolInfo->transID,
216  &protocolInfo->transIDsize,
217  4, CRYPT_MAX_HASHSIZE );
218  if( cryptStatusError( status ) )
219  return( status );
220  DEBUG_PRINT(( "%s: Read initial transID.\n",
221  protocolInfo->isServer ? "SVR" : "CLI" ));
222  DEBUG_DUMP_HEX( protocolInfo->isServer ? "SVR" : "CLI",
223  protocolInfo->transID, protocolInfo->transIDsize );
224  return( CRYPT_OK );
225  }
226 
227  /* Make sure that the transaction ID for this message matches the
228  recorded value (the bad signature error code is the best that we can
229  provide here) */
230  status = readOctetString( stream, buffer, &length, 4,
232  if( cryptStatusError( status ) )
233  return( status );
234  ANALYSER_HINT( length >= 4 && length <= CRYPT_MAX_HASHSIZE );
235  DEBUG_PRINT(( "%s: Read transID.\n",
236  protocolInfo->isServer ? "SVR" : "CLI" ));
237  DEBUG_DUMP_HEX( protocolInfo->isServer ? "SVR" : "CLI",
238  protocolInfo->transID, protocolInfo->transIDsize );
239  if( protocolInfo->transIDsize != length || \
240  memcmp( protocolInfo->transID, buffer, length ) )
241  return( CRYPT_ERROR_SIGNATURE );
242 
243  return( CRYPT_OK );
244  }
245 
246 /* Read the integrity protection algorithm information in the PKI header */
247 
248 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
249 static int readProtectionAlgo( INOUT STREAM *stream,
250  INOUT CMP_PROTOCOL_INFO *protocolInfo )
251  {
253  int hashParam, streamPos, status;
254 
255  assert( isWritePtr( stream, sizeof( STREAM ) ) );
256  assert( isWritePtr( protocolInfo, sizeof( CMP_PROTOCOL_INFO ) ) );
257 
258  /* Read the wrapper. If there's a problem we exit immediately since an
259  error status from the readAlgoIDex() that follows is interpreted to
260  indicate the presence of the weird Entrust MAC rather than a real
261  error */
262  status = readConstructed( stream, NULL, CTAG_PH_PROTECTIONALGO );
263  if( cryptStatusError( status ) )
264  return( status );
265  streamPos = stell( stream );
266  status = readAlgoIDex( stream, &cryptAlgo, &hashAlgo, &hashParam,
268  if( cryptStatusOK( status ) )
269  {
270  /* Make sure that it's a recognised signature algorithm to avoid
271  false positives if the other side sends some bizarre algorithm
272  ID */
273  if( !isSigAlgo( cryptAlgo ) )
274  return( CRYPT_ERROR_NOTAVAIL );
275 
276  /* It's a recognised signature algorithm, use the CA certificate to
277  verify it rather than the MAC */
278  protocolInfo->useMACreceive = FALSE;
279  protocolInfo->hashAlgo = hashAlgo;
280  protocolInfo->hashParam = hashParam;
281 
282  return( CRYPT_OK );
283  }
284  ENSURES( cryptStatusError( status ) );
285 
286  /* It's nothing normal, it must be the Entrust MAC algorithm information,
287  remember where it starts so that we can process it later */
288  sClearError( stream );
289  protocolInfo->macInfoPos = streamPos;
290  status = readUniversal( stream );
291  protocolInfo->useMACreceive = TRUE;
292 
293  return( status );
294  }
295 
296 /* Update the session's user ID and certificate ID information from the
297  newly-read protocol information */
298 
299 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
300 int updateUserID( INOUT SESSION_INFO *sessionInfoPtr,
301  INOUT CMP_PROTOCOL_INFO *protocolInfo,
302  const BOOLEAN isServerInitialMessage,
303  const BOOLEAN useMAC )
304  {
305  int status;
306 
307  assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
308  assert( isWritePtr( protocolInfo, sizeof( CMP_PROTOCOL_INFO ) ) );
309 
310  /* We've got a new PKI user ID, if it looks like a cryptlib encoded ID
311  save it in encoded form, otherwise save it as is. Again, CMP's
312  totally ambiguous protocol fields complicate things for us because
313  although in theory we could reject any message containing a
314  non-cryptlib user ID on the basis that it couldn't have been assigned
315  to the user by a cryptlib server, the fact that an arbitrary client
316  could be sending us who knows what sort of data in the user ID field,
317  expecting the key to be identified through other means, means that we
318  can't perform this simple check. We can at least reject a
319  non-cryptlib ID for the ir, which must be MAC'd */
320  if( isServer( sessionInfoPtr ) && protocolInfo->userIDsize == 9 )
321  {
322  BYTE encodedUserID[ CRYPT_MAX_TEXTSIZE + 8 ];
323  int encodedUserIDLength;
324 
325  status = encodePKIUserValue( encodedUserID, CRYPT_MAX_TEXTSIZE,
326  &encodedUserIDLength,
327  protocolInfo->userID,
328  protocolInfo->userIDsize, 3 );
329  if( cryptStatusError( status ) )
330  return( status );
331  status = updateSessionInfo( &sessionInfoPtr->attributeList,
332  CRYPT_SESSINFO_USERNAME, encodedUserID,
333  encodedUserIDLength, CRYPT_MAX_TEXTSIZE,
335  }
336  else
337  {
338  /* If we're processing an ir then that at least must have a valid
339  cryptlib user ID */
340  if( isServerInitialMessage && useMAC )
341  {
344  "User ID provided by client isn't a cryptlib user "
345  "ID" ) );
346  }
347 
348  /* It's not a valid cryptlib PKI user ID, save it anyway since
349  it'll be used for diagnostic/error-reporting purposes */
350  status = updateSessionInfo( &sessionInfoPtr->attributeList,
352  protocolInfo->userID,
353  protocolInfo->userIDsize,
355  }
356  if( cryptStatusError( status ) )
357  return( status );
358 
359  /* If this is the first message to the server and we're using MAC-based
360  authentication, set up the server's MAC context based on the
361  information supplied by the client */
362  if( isServerInitialMessage && useMAC )
363  return( initServerAuthentMAC( sessionInfoPtr, protocolInfo ) );
364 
365  return( CRYPT_OK );
366  }
367 
368 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
369 int updateCertID( INOUT SESSION_INFO *sessionInfoPtr,
370  INOUT CMP_PROTOCOL_INFO *protocolInfo,
371  const BOOLEAN isServerInitialMessage )
372  {
373  int status;
374 
375  assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
376  assert( isWritePtr( protocolInfo, sizeof( CMP_PROTOCOL_INFO ) ) );
377 
378  status = addSessionInfoS( &sessionInfoPtr->attributeList,
380  protocolInfo->certID,
381  protocolInfo->certIDsize );
382  if( cryptStatusError( status ) )
383  return( status );
384 
385  /* If this is the first message to the server, set up the server's
386  public-key context for the client's key based on the information
387  supplied by the client */
388  if( isServerInitialMessage )
389  return( initServerAuthentSign( sessionInfoPtr, protocolInfo ) );
390 
391  return( CRYPT_OK );
392  }
393 
394 /* In another piece of brilliant design, CMP provides the information
395  required to set up MAC processing in reverse order, so we don't know what
396  to do with any MAC information that may be present in the header until
397  we've read the start of the message body. To handle this we have to
398  record the position of the MAC information in the header and then go back
399  and process it once we've read the necessary additional data from the
400  message body, which is handled by the following function */
401 
402 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2, 3 ) ) \
403 static int updateMacInfo( INOUT SESSION_INFO *sessionInfoPtr,
404  INOUT CMP_PROTOCOL_INFO *protocolInfo,
405  INOUT STREAM *stream )
406  {
407  const ATTRIBUTE_LIST *passwordPtr = \
408  findSessionInfo( sessionInfoPtr->attributeList,
410  BYTE macKey[ 64 + 8 ];
411  const BYTE *macKeyPtr = macKey;
412  const int streamPos = stell( stream );
413  int macKeyLength, status;
414 
415  REQUIRES( passwordPtr != NULL );
416 
417  sseek( stream, protocolInfo->macInfoPos );
418  if( passwordPtr->flags & ATTR_FLAG_ENCODEDVALUE )
419  {
420  /* It's an encoded value, get the decoded form */
421  status = decodePKIUserValue( macKey, 64, &macKeyLength,
422  passwordPtr->value,
423  passwordPtr->valueLength );
424  ENSURES( cryptStatusOK( status ) );
425  }
426  else
427  {
428  macKeyPtr = passwordPtr->value;
429  macKeyLength = passwordPtr->valueLength;
430  }
431  status = readMacInfo( stream, protocolInfo, macKeyPtr,
432  macKeyLength, SESSION_ERRINFO );
433  zeroise( macKey, CRYPT_MAX_TEXTSIZE );
434  if( cryptStatusError( status ) )
435  return( status );
436  sseek( stream, streamPos );
437 
438  return( CRYPT_OK );
439  }
440 
441 /****************************************************************************
442 * *
443 * Read a PKI Header *
444 * *
445 ****************************************************************************/
446 
447 /* Read a PKI header and make sure that it matches the header that we sent
448  (for EE or non-initial CA/RA messages) or set up the EE information in
449  response to an initial message (for an initial CA/RA message). We ignore
450  all of the redundant fields in the header that don't directly affect the
451  protocol, based on the results of CMP interop testing this appears to be
452  standard practice among implementers. This also helps get around
453  problems with implementations that get the fields wrong, since most of
454  the fields aren't useful it doesn't affect the processing while making
455  the code more tolerant of implementation errors:
456 
457  header SEQUENCE {
458  version INTEGER (2),
459  senderDN [4] EXPLICIT DirectoryName, -- Copied if non-clib
460  dummy [4] EXPLICIT DirectoryName, -- Ignored
461  dummy [0] EXPLICIT GeneralisedTime OPT,-- Ignored
462  protAlgo [1] EXPLICIT AlgorithmIdentifier,
463  protKeyID [2] EXPLICIT OCTET STRING, -- Copied if changed
464  dummy [3] EXPLICIT OCTET STRING OPT, -- Ignored
465  transID [4] EXPLICIT OCTET STRING,
466  nonce [5] EXPLICIT OCTET STRING OPT, -- Copied if non-clib
467  dummy [6] EXPLICIT OCTET STRING OPT, -- Ignored
468  dummy [7] SEQUENCE OF UTF8String OPT, -- Ignored
469  generalInfo [8] EXPLICIT SEQUENCE OF Info OPT -- cryptlib-specific info
470  } */
471 
472 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2, 3 ) ) \
473 static int readPkiHeader( INOUT STREAM *stream,
474  INOUT CMP_PROTOCOL_INFO *protocolInfo,
476  const BOOLEAN isServerInitialMessage )
477  {
478  int length, endPos, status;
479 
480  assert( isWritePtr( stream, sizeof( STREAM ) ) );
481  assert( isWritePtr( protocolInfo, sizeof( CMP_PROTOCOL_INFO ) ) );
482  assert( isWritePtr( errorInfo, sizeof( ERROR_INFO ) ) );
483 
484  /* Clear per-message state information */
485  protocolInfo->userIDchanged = protocolInfo->certIDchanged = \
486  protocolInfo->useMACreceive = FALSE;
487  protocolInfo->macInfoPos = CRYPT_ERROR;
488  protocolInfo->senderDNPtr = NULL;
489  protocolInfo->senderDNlength = 0;
490  protocolInfo->headerRead = FALSE;
491 
492  /* Read the wrapper and skip the static information, which matches what
493  we sent and is protected by the MAC so there's little point in
494  looking at it */
495  status = readSequence( stream, &length );
496  if( cryptStatusError( status ) )
497  return( status );
498  endPos = stell( stream ) + length;
499  readShortInteger( stream, NULL ); /* Version */
500  if( !protocolInfo->isCryptlib )
501  {
502  /* The ID of the key used for integrity protection (or in general
503  the identity of the sender) can be specified either as the sender
504  DN or the senderKID or both, or in some cases even indirectly via
505  the transaction ID. With no real guidance as to which one to
506  use, implementors are using any of these options to identify the
507  key. Since we need to check that the integrity-protection key
508  that we're using is correct so that we can report a more
509  appropriate error than bad signature or bad data, we need to
510  remember the sender DN for later in case this is the only form of
511  key identification provided. Unfortunately since the sender DN
512  can't uniquely identify a certificate, if this is the only
513  identifier that we're given then the caller can still get a bad
514  signature error, yet another one of CMPs many wonderful features */
515  status = readConstructed( stream, &protocolInfo->senderDNlength, 4 );
516  if( cryptStatusOK( status ) && protocolInfo->senderDNlength > 0 )
517  {
518  status = sMemGetDataBlock( stream, &protocolInfo->senderDNPtr,
519  protocolInfo->senderDNlength );
520  if( cryptStatusOK( status ) )
521  status = readUniversal( stream );
522  }
523  }
524  else
525  {
526  /* cryptlib includes a proper certID so the whole signer
527  identification mess is avoided and we can ignore the sender DN */
528  status = readUniversal( stream ); /* Sender DN */
529  }
530  if( cryptStatusOK( status ) )
531  status = readUniversal( stream ); /* Recipient DN */
532  if( peekTag( stream ) == MAKE_CTAG( CTAG_PH_MESSAGETIME ) )
533  status = readUniversal( stream ); /* Message time */
534  if( cryptStatusError( status ) )
535  {
537  ( CRYPT_ERROR_BADDATA, errorInfo,
538  "Invalid DN information in PKI header" ) );
539  }
540  if( peekTag( stream ) != MAKE_CTAG( CTAG_PH_PROTECTIONALGO ) )
541  {
542  /* The message was sent without integrity protection, report it as
543  a signature error rather than the generic bad data error that
544  we'd get from the following read */
546  ( CRYPT_ERROR_SIGNATURE, errorInfo,
547  "Message was sent without integrity protection" ) );
548  }
549  status = readProtectionAlgo( stream, protocolInfo );
550  if( cryptStatusError( status ) )
551  {
552  retExt( status,
553  ( status, errorInfo,
554  "Invalid integrity protection information in PKI "
555  "header" ) );
556  }
557  if( peekTag( stream ) == MAKE_CTAG( CTAG_PH_SENDERKID ) )
558  { /* Sender protection keyID */
559  status = readUserID( stream, protocolInfo, isServerInitialMessage );
560  if( cryptStatusError( status ) )
561  {
562  retExt( status,
563  ( status, errorInfo,
564  "Invalid PKI user ID in PKI header" ) );
565  }
566  }
567  else
568  {
569  /* If we're the server, the client must provide a PKI user ID in the
570  first message unless we got one in an earlier transaction */
571  if( isServerInitialMessage && protocolInfo->userIDsize <= 0 )
572  {
574  ( CRYPT_ERROR_BADDATA, errorInfo,
575  "Missing PKI user ID in PKI header" ) );
576  }
577  }
578  if( peekTag( stream ) == MAKE_CTAG( CTAG_PH_RECIPKID ) )
579  readUniversal( stream ); /* Recipient protection keyID */
580 
581  /* Record the transaction ID (which is effectively the nonce) or make
582  sure that it matches the one that we sent. There's no real need to
583  do an explicit duplicate check since a replay attempt will be
584  rejected as a duplicate by the certificate store and the locking
585  performed at that level makes it a much better place to catch
586  duplicates, but we do it anyway because it doesn't cost anything and
587  we can catch at least some problems a bit earlier */
588  status = readConstructed( stream, NULL, CTAG_PH_TRANSACTIONID );
589  if( cryptStatusError( status ) )
590  {
591  retExt( status,
592  ( status, errorInfo,
593  "Missing transaction ID in PKI header" ) );
594  }
595  status = readTransactionID( stream, protocolInfo,
596  isServerInitialMessage );
597  if( cryptStatusError( status ) )
598  {
599  protocolInfo->pkiFailInfo = CMPFAILINFO_BADRECIPIENTNONCE;
600  retExt( status,
601  ( status, errorInfo,
602  ( status == CRYPT_ERROR_SIGNATURE ) ? \
603  "Returned message transaction ID doesn't match our "
604  "transaction ID" : \
605  "Invalid transaction ID in PKI header" ) );
606  }
607 
608  /* Read the sender nonce, which becomes the new recipient nonce, and skip
609  the recipient nonce if there's one present. These values may be
610  absent, either because the other side doesn't implement them or
611  because they're not available, for example because it's sending a
612  response to an error that occurred before it could read the nonce from
613  a request. In any case we don't bother checking the nonce values
614  since the transaction ID serves the same purpose */
615  if( stell( stream ) < endPos && \
616  peekTag( stream ) == MAKE_CTAG( CTAG_PH_SENDERNONCE ) )
617  {
618  readConstructed( stream, NULL, CTAG_PH_SENDERNONCE );
619  status = readOctetString( stream, protocolInfo->recipNonce,
620  &protocolInfo->recipNonceSize,
621  4, CRYPT_MAX_HASHSIZE );
622  if( cryptStatusError( status ) )
623  {
624  protocolInfo->pkiFailInfo = CMPFAILINFO_BADSENDERNONCE;
625  retExt( status,
626  ( status, errorInfo,
627  "Invalid sender nonce in PKI header" ) );
628  }
629  }
630  if( stell( stream ) < endPos && \
631  peekTag( stream ) == MAKE_CTAG( CTAG_PH_RECIPNONCE ) )
632  {
633  readConstructed( stream, NULL, CTAG_PH_RECIPNONCE );
634  status = readUniversal( stream );
635  if( cryptStatusError( status ) )
636  {
637  protocolInfo->pkiFailInfo = CMPFAILINFO_BADRECIPIENTNONCE;
638  retExt( status,
639  ( status, errorInfo,
640  "Invalid recipient nonce in PKI header" ) );
641  }
642  }
643 
644  /* Remember that we've successfully read enough of the header
645  information to generate a response */
646  protocolInfo->headerRead = TRUE;
647 
648  /* Skip any further junk and process the general information if there is
649  any */
650  if( stell( stream ) < endPos && \
651  peekTag( stream ) == MAKE_CTAG( CTAG_PH_FREETEXT ) )
652  {
653  status = readUniversal( stream ); /* Junk */
654  if( cryptStatusError( status ) )
655  return( status );
656  }
657  if( stell( stream ) < endPos && \
658  peekTag( stream ) == MAKE_CTAG( CTAG_PH_GENERALINFO ) )
659  {
660  status = readGeneralInfo( stream, protocolInfo );
661  if( cryptStatusError( status ) )
662  {
663  retExt( status,
664  ( status, errorInfo,
665  "Invalid generalInfo information in PKI header" ) );
666  }
667  }
668 
669  return( CRYPT_OK );
670  }
671 
672 /****************************************************************************
673 * *
674 * Read a PKI Message *
675 * *
676 ****************************************************************************/
677 
678 /* Read a PKI message:
679 
680  PkiMessage ::= SEQUENCE {
681  header PKIHeader,
682  body CHOICE { [0]... [24]... },
683  protection [0] BIT STRING
684  }
685 
686  Note that readPkiDatagram() has already performed an initial valid-ASN.1
687  check before we get here */
688 
689 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
690 int readPkiMessage( INOUT SESSION_INFO *sessionInfoPtr,
691  INOUT CMP_PROTOCOL_INFO *protocolInfo,
692  IN_ENUM_OPT( CTAG_PB ) CMP_MESSAGE_TYPE messageType )
693  {
694 #ifdef USE_ERRMSGS
695  ERROR_INFO *errorInfo = &sessionInfoPtr->errorInfo;
696 #endif /* USE_ERRMSGS */
697  READMESSAGE_FUNCTION readMessageFunction;
698  STREAM stream;
699  const BOOLEAN isServerInitialMessage = \
700  ( messageType == CTAG_PB_READ_ANY ) ? TRUE : FALSE;
701  void *integrityInfoPtr = DUMMY_INIT_PTR;
702  int protPartStart = DUMMY_INIT, protPartSize, bodyStart = DUMMY_INIT;
703  int length, integrityInfoLength, tag, status;
704 
705  assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
706  assert( isWritePtr( protocolInfo, sizeof( CMP_PROTOCOL_INFO ) ) );
707 
708  REQUIRES( ( messageType >= CTAG_PB_IR && \
709  messageType < CTAG_PB_LAST ) || \
710  ( messageType == CTAG_PB_READ_ANY ) );
711  /* CTAG_PB_IR == 0 so this is the same as _NONE */
712 
713  DEBUG_PRINT(( "%s: Reading message type %d.\n",
714  protocolInfo->isServer ? "SVR" : "CLI", messageType ));
715 
716  /* Strip off the header and PKIStatus wrapper */
717  sMemConnect( &stream, sessionInfoPtr->receiveBuffer,
718  sessionInfoPtr->receiveBufEnd );
719  status = readSequence( &stream, NULL ); /* Outer wrapper */
720  if( cryptStatusOK( status ) )
721  {
722  protPartStart = stell( &stream );
723  status = readPkiHeader( &stream, protocolInfo, SESSION_ERRINFO,
724  isServerInitialMessage );
725  }
726  if( cryptStatusError( status ) )
727  {
728  sMemDisconnect( &stream );
729  return( status );
730  }
731  ENSURES( protocolInfo->transIDsize > 0 && \
732  protocolInfo->transIDsize <= CRYPT_MAX_HASHSIZE );
733 
734  /* Set up session state information based on the header that we've just
735  read */
736  if( protocolInfo->isCryptlib )
737  sessionInfoPtr->flags |= SESSION_ISCRYPTLIB;
738 
739  /* In order to fix CMP's inability to properly identify keys via
740  certificates, we use the certID field in the generalInfo. If there's
741  no PKI user ID present but no certID either then we can't identify
742  the key that's needed in order to continue. This also retroactively
743  invalidates the headerRead flag, since we don't know which key to use
744  to authenticate our response.
745 
746  In theory we shouldn't ever get into this state because we require
747  a PKI user ID for the client's initial message and the server will
748  always send a certID for its signing certificate, but due to the
749  confusing combination of values that can affect the protocol state
750  (see the start of writePkiHeader() in cmp_wr.c for an example) we
751  do the following as a safety check to catch potential problems
752  early.
753 
754  This also leads to a special-case exception, if we're the client then
755  the server may identify its signing key purely through the
756  dysfunctional sender DN mechanism (see the comment in
757  readPkiHeader()) so we allow this option as well. The DN can't
758  usefully tell us whether it's the correct key or not (see the
759  comment in checkMessageSignature()) but there's not much else that
760  we can do */
761  if( protocolInfo->useMACreceive )
762  {
763  /* There is one special-case situation in which we can have no user
764  ID present and that's when we're doing a PnP PKI transaction with
765  an initial PKIBoot that creates the required MAC context followed
766  by an ir that doesn't need to send any ID information since it's
767  reusing the MAC context that was created by the PKIBoot */
768  if( protocolInfo->userIDsize <= 0 && \
769  !( protocolInfo->isCryptlib && \
770  protocolInfo->iMacContext != CRYPT_ERROR ) )
771  {
772  sMemDisconnect( &stream );
773  protocolInfo->headerRead = FALSE;
775  ( CRYPT_ERROR_BADDATA, errorInfo,
776  "Missing PKI user ID for MAC authentication of PKI "
777  "messages" ) );
778  }
779  }
780  else
781  {
782  /* As with the case for MAC contexts, there's a special-case
783  situation in which there's no certID present and that's during a
784  PnP PKI transaction preceded by a PKIBoot that communicates the
785  CA's certificate, where the PKIBoot creates the required
786  sig-check context as part of the initialisation process. In
787  addition we have to allow for DN-only identification from
788  servers, see the comment above for details */
789  if( protocolInfo->certIDsize <= 0 && \
790  ( !isServer ( sessionInfoPtr ) &&
791  protocolInfo->senderDNlength <= 0 ) && \
792  !( protocolInfo->isCryptlib && \
793  sessionInfoPtr->iAuthInContext != CRYPT_ERROR ) )
794  {
795  sMemDisconnect( &stream );
796  protocolInfo->headerRead = FALSE;
798  ( CRYPT_ERROR_BADDATA, errorInfo,
799  "Missing certificate ID for signature authentication "
800  "of PKI messages" ) );
801  }
802  }
803 
804  /* If this is the first message from the client and we've been sent a
805  new user ID or certificate ID (via the ESSCertID in the header's
806  kitchen-sink field, used to identify the signing certificate when
807  signature-based authentication is used), process the user/
808  authentication information */
809  if( protocolInfo->userIDchanged )
810  {
811  status = updateUserID( sessionInfoPtr, protocolInfo,
812  isServerInitialMessage,
813  protocolInfo->useMACreceive );
814  if( cryptStatusError( status ) )
815  {
816  sMemDisconnect( &stream );
817  return( status );
818  }
819  }
820  if( protocolInfo->certIDchanged )
821  {
822  status = updateCertID( sessionInfoPtr, protocolInfo,
823  isServerInitialMessage );
824  if( cryptStatusError( status ) )
825  {
826  sMemDisconnect( &stream );
827  return( status );
828  }
829  }
830 
831  /* Determine the message body type. An error response can occur at any
832  point in an exchange so we process this immediately. We don't do an
833  integrity verification for this one since it's not certain what we
834  should report if the check fails (what if the error response is to
835  report that no key is available to authenticate the user, for
836  example?), and an unauthenticated error message is better than an
837  authenticated paketewhainau */
838  tag = peekTag( &stream );
839  if( cryptStatusError( tag ) )
840  return( tag );
841  tag = EXTRACT_CTAG( tag );
842  if( tag == CTAG_PB_ERROR )
843  {
844  readMessageFunction = getMessageReadFunction( CTAG_PB_ERROR );
845  ENSURES( readMessageFunction != NULL );
846  readConstructed( &stream, NULL, CTAG_PB_ERROR );
847  status = readSequence( &stream, &length );
848  if( cryptStatusOK( status ) )
849  status = readMessageFunction( &stream, sessionInfoPtr,
850  protocolInfo, CTAG_PB_ERROR,
851  length );
852  sMemDisconnect( &stream );
853  return( status );
854  }
855 
856  /* If this is an initial message then we don't know what to expect yet
857  so we set the type to whatever we find, as long as it's a valid
858  message to send to a CA */
859  if( messageType == CTAG_PB_READ_ANY )
860  {
861  if( tag == CTAG_PB_IR || tag == CTAG_PB_CR || \
862  tag == CTAG_PB_P10CR || tag == CTAG_PB_KUR || \
863  tag == CTAG_PB_RR || \
864  ( messageType == CTAG_PB_READ_ANY && tag == CTAG_PB_GENM ) )
865  protocolInfo->operation = messageType = tag;
866  else
867  {
868  sMemDisconnect( &stream );
869  protocolInfo->pkiFailInfo = CMPFAILINFO_BADREQUEST;
872  "Client sent invalid initial message type %d",
873  tag ) );
874  }
875  }
876  else
877  {
878  /* Make sure that this is what we're after */
879  if( tag != messageType )
880  {
881  sMemDisconnect( &stream );
882  protocolInfo->pkiFailInfo = CMPFAILINFO_BADREQUEST;
885  "Invalid message type, expected %d, got %d",
886  messageType, tag ) );
887  }
888  }
889 
890  /* If we're using a MAC for authentication, we can finally set up the
891  MAC information using the appropriate password. We couldn't do this
892  when we read the header because the order of the information used to
893  set this up is backwards, so we have to go back and re-process it
894  now */
895  if( protocolInfo->useMACreceive )
896  {
897  status = updateMacInfo( sessionInfoPtr, protocolInfo, &stream );
898  if( cryptStatusError( status ) )
899  {
900  sMemDisconnect( &stream );
901  return( status );
902  }
903  }
904 
905  /* Remember where the message body starts and skip it (it'll be
906  processed after we verify its integrity) */
907  status = readConstructed( &stream, &length, messageType );
908  if( cryptStatusOK ( status ) )
909  {
910  bodyStart = stell( &stream );
911  status = sSkip( &stream, length );
912  }
913  if( cryptStatusError( status ) )
914  {
915  sMemDisconnect( &stream );
916  protocolInfo->pkiFailInfo = CMPFAILINFO_BADDATAFORMAT;
919  "Invalid message body start" ) );
920  }
921 
922  /* Read the start of the message integrity information */
923  protPartSize = stell( &stream ) - protPartStart;
924  ENSURES( protPartSize > 0 && protPartSize < MAX_INTLENGTH_SHORT );
925  status = readConstructed( &stream, &integrityInfoLength,
927  if( cryptStatusOK( status ) )
928  {
929  status = sMemGetDataBlock( &stream, &integrityInfoPtr,
930  integrityInfoLength );
931  }
932  if( cryptStatusError( status ) )
933  {
934  /* If the integrity protection is missing report it as a wrong-
935  integrity-information problem, the closest that we can get to the
936  real error */
937  protocolInfo->pkiFailInfo = CMPFAILINFO_WRONGINTEGRITY;
938  sMemDisconnect( &stream );
940  ( CRYPT_ERROR_SIGNATURE, errorInfo,
941  "Signature/MAC data is missing or truncated" ) );
942  }
943  if( tag == CTAG_PB_IR && !protocolInfo->useMACreceive )
944  {
945  /* An ir has to be MAC'd, in theory this doesn't really matter but
946  the spec requires that we only allow a MAC. If it's not MAC'd it
947  has to be a cr, which is exactly the same only different */
948  sMemDisconnect( &stream );
949  protocolInfo->pkiFailInfo = CMPFAILINFO_WRONGINTEGRITY;
951  ( CRYPT_ERROR_SIGNATURE, errorInfo,
952  "Received signed ir, should be MAC'd" ) );
953  }
954  if( tag == CTAG_PB_RR && protocolInfo->useMACreceive )
955  {
956  /* An rr can't be MAC'd because the trail from the original PKI
957  user to the certificate being revoked can become arbitrarily
958  blurred, with the certificate being revoked having a different
959  DN, issuerAndSerialNumber, and public key after various updates,
960  replacements, and reissues. cryptlib does actually track the
961  resulting directed graph via the certificate store log and allows
962  fetching the original authorising issuer of a certificate using
963  the KEYMGMT_FLAG_GETISSUER option, however this requires that the
964  client also be running cryptlib, or specifically that it submit
965  a certificate ID in the request, this being the only identifier
966  that reliably identifies the certificate being revoked. Since
967  it's somewhat unsound to assume this, we don't currently handle
968  MAC'ed rr's, however everything is in place to allow them to be
969  implemented if they're really needed */
970  sMemDisconnect( &stream );
971  protocolInfo->pkiFailInfo = CMPFAILINFO_WRONGINTEGRITY;
973  ( CRYPT_ERROR_SIGNATURE, errorInfo,
974  "Received MAC'd rr, should be signed" ) );
975  }
976  ANALYSER_HINT( integrityInfoPtr != NULL );
977 
978  /* Verify the message integrity. We convert any error that we encounter
979  during this check to a CRYPT_ERROR_SIGNATURE, this is somewhat
980  overreaching since it could have been something like a formatting
981  error but overall the problem is in the signature-check so we make
982  this explicit rather than returning a somewhat vague underflow/
983  overflow/bad-data/whatever */
984  if( protocolInfo->useMACreceive )
985  {
986  status = checkMessageMAC( &stream, protocolInfo,
987  sessionInfoPtr->receiveBuffer + protPartStart,
988  protPartSize );
989  if( cryptStatusError( status ) )
990  {
991  sMemDisconnect( &stream );
994  "Bad message MAC" ) );
995  }
996  }
997  else
998  {
999  status = checkMessageSignature( protocolInfo,
1000  sessionInfoPtr->receiveBuffer + protPartStart,
1001  protPartSize, integrityInfoPtr, integrityInfoLength,
1002  sessionInfoPtr->iAuthInContext );
1003  if( cryptStatusError( status ) )
1004  {
1005  sMemDisconnect( &stream );
1006  if( status == CRYPT_ERROR_WRONGKEY )
1007  {
1008  /* Provide a more specific error message for the wrong-key
1009  error */
1012  "Message signature key doesn't match our "
1013  "signature check key, signature can't be "
1014  "checked" ) );
1015  }
1018  "Bad message signature" ) );
1019  }
1020  }
1021  sseek( &stream, bodyStart );
1022 
1023  /* In the usual CMP tradition there's a nonstandard way used to encode
1024  one of the message types, which we have to handle specially here */
1025  if( messageType == CTAG_PB_PKICONF )
1026  {
1027  status = readNull( &stream );
1028  sMemDisconnect( &stream );
1029  return( status );
1030  }
1031 
1032  /* Read the message body wrapper */
1033  status = readSequence( &stream, &length );
1034  if( cryptStatusError( status ) )
1035  return( status );
1036 
1037  /* Process the message body */
1038  readMessageFunction = getMessageReadFunction( messageType );
1039  if( readMessageFunction == NULL )
1040  {
1041  DEBUG_DIAG(( "No message-read function available for message "
1042  "type %d", messageType ));
1043  assert( DEBUG_WARN );
1046  "Unexpected message type %d", messageType ) );
1047  }
1048  status = readMessageFunction( &stream, sessionInfoPtr, protocolInfo,
1049  messageType, length );
1050  sMemDisconnect( &stream );
1051  return( status );
1052  }
1053 #endif /* USE_CMP */