cryptlib  3.4.1
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Macros
sign_cms.c
Go to the documentation of this file.
1 /****************************************************************************
2 * *
3 * CMS Signature Routines *
4 * Copyright Peter Gutmann 1993-2007 *
5 * *
6 ****************************************************************************/
7 
8 #if defined( INC_ALL )
9  #include "crypt.h"
10  #include "asn1.h"
11  #include "asn1_ext.h"
12  #include "misc_rw.h"
13  #include "mech.h"
14 #else
15  #include "crypt.h"
16  #include "enc_dec/asn1.h"
17  #include "enc_dec/asn1_ext.h"
18  #include "enc_dec/misc_rw.h"
19  #include "mechs/mech.h"
20 #endif /* Compiler-specific includes */
21 
22 /* CMS version */
23 
24 #define CMS_VERSION 1
25 
26 /* The maximum size for the encoded CMS signed attributes */
27 
28 #define ENCODED_ATTRIBUTE_SIZE 512
29 
30 /* A structure to store CMS attribute information */
31 
32 typedef struct {
33  /* The format of the signature: Basic CMS or full S/MIME */
35 
36  /* Objects needed to create the attributes. The time source is a device
37  associated with the signing key (usually the system device, but can
38  be a crypto device) used to obtain the signing time. The TSP session
39  is an optional session that's used to timestamp the signature */
40  BOOLEAN useDefaultAttributes; /* Whether we provide default attrs.*/
41  CRYPT_CERTIFICATE iCmsAttributes; /* CMS attributes */
42  CRYPT_CONTEXT iMessageHash; /* Hash for MessageDigest */
43  CRYPT_HANDLE iTimeSource; /* Time source for signing time */
44  CRYPT_SESSION iTspSession; /* Optional TSP session */
45 
46  /* The encoded attributes. The encodedAttributes pointer is null if
47  there are no attributes present, or points to the buffer containing
48  the encoded attributes */
49  BYTE attributeBuffer[ ENCODED_ATTRIBUTE_SIZE + 8 ];
50  BUFFER_OPT( maxEncodedAttributeSize, encodedAttributeSize ) \
51  BYTE *encodedAttributes;
52  int maxEncodedAttributeSize;
53 
54  /* Returned data: The size of the encoded attribute information in the
55  buffer */
56  int encodedAttributeSize;
58 
59 #define initCmsAttributeInfo( attributeInfo, format, useDefault, cmsAttributes, messageHash, timeSource, tspSession ) \
60  memset( attributeInfo, 0, sizeof( CMS_ATTRIBUTE_INFO ) ); \
61  ( attributeInfo )->formatType = format; \
62  ( attributeInfo )->useDefaultAttributes = useDefault; \
63  ( attributeInfo )->iCmsAttributes = cmsAttributes; \
64  ( attributeInfo )->iMessageHash = messageHash; \
65  ( attributeInfo )->iTimeSource = timeSource; \
66  ( attributeInfo )->iTspSession = tspSession; \
67  ( attributeInfo )->maxEncodedAttributeSize = ENCODED_ATTRIBUTE_SIZE;
68 
69 /****************************************************************************
70 * *
71 * Utility Functions *
72 * *
73 ****************************************************************************/
74 
75 /* Write CMS signer information:
76 
77  SignerInfo ::= SEQUENCE {
78  version INTEGER (1),
79  issuerAndSerialNumber IssuerAndSerialNumber,
80  digestAlgorithm AlgorithmIdentifier,
81  signedAttrs [ 0 ] IMPLICIT SET OF Attribute OPTIONAL,
82  signatureAlgorithm AlgorithmIdentifier,
83  signature OCTET STRING,
84  unsignedAttrs [ 1 ] IMPLICIT SET OF Attribute OPTIONAL
85  } */
86 
88 static int writeCmsSignerInfo( INOUT STREAM *stream,
92  const void *attributes,
93  IN_LENGTH_Z const int attributeSize,
94  IN_BUFFER( signatureSize ) const void *signature,
95  IN_LENGTH_SHORT const int signatureSize,
96  IN_HANDLE_OPT const CRYPT_HANDLE unsignedAttrObject )
97  {
99  DYNBUF iAndSDB;
100  const int sizeofHashAlgoID = sizeofAlgoID( hashAlgo );
101  int timeStampSize = DUMMY_INIT, unsignedAttributeSize = 0, status;
102 
103  assert( isWritePtr( stream, sizeof( STREAM ) ) );
104  assert( ( attributes == NULL && attributeSize == 0 ) || \
105  isReadPtr( attributes, attributeSize ) );
106  assert( isReadPtr( signature, signatureSize ) );
107 
108  REQUIRES( isHandleRangeValid( certificate ) );
109  REQUIRES( isHashAlgo( hashAlgo ) );
110  REQUIRES( ( attributes == NULL && attributeSize == 0 ) || \
111  ( attributes != NULL && \
112  attributeSize > 0 && attributeSize < MAX_INTLENGTH ) );
113  REQUIRES( signatureSize > MIN_CRYPT_OBJECTSIZE && \
114  signatureSize < MAX_INTLENGTH_SHORT );
115  REQUIRES( unsignedAttrObject == CRYPT_UNUSED || \
116  isHandleRangeValid( unsignedAttrObject ) );
117 
118  if( cryptStatusError( sizeofHashAlgoID ) )
119  return( sizeofHashAlgoID );
120 
121  /* Get the signerInfo information */
122  if( unsignedAttrObject != CRYPT_UNUSED )
123  {
124  setMessageData( &msgData, NULL, 0 );
125  status = krnlSendMessage( unsignedAttrObject, IMESSAGE_GETATTRIBUTE_S,
126  &msgData, CRYPT_IATTRIBUTE_ENC_TIMESTAMP );
127  if( cryptStatusError( status ) )
128  return( status );
129  timeStampSize = msgData.length;
130  unsignedAttributeSize = ( int ) \
131  sizeofObject( sizeofOID( OID_TSP_TSTOKEN ) + \
132  sizeofObject( timeStampSize ) );
133  }
134  status = dynCreate( &iAndSDB, certificate,
135  CRYPT_IATTRIBUTE_ISSUERANDSERIALNUMBER );
136  if( cryptStatusError( status ) )
137  return( status );
138 
139  /* Write the outer SEQUENCE wrapper and version number */
140  writeSequence( stream, sizeofShortInteger( CMS_VERSION ) + \
141  dynLength( iAndSDB ) + sizeofHashAlgoID + \
142  attributeSize + signatureSize + \
143  ( ( unsignedAttributeSize ) ? \
144  ( int ) sizeofObject( unsignedAttributeSize ) : 0 ) );
145  writeShortInteger( stream, CMS_VERSION, DEFAULT_TAG );
146 
147  /* Write the issuerAndSerialNumber, digest algorithm identifier,
148  attributes (if there are any) and signature */
149  swrite( stream, dynData( iAndSDB ), dynLength( iAndSDB ) );
150  writeAlgoID( stream, hashAlgo );
151  if( attributeSize > 0 )
152  swrite( stream, attributes, attributeSize );
153  status = swrite( stream, signature, signatureSize );
154  dynDestroy( &iAndSDB );
155  if( cryptStatusError( status ) || unsignedAttributeSize <= 0 )
156  return( status );
157 
158  /* Write the unsigned attributes. Note that the only unsigned attribute
159  in use at this time is a (not-quite) countersignature containing a
160  timestamp, so the following code always assumes that the attribute is
161  a timestamp. First we write the [1] IMPLICT SET OF attribute
162  wrapper */
163  writeConstructed( stream, unsignedAttributeSize, 1 );
164  writeSequence( stream, sizeofOID( OID_TSP_TSTOKEN ) + \
165  sizeofObject( timeStampSize ) );
166  writeOID( stream, OID_TSP_TSTOKEN );
167  status = writeSet( stream, timeStampSize );
168  if( cryptStatusError( status ) )
169  return( status );
170 
171  /* Then we copy the timestamp data directly into the stream */
172  return( exportAttributeToStream( stream, unsignedAttrObject,
173  CRYPT_IATTRIBUTE_ENC_TIMESTAMP ) );
174  }
175 
176 /* Create a CMS countersignature */
177 
179 static int createCmsCountersignature( IN_BUFFER( dataSignatureSize ) \
180  const void *dataSignature,
181  IN_LENGTH_SHORT const int dataSignatureSize,
182  IN_ALGO const CRYPT_ALGO_TYPE hashAlgo,
184  {
186  MESSAGE_CREATEOBJECT_INFO createInfo;
187  STREAM stream;
188  int length, status;
189 
190  assert( isReadPtr( dataSignature, dataSignatureSize ) );
191 
192  REQUIRES( dataSignatureSize > MIN_CRYPT_OBJECTSIZE && \
193  dataSignatureSize < MAX_INTLENGTH_SHORT );
194  REQUIRES( isHashAlgo( hashAlgo ) );
195  REQUIRES( isHandleRangeValid( iTspSession ) );
196 
197  /* Hash the signature data to create the hash value to countersign.
198  The CMS spec requires that the signature is calculated on the
199  contents octets (in other words the V of the TLV) of the signature,
200  so we have to skip the signature algorithm and OCTET STRING wrapper */
201  setMessageCreateObjectInfo( &createInfo, hashAlgo );
203  IMESSAGE_DEV_CREATEOBJECT, &createInfo,
205  if( cryptStatusError( status ) )
206  return( status );
207  iHashContext = createInfo.cryptHandle;
208 #if 1 /* Standard CMS countersignature */
209  sMemConnect( &stream, dataSignature, dataSignatureSize );
210  readUniversal( &stream );
211  status = readOctetStringHole( &stream, &length, 16, DEFAULT_TAG );
212  if( cryptStatusOK( status ) )
213  {
214  void *dataPtr;
215 
216  status = sMemGetDataBlock( &stream, &dataPtr, length );
217  if( cryptStatusOK( status ) )
218  {
219  status = krnlSendMessage( iHashContext, IMESSAGE_CTX_HASH,
220  dataPtr, length );
221  }
222  }
223  sMemDisconnect( &stream );
224 #else /* Broken TSP not-quite-countersignature */
225  krnlSendMessage( iHashContext, IMESSAGE_CTX_HASH,
226  ( MESSAGE_CAST ) dataSignature, dataSignatureSize );
227 #endif /* 1 */
228  if( cryptStatusOK( status ) )
229  status = krnlSendMessage( iHashContext, IMESSAGE_CTX_HASH, "", 0 );
230  if( cryptStatusOK( status ) )
231  {
232  status = krnlSendMessage( iTspSession, IMESSAGE_SETATTRIBUTE,
233  &iHashContext,
235  }
236  krnlSendNotifier( iHashContext, IMESSAGE_DECREFCOUNT );
237  if( cryptStatusError( status ) )
238  return( status );
239 
240  /* Send the result to the TSA for countersigning */
241  return( krnlSendMessage( iTspSession, IMESSAGE_SETATTRIBUTE,
243  }
244 
245 /* Add sMimeCapabilities to a CMS attribute object */
246 
247 static void addSmimeCapabilities( IN_HANDLE const CRYPT_CERTIFICATE iCmsAttributes )
248  {
249  typedef struct {
251  CRYPT_ALGO_TYPE secondaryAlgorithm;
252  CRYPT_ATTRIBUTE_TYPE smimeCapability;
253  } SMIMECAP_INFO;
254  static const SMIMECAP_INFO smimeCapInfo[] = {
259 #ifdef USE_SHAng
262 #endif /* USE_SHAng */
263 #ifdef USE_SHA2
266 #endif /* USE_SHA2 */
269 #ifdef USE_SHAng
272 #endif /* USE_SHAng */
273 #ifdef USE_SHA2
276 #endif /* USE_SHA2 */
279  { CRYPT_IALGO_GENERIC_SECRET, CRYPT_ALGO_NONE,
281  { CRYPT_IALGO_GENERIC_SECRET, CRYPT_ALGO_NONE,
287  { CRYPT_ALGO_RSA, CRYPT_ALGO_NONE, /* 'None' since always avail. */
289  { CRYPT_ALGO_DSA, CRYPT_ALGO_NONE, /* 'None' since always avail. */
295  { CRYPT_ALGO_ECDSA, CRYPT_ALGO_NONE, /* 'None' since always avail. */
299  };
300  CRYPT_ALGO_TYPE prevAlgo = CRYPT_ALGO_NONE;
301  int value, i, status;
302 
303  REQUIRES_V( isHandleRangeValid( iCmsAttributes ) );
304 
305  /* If there are already sMIMECapabilities present don't try and add
306  anything further */
307  status = krnlSendMessage( iCmsAttributes, IMESSAGE_GETATTRIBUTE,
309  if( cryptStatusOK( status ) )
310  return;
311 
312  /* Add an sMIMECapability for each supported algorithm or algorithm
313  combination. Since these are no-value attributes it's not worth
314  aborting the signature generation if the attempt to add them fails
315  so we don't bother checking the return value */
316  for( i = 0; smimeCapInfo[ i ].algorithm != CRYPT_ALGO_NONE && \
317  i < FAILSAFE_ARRAYSIZE( smimeCapInfo, SMIMECAP_INFO );
318  i++ )
319  {
320  const CRYPT_ALGO_TYPE algorithm = smimeCapInfo[ i ].algorithm;
321 
322  /* Make sure that the primary algorithm is available */
323  if( prevAlgo != algorithm && !algoAvailable( algorithm ) )
324  {
325  prevAlgo = algorithm;
326  continue;
327  }
328  prevAlgo = algorithm;
329 
330  /* If there's a secondary algorithm, make sure that it's available */
331  if( smimeCapInfo[ i ].secondaryAlgorithm != CRYPT_ALGO_NONE && \
332  !algoAvailable( smimeCapInfo[ i ].secondaryAlgorithm ) )
333  continue;
334 
335  /* List this algorithm or algorithm combination as an available
336  capability */
337  ( void ) krnlSendMessage( iCmsAttributes, IMESSAGE_SETATTRIBUTE,
339  smimeCapInfo[ i ].smimeCapability );
340  }
341  ENSURES_V( i < FAILSAFE_ARRAYSIZE( smimeCapInfo, SMIMECAP_INFO ) );
342 
343  /* Add any futher non-algorithm-related sMIMECapabilities */
344  ( void ) krnlSendMessage( iCmsAttributes, IMESSAGE_SETATTRIBUTE,
347  }
348 
349 /****************************************************************************
350 * *
351 * Create CMS Attributes *
352 * *
353 ****************************************************************************/
354 
355 /* Finalise processing of and hash the CMS attributes */
356 
358 static int hashCmsAttributes( INOUT CMS_ATTRIBUTE_INFO *cmsAttributeInfo,
359  IN_HANDLE const CRYPT_CONTEXT iAttributeHash,
360  const BOOLEAN lengthCheckOnly )
361  {
363  BYTE temp, hash[ CRYPT_MAX_HASHSIZE + 8 ];
364  int status;
365 
366  assert( isWritePtr( cmsAttributeInfo, sizeof( CMS_ATTRIBUTE_INFO ) ) );
367  assert( isWritePtr( cmsAttributeInfo->encodedAttributes, \
368  cmsAttributeInfo->maxEncodedAttributeSize ) );
369 
370  REQUIRES( isHandleRangeValid( cmsAttributeInfo->iCmsAttributes ) );
371  REQUIRES( isHandleRangeValid( cmsAttributeInfo->iMessageHash ) );
372  REQUIRES( isHandleRangeValid( iAttributeHash ) );
373 
374  /* Extract the message hash information and add it as a messageDigest
375  attribute, replacing any existing value if necessary (we don't bother
376  checking the return value because the attribute may or may not be
377  present, and a failure to delete it will be detected immediately
378  afterwards when we try and set it). If we're doing a call just to
379  get the length of the exported data we use a dummy hash value since
380  the hashing may not have completed yet */
381  ( void ) krnlSendMessage( cmsAttributeInfo->iCmsAttributes,
384  setMessageData( &msgData, hash, CRYPT_MAX_HASHSIZE );
385  if( lengthCheckOnly )
386  {
387  memset( hash, 0, CRYPT_MAX_HASHSIZE ); /* Keep mem.checkers happy */
388  status = krnlSendMessage( cmsAttributeInfo->iMessageHash,
389  IMESSAGE_GETATTRIBUTE, &msgData.length,
391  }
392  else
393  {
394  status = krnlSendMessage( cmsAttributeInfo->iMessageHash,
395  IMESSAGE_GETATTRIBUTE_S, &msgData,
397  }
398  if( cryptStatusOK( status ) )
399  {
400  status = krnlSendMessage( cmsAttributeInfo->iCmsAttributes,
401  IMESSAGE_SETATTRIBUTE_S, &msgData,
403  }
404  if( cryptStatusError( status ) )
405  return( status );
406 
407  /* If we're creating the attributes for a real signature rather than
408  just as part of a size check and there's a reliable time source
409  present, use the time from that instead of the built-in system time.
410  Although this seems like a trivial thing it's likely that the
411  presence of a high-assurance time source means that the accuracy of
412  timekeeping is considered critical so we fail the signature
413  generation if we can't set the time from the reliable source */
414  if( !lengthCheckOnly )
415  {
416  const time_t currentTime = \
417  getReliableTime( cmsAttributeInfo->iTimeSource );
418 
419  if( currentTime > MIN_TIME_VALUE )
420  {
421  setMessageData( &msgData, ( MESSAGE_CAST ) &currentTime,
422  sizeof( time_t ) );
423  ( void ) krnlSendMessage( cmsAttributeInfo->iCmsAttributes,
426  status = krnlSendMessage( cmsAttributeInfo->iCmsAttributes,
427  IMESSAGE_SETATTRIBUTE_S, &msgData,
429  if( cryptStatusError( status ) )
430  return( status );
431  }
432  }
433 
434  /* Export the attributes into an encoded signedAttributes data block */
435  if( lengthCheckOnly )
436  { setMessageData( &msgData, NULL, 0 ); }
437  else
438  {
439  setMessageData( &msgData, cmsAttributeInfo->encodedAttributes,
440  cmsAttributeInfo->maxEncodedAttributeSize );
441  }
442  status = krnlSendMessage( cmsAttributeInfo->iCmsAttributes,
443  IMESSAGE_CRT_EXPORT, &msgData,
444  CRYPT_ICERTFORMAT_DATA );
445  if( cryptStatusError( status ) )
446  return( status );
447  cmsAttributeInfo->encodedAttributeSize = msgData.length;
448 
449  /* If it's a length check, just generate a dummy hash value and exit */
450  if( lengthCheckOnly )
451  return( krnlSendMessage( iAttributeHash, IMESSAGE_CTX_HASH, "", 0 ) );
452 
453  /* Replace the IMPLICIT [ 0 ] tag at the start with a SET OF tag to
454  allow the attributes to be hashed, hash them into the attribute hash
455  context, and replace the original tag */
456  temp = cmsAttributeInfo->encodedAttributes[ 0 ];
457  cmsAttributeInfo->encodedAttributes[ 0 ] = BER_SET;
458  status = krnlSendMessage( iAttributeHash, IMESSAGE_CTX_HASH,
459  cmsAttributeInfo->encodedAttributes,
460  cmsAttributeInfo->encodedAttributeSize );
461  if( cryptStatusOK( status ) )
462  status = krnlSendMessage( iAttributeHash, IMESSAGE_CTX_HASH, "", 0 );
463  cmsAttributeInfo->encodedAttributes[ 0 ] = temp;
464  return( status );
465  }
466 
468 static int createCmsAttributes( INOUT CMS_ATTRIBUTE_INFO *cmsAttributeInfo,
469  OUT_HANDLE_OPT CRYPT_CONTEXT *iCmsHashContext,
470  IN_ALGO const CRYPT_ALGO_TYPE hashAlgo,
471  const BOOLEAN lengthCheckOnly )
472  {
473  MESSAGE_CREATEOBJECT_INFO createInfo;
474  BOOLEAN createdHashContext = FALSE;
475  int status;
476 
477  assert( isWritePtr( cmsAttributeInfo, sizeof( CMS_ATTRIBUTE_INFO ) ) );
478  assert( isWritePtr( cmsAttributeInfo->attributeBuffer, \
479  cmsAttributeInfo->maxEncodedAttributeSize ) );
480  assert( isWritePtr( iCmsHashContext, sizeof( CRYPT_CONTEXT ) ) );
481 
482  REQUIRES( cmsAttributeInfo->formatType == CRYPT_FORMAT_CMS || \
483  cmsAttributeInfo->formatType == CRYPT_FORMAT_SMIME );
484  REQUIRES( ( cmsAttributeInfo->iCmsAttributes == CRYPT_UNUSED && \
485  cmsAttributeInfo->useDefaultAttributes == FALSE ) || \
486  ( cmsAttributeInfo->iCmsAttributes == CRYPT_UNUSED && \
487  cmsAttributeInfo->useDefaultAttributes == TRUE ) || \
488  ( isHandleRangeValid( cmsAttributeInfo->iCmsAttributes ) && \
489  cmsAttributeInfo->useDefaultAttributes == FALSE ) );
490  REQUIRES( isHandleRangeValid( cmsAttributeInfo->iMessageHash ) );
491  REQUIRES( isHandleRangeValid( cmsAttributeInfo->iTimeSource ) );
492  REQUIRES( ( cmsAttributeInfo->iTspSession == CRYPT_UNUSED ) || \
493  isHandleRangeValid( cmsAttributeInfo->iTspSession ) );
494  REQUIRES( cmsAttributeInfo->encodedAttributes == NULL && \
495  cmsAttributeInfo->encodedAttributeSize == 0 );
496  REQUIRES( isHashAlgo( hashAlgo ) );
497 
498  /* Clear return value */
499  *iCmsHashContext = CRYPT_ERROR;
500 
501  /* Set up the attribute buffer */
502  cmsAttributeInfo->encodedAttributes = cmsAttributeInfo->attributeBuffer;
503 
504  /* If the user hasn't supplied the attributes, generate them ourselves */
505  if( cmsAttributeInfo->useDefaultAttributes )
506  {
507  setMessageCreateObjectInfo( &createInfo,
511  &createInfo, OBJECT_TYPE_CERTIFICATE );
512  if( cryptStatusError( status ) )
513  return( status );
514  cmsAttributeInfo->iCmsAttributes = createInfo.cryptHandle;
515  }
516  ENSURES( isHandleRangeValid( cmsAttributeInfo->iCmsAttributes ) );
517 
518  /* If it's an S/MIME (vs.pure CMS) signature add the sMIMECapabilities
519  to further bloat things up. Since these are no-value attributes
520  it's not worth aborting the signature generation if the attempt to
521  add them fails so we don't bother checking a return value */
522  if( cmsAttributeInfo->formatType == CRYPT_FORMAT_SMIME )
523  addSmimeCapabilities( cmsAttributeInfo->iCmsAttributes );
524 
525  /* Generate the attributes and hash them into the CMS hash context */
526  setMessageCreateObjectInfo( &createInfo, hashAlgo );
528  IMESSAGE_DEV_CREATEOBJECT, &createInfo,
530  if( cryptStatusOK( status ) )
531  {
532  createdHashContext = TRUE;
533  status = hashCmsAttributes( cmsAttributeInfo, createInfo.cryptHandle,
534  lengthCheckOnly );
535  }
536  if( cmsAttributeInfo->useDefaultAttributes )
537  {
538  krnlSendNotifier( cmsAttributeInfo->iCmsAttributes,
540  cmsAttributeInfo->iCmsAttributes = CRYPT_UNUSED;
541  }
542  if( cryptStatusError( status ) )
543  {
544  if( createdHashContext )
546  return( status );
547  }
548 
549  /* Return the hash of the attributes to the caller */
550  *iCmsHashContext = createInfo.cryptHandle;
551 
552  return( CRYPT_OK );
553  }
554 
555 /****************************************************************************
556 * *
557 * Create/Check a CMS Signature *
558 * *
559 ****************************************************************************/
560 
561 /* Create a CMS signature. The use of authenticated attributes is a three-
562  way choice:
563 
564  useDefaultAuthAttr = FALSE, No attributes.
565  iAuthAttr = CRYPT_UNUSED
566 
567  useDefaultAuthAttr = TRUE, We supply default attributes.
568  iAuthAttr = CRYPT_UNUSED
569 
570  useDefaultAuthAttr = FALSE, Caller has supplied attributes
571  iAuthAttr = validhandle */
572 
574 int createSignatureCMS( OUT_BUFFER_OPT( sigMaxLength, *signatureLength ) \
575  void *signature, IN_LENGTH_Z const int sigMaxLength,
582  IN_ENUM( CRYPT_FORMAT ) \
584  {
585  CRYPT_CONTEXT iCmsHashContext = iHashContext;
586  CRYPT_CERTIFICATE iSigningCert;
587  STREAM stream;
588  CMS_ATTRIBUTE_INFO cmsAttributeInfo;
589  BYTE buffer[ CRYPT_MAX_PKCSIZE + 128 + 8 ];
590  BYTE *bufPtr = ( signature == NULL ) ? NULL : buffer;
591  const int bufSize = ( signature == NULL ) ? 0 : CRYPT_MAX_PKCSIZE + 128;
592  int hashAlgo, dataSignatureSize, length = DUMMY_INIT, status;
593 
594  assert( ( signature == NULL && sigMaxLength == 0 ) || \
595  isReadPtr( signature, sigMaxLength ) );
596  assert( isWritePtr( signatureLength, sizeof( int ) ) );
597 
598  REQUIRES( ( signature == NULL && sigMaxLength == 0 ) || \
599  ( signature != NULL && \
600  sigMaxLength > MIN_CRYPT_OBJECTSIZE && \
601  sigMaxLength < MAX_INTLENGTH ) );
602  REQUIRES( isHandleRangeValid( signContext ) );
603  REQUIRES( isHandleRangeValid( iHashContext ) );
604  REQUIRES( ( iAuthAttr == CRYPT_UNUSED && \
605  useDefaultAuthAttr == FALSE ) || \
606  ( iAuthAttr == CRYPT_UNUSED && \
607  useDefaultAuthAttr == TRUE ) || \
608  ( isHandleRangeValid( iAuthAttr ) && \
609  useDefaultAuthAttr == FALSE ) );
610  REQUIRES( ( iTspSession == CRYPT_UNUSED ) || \
611  isHandleRangeValid( iTspSession ) );
612  REQUIRES( formatType == CRYPT_FORMAT_CMS || \
613  formatType == CRYPT_FORMAT_SMIME );
614 
615  /* Clear return value */
616  *signatureLength = 0;
617 
618  initCmsAttributeInfo( &cmsAttributeInfo, formatType,
619  useDefaultAuthAttr, iAuthAttr, iHashContext,
620  signContext, iTspSession );
621 
622  /* Get the message hash algo and signing certificate */
623  status = krnlSendMessage( iHashContext, IMESSAGE_GETATTRIBUTE,
624  &hashAlgo, CRYPT_CTXINFO_ALGO );
625  if( cryptStatusError( status ) )
626  return( cryptArgError( status ) ? CRYPT_ARGERROR_NUM2 : status );
627  status = krnlSendMessage( signContext, IMESSAGE_GETDEPENDENT,
628  &iSigningCert, OBJECT_TYPE_CERTIFICATE );
629  if( cryptStatusError( status ) )
630  return( cryptArgError( status ) ? CRYPT_ARGERROR_NUM1 : status );
631 
632  /* If we're using signed attributes, set them up to be added to the
633  signature info */
634  if( useDefaultAuthAttr || iAuthAttr != CRYPT_UNUSED )
635  {
636  status = createCmsAttributes( &cmsAttributeInfo, &iCmsHashContext,
637  hashAlgo, ( signature == NULL ) ? \
638  TRUE : FALSE );
639  if( cryptStatusError( status ) )
640  return( status );
641  }
642 
643  /* Create the signature */
644  status = createSignature( bufPtr, bufSize, &dataSignatureSize,
645  signContext, iCmsHashContext, CRYPT_UNUSED,
646  SIGNATURE_CMS );
647  if( iCmsHashContext != iHashContext )
648  krnlSendNotifier( iCmsHashContext, IMESSAGE_DECREFCOUNT );
649  if( cryptStatusError( status ) )
650  return( status );
651 
652  /* If we're countersigning the signature (typically done via a
653  timestamp), create the countersignature */
654  if( iTspSession != CRYPT_UNUSED && signature != NULL )
655  {
656  status = createCmsCountersignature( buffer, dataSignatureSize,
657  hashAlgo, iTspSession );
658  if( cryptStatusError( status ) )
659  return( status );
660  }
661 
662  /* Write the signerInfo record */
663  sMemOpenOpt( &stream, signature, ( signature == NULL ) ? 0 : sigMaxLength );
664  status = writeCmsSignerInfo( &stream, iSigningCert, hashAlgo,
665  cmsAttributeInfo.encodedAttributes,
666  cmsAttributeInfo.encodedAttributeSize,
667  buffer, dataSignatureSize,
668  ( signature == NULL ) ? CRYPT_UNUSED : iTspSession );
669  if( cryptStatusOK( status ) )
670  length = stell( &stream );
671  sMemDisconnect( &stream );
672  if( cryptStatusError( status ) )
673  return( status );
674  if( iTspSession != CRYPT_UNUSED && signature == NULL )
675  {
676  /* If we're countersigning the signature with a timestamp and doing
677  a length check only, inflate the total size to the nearest
678  multiple of the envelope parameter MIN_BUFFER_SIZE, which is the
679  size of the envelope's auxData buffer used to contain the
680  signature. In other words we're always going to trigger an
681  increase in the auxBuffer size because its initial size is
682  MIN_BUFFER_SIZE, so when we grow it we grow it to a nice round
683  value rather than just ( length + MIN_BUFFER_SIZE ). The actual
684  size increase is just a guess since we can't really be sure how
685  much bigger it'll get without contacting the TSA, however this
686  should be big enough to hold a simple SignedData value without
687  attached certificates. If a TSA gets the implementation wrong
688  and returns a timestamp with an attached certificate chain and
689  the chain is too large the worst that'll happen is that we'll
690  get a CRYPT_ERROR_OVERFLOW when we try and read the TSA data
691  from the session object. Note that this behaviour is envelope-
692  specific and assumes that we're being called from the enveloping
693  code, this is curently the only location from which we can be
694  called because a timestamp only makes sense as a countersignature
695  on CMS data. It's somewhat ugly because it asumes internal
696  knowledge of the envelope abstraction but there isn't really any
697  clean way to handle this because we can't tell in advance how
698  much data the TSA will send us */
699  if( MIN_BUFFER_SIZE - length <= 1024 )
700  length = roundUp( length, MIN_BUFFER_SIZE ) + MIN_BUFFER_SIZE;
701  else
702  {
703  /* It should fit in the buffer, don't bother expanding it */
704  length = 1024;
705  }
706  }
707  *signatureLength = length;
708 
709  return( CRYPT_OK );
710  }
711 
712 /* Check a CMS signature */
713 
715 int checkSignatureCMS( IN_BUFFER( signatureLength ) const void *signature,
716  IN_LENGTH_SHORT const int signatureLength,
718  IN_HANDLE const CRYPT_CONTEXT iHashContext,
721  {
722  CRYPT_CERTIFICATE iLocalExtraData;
723  CRYPT_CONTEXT iCmsHashContext = iHashContext;
724  MESSAGE_CREATEOBJECT_INFO createInfo;
727  STREAM stream;
728  static const BYTE setTag[] = { BER_SET };
730  int hashAlgo, status; /* int vs.enum */
731 
732  assert( isReadPtr( signature, signatureLength ) );
733  assert( ( iExtraData == NULL ) || \
734  isWritePtr( iExtraData, sizeof( CRYPT_CERTIFICATE ) ) );
735 
736  REQUIRES( signatureLength > 40 && signatureLength < MAX_INTLENGTH );
737  REQUIRES( isHandleRangeValid( sigCheckContext ) );
738  REQUIRES( isHandleRangeValid( iHashContext ) );
739  REQUIRES( isHandleRangeValid( iSigCheckKey ) );
740 
741  if( iExtraData != NULL )
742  *iExtraData = CRYPT_ERROR;
743 
744  /* Get the message hash algo */
745  status = krnlSendMessage( iHashContext, IMESSAGE_GETATTRIBUTE,
746  &hashAlgo, CRYPT_CTXINFO_ALGO );
747  if( cryptStatusError( status ) )
748  return( cryptArgError( status ) ? CRYPT_ARGERROR_NUM2 : status );
749 
750  /* Unpack the SignerInfo record and make sure that the supplied key is
751  the correct one for the sig.check and the supplied hash context
752  matches the algorithm used in the signature */
753  sMemConnect( &stream, signature, signatureLength );
754  status = queryAsn1Object( &stream, &queryInfo );
755  if( cryptStatusOK( status ) && \
756  ( queryInfo.formatType != CRYPT_FORMAT_CMS && \
757  queryInfo.formatType != CRYPT_FORMAT_SMIME ) )
758  status = CRYPT_ERROR_BADDATA;
759  sMemDisconnect( &stream );
760  if( cryptStatusError( status ) )
761  return( status );
762  REQUIRES( rangeCheck( queryInfo.iAndSStart, queryInfo.iAndSLength,
763  queryInfo.size ) );
764  setMessageData( &msgData, \
765  ( BYTE * ) signature + queryInfo.iAndSStart, \
766  queryInfo.iAndSLength );
767  status = krnlSendMessage( iSigCheckKey, IMESSAGE_COMPARE, &msgData,
769  if( cryptStatusError( status ) )
770  {
771  /* A failed comparison is reported as a generic CRYPT_ERROR,
772  convert it into a wrong-key error if necessary */
773  return( ( status == CRYPT_ERROR ) ? \
774  CRYPT_ERROR_WRONGKEY : status );
775  }
776  if( queryInfo.hashAlgo != hashAlgo )
777  return( CRYPT_ARGERROR_NUM2 );
778 
779  /* If there are no signed attributes present, just check the signature
780  and exit */
781  if( queryInfo.attributeStart <= 0 )
782  {
783  return( checkSignature( signature, signatureLength, sigCheckContext,
784  iCmsHashContext, CRYPT_UNUSED,
785  SIGNATURE_CMS ) );
786  }
787 
788  /* There are signedAttributes present, hash the data, substituting a SET
789  OF tag for the IMPLICIT [ 0 ] tag at the start */
790  REQUIRES( rangeCheck( queryInfo.attributeStart,
791  queryInfo.attributeLength, queryInfo.size ) );
792  setMessageCreateObjectInfo( &createInfo, queryInfo.hashAlgo );
794  IMESSAGE_DEV_CREATEOBJECT, &createInfo,
796  if( cryptStatusError( status ) )
797  return( status );
798  iCmsHashContext = createInfo.cryptHandle;
799  status = krnlSendMessage( iCmsHashContext, IMESSAGE_CTX_HASH,
800  ( BYTE * ) setTag, sizeof( BYTE ) );
801  if( cryptStatusOK( status ) )
802  status = krnlSendMessage( iCmsHashContext, IMESSAGE_CTX_HASH,
803  ( BYTE * ) signature + queryInfo.attributeStart + 1,
804  queryInfo.attributeLength - 1 );
805  if( cryptStatusOK( status ) )
806  status = krnlSendMessage( iCmsHashContext, IMESSAGE_CTX_HASH, "", 0 );
807  if( cryptStatusError( status ) )
808  {
809  krnlSendNotifier( iCmsHashContext, IMESSAGE_DECREFCOUNT );
810  return( status );
811  }
812 
813  /* Check the signature */
814  status = checkSignature( signature, signatureLength, sigCheckContext,
815  iCmsHashContext, CRYPT_UNUSED, SIGNATURE_CMS );
816  krnlSendNotifier( iCmsHashContext, IMESSAGE_DECREFCOUNT );
817  if( cryptStatusError( status ) )
818  return( status );
819 
820  /* Import the attributes and make sure that the data hash value given in
821  the signed attributes matches the user-supplied hash */
822  REQUIRES( rangeCheck( queryInfo.attributeStart,
823  queryInfo.attributeLength, queryInfo.size ) );
825  ( BYTE * ) signature + queryInfo.attributeStart,
826  queryInfo.attributeLength,
830  &createInfo, OBJECT_TYPE_CERTIFICATE );
831  if( cryptStatusError( status ) )
832  return( status );
833  iLocalExtraData = createInfo.cryptHandle;
834  setMessageData( &msgData, hashValue, CRYPT_MAX_HASHSIZE );
835  status = krnlSendMessage( iLocalExtraData, IMESSAGE_GETATTRIBUTE_S,
837  if( cryptStatusOK( status ) )
838  {
839  status = krnlSendMessage( iHashContext, IMESSAGE_COMPARE, &msgData,
841  if( cryptStatusError( status ) )
842  status = CRYPT_ERROR_SIGNATURE;
843  }
844  if( cryptStatusError( status ) )
845  {
846  krnlSendNotifier( iLocalExtraData, IMESSAGE_DECREFCOUNT );
847  return( status );
848  }
849 
850  /* If the user wants to look at the authenticated attributes, make them
851  externally visible, otherwise delete them */
852  if( iExtraData != NULL )
853  *iExtraData = iLocalExtraData;
854  else
855  krnlSendNotifier( iLocalExtraData, IMESSAGE_DECREFCOUNT );
856 
857  return( CRYPT_OK );
858  }