cryptlib  3.4.1
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Macros
cmp_cry.c
Go to the documentation of this file.
1 /****************************************************************************
2 * *
3 * cryptlib CMP Crypto Routines *
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 * Utility Routines *
27 * *
28 ****************************************************************************/
29 
30 /* Hash/MAC the message header and body */
31 
33 int hashMessageContents( IN_HANDLE const CRYPT_CONTEXT iHashContext,
34  IN_BUFFER( length ) const void *data,
35  IN_LENGTH_SHORT const int length )
36  {
37  STREAM stream;
38  BYTE buffer[ 8 + 8 ];
39  int status;
40 
41  assert( isReadPtr( data, length ) );
42 
43  REQUIRES( isHandleRangeValid( iHashContext ) );
44  REQUIRES( length > 0 && length < MAX_INTLENGTH_SHORT );
45 
46  /* Delete the hash/MAC value, which resets the context */
47  status = krnlSendMessage( iHashContext, IMESSAGE_DELETEATTRIBUTE, NULL,
49  if( cryptStatusError( status ) )
50  return( status );
51 
52  /* Write the pseudoheader used for hashing/MACing the message and
53  hash/MAC it */
54  sMemOpen( &stream, buffer, 8 );
55  status = writeSequence( &stream, length );
56  if( cryptStatusOK( status ) )
57  status = krnlSendMessage( iHashContext, IMESSAGE_CTX_HASH, buffer,
58  stell( &stream ) );
59  sMemClose( &stream );
60  if( cryptStatusError( status ) )
61  return( status );
62 
63  /* Hash/MAC the message itself */
64  status = krnlSendMessage( iHashContext, IMESSAGE_CTX_HASH,
65  ( MESSAGE_CAST ) data, length );
66  if( cryptStatusOK( status ) )
67  status = krnlSendMessage( iHashContext, IMESSAGE_CTX_HASH, buffer, 0 );
68  return( status );
69  }
70 
71 /****************************************************************************
72 * *
73 * MAC Routines *
74 * *
75 ****************************************************************************/
76 
77 /* Initialise the MAC information used to protect the messages */
78 
79 CHECK_RETVAL STDC_NONNULL_ARG( ( 2, 4 ) ) \
80 int initMacInfo( IN_HANDLE const CRYPT_CONTEXT iMacContext,
81  IN_BUFFER( passwordLength ) const void *password,
83  IN_BUFFER( saltLength ) const void *salt,
84  IN_LENGTH_SHORT const int saltLength,
85  IN_RANGE( 1, CMP_MAX_PASSWORD_ITERATIONS ) const int iterations )
86  {
89  BYTE macKey[ CRYPT_MAX_HASHSIZE + 8 ];
90  int status;
91 
92  assert( isReadPtr( password, passwordLength ) );
93  assert( isReadPtr( salt, saltLength ) );
94 
95  REQUIRES( isHandleRangeValid( iMacContext ) );
96  REQUIRES( passwordLength > 0 && passwordLength < MAX_INTLENGTH_SHORT );
97  REQUIRES( saltLength > 0 && saltLength < MAX_INTLENGTH_SHORT );
98  REQUIRES( iterations >= 1 && iterations <= CMP_MAX_PW_ITERATIONS );
99 
100  /* Turn the password into an HMAC key using the CMP/Entrust password
101  derivation mechanism */
102  setMechanismDeriveInfo( &mechanismInfo, macKey, CMP_HMAC_KEYSIZE,
103  password, passwordLength, CRYPT_ALGO_SHA1,
104  ( MESSAGE_CAST ) salt, saltLength, iterations );
106  &mechanismInfo, MECHANISM_DERIVE_CMP );
107  if( cryptStatusError( status ) )
108  return( status );
109 
110  /* Load the key into the MAC context */
111  setMessageData( &msgData, macKey, CMP_HMAC_KEYSIZE );
112  status = krnlSendMessage( iMacContext, IMESSAGE_SETATTRIBUTE_S,
113  &msgData, CRYPT_CTXINFO_KEY );
114  zeroise( macKey, CRYPT_MAX_HASHSIZE );
115  return( status );
116  }
117 
118 /* Read/write the CMP/Entrust MAC information:
119 
120  macInfo ::= SEQUENCE {
121  algoID OBJECT IDENTIFIER (entrustMAC),
122  algoParams SEQUENCE {
123  salt OCTET STRING,
124  pwHashAlgo AlgorithmIdentifier (SHA-1)
125  iterations INTEGER,
126  macAlgo AlgorithmIdentifier (HMAC-SHA1)
127  } OPTIONAL
128  } */
129 
130 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2, 3, 5 ) ) \
131 int readMacInfo( INOUT STREAM *stream,
133  IN_BUFFER( passwordLength ) const void *password,
134  IN_LENGTH_SHORT const int passwordLength,
136  {
138  MESSAGE_CREATEOBJECT_INFO createInfo;
139  BYTE salt[ CRYPT_MAX_HASHSIZE + 8 ];
140  long value = DUMMY_INIT;
142 
143  assert( isWritePtr( stream, sizeof( STREAM ) ) );
144  assert( isWritePtr( protocolInfo, sizeof( CMP_PROTOCOL_INFO ) ) );
145  assert( isReadPtr( password, passwordLength ) );
146  assert( isWritePtr( errorInfo, sizeof( ERROR_INFO ) ) );
147 
148  REQUIRES( passwordLength > 0 && passwordLength < MAX_INTLENGTH_SHORT );
149 
150  /* Read the various parameter fields */
151  readSequence( stream, NULL );
152  status = readFixedOID( stream, OID_ENTRUST_MAC,
154  if( cryptStatusError( status ) )
155  {
156  /* If we don't find the Entrust MAC OID we specifically report it
157  as an unknown algorithm problem rather than a generic bad data
158  error */
159  protocolInfo->pkiFailInfo = CMPFAILINFO_BADALG;
160  retExt( status,
161  ( status, errorInfo, "Unrecognised passwod-based MAC "
162  "mechanism" ) );
163  }
164  if( peekTag( stream ) == BER_NULL )
165  {
166  /* No parameters, use the same values as for the previous
167  transaction */
168  return( CRYPT_OK );
169  }
170  readSequence( stream, NULL );
171  readOctetString( stream, salt, &saltLength, 4, CRYPT_MAX_HASHSIZE );
172  status = readAlgoID( stream, &algorithm, ALGOID_CLASS_HASH );
173  if( cryptStatusOK( status ) && algorithm != CRYPT_ALGO_SHA1 )
174  status = CRYPT_ERROR_NOTAVAIL;
175  if( cryptStatusOK( status ) )
176  {
177  readShortInteger( stream, &value );
178  status = readAlgoID( stream, &algorithm, ALGOID_CLASS_HASH );
179  if( cryptStatusOK( status ) && algorithm != CRYPT_ALGO_HMAC_SHA1 )
180  status = CRYPT_ERROR_NOTAVAIL;
181  }
182  if( cryptStatusError( status ) )
183  {
184  retExt( status,
185  ( status, errorInfo,
186  "Invalid passwod-based MAC algorithm information" ) );
187  }
189  {
190  /* Prevent DoS attacks due to excessive iteration counts (bad
191  algorithm is about the most appropriate error that we can return
192  here). The spec never defines any appropriate limits for this
193  value, which leads to interesting effects when submitting a
194  request for bignum iterations to some implementations */
195  protocolInfo->pkiFailInfo = CMPFAILINFO_BADALG;
197  ( CRYPT_ERROR_BADDATA, errorInfo,
198  "Invalid passwod-based MAC iteration count %ld",
199  value ) );
200  }
201  iterations = ( int ) value;
202 
203  /* If the MAC parameters aren't set yet (meaing that we're the server),
204  set them based on the client's values */
205  if( protocolInfo->saltSize <= 0 )
206  {
207  status = initMacInfo( protocolInfo->iMacContext, password,
208  passwordLength, salt, saltLength, iterations );
209  if( cryptStatusError( status ) )
210  {
211  retExt( status,
212  ( status, errorInfo,
213  "Couldn't initialise passwod-based MAC "
214  "information" ) );
215  }
216  ENSURES( rangeCheckZ( 0, saltLength, CRYPT_MAX_HASHSIZE ) );
217  memcpy( protocolInfo->salt, salt, saltLength );
218  protocolInfo->saltSize = saltLength;
219  protocolInfo->iterations = iterations;
220  DEBUG_PRINT(( "%s: Read initial MAC params with salt, %d iterations.\n",
221  protocolInfo->isServer ? "SVR" : "CLI",
222  protocolInfo->iterations ));
223  DEBUG_DUMP_HEX( protocolInfo->isServer ? "SVR" : "CLI",
224  protocolInfo->salt, protocolInfo->saltSize );
225  return( CRYPT_OK );
226  }
227 
228  /* If the new parameters match our original MAC parameters, reuse the
229  existing MAC context. As usual the spec is ambiguous over the use of
230  the MAC information, leaving it possible for implementations to re-
231  key the MAC on a per-message basis. We try and cache MAC information
232  as much as possible to reduce the performance hit from re-keying for
233  each message */
234  if( protocolInfo->iterations && \
235  saltLength == protocolInfo->saltSize && \
236  !memcmp( salt, protocolInfo->salt, saltLength ) && \
237  iterations == protocolInfo->iterations )
238  {
239  DEBUG_PRINT(( "%s: Skipped repeated MAC params with salt, "
240  "%d iterations.\n",
241  protocolInfo->isServer ? "SVR" : "CLI",
242  protocolInfo->iterations ));
243  DEBUG_DUMP_HEX( protocolInfo->isServer ? "SVR" : "CLI",
244  protocolInfo->salt, protocolInfo->saltSize );
245  return( CRYPT_OK );
246  }
247 
248  /* This is a new set of parameters, recreate the MAC context with them */
251  &createInfo, OBJECT_TYPE_CONTEXT );
252  if( cryptStatusError( status ) )
253  return( status );
254  status = initMacInfo( createInfo.cryptHandle, password, passwordLength,
255  salt, saltLength, iterations );
256  if( cryptStatusError( status ) )
257  {
259  retExt( status,
260  ( status, errorInfo,
261  "Couldn't initialise passwod-based MAC information" ) );
262  }
263  if( protocolInfo->iMacContext != CRYPT_ERROR )
264  krnlSendNotifier( protocolInfo->iMacContext,
266  protocolInfo->iMacContext = createInfo.cryptHandle;
267 
268  /* Remember the parameters that were used to set up the MAC context */
269  ENSURES( rangeCheckZ( 0, saltLength, CRYPT_MAX_HASHSIZE ) );
270  memcpy( protocolInfo->salt, salt, saltLength );
271  protocolInfo->saltSize = saltLength;
272  protocolInfo->iterations = iterations;
273  DEBUG_PRINT(( "%s: Read new MAC params with salt, %d iterations.\n",
274  protocolInfo->isServer ? "SVR" : "CLI",
275  protocolInfo->iterations ));
276  DEBUG_DUMP_HEX( protocolInfo->isServer ? "SVR" : "CLI",
277  protocolInfo->salt, protocolInfo->saltSize );
278 
279  return( CRYPT_OK );
280  }
281 
282 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
283 int writeMacInfo( INOUT STREAM *stream,
284  const CMP_PROTOCOL_INFO *protocolInfo,
285  const BOOLEAN sendFullInfo )
286  {
287  int paramSize;
288 
289  assert( isWritePtr( stream, sizeof( STREAM ) ) );
290  assert( isReadPtr( protocolInfo, sizeof( CMP_PROTOCOL_INFO ) ) );
291 
292  /* If we've already sent the MAC parameters in an earlier transaction,
293  just send an indication that we're using MAC protection */
294  if( !sendFullInfo )
295  {
296  writeSequence( stream, sizeofOID( OID_ENTRUST_MAC ) + sizeofNull() );
297  writeOID( stream, OID_ENTRUST_MAC );
298  return( writeNull( stream, DEFAULT_TAG ) );
299  }
300 
301  /* Determine how big the payload will be */
302  paramSize = ( int ) sizeofObject( protocolInfo->saltSize ) + \
303  sizeofAlgoID( CRYPT_ALGO_SHA1 ) + \
304  sizeofShortInteger( protocolInfo->iterations ) + \
305  sizeofAlgoID( CRYPT_ALGO_HMAC_SHA1 );
306 
307  /* Write the wrapper */
308  writeSequence( stream, sizeofOID( OID_ENTRUST_MAC ) + \
309  ( int ) sizeofObject( paramSize ) );
310  writeOID( stream, OID_ENTRUST_MAC );
311 
312  /* Write the payload */
313  DEBUG_PRINT(( "%s: Writing MAC params with salt, %d iterations.\n",
314  protocolInfo->isServer ? "SVR" : "CLI",
315  protocolInfo->iterations ));
316  DEBUG_DUMP_HEX( protocolInfo->isServer ? "SVR" : "CLI",
317  protocolInfo->salt, protocolInfo->saltSize );
318  writeSequence( stream, paramSize );
319  writeOctetString( stream, protocolInfo->salt, protocolInfo->saltSize,
320  DEFAULT_TAG );
321  writeAlgoID( stream, CRYPT_ALGO_SHA1 );
322  writeShortInteger( stream, protocolInfo->iterations, DEFAULT_TAG );
323  return( writeAlgoID( stream, CRYPT_ALGO_HMAC_SHA1 ) );
324  }
325 
326 /****************************************************************************
327 * *
328 * Verify Integrity-Protection Information *
329 * *
330 ****************************************************************************/
331 
332 /* Check integrity protection on a message */
333 
334 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2, 3 ) ) \
335 int checkMessageMAC( INOUT STREAM *stream,
336  INOUT CMP_PROTOCOL_INFO *protocolInfo,
337  IN_BUFFER( messageLength ) const void *message,
338  IN_LENGTH const int messageLength )
339  {
341  BYTE macValue[ CRYPT_MAX_HASHSIZE + 8 ];
342  int macValueLength, status;
343 
344  assert( isWritePtr( stream, sizeof( STREAM ) ) );
345  assert( isWritePtr( protocolInfo, sizeof( CMP_PROTOCOL_INFO ) ) );
346  assert( isReadPtr( message, messageLength ) );
347 
348  REQUIRES( messageLength > 0 && messageLength < MAX_INTLENGTH );
349 
350  /* Read the BIT STRING encapsulation and get a pointer to the MAC value */
351  status = readBitStringHole( stream, &macValueLength, 16, DEFAULT_TAG );
352  if( cryptStatusError( status ) )
353  return( status );
354  if( macValueLength < 16 || macValueLength > CRYPT_MAX_HASHSIZE )
355  return( CRYPT_ERROR_BADDATA );
356  status = sread( stream, macValue, macValueLength );
357  if( cryptStatusError( status ) )
358  return( status );
359 
360  /* MAC the data, and make sure that it matches the value attached to the
361  message */
362  status = hashMessageContents( protocolInfo->iMacContext, message,
363  messageLength );
364  if( cryptStatusOK( status ) )
365  {
366  setMessageData( &msgData, macValue, macValueLength );
367  if( cryptStatusError( \
368  krnlSendMessage( protocolInfo->iMacContext, IMESSAGE_COMPARE,
369  &msgData, MESSAGE_COMPARE_HASH ) ) )
370  status = CRYPT_ERROR_SIGNATURE;
371  }
372  return( status );
373  }
374 
375 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2, 4 ) ) \
376 int checkMessageSignature( INOUT CMP_PROTOCOL_INFO *protocolInfo,
377  IN_BUFFER( messageLength ) const void *message,
378  IN_LENGTH const int messageLength,
379  IN_BUFFER( signatureLength ) const void *signature,
382  {
384  MESSAGE_CREATEOBJECT_INFO createInfo;
385  int status;
386 
387  assert( isWritePtr( protocolInfo, sizeof( CMP_PROTOCOL_INFO ) ) );
388  assert( isReadPtr( message, messageLength ) );
389  assert( isReadPtr( signature, signatureLength ) );
390 
391  REQUIRES( messageLength > 0 && messageLength < MAX_INTLENGTH );
392  REQUIRES( signatureLength > 0 && signatureLength < MAX_INTLENGTH_SHORT );
393  REQUIRES( isHandleRangeValid( iAuthContext ) );
394 
395  /* If it's a non-cryptlib message (in other words one where the
396  certificate/signing key isn't unambiguously identified via a certID),
397  make sure that the sig-check key that we'll be using is the correct
398  one. Because of CMP's use of a raw signature format we have to do
399  this manually rather than relying on the sig-check code to do it for
400  us, and because of the braindamaged way of identifying integrity-
401  protection keys for non-cryptlib messages even this isn't enough to
402  definitely tell us that we're using the right key, in which case
403  we'll get a bad data or bad signature response from the sig-check
404  code */
405  if( !protocolInfo->isCryptlib )
406  {
408 
409  setMessageData( &msgData, protocolInfo->senderDNPtr,
410  protocolInfo->senderDNlength );
411  status = krnlSendMessage( iAuthContext, IMESSAGE_COMPARE, &msgData,
413  if( cryptStatusError( status ) )
414  {
415  /* A failed comparison is reported as a generic CRYPT_ERROR,
416  convert it into a wrong-key error if necessary */
417  return( ( status == CRYPT_ERROR ) ? \
418  CRYPT_ERROR_WRONGKEY : status );
419  }
420  }
421 
422  /* Hash the data and verify the signature */
423  setMessageCreateObjectInfo( &createInfo, protocolInfo->hashAlgo );
425  IMESSAGE_DEV_CREATEOBJECT, &createInfo,
427  if( cryptStatusError( status ) )
428  return( status );
429  iHashContext = createInfo.cryptHandle;
430  if( protocolInfo->hashParam != 0 )
431  {
432  /* Some hash algorithms have variable output size, in which case
433  we need to explicitly tell the context which one we're working
434  with */
435  status = krnlSendMessage( iHashContext, IMESSAGE_SETATTRIBUTE,
436  &protocolInfo->hashParam,
438  if( cryptStatusError( status ) )
439  return( status );
440  }
441  status = hashMessageContents( iHashContext, message, messageLength );
442  if( cryptStatusOK( status ) )
443  {
444  status = checkRawSignature( signature, signatureLength,
445  iAuthContext, iHashContext );
446  }
447  krnlSendNotifier( iHashContext, IMESSAGE_DECREFCOUNT );
448  return( status );
449  }
450 
451 /****************************************************************************
452 * *
453 * Create Integrity-Protection Information *
454 * *
455 ****************************************************************************/
456 
457 /* Write MACd/signed message protection information */
458 
459 CHECK_RETVAL STDC_NONNULL_ARG( ( 2, 4, 6 ) ) \
460 int writeMacProtinfo( IN_HANDLE const CRYPT_CONTEXT iMacContext,
461  IN_BUFFER( messageLength ) const void *message,
462  IN_LENGTH_SHORT const int messageLength,
463  OUT_BUFFER( protInfoMaxLength, *protInfoLength ) \
464  void *protInfo,
465  IN_LENGTH_SHORT_MIN( 16 ) const int protInfoMaxLength,
467  {
468  STREAM macStream;
470  BYTE macValue[ CRYPT_MAX_HASHSIZE + 8 ];
471  int macLength, status;
472 
473  assert( isReadPtr( message, messageLength ) );
474  assert( isWritePtr( protInfo, protInfoMaxLength ) );
475  assert( isWritePtr( protInfoLength, sizeof( int ) ) );
476 
477  REQUIRES( isHandleRangeValid( iMacContext ) );
478  REQUIRES( messageLength > 0 && messageLength < MAX_INTLENGTH_SHORT );
479  REQUIRES( protInfoMaxLength >= 16 && \
480  protInfoMaxLength < MAX_INTLENGTH_SHORT );
481 
482  /* Clear return values */
483  memset( protInfo, 0, min( 16, protInfoMaxLength ) );
484  *protInfoLength = 0;
485 
486  /* MAC the message and get the MAC value */
487  status = hashMessageContents( iMacContext, message, messageLength );
488  if( cryptStatusError( status ) )
489  return( status );
490  setMessageData( &msgData, macValue, CRYPT_MAX_HASHSIZE );
491  status = krnlSendMessage( iMacContext, IMESSAGE_GETATTRIBUTE_S,
492  &msgData, CRYPT_CTXINFO_HASHVALUE );
493  if( cryptStatusError( status ) )
494  return( status );
495  macLength = msgData.length;
496 
497  /* Write the MAC value with BIT STRING encapsulation */
498  sMemOpen( &macStream, protInfo, protInfoMaxLength );
499  writeBitStringHole( &macStream, macLength, DEFAULT_TAG );
500  status = swrite( &macStream, macValue, macLength );
501  if( cryptStatusOK( status ) )
502  *protInfoLength = stell( &macStream );
503  sMemDisconnect( &macStream );
504 
505  return( status );
506  }
507 
508 CHECK_RETVAL STDC_NONNULL_ARG( ( 4, 6, 8 ) ) \
509 int writeSignedProtinfo( IN_HANDLE const CRYPT_CONTEXT iSignContext,
511  IN_RANGE( 0, 999 ) const int hashParam,
512  IN_BUFFER( messageLength ) const void *message,
513  IN_LENGTH_SHORT const int messageLength,
514  OUT_BUFFER( protInfoMaxLength, *protInfoLength ) \
515  void *protInfo,
516  IN_LENGTH_SHORT_MIN( 32 ) const int protInfoMaxLength,
517  OUT_LENGTH_SHORT_Z int *protInfoLength )
518  {
520  MESSAGE_CREATEOBJECT_INFO createInfo;
521  int status;
522 
523  assert( isReadPtr( message, messageLength ) );
524  assert( isWritePtr( protInfo, protInfoMaxLength ) );
525  assert( isWritePtr( protInfoLength, sizeof( int ) ) );
526 
527  REQUIRES( isHandleRangeValid( iSignContext ) );
528  REQUIRES( isHashAlgo( hashAlgo ) );
529  REQUIRES( hashParam >= 0 && hashParam <= 999 );
530  REQUIRES( messageLength > 0 && messageLength < MAX_INTLENGTH_SHORT );
531  REQUIRES( protInfoMaxLength >= 32 && \
532  protInfoMaxLength < MAX_INTLENGTH_SHORT );
533 
534  /* Hash the data */
535  setMessageCreateObjectInfo( &createInfo, hashAlgo );
537  &createInfo, OBJECT_TYPE_CONTEXT );
538  if( cryptStatusError( status ) )
539  return( status );
540  iHashContext = createInfo.cryptHandle;
541  if( hashParam != 0 )
542  {
543  /* Some hash algorithms have variable output size, in which case
544  we need to explicitly tell the context which one we're working
545  with */
546  status = krnlSendMessage( iHashContext, IMESSAGE_SETATTRIBUTE,
547  ( MESSAGE_CAST ) &hashParam,
549  if( cryptStatusError( status ) )
550  return( status );
551  }
552  status = hashMessageContents( iHashContext, message, messageLength );
553  if( cryptStatusError( status ) )
554  {
555  krnlSendNotifier( iHashContext, IMESSAGE_DECREFCOUNT );
556  return( status );
557  }
558 
559  /* Create the signature */
560  status = createRawSignature( protInfo, protInfoMaxLength,
561  protInfoLength, iSignContext,
562  iHashContext );
563  krnlSendNotifier( iHashContext, IMESSAGE_DECREFCOUNT );
564 
565  return( status );
566  }
567 #endif /* USE_CMP */