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 ) &currentTime,
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