1 /****************************************************************************
2 * *
3 * CMS Signature Routines *
4 * Copyright Peter Gutmann 1993-2011 *
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 */
34 CRYPT_FORMAT_TYPE formatType;
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;
57 } CMS_ATTRIBUTE_INFO;
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 #ifdef USE_INT_CMS
70
71 /****************************************************************************
72 * *
73 * Utility Functions *
74 * *
75 ****************************************************************************/
76
77 /* Write CMS signer information:
78
79 SignerInfo ::= SEQUENCE {
80 version INTEGER (1),
81 issuerAndSerialNumber IssuerAndSerialNumber,
82 digestAlgorithm AlgorithmIdentifier,
83 signedAttrs [ 0 ] IMPLICIT SET OF Attribute OPTIONAL,
84 signatureAlgorithm AlgorithmIdentifier,
85 signature OCTET STRING,
86 unsignedAttrs [ 1 ] IMPLICIT SET OF Attribute OPTIONAL
87 } */
88
89 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 7 ) ) \
90 static int writeCmsSignerInfo( INOUT STREAM *stream,
91 IN_HANDLE const CRYPT_CERTIFICATE certificate,
92 IN_ALGO const CRYPT_ALGO_TYPE hashAlgo,
93 IN_RANGE( 0, CRYPT_MAX_HASHSIZE ) \
94 const int hashAlgoParam,
95 IN_BUFFER_OPT( attributeSize ) \
96 const void *attributes,
97 IN_DATALENGTH_Z const int attributeSize,
98 IN_BUFFER( signatureSize ) const void *signature,
99 IN_LENGTH_SHORT const int signatureSize,
100 IN_HANDLE_OPT const CRYPT_HANDLE unsignedAttrObject )
101 {
102 MESSAGE_DATA msgData;
103 DYNBUF iAndSDB;
104 const int sizeofHashAlgoID = sizeofAlgoIDex( hashAlgo, hashAlgoParam, 0 );
105 int timeStampSize DUMMY_INIT, unsignedAttributeSize = 0, status;
106
107 assert( isWritePtr( stream, sizeof( STREAM ) ) );
108 assert( ( attributes == NULL && attributeSize == 0 ) || \
109 isReadPtr( attributes, attributeSize ) );
110 assert( isReadPtr( signature, signatureSize ) );
111
112 REQUIRES( isHandleRangeValid( certificate ) );
113 REQUIRES( isHashAlgo( hashAlgo ) );
114 REQUIRES( hashAlgoParam >= 0 && hashAlgoParam <= CRYPT_MAX_HASHSIZE );
115 REQUIRES( ( attributes == NULL && attributeSize == 0 ) || \
116 ( attributes != NULL && \
117 attributeSize > 0 && attributeSize < MAX_BUFFER_SIZE ) );
118 REQUIRES( signatureSize > MIN_CRYPT_OBJECTSIZE && \
119 signatureSize < MAX_INTLENGTH_SHORT );
120 REQUIRES( unsignedAttrObject == CRYPT_UNUSED || \
121 isHandleRangeValid( unsignedAttrObject ) );
122
123 if( cryptStatusError( sizeofHashAlgoID ) )
124 return( sizeofHashAlgoID );
125
126 /* Get the signerInfo information */
127 if( unsignedAttrObject != CRYPT_UNUSED )
128 {
129 setMessageData( &msgData, NULL, 0 );
130 status = krnlSendMessage( unsignedAttrObject, IMESSAGE_GETATTRIBUTE_S,
131 &msgData, CRYPT_IATTRIBUTE_ENC_TIMESTAMP );
132 if( cryptStatusError( status ) )
133 return( status );
134 timeStampSize = msgData.length;
135 unsignedAttributeSize = ( int ) \
136 sizeofObject( sizeofOID( OID_TSP_TSTOKEN ) + \
137 sizeofObject( timeStampSize ) );
138 }
139 status = dynCreate( &iAndSDB, certificate,
140 CRYPT_IATTRIBUTE_ISSUERANDSERIALNUMBER );
141 if( cryptStatusError( status ) )
142 return( status );
143
144 /* Write the outer SEQUENCE wrapper and version number */
145 writeSequence( stream, sizeofShortInteger( CMS_VERSION ) + \
146 dynLength( iAndSDB ) + sizeofHashAlgoID + \
147 attributeSize + signatureSize + \
148 ( ( unsignedAttributeSize ) ? \
149 ( int ) sizeofObject( unsignedAttributeSize ) : 0 ) );
150 writeShortInteger( stream, CMS_VERSION, DEFAULT_TAG );
151
152 /* Write the issuerAndSerialNumber, digest algorithm identifier,
153 attributes (if there are any) and signature */
154 swrite( stream, dynData( iAndSDB ), dynLength( iAndSDB ) );
155 writeAlgoIDex( stream, hashAlgo, hashAlgoParam, 0 );
156 if( attributeSize > 0 )
157 swrite( stream, attributes, attributeSize );
158 status = swrite( stream, signature, signatureSize );
159 dynDestroy( &iAndSDB );
160 if( cryptStatusError( status ) || unsignedAttributeSize <= 0 )
161 return( status );
162
163 /* Write the unsigned attributes. Note that the only unsigned attribute
164 in use at this time is a (not-quite) countersignature containing a
165 timestamp, so the following code always assumes that the attribute is
166 a timestamp. First we write the [1] IMPLICT SET OF attribute
167 wrapper */
168 writeConstructed( stream, unsignedAttributeSize, 1 );
169 writeSequence( stream, sizeofOID( OID_TSP_TSTOKEN ) + \
170 sizeofObject( timeStampSize ) );
171 writeOID( stream, OID_TSP_TSTOKEN );
172 status = writeSet( stream, timeStampSize );
173 if( cryptStatusError( status ) )
174 return( status );
175
176 /* Then we copy the timestamp data directly into the stream */
177 return( exportAttributeToStream( stream, unsignedAttrObject,
178 CRYPT_IATTRIBUTE_ENC_TIMESTAMP ) );
179 }
180
181 /* Create a CMS countersignature */
182
183 CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
IN_BUFFER(dataSignatureSize)184 static int createCmsCountersignature( IN_BUFFER( dataSignatureSize ) \
185 const void *dataSignature,
186 IN_LENGTH_SHORT const int dataSignatureSize,
187 IN_ALGO const CRYPT_ALGO_TYPE hashAlgo,
188 IN_RANGE( 0, CRYPT_MAX_HASHSIZE ) \
189 const int hashAlgoParam,
190 IN_HANDLE const CRYPT_SESSION iTspSession )
191 {
192 CRYPT_CONTEXT iHashContext;
193 MESSAGE_CREATEOBJECT_INFO createInfo;
194 STREAM stream;
195 int length, status;
196
197 assert( isReadPtr( dataSignature, dataSignatureSize ) );
198
199 REQUIRES( dataSignatureSize > MIN_CRYPT_OBJECTSIZE && \
200 dataSignatureSize < MAX_INTLENGTH_SHORT );
201 REQUIRES( isHashAlgo( hashAlgo ) );
202 REQUIRES( hashAlgoParam >= 0 && hashAlgoParam <= CRYPT_MAX_HASHSIZE );
203 REQUIRES( isHandleRangeValid( iTspSession ) );
204
205 /* Hash the signature data to create the hash value to countersign.
206 The CMS spec requires that the signature is calculated on the
207 contents octets (in other words the V of the TLV) of the signature,
208 so we have to skip the signature algorithm and OCTET STRING wrapper */
209 setMessageCreateObjectInfo( &createInfo, hashAlgo );
210 status = krnlSendMessage( SYSTEM_OBJECT_HANDLE,
211 IMESSAGE_DEV_CREATEOBJECT, &createInfo,
212 OBJECT_TYPE_CONTEXT );
213 if( cryptStatusError( status ) )
214 return( status );
215 if( isHashMacExtAlgo( hashAlgo ) )
216 {
217 status = krnlSendMessage( createInfo.cryptHandle,
218 IMESSAGE_SETATTRIBUTE,
219 ( MESSAGE_CAST ) &hashAlgoParam,
220 CRYPT_CTXINFO_BLOCKSIZE );
221 if( cryptStatusError( status ) )
222 {
223 krnlSendNotifier( createInfo.cryptHandle,
224 IMESSAGE_DECREFCOUNT );
225 return( status );
226 }
227 }
228 iHashContext = createInfo.cryptHandle;
229 #if 1 /* Standard CMS countersignature */
230 sMemConnect( &stream, dataSignature, dataSignatureSize );
231 readUniversal( &stream );
232 status = readOctetStringHole( &stream, &length, 16, DEFAULT_TAG );
233 if( cryptStatusOK( status ) )
234 {
235 void *dataPtr;
236
237 status = sMemGetDataBlock( &stream, &dataPtr, length );
238 if( cryptStatusOK( status ) )
239 {
240 status = krnlSendMessage( iHashContext, IMESSAGE_CTX_HASH,
241 dataPtr, length );
242 }
243 }
244 sMemDisconnect( &stream );
245 #else /* Broken TSP not-quite-countersignature */
246 krnlSendMessage( iHashContext, IMESSAGE_CTX_HASH,
247 ( MESSAGE_CAST ) dataSignature, dataSignatureSize );
248 #endif /* 1 */
249 if( cryptStatusOK( status ) )
250 status = krnlSendMessage( iHashContext, IMESSAGE_CTX_HASH, "", 0 );
251 if( cryptStatusOK( status ) )
252 {
253 status = krnlSendMessage( iTspSession, IMESSAGE_SETATTRIBUTE,
254 &iHashContext,
255 CRYPT_SESSINFO_TSP_MSGIMPRINT );
256 }
257 krnlSendNotifier( iHashContext, IMESSAGE_DECREFCOUNT );
258 if( cryptStatusError( status ) )
259 return( status );
260
261 /* Send the result to the TSA for countersigning */
262 return( krnlSendMessage( iTspSession, IMESSAGE_SETATTRIBUTE,
263 MESSAGE_VALUE_TRUE, CRYPT_SESSINFO_ACTIVE ) );
264 }
265
266 /* Add sMimeCapabilities to a CMS attribute object */
267
addSmimeCapabilities(IN_HANDLE const CRYPT_CERTIFICATE iCmsAttributes)268 static void addSmimeCapabilities( IN_HANDLE const CRYPT_CERTIFICATE iCmsAttributes )
269 {
270 typedef struct {
271 CRYPT_ALGO_TYPE algorithm;
272 CRYPT_ALGO_TYPE secondaryAlgorithm;
273 CRYPT_ATTRIBUTE_TYPE smimeCapability;
274 } SMIMECAP_INFO;
275 static const SMIMECAP_INFO smimeCapInfo[] = {
276 { CRYPT_ALGO_3DES, CRYPT_ALGO_NONE,
277 CRYPT_CERTINFO_CMS_SMIMECAP_3DES },
278 { CRYPT_ALGO_AES, CRYPT_ALGO_NONE,
279 CRYPT_CERTINFO_CMS_SMIMECAP_AES },
280 #ifdef USE_SHAng
281 { CRYPT_ALGO_SHAng, CRYPT_ALGO_NONE,
282 CRYPT_CERTINFO_CMS_SMIMECAP_SHAng },
283 #endif /* USE_SHAng */
284 { CRYPT_ALGO_SHA2, CRYPT_ALGO_NONE,
285 CRYPT_CERTINFO_CMS_SMIMECAP_SHA2 },
286 { CRYPT_ALGO_SHA1, CRYPT_ALGO_NONE,
287 CRYPT_CERTINFO_CMS_SMIMECAP_SHA1 },
288 #ifdef USE_SHAng
289 { CRYPT_ALGO_HMAC_SHAng, CRYPT_ALGO_NONE,
290 CRYPT_CERTINFO_CMS_SMIMECAP_HMAC_SHAng },
291 #endif /* USE_SHAng */
292 { CRYPT_ALGO_HMAC_SHA2, CRYPT_ALGO_NONE,
293 CRYPT_CERTINFO_CMS_SMIMECAP_HMAC_SHA2 },
294 { CRYPT_ALGO_HMAC_SHA1, CRYPT_ALGO_NONE,
295 CRYPT_CERTINFO_CMS_SMIMECAP_HMAC_SHA1 },
296 { CRYPT_IALGO_GENERIC_SECRET, CRYPT_ALGO_NONE,
297 CRYPT_CERTINFO_CMS_SMIMECAP_AUTHENC256 },
298 { CRYPT_IALGO_GENERIC_SECRET, CRYPT_ALGO_NONE,
299 CRYPT_CERTINFO_CMS_SMIMECAP_AUTHENC128 },
300 #ifdef USE_SHAng
301 { CRYPT_ALGO_RSA, CRYPT_ALGO_SHAng,
302 CRYPT_CERTINFO_CMS_SMIMECAP_RSA_SHAng },
303 #endif /* USE_SHAng */
304 { CRYPT_ALGO_RSA, CRYPT_ALGO_SHA2,
305 CRYPT_CERTINFO_CMS_SMIMECAP_RSA_SHA2 },
306 { CRYPT_ALGO_RSA, CRYPT_ALGO_NONE, /* 'None' since always avail. */
307 CRYPT_CERTINFO_CMS_SMIMECAP_RSA_SHA1 },
308 { CRYPT_ALGO_DSA, CRYPT_ALGO_NONE, /* 'None' since always avail. */
309 CRYPT_CERTINFO_CMS_SMIMECAP_DSA_SHA1 },
310 #ifdef USE_ECDSA
311 #ifdef USE_SHAng
312 { CRYPT_ALGO_ECDSA, CRYPT_ALGO_SHAng,
313 CRYPT_CERTINFO_CMS_SMIMECAP_ECDSA_SHAng },
314 #endif /* USE_SHAng */
315 { CRYPT_ALGO_ECDSA, CRYPT_ALGO_SHA2,
316 CRYPT_CERTINFO_CMS_SMIMECAP_ECDSA_SHA2 },
317 { CRYPT_ALGO_ECDSA, CRYPT_ALGO_NONE, /* 'None' since always avail. */
318 CRYPT_CERTINFO_CMS_SMIMECAP_ECDSA_SHA1 },
319 #endif /* USE_ECDSA */
320 { CRYPT_ALGO_NONE, CRYPT_ALGO_NONE, CRYPT_ATTRIBUTE_NONE },
321 { CRYPT_ALGO_NONE, CRYPT_ALGO_NONE, CRYPT_ATTRIBUTE_NONE },
322 };
323 CRYPT_ALGO_TYPE prevAlgo = CRYPT_ALGO_NONE;
324 int value, i, status;
325
326 REQUIRES_V( isHandleRangeValid( iCmsAttributes ) );
327
328 /* If there are already sMIMECapabilities present don't try and add
329 anything further */
330 status = krnlSendMessage( iCmsAttributes, IMESSAGE_GETATTRIBUTE,
331 &value, CRYPT_CERTINFO_CMS_SMIMECAPABILITIES );
332 if( cryptStatusOK( status ) )
333 return;
334
335 /* Add an sMIMECapability for each supported algorithm or algorithm
336 combination. Since these are no-value attributes it's not worth
337 aborting the signature generation if the attempt to add them fails
338 so we don't bother checking the return value */
339 for( i = 0; smimeCapInfo[ i ].algorithm != CRYPT_ALGO_NONE && \
340 i < FAILSAFE_ARRAYSIZE( smimeCapInfo, SMIMECAP_INFO );
341 i++ )
342 {
343 const CRYPT_ALGO_TYPE algorithm = smimeCapInfo[ i ].algorithm;
344
345 /* Make sure that the primary algorithm is available */
346 if( prevAlgo != algorithm && !algoAvailable( algorithm ) )
347 {
348 prevAlgo = algorithm;
349 continue;
350 }
351 prevAlgo = algorithm;
352
353 /* If there's a secondary algorithm, make sure that it's available */
354 if( smimeCapInfo[ i ].secondaryAlgorithm != CRYPT_ALGO_NONE && \
355 !algoAvailable( smimeCapInfo[ i ].secondaryAlgorithm ) )
356 continue;
357
358 /* List this algorithm or algorithm combination as an available
359 capability */
360 ( void ) krnlSendMessage( iCmsAttributes, IMESSAGE_SETATTRIBUTE,
361 MESSAGE_VALUE_UNUSED,
362 smimeCapInfo[ i ].smimeCapability );
363 }
364 ENSURES_V( i < FAILSAFE_ARRAYSIZE( smimeCapInfo, SMIMECAP_INFO ) );
365
366 /* Add any futher non-algorithm-related sMIMECapabilities */
367 ( void ) krnlSendMessage( iCmsAttributes, IMESSAGE_SETATTRIBUTE,
368 MESSAGE_VALUE_UNUSED,
369 CRYPT_CERTINFO_CMS_SMIMECAP_PREFERBINARYINSIDE );
370 }
371
372 /****************************************************************************
373 * *
374 * Create CMS Attributes *
375 * *
376 ****************************************************************************/
377
378 /* Finalise processing of and hash the CMS attributes */
379
380 CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
hashCmsAttributes(INOUT CMS_ATTRIBUTE_INFO * cmsAttributeInfo,IN_HANDLE const CRYPT_CONTEXT iAttributeHash,const BOOLEAN lengthCheckOnly)381 static int hashCmsAttributes( INOUT CMS_ATTRIBUTE_INFO *cmsAttributeInfo,
382 IN_HANDLE const CRYPT_CONTEXT iAttributeHash,
383 const BOOLEAN lengthCheckOnly )
384 {
385 MESSAGE_DATA msgData;
386 BYTE temp, hash[ CRYPT_MAX_HASHSIZE + 8 ];
387 int status;
388
389 assert( isWritePtr( cmsAttributeInfo, sizeof( CMS_ATTRIBUTE_INFO ) ) );
390 assert( isWritePtr( cmsAttributeInfo->encodedAttributes, \
391 cmsAttributeInfo->maxEncodedAttributeSize ) );
392
393 REQUIRES( isHandleRangeValid( cmsAttributeInfo->iCmsAttributes ) );
394 REQUIRES( isHandleRangeValid( cmsAttributeInfo->iMessageHash ) );
395 REQUIRES( isHandleRangeValid( iAttributeHash ) );
396
397 /* Extract the message hash information and add it as a messageDigest
398 attribute, replacing any existing value if necessary (we don't bother
399 checking the return value because the attribute may or may not be
400 present, and a failure to delete it will be detected immediately
401 afterwards when we try and set it). If we're doing a call just to
402 get the length of the exported data we use a dummy hash value since
403 the hashing may not have completed yet */
404 ( void ) krnlSendMessage( cmsAttributeInfo->iCmsAttributes,
405 IMESSAGE_DELETEATTRIBUTE, NULL,
406 CRYPT_CERTINFO_CMS_MESSAGEDIGEST );
407 setMessageData( &msgData, hash, CRYPT_MAX_HASHSIZE );
408 if( lengthCheckOnly )
409 {
410 memset( hash, 0, CRYPT_MAX_HASHSIZE ); /* Keep mem.checkers happy */
411 status = krnlSendMessage( cmsAttributeInfo->iMessageHash,
412 IMESSAGE_GETATTRIBUTE, &msgData.length,
413 CRYPT_CTXINFO_BLOCKSIZE );
414 }
415 else
416 {
417 status = krnlSendMessage( cmsAttributeInfo->iMessageHash,
418 IMESSAGE_GETATTRIBUTE_S, &msgData,
419 CRYPT_CTXINFO_HASHVALUE );
420 }
421 if( cryptStatusOK( status ) )
422 {
423 status = krnlSendMessage( cmsAttributeInfo->iCmsAttributes,
424 IMESSAGE_SETATTRIBUTE_S, &msgData,
425 CRYPT_CERTINFO_CMS_MESSAGEDIGEST );
426 }
427 if( cryptStatusError( status ) )
428 return( status );
429
430 /* If we're creating the attributes for a real signature rather than
431 just as part of a size check and there's a reliable time source
432 present, use the time from that instead of the built-in system time.
433 Although this seems like a trivial thing it's likely that the
434 presence of a high-assurance time source means that the accuracy of
435 timekeeping is considered critical so we fail the signature
436 generation if we can't set the time from the reliable source */
437 if( !lengthCheckOnly )
438 {
439 const time_t currentTime = \
440 getReliableTime( cmsAttributeInfo->iTimeSource );
441
442 if( currentTime > MIN_TIME_VALUE )
443 {
444 setMessageData( &msgData, ( MESSAGE_CAST ) ¤tTime,
445 sizeof( time_t ) );
446 ( void ) krnlSendMessage( cmsAttributeInfo->iCmsAttributes,
447 IMESSAGE_DELETEATTRIBUTE, NULL,
448 CRYPT_CERTINFO_CMS_SIGNINGTIME );
449 status = krnlSendMessage( cmsAttributeInfo->iCmsAttributes,
450 IMESSAGE_SETATTRIBUTE_S, &msgData,
451 CRYPT_CERTINFO_CMS_SIGNINGTIME );
452 if( cryptStatusError( status ) )
453 return( status );
454 }
455 }
456
457 /* Export the attributes into an encoded signedAttributes data block */
458 if( lengthCheckOnly )
459 { setMessageData( &msgData, NULL, 0 ); }
460 else
461 {
462 setMessageData( &msgData, cmsAttributeInfo->encodedAttributes,
463 cmsAttributeInfo->maxEncodedAttributeSize );
464 }
465 status = krnlSendMessage( cmsAttributeInfo->iCmsAttributes,
466 IMESSAGE_CRT_EXPORT, &msgData,
467 CRYPT_ICERTFORMAT_DATA );
468 if( cryptStatusError( status ) )
469 return( status );
470 cmsAttributeInfo->encodedAttributeSize = msgData.length;
471
472 /* If it's a length check, just generate a dummy hash value and exit */
473 if( lengthCheckOnly )
474 return( krnlSendMessage( iAttributeHash, IMESSAGE_CTX_HASH, "", 0 ) );
475
476 /* Replace the IMPLICIT [ 0 ] tag at the start with a SET OF tag to
477 allow the attributes to be hashed, hash them into the attribute hash
478 context, and replace the original tag */
479 temp = cmsAttributeInfo->encodedAttributes[ 0 ];
480 cmsAttributeInfo->encodedAttributes[ 0 ] = BER_SET;
481 status = krnlSendMessage( iAttributeHash, IMESSAGE_CTX_HASH,
482 cmsAttributeInfo->encodedAttributes,
483 cmsAttributeInfo->encodedAttributeSize );
484 if( cryptStatusOK( status ) )
485 status = krnlSendMessage( iAttributeHash, IMESSAGE_CTX_HASH, "", 0 );
486 cmsAttributeInfo->encodedAttributes[ 0 ] = temp;
487 return( status );
488 }
489
490 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
491 static int createCmsAttributes( INOUT CMS_ATTRIBUTE_INFO *cmsAttributeInfo,
492 OUT_HANDLE_OPT CRYPT_CONTEXT *iCmsHashContext,
493 IN_ALGO const CRYPT_ALGO_TYPE hashAlgo,
494 IN_RANGE( 0, CRYPT_MAX_HASHSIZE ) \
495 const int hashAlgoParam,
496 const BOOLEAN lengthCheckOnly )
497 {
498 MESSAGE_CREATEOBJECT_INFO createInfo;
499 BOOLEAN createdHashContext = FALSE;
500 int status;
501
502 assert( isWritePtr( cmsAttributeInfo, sizeof( CMS_ATTRIBUTE_INFO ) ) );
503 assert( isWritePtr( cmsAttributeInfo->attributeBuffer, \
504 cmsAttributeInfo->maxEncodedAttributeSize ) );
505 assert( isWritePtr( iCmsHashContext, sizeof( CRYPT_CONTEXT ) ) );
506
507 REQUIRES( cmsAttributeInfo->formatType == CRYPT_FORMAT_CMS || \
508 cmsAttributeInfo->formatType == CRYPT_FORMAT_SMIME );
509 REQUIRES( ( cmsAttributeInfo->iCmsAttributes == CRYPT_UNUSED && \
510 cmsAttributeInfo->useDefaultAttributes == FALSE ) || \
511 ( cmsAttributeInfo->iCmsAttributes == CRYPT_UNUSED && \
512 cmsAttributeInfo->useDefaultAttributes == TRUE ) || \
513 ( isHandleRangeValid( cmsAttributeInfo->iCmsAttributes ) && \
514 cmsAttributeInfo->useDefaultAttributes == FALSE ) );
515 REQUIRES( isHandleRangeValid( cmsAttributeInfo->iMessageHash ) );
516 REQUIRES( isHandleRangeValid( cmsAttributeInfo->iTimeSource ) );
517 REQUIRES( ( cmsAttributeInfo->iTspSession == CRYPT_UNUSED ) || \
518 isHandleRangeValid( cmsAttributeInfo->iTspSession ) );
519 REQUIRES( cmsAttributeInfo->encodedAttributes == NULL && \
520 cmsAttributeInfo->encodedAttributeSize == 0 );
521 REQUIRES( isHashAlgo( hashAlgo ) );
522 REQUIRES( hashAlgoParam >= 0 && hashAlgoParam <= CRYPT_MAX_HASHSIZE );
523
524 /* Clear return value */
525 *iCmsHashContext = CRYPT_ERROR;
526
527 /* Set up the attribute buffer */
528 cmsAttributeInfo->encodedAttributes = cmsAttributeInfo->attributeBuffer;
529
530 /* If the user hasn't supplied the attributes, generate them ourselves */
531 if( cmsAttributeInfo->useDefaultAttributes )
532 {
533 setMessageCreateObjectInfo( &createInfo,
534 CRYPT_CERTTYPE_CMS_ATTRIBUTES );
535 status = krnlSendMessage( SYSTEM_OBJECT_HANDLE,
536 IMESSAGE_DEV_CREATEOBJECT,
537 &createInfo, OBJECT_TYPE_CERTIFICATE );
538 if( cryptStatusError( status ) )
539 return( status );
540 cmsAttributeInfo->iCmsAttributes = createInfo.cryptHandle;
541 }
542 ENSURES( isHandleRangeValid( cmsAttributeInfo->iCmsAttributes ) );
543
544 /* If it's an S/MIME (vs.pure CMS) signature add the sMIMECapabilities
545 to further bloat things up. Since these are no-value attributes
546 it's not worth aborting the signature generation if the attempt to
547 add them fails so we don't bother checking a return value */
548 if( cmsAttributeInfo->formatType == CRYPT_FORMAT_SMIME )
549 ( void ) addSmimeCapabilities( cmsAttributeInfo->iCmsAttributes );
550
551 /* Generate the attributes and hash them into the CMS hash context */
552 setMessageCreateObjectInfo( &createInfo, hashAlgo );
553 status = krnlSendMessage( SYSTEM_OBJECT_HANDLE,
554 IMESSAGE_DEV_CREATEOBJECT, &createInfo,
555 OBJECT_TYPE_CONTEXT );
556 if( cryptStatusOK( status ) && isHashMacExtAlgo( hashAlgo ) )
557 {
558 status = krnlSendMessage( createInfo.cryptHandle,
559 IMESSAGE_SETATTRIBUTE,
560 ( MESSAGE_CAST ) &hashAlgoParam,
561 CRYPT_CTXINFO_BLOCKSIZE );
562 }
563 if( cryptStatusOK( status ) )
564 {
565 createdHashContext = TRUE;
566 status = hashCmsAttributes( cmsAttributeInfo, createInfo.cryptHandle,
567 lengthCheckOnly );
568 }
569 if( cmsAttributeInfo->useDefaultAttributes )
570 {
571 krnlSendNotifier( cmsAttributeInfo->iCmsAttributes,
572 IMESSAGE_DECREFCOUNT );
573 cmsAttributeInfo->iCmsAttributes = CRYPT_UNUSED;
574 }
575 if( cryptStatusError( status ) )
576 {
577 if( createdHashContext )
578 krnlSendNotifier( createInfo.cryptHandle, IMESSAGE_DECREFCOUNT );
579 return( status );
580 }
581
582 /* Return the hash of the attributes to the caller */
583 *iCmsHashContext = createInfo.cryptHandle;
584
585 return( CRYPT_OK );
586 }
587
588 /****************************************************************************
589 * *
590 * Create/Check a CMS Signature *
591 * *
592 ****************************************************************************/
593
594 /* Create a CMS signature. The use of authenticated attributes is a three-
595 way choice:
596
597 useDefaultAuthAttr = FALSE, No attributes.
598 iAuthAttr = CRYPT_UNUSED
599
600 useDefaultAuthAttr = TRUE, We supply default attributes.
601 iAuthAttr = CRYPT_UNUSED
602
603 useDefaultAuthAttr = FALSE, Caller has supplied attributes
604 iAuthAttr = validhandle */
605
606 CHECK_RETVAL STDC_NONNULL_ARG( ( 3 ) ) \
createSignatureCMS(OUT_BUFFER_OPT (sigMaxLength,* signatureLength)void * signature,IN_DATALENGTH_Z const int sigMaxLength,OUT_DATALENGTH_Z int * signatureLength,IN_HANDLE const CRYPT_CONTEXT signContext,IN_HANDLE const CRYPT_CONTEXT iHashContext,const BOOLEAN useDefaultAuthAttr,IN_HANDLE_OPT const CRYPT_CERTIFICATE iAuthAttr,IN_HANDLE_OPT const CRYPT_SESSION iTspSession,IN_ENUM (CRYPT_FORMAT)const CRYPT_FORMAT_TYPE formatType)607 int createSignatureCMS( OUT_BUFFER_OPT( sigMaxLength, *signatureLength ) \
608 void *signature,
609 IN_DATALENGTH_Z const int sigMaxLength,
610 OUT_DATALENGTH_Z int *signatureLength,
611 IN_HANDLE const CRYPT_CONTEXT signContext,
612 IN_HANDLE const CRYPT_CONTEXT iHashContext,
613 const BOOLEAN useDefaultAuthAttr,
614 IN_HANDLE_OPT const CRYPT_CERTIFICATE iAuthAttr,
615 IN_HANDLE_OPT const CRYPT_SESSION iTspSession,
616 IN_ENUM( CRYPT_FORMAT ) \
617 const CRYPT_FORMAT_TYPE formatType )
618 {
619 CRYPT_CONTEXT iCmsHashContext = iHashContext;
620 CRYPT_CERTIFICATE iSigningCert;
621 STREAM stream;
622 CMS_ATTRIBUTE_INFO cmsAttributeInfo;
623 BYTE buffer[ CRYPT_MAX_PKCSIZE + 128 + 8 ];
624 BYTE *bufPtr = ( signature == NULL ) ? NULL : buffer;
625 const int bufSize = ( signature == NULL ) ? 0 : CRYPT_MAX_PKCSIZE + 128;
626 int hashAlgo, hashAlgoParam = 0;
627 int dataSignatureSize, length DUMMY_INIT, status;
628
629 assert( ( signature == NULL && sigMaxLength == 0 ) || \
630 isReadPtr( signature, sigMaxLength ) );
631 assert( isWritePtr( signatureLength, sizeof( int ) ) );
632
633 REQUIRES( ( signature == NULL && sigMaxLength == 0 ) || \
634 ( signature != NULL && \
635 sigMaxLength > MIN_CRYPT_OBJECTSIZE && \
636 sigMaxLength < MAX_BUFFER_SIZE ) );
637 REQUIRES( isHandleRangeValid( signContext ) );
638 REQUIRES( isHandleRangeValid( iHashContext ) );
639 REQUIRES( ( iAuthAttr == CRYPT_UNUSED && \
640 useDefaultAuthAttr == FALSE ) || \
641 ( iAuthAttr == CRYPT_UNUSED && \
642 useDefaultAuthAttr == TRUE ) || \
643 ( isHandleRangeValid( iAuthAttr ) && \
644 useDefaultAuthAttr == FALSE ) );
645 REQUIRES( ( iTspSession == CRYPT_UNUSED ) || \
646 isHandleRangeValid( iTspSession ) );
647 REQUIRES( formatType == CRYPT_FORMAT_CMS || \
648 formatType == CRYPT_FORMAT_SMIME );
649
650 /* Clear return value */
651 *signatureLength = 0;
652
653 initCmsAttributeInfo( &cmsAttributeInfo, formatType,
654 useDefaultAuthAttr, iAuthAttr, iHashContext,
655 signContext, iTspSession );
656
657 /* Get the message hash algo and signing certificate */
658 status = krnlSendMessage( iHashContext, IMESSAGE_GETATTRIBUTE,
659 &hashAlgo, CRYPT_CTXINFO_ALGO );
660 if( cryptStatusOK( status ) && isHashMacExtAlgo( hashAlgo ) )
661 status = krnlSendMessage( iHashContext, IMESSAGE_GETATTRIBUTE,
662 &hashAlgoParam, CRYPT_CTXINFO_BLOCKSIZE );
663 if( cryptStatusError( status ) )
664 return( cryptArgError( status ) ? CRYPT_ARGERROR_NUM2 : status );
665 status = krnlSendMessage( signContext, IMESSAGE_GETDEPENDENT,
666 &iSigningCert, OBJECT_TYPE_CERTIFICATE );
667 if( cryptStatusError( status ) )
668 return( cryptArgError( status ) ? CRYPT_ARGERROR_NUM1 : status );
669
670 /* If we're using signed attributes, set them up to be added to the
671 signature info */
672 if( useDefaultAuthAttr || iAuthAttr != CRYPT_UNUSED )
673 {
674 status = createCmsAttributes( &cmsAttributeInfo, &iCmsHashContext,
675 hashAlgo, hashAlgoParam,
676 ( signature == NULL ) ? TRUE : FALSE );
677 if( cryptStatusError( status ) )
678 return( status );
679 }
680
681 /* Create the signature */
682 status = createSignature( bufPtr, bufSize, &dataSignatureSize,
683 signContext, iCmsHashContext, CRYPT_UNUSED,
684 SIGNATURE_CMS );
685 if( iCmsHashContext != iHashContext )
686 krnlSendNotifier( iCmsHashContext, IMESSAGE_DECREFCOUNT );
687 if( cryptStatusError( status ) )
688 return( status );
689
690 /* If we're countersigning the signature (typically done via a
691 timestamp), create the countersignature */
692 if( iTspSession != CRYPT_UNUSED && signature != NULL )
693 {
694 status = createCmsCountersignature( buffer, dataSignatureSize,
695 hashAlgo, hashAlgoParam,
696 iTspSession );
697 if( cryptStatusError( status ) )
698 return( status );
699 }
700
701 /* Write the signerInfo record */
702 sMemOpenOpt( &stream, signature, ( signature == NULL ) ? 0 : sigMaxLength );
703 status = writeCmsSignerInfo( &stream, iSigningCert, hashAlgo, hashAlgoParam,
704 cmsAttributeInfo.encodedAttributes,
705 cmsAttributeInfo.encodedAttributeSize,
706 buffer, dataSignatureSize,
707 ( signature == NULL ) ? CRYPT_UNUSED : iTspSession );
708 if( cryptStatusOK( status ) )
709 length = stell( &stream );
710 sMemDisconnect( &stream );
711 if( cryptStatusError( status ) )
712 return( status );
713 if( iTspSession != CRYPT_UNUSED && signature == NULL )
714 {
715 /* If we're countersigning the signature with a timestamp and doing
716 a length check only then we run into problems because we can't
717 report how big the final size will be without contacting the TSA
718 for a timestamp. In theory we could report some hopefully-OK
719 large-enough size, but at the moment we take advantage of the
720 fact that we're only going to be called from the enveloping code
721 because a timestamp only makes sense as a countersignature on CMS
722 data. It's somewhat ugly because it asumes internal knowledge of
723 the envelope abstraction but there isn't really any clean way to
724 handle this because we can't tell in advance how much data the
725 TSA will send us.
726
727 In theory the code doesn't even need to return an estimate
728 because, without knowing the exact length of the
729 countersignature, the enveloping code has to fall back to using
730 indefinite-length encoding (see envelope/cms_envpre.c), so the
731 length value that we return here is ignored */
732 length += MIN_BUFFER_SIZE;
733 }
734 *signatureLength = length;
735
736 return( CRYPT_OK );
737 }
738
739 /* Check a CMS signature. The reason why there are apparently two
740 signature-checking objects present in the function arguments is that
741 sigCheckContext is the raw public-key context while iSigCheckKey is the
742 overall signature-checking object, which may include attached
743 certificates and other information */
744
745 CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
checkSignatureCMS(IN_BUFFER (signatureLength)const void * signature,IN_DATALENGTH const int signatureLength,IN_HANDLE const CRYPT_CONTEXT sigCheckContext,IN_HANDLE const CRYPT_CONTEXT iHashContext,OUT_OPT_HANDLE_OPT CRYPT_CERTIFICATE * iExtraData,IN_HANDLE const CRYPT_HANDLE iSigCheckKey)746 int checkSignatureCMS( IN_BUFFER( signatureLength ) const void *signature,
747 IN_DATALENGTH const int signatureLength,
748 IN_HANDLE const CRYPT_CONTEXT sigCheckContext,
749 IN_HANDLE const CRYPT_CONTEXT iHashContext,
750 OUT_OPT_HANDLE_OPT CRYPT_CERTIFICATE *iExtraData,
751 IN_HANDLE const CRYPT_HANDLE iSigCheckKey )
752 {
753 CRYPT_CERTIFICATE iLocalExtraData;
754 CRYPT_CONTEXT iCmsHashContext = iHashContext;
755 MESSAGE_CREATEOBJECT_INFO createInfo;
756 QUERY_INFO queryInfo;
757 MESSAGE_DATA msgData;
758 STREAM stream;
759 static const BYTE setTag[] = { BER_SET };
760 BYTE hashValue[ CRYPT_MAX_HASHSIZE + 8 ];
761 int hashAlgo, hashAlgoParam = 0, status; /* int vs.enum */
762
763 assert( isReadPtr( signature, signatureLength ) );
764 assert( ( iExtraData == NULL ) || \
765 isWritePtr( iExtraData, sizeof( CRYPT_CERTIFICATE ) ) );
766
767 REQUIRES( signatureLength > 40 && signatureLength < MAX_BUFFER_SIZE );
768 REQUIRES( isHandleRangeValid( sigCheckContext ) );
769 REQUIRES( isHandleRangeValid( iHashContext ) );
770 REQUIRES( isHandleRangeValid( iSigCheckKey ) );
771
772 if( iExtraData != NULL )
773 *iExtraData = CRYPT_ERROR;
774
775 /* Get the message hash algo */
776 status = krnlSendMessage( iHashContext, IMESSAGE_GETATTRIBUTE,
777 &hashAlgo, CRYPT_CTXINFO_ALGO );
778 if( cryptStatusOK( status ) && isHashMacExtAlgo( hashAlgo ) )
779 status = krnlSendMessage( iHashContext, IMESSAGE_GETATTRIBUTE,
780 &hashAlgoParam, CRYPT_CTXINFO_BLOCKSIZE );
781 if( cryptStatusError( status ) )
782 return( cryptArgError( status ) ? CRYPT_ARGERROR_NUM2 : status );
783
784 /* Unpack the SignerInfo record and make sure that the supplied key is
785 the correct one for the sig.check and the supplied hash context
786 matches the algorithm used in the signature */
787 sMemConnect( &stream, signature, signatureLength );
788 status = queryAsn1Object( &stream, &queryInfo );
789 if( cryptStatusOK( status ) && \
790 ( queryInfo.formatType != CRYPT_FORMAT_CMS && \
791 queryInfo.formatType != CRYPT_FORMAT_SMIME ) )
792 status = CRYPT_ERROR_BADDATA;
793 sMemDisconnect( &stream );
794 if( cryptStatusError( status ) )
795 return( status );
796 REQUIRES( rangeCheck( queryInfo.iAndSStart, queryInfo.iAndSLength,
797 queryInfo.size ) );
798 setMessageData( &msgData, \
799 ( BYTE * ) signature + queryInfo.iAndSStart, \
800 queryInfo.iAndSLength );
801 status = krnlSendMessage( iSigCheckKey, IMESSAGE_COMPARE, &msgData,
802 MESSAGE_COMPARE_ISSUERANDSERIALNUMBER );
803 if( cryptStatusError( status ) )
804 {
805 /* A failed comparison is reported as a generic CRYPT_ERROR,
806 convert it into a wrong-key error if necessary */
807 return( ( status == CRYPT_ERROR ) ? \
808 CRYPT_ERROR_WRONGKEY : status );
809 }
810 if( queryInfo.hashAlgo != hashAlgo || \
811 queryInfo.hashAlgoParam != hashAlgoParam )
812 return( CRYPT_ARGERROR_NUM2 );
813
814 /* If there are no signed attributes present, just check the signature
815 and exit */
816 if( queryInfo.attributeStart <= 0 )
817 {
818 return( checkSignature( signature, signatureLength, sigCheckContext,
819 iCmsHashContext, CRYPT_UNUSED,
820 SIGNATURE_CMS ) );
821 }
822
823 /* There are signedAttributes present, hash the data, substituting a SET
824 OF tag for the IMPLICIT [ 0 ] tag at the start */
825 REQUIRES( rangeCheck( queryInfo.attributeStart,
826 queryInfo.attributeLength, queryInfo.size ) );
827 setMessageCreateObjectInfo( &createInfo, queryInfo.hashAlgo );
828 status = krnlSendMessage( SYSTEM_OBJECT_HANDLE,
829 IMESSAGE_DEV_CREATEOBJECT, &createInfo,
830 OBJECT_TYPE_CONTEXT );
831 if( cryptStatusError( status ) )
832 return( status );
833 if( isHashMacExtAlgo( queryInfo.hashAlgo ) )
834 {
835 status = krnlSendMessage( createInfo.cryptHandle,
836 IMESSAGE_SETATTRIBUTE,
837 &queryInfo.hashAlgoParam,
838 CRYPT_CTXINFO_BLOCKSIZE );
839 if( cryptStatusError( status ) )
840 {
841 krnlSendNotifier( createInfo.cryptHandle, IMESSAGE_DECREFCOUNT );
842 return( status );
843 }
844 }
845 iCmsHashContext = createInfo.cryptHandle;
846 status = krnlSendMessage( iCmsHashContext, IMESSAGE_CTX_HASH,
847 ( BYTE * ) setTag, sizeof( BYTE ) );
848 if( cryptStatusOK( status ) )
849 {
850 status = krnlSendMessage( iCmsHashContext, IMESSAGE_CTX_HASH,
851 ( BYTE * ) signature + queryInfo.attributeStart + 1,
852 queryInfo.attributeLength - 1 );
853 }
854 if( cryptStatusOK( status ) )
855 status = krnlSendMessage( iCmsHashContext, IMESSAGE_CTX_HASH, "", 0 );
856 if( cryptStatusError( status ) )
857 {
858 krnlSendNotifier( iCmsHashContext, IMESSAGE_DECREFCOUNT );
859 return( status );
860 }
861
862 /* Check the signature */
863 status = checkSignature( signature, signatureLength, sigCheckContext,
864 iCmsHashContext, CRYPT_UNUSED, SIGNATURE_CMS );
865 krnlSendNotifier( iCmsHashContext, IMESSAGE_DECREFCOUNT );
866 if( cryptStatusError( status ) )
867 return( status );
868
869 /* Import the attributes and make sure that the data hash value given in
870 the signed attributes matches the user-supplied hash */
871 REQUIRES( rangeCheck( queryInfo.attributeStart,
872 queryInfo.attributeLength, queryInfo.size ) );
873 setMessageCreateObjectIndirectInfo( &createInfo,
874 ( BYTE * ) signature + queryInfo.attributeStart,
875 queryInfo.attributeLength,
876 CRYPT_CERTTYPE_CMS_ATTRIBUTES );
877 status = krnlSendMessage( SYSTEM_OBJECT_HANDLE,
878 IMESSAGE_DEV_CREATEOBJECT_INDIRECT,
879 &createInfo, OBJECT_TYPE_CERTIFICATE );
880 if( cryptStatusError( status ) )
881 return( status );
882 iLocalExtraData = createInfo.cryptHandle;
883 setMessageData( &msgData, hashValue, CRYPT_MAX_HASHSIZE );
884 status = krnlSendMessage( iLocalExtraData, IMESSAGE_GETATTRIBUTE_S,
885 &msgData, CRYPT_CERTINFO_CMS_MESSAGEDIGEST );
886 if( cryptStatusOK( status ) )
887 {
888 status = krnlSendMessage( iHashContext, IMESSAGE_COMPARE, &msgData,
889 MESSAGE_COMPARE_HASH );
890 if( cryptStatusError( status ) )
891 status = CRYPT_ERROR_SIGNATURE;
892 }
893 if( cryptStatusError( status ) )
894 {
895 krnlSendNotifier( iLocalExtraData, IMESSAGE_DECREFCOUNT );
896 return( status );
897 }
898
899 /* If the user wants to look at the authenticated attributes, make them
900 externally visible, otherwise delete them */
901 if( iExtraData != NULL )
902 *iExtraData = iLocalExtraData;
903 else
904 krnlSendNotifier( iLocalExtraData, IMESSAGE_DECREFCOUNT );
905
906 return( CRYPT_OK );
907 }
908 #endif /* USE_INT_CMS */
909