1 /****************************************************************************
2 *																			*
3 *						Internal Key Exchange 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 "mech.h"
12   #include "pgp.h"
13 #else
14   #include "crypt.h"
15   #include "enc_dec/asn1.h"
16   #include "mechs/mech.h"
17   #include "misc/pgp.h"
18 #endif /* Compiler-specific includes */
19 
20 #ifdef USE_INT_CMS
21 
22 /****************************************************************************
23 *																			*
24 *							Low-level Key Export Functions					*
25 *																			*
26 ****************************************************************************/
27 
28 /* Export a conventionally encrypted session key */
29 
30 CHECK_RETVAL STDC_NONNULL_ARG( ( 3 ) ) \
exportConventionalKey(OUT_BUFFER_OPT (encryptedKeyMaxLength,* encryptedKeyLength)void * encryptedKey,IN_DATALENGTH_Z const int encryptedKeyMaxLength,OUT_DATALENGTH_Z int * encryptedKeyLength,IN_HANDLE_OPT const CRYPT_CONTEXT iSessionKeyContext,IN_HANDLE const CRYPT_CONTEXT iExportContext,IN_ENUM (KEYEX)const KEYEX_TYPE keyexType)31 int exportConventionalKey( OUT_BUFFER_OPT( encryptedKeyMaxLength, *encryptedKeyLength ) \
32 							void *encryptedKey,
33 						   IN_DATALENGTH_Z const int encryptedKeyMaxLength,
34 						   OUT_DATALENGTH_Z int *encryptedKeyLength,
35 						   IN_HANDLE_OPT const CRYPT_CONTEXT iSessionKeyContext,
36 						   IN_HANDLE const CRYPT_CONTEXT iExportContext,
37 						   IN_ENUM( KEYEX ) const KEYEX_TYPE keyexType )
38 	{
39 	MECHANISM_WRAP_INFO mechanismInfo;
40 	const WRITEKEK_FUNCTION writeKeyexFunction = getWriteKekFunction( keyexType );
41 	BYTE buffer[ CRYPT_MAX_KEYSIZE + 16 + 8 ];
42 	int keySize, ivSize, status;
43 
44 	assert( ( encryptedKey == NULL && encryptedKeyMaxLength == 0 ) || \
45 			isWritePtr( encryptedKey, encryptedKeyMaxLength ) );
46 	assert( isWritePtr( encryptedKeyLength, sizeof( int ) ) );
47 
48 	REQUIRES( ( encryptedKey == NULL && encryptedKeyMaxLength == 0 ) || \
49 			  ( encryptedKey != NULL && \
50 				encryptedKeyMaxLength > MIN_CRYPT_OBJECTSIZE && \
51 				encryptedKeyMaxLength < MAX_BUFFER_SIZE ) );
52 	REQUIRES( ( keyexType == KEYEX_PGP && \
53 				iSessionKeyContext == CRYPT_UNUSED ) || \
54 			  ( keyexType != KEYEX_PGP && \
55 				isHandleRangeValid( iSessionKeyContext ) ) );
56 	REQUIRES( isHandleRangeValid( iExportContext ) );
57 	REQUIRES( keyexType > KEYEX_NONE && keyexType < KEYEX_LAST );
58 
59 	/* Clear return value */
60 	*encryptedKeyLength = 0;
61 
62 	/* Make sure that the requested key exchange format is available */
63 	if( writeKeyexFunction == NULL )
64 		return( CRYPT_ERROR_NOTAVAIL );
65 
66 #ifdef USE_PGP
67 	/* PGP doesn't actually wrap up a key but derives the session key
68 	   directly from the password.  Because of this there isn't any key
69 	   wrapping to be done so we just write the key derivation parameters
70 	   and exit */
71 	if( keyexType == KEYEX_PGP )
72 		{
73 		STREAM stream;
74 
75 		sMemOpenOpt( &stream, encryptedKey, encryptedKeyMaxLength );
76 		status = writeKeyexFunction( &stream, iExportContext, NULL, 0 );
77 		if( cryptStatusOK( status ) )
78 			*encryptedKeyLength = stell( &stream );
79 		sMemDisconnect( &stream );
80 
81 		return( status );
82 		}
83 #endif /* USE_PGP */
84 
85 	/* Get the export parameters */
86 	status = krnlSendMessage( iSessionKeyContext, IMESSAGE_GETATTRIBUTE,
87 							  &keySize, CRYPT_CTXINFO_KEYSIZE );
88 	if( cryptStatusError( status ) )
89 		return( cryptArgError( status ) ? CRYPT_ARGERROR_NUM1 : status );
90 	if( cryptStatusError( krnlSendMessage( iExportContext,
91 										   IMESSAGE_GETATTRIBUTE, &ivSize,
92 										   CRYPT_CTXINFO_IVSIZE ) ) )
93 		ivSize = 0;
94 
95 	/* Load an IV into the exporting context.  This is somewhat nasty in that
96 	   a side-effect of exporting a key is to load an IV, which isn't really
97 	   part of the function's job description.  The alternative would be to
98 	   require the user to explicitly load an IV before exporting the key,
99 	   but this is equally nasty because they'll never remember.  The lesser
100 	   of the two evils is to load the IV here and assume that anyone
101 	   loading the IV themselves will read the docs, which warn about the
102 	   side-effects of exporting a key.
103 
104 	   Note that we always load a new IV when we export a key because the
105 	   caller may be using the context to exchange multiple keys.  Since each
106 	   exported key requires its own IV we perform an unconditional reload.
107 	   In addition because we don't want another thread coming along and
108 	   changing the IV while we're in the process of encrypting with it, we
109 	   lock the exporting key object until the encryption has completed and
110 	   the IV is written to the output */
111 	status = krnlSendMessage( iExportContext, IMESSAGE_SETATTRIBUTE,
112 							  MESSAGE_VALUE_TRUE, CRYPT_IATTRIBUTE_LOCKED );
113 	if( cryptStatusError( status ) )
114 		return( status );
115 	if( ivSize > 0 )
116 		{
117 		status = krnlSendNotifier( iExportContext, IMESSAGE_CTX_GENIV );
118 		if( cryptStatusError( status ) )
119 			return( status );
120 		}
121 
122 	/* Encrypt the session key and write the result to the output stream */
123 	if( encryptedKey == NULL )
124 		{
125 		setMechanismWrapInfo( &mechanismInfo, NULL, 0, NULL, 0,
126 							  iSessionKeyContext, iExportContext );
127 		}
128 	else
129 		{
130 		setMechanismWrapInfo( &mechanismInfo, buffer, CRYPT_MAX_KEYSIZE + 16,
131 							  NULL, 0, iSessionKeyContext, iExportContext );
132 		}
133 	status = krnlSendMessage( SYSTEM_OBJECT_HANDLE, IMESSAGE_DEV_EXPORT,
134 							  &mechanismInfo, MECHANISM_ENC_CMS );
135 	if( cryptStatusOK( status ) )
136 		{
137 		STREAM stream;
138 
139 		/* If we're perfoming a dummy export for a length check, set up a
140 		   dummy value to write */
141 		if( encryptedKey == NULL )
142 			memset( buffer, 0x01, mechanismInfo.wrappedDataLength );
143 
144 		sMemOpenOpt( &stream, encryptedKey, encryptedKeyMaxLength );
145 		status = writeKeyexFunction( &stream, iExportContext,
146 									 ( encryptedKey != NULL ) ? \
147 										mechanismInfo.wrappedData : buffer,
148 									 mechanismInfo.wrappedDataLength );
149 		if( cryptStatusOK( status ) )
150 			*encryptedKeyLength = stell( &stream );
151 		sMemDisconnect( &stream );
152 		}
153 	( void ) krnlSendMessage( iExportContext, IMESSAGE_SETATTRIBUTE,
154 							  MESSAGE_VALUE_FALSE, CRYPT_IATTRIBUTE_LOCKED );
155 	clearMechanismInfo( &mechanismInfo );
156 	zeroise( buffer, CRYPT_MAX_KEYSIZE + 16 );
157 	return( status );
158 	}
159 
160 /* Export a public-key encrypted session key */
161 
162 CHECK_RETVAL STDC_NONNULL_ARG( ( 3 ) ) \
exportPublicKey(OUT_BUFFER_OPT (encryptedKeyMaxLength,* encryptedKeyLength)void * encryptedKey,IN_DATALENGTH_Z const int encryptedKeyMaxLength,OUT_DATALENGTH_Z int * encryptedKeyLength,IN_HANDLE const CRYPT_CONTEXT iSessionKeyContext,IN_HANDLE const CRYPT_CONTEXT iExportContext,IN_BUFFER_OPT (auxInfoLength)const void * auxInfo,IN_LENGTH_SHORT_Z const int auxInfoLength,IN_ENUM (KEYEX)const KEYEX_TYPE keyexType)163 int exportPublicKey( OUT_BUFFER_OPT( encryptedKeyMaxLength, *encryptedKeyLength ) \
164 						void *encryptedKey,
165 					 IN_DATALENGTH_Z const int encryptedKeyMaxLength,
166 					 OUT_DATALENGTH_Z int *encryptedKeyLength,
167 					 IN_HANDLE const CRYPT_CONTEXT iSessionKeyContext,
168 					 IN_HANDLE const CRYPT_CONTEXT iExportContext,
169 					 IN_BUFFER_OPT( auxInfoLength ) const void *auxInfo,
170 					 IN_LENGTH_SHORT_Z const int auxInfoLength,
171 					 IN_ENUM( KEYEX ) const KEYEX_TYPE keyexType )
172 	{
173 	MECHANISM_WRAP_INFO mechanismInfo;
174 	const WRITEKEYTRANS_FUNCTION writeKeytransFunction = getWriteKeytransFunction( keyexType );
175 	const BOOLEAN requiresSizeFixup = \
176 				( ( keyexType == KEYEX_CMS || keyexType == KEYEX_CRYPTLIB ) && \
177 				  ( encryptedKey != NULL ) ) ? TRUE : FALSE;
178 	BYTE buffer[ MAX_PKCENCRYPTED_SIZE + 8 ];
179 	int exportKeySize DUMMY_INIT, status;
180 
181 	assert( ( encryptedKey == NULL && encryptedKeyMaxLength == 0 ) || \
182 			isWritePtr( encryptedKey, encryptedKeyMaxLength ) );
183 	assert( isWritePtr( encryptedKeyLength, sizeof( int ) ) );
184 	assert( ( auxInfo == NULL && auxInfoLength == 0 ) || \
185 			isReadPtr( auxInfo, auxInfoLength ) );
186 
187 	REQUIRES( ( encryptedKey == NULL && encryptedKeyMaxLength == 0 ) || \
188 			  ( encryptedKey != NULL && \
189 				encryptedKeyMaxLength > MIN_CRYPT_OBJECTSIZE && \
190 				encryptedKeyMaxLength < MAX_BUFFER_SIZE ) );
191 	REQUIRES( isHandleRangeValid( iSessionKeyContext ) );
192 	REQUIRES( isHandleRangeValid( iExportContext ) );
193 	REQUIRES( ( auxInfo == NULL && auxInfoLength == 0 ) || \
194 			  ( auxInfo != NULL && \
195 				auxInfoLength > 0 && auxInfoLength < MAX_INTLENGTH_SHORT ) );
196 	REQUIRES( keyexType > KEYEX_NONE && keyexType < KEYEX_LAST );
197 
198 	/* Clear return value */
199 	*encryptedKeyLength = 0;
200 
201 	/* Make sure that the requested key exchange format is available */
202 	if( writeKeytransFunction  == NULL )
203 		return( CRYPT_ERROR_NOTAVAIL );
204 
205 	/* Get the export parameters */
206 	if( requiresSizeFixup )
207 		{
208 		status = krnlSendMessage( iExportContext, IMESSAGE_GETATTRIBUTE,
209 								  &exportKeySize, CRYPT_CTXINFO_KEYSIZE );
210 		if( cryptStatusError( status ) )
211 			return( cryptArgError( status ) ? CRYPT_ARGERROR_NUM2 : status );
212 		if( exportKeySize > encryptedKeyMaxLength )
213 			return( CRYPT_ERROR_OVERFLOW );
214 		}
215 
216 	/* Encrypt the session key and write the result to the output stream */
217 	if( encryptedKey == NULL )
218 		{
219 		setMechanismWrapInfo( &mechanismInfo, NULL, 0, NULL, 0,
220 							  iSessionKeyContext, iExportContext );
221 		}
222 	else
223 		{
224 		setMechanismWrapInfo( &mechanismInfo, buffer, MAX_PKCENCRYPTED_SIZE,
225 							  NULL, 0, iSessionKeyContext, iExportContext );
226 		}
227 	status = krnlSendMessage( iExportContext, IMESSAGE_DEV_EXPORT,
228 							  &mechanismInfo, ( keyexType == KEYEX_PGP ) ? \
229 								MECHANISM_ENC_PKCS1_PGP : \
230 								MECHANISM_ENC_PKCS1 );
231 	if( cryptStatusOK( status ) )
232 		{
233 		STREAM stream;
234 
235 		/* If we're perfoming a dummy export for a length check, set up a
236 		   dummy value to write */
237 		if( encryptedKey == NULL )
238 			{
239 			memset( buffer, 0x01, mechanismInfo.wrappedDataLength );
240 			mechanismInfo.wrappedData = buffer;
241 			}
242 
243 		/* If we're using the CMS data format then we need to fix up the
244 		   size of the wrapped key data to match the exporting key size.
245 		   This is necessary because the higher-level ASN.1 wrappers at the
246 		   CMS envelope level won't reflect the fact that the size has
247 		   changed at the CMS keyex level, so we need to adjust the data
248 		   size to ensure that the amount of data we output matches what
249 		   was promised in the size check */
250 		if( requiresSizeFixup && mechanismInfo.wrappedDataLength < exportKeySize )
251 			{
252 			const int delta = exportKeySize - mechanismInfo.wrappedDataLength;
253 
254 			memmove( ( BYTE * ) mechanismInfo.wrappedData + delta,
255 					 mechanismInfo.wrappedData,
256 					 mechanismInfo.wrappedDataLength );
257 			memset( mechanismInfo.wrappedData, 0, delta );
258 			mechanismInfo.wrappedDataLength = exportKeySize;
259 			}
260 
261 		sMemOpenOpt( &stream, encryptedKey, encryptedKeyMaxLength );
262 		status = writeKeytransFunction ( &stream, iExportContext,
263 										 mechanismInfo.wrappedData,
264 										 mechanismInfo.wrappedDataLength,
265 										 auxInfo, auxInfoLength );
266 		if( cryptStatusOK( status ) )
267 			*encryptedKeyLength = stell( &stream );
268 		sMemDisconnect( &stream );
269 		}
270 	clearMechanismInfo( &mechanismInfo );
271 
272 	/* Clean up */
273 	zeroise( buffer, MAX_PKCENCRYPTED_SIZE );
274 	return( status );
275 	}
276 
277 /****************************************************************************
278 *																			*
279 *							Low-level Key Import Functions					*
280 *																			*
281 ****************************************************************************/
282 
283 /* Import a conventionally encrypted session key */
284 
285 CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
importConventionalKey(IN_BUFFER (encryptedKeyLength)const void * encryptedKey,IN_DATALENGTH const int encryptedKeyLength,IN_HANDLE const CRYPT_CONTEXT iSessionKeyContext,IN_HANDLE const CRYPT_CONTEXT iImportContext,IN_ENUM (KEYEX)const KEYEX_TYPE keyexType)286 int importConventionalKey( IN_BUFFER( encryptedKeyLength ) \
287 							const void *encryptedKey,
288 						   IN_DATALENGTH const int encryptedKeyLength,
289 						   IN_HANDLE const CRYPT_CONTEXT iSessionKeyContext,
290 						   IN_HANDLE const CRYPT_CONTEXT iImportContext,
291 						   IN_ENUM( KEYEX ) const KEYEX_TYPE keyexType )
292 	{
293 	MECHANISM_WRAP_INFO mechanismInfo;
294 	const READKEK_FUNCTION readKeyexFunction = getReadKekFunction( keyexType );
295 	QUERY_INFO queryInfo;
296 	MESSAGE_DATA msgData;
297 	STREAM stream;
298 	int importAlgo, importMode DUMMY_INIT, status;	/* int vs.enum */
299 
300 	assert( isReadPtr( encryptedKey, encryptedKeyLength ) );
301 
302 	REQUIRES( encryptedKeyLength > MIN_CRYPT_OBJECTSIZE && \
303 			  encryptedKeyLength < MAX_BUFFER_SIZE );
304 	REQUIRES( isHandleRangeValid( iSessionKeyContext ) );
305 	REQUIRES( isHandleRangeValid( iImportContext ) );
306 	REQUIRES( keyexType > KEYEX_NONE && keyexType < KEYEX_LAST );
307 
308 	/* Make sure that the requested key exchange format is available */
309 	if( readKeyexFunction == NULL )
310 		return( CRYPT_ERROR_NOTAVAIL );
311 
312 	/* Get the import parameters */
313 	status = krnlSendMessage( iImportContext, IMESSAGE_GETATTRIBUTE,
314 							  &importAlgo, CRYPT_CTXINFO_ALGO );
315 	if( cryptStatusOK( status ) )
316 		status = krnlSendMessage( iImportContext, IMESSAGE_GETATTRIBUTE,
317 								  &importMode, CRYPT_CTXINFO_MODE );
318 	if( cryptStatusError( status ) )
319 		return( cryptArgError( status ) ? CRYPT_ARGERROR_NUM2 : status );
320 
321 	/* Read and check the encrypted key record and make sure that we'll be
322 	   using the correct type of encryption context to decrypt it */
323 	sMemConnect( &stream, encryptedKey, encryptedKeyLength );
324 	status = readKeyexFunction( &stream, &queryInfo );
325 	sMemDisconnect( &stream );
326 	if( cryptStatusOK( status ) && \
327 		( importAlgo != queryInfo.cryptAlgo || \
328 		  importMode != queryInfo.cryptMode ) )
329 		status = CRYPT_ARGERROR_NUM1;
330 	if( cryptStatusError( status ) )
331 		{
332 		zeroise( &queryInfo, sizeof( QUERY_INFO ) );
333 		return( status );
334 		}
335 
336 	/* Extract the encrypted key from the buffer and decrypt it.  Since we
337 	   don't want another thread changing the IV while we're using the import
338 	   context, we lock it for the duration */
339 	status = krnlSendMessage( iImportContext, IMESSAGE_SETATTRIBUTE,
340 							  MESSAGE_VALUE_TRUE, CRYPT_IATTRIBUTE_LOCKED );
341 	if( cryptStatusError( status ) )
342 		{
343 		zeroise( &queryInfo, sizeof( QUERY_INFO ) );
344 		return( status );
345 		}
346 	if( needsIV( importMode ) && importAlgo != CRYPT_ALGO_RC4 )
347 		{
348 		setMessageData( &msgData, queryInfo.iv, queryInfo.ivLength );
349 		status = krnlSendMessage( iImportContext, IMESSAGE_SETATTRIBUTE_S,
350 								  &msgData, CRYPT_CTXINFO_IV );
351 		if( cryptStatusError( status ) )
352 			{
353 			( void ) krnlSendMessage( iImportContext, IMESSAGE_SETATTRIBUTE,
354 									  MESSAGE_VALUE_FALSE,
355 									  CRYPT_IATTRIBUTE_LOCKED );
356 			zeroise( &queryInfo, sizeof( QUERY_INFO ) );
357 			return( status );
358 			}
359 		}
360 	ENSURES( rangeCheck( queryInfo.dataStart, queryInfo.dataLength,
361 						 encryptedKeyLength ) );
362 	setMechanismWrapInfo( &mechanismInfo,
363 						  ( BYTE * ) encryptedKey + queryInfo.dataStart,
364 						  queryInfo.dataLength, NULL, 0,
365 						  iSessionKeyContext, iImportContext );
366 	status = krnlSendMessage( SYSTEM_OBJECT_HANDLE, IMESSAGE_DEV_IMPORT,
367 							  &mechanismInfo, MECHANISM_ENC_CMS );
368 	( void ) krnlSendMessage( iImportContext, IMESSAGE_SETATTRIBUTE,
369 							  MESSAGE_VALUE_FALSE,
370 							  CRYPT_IATTRIBUTE_LOCKED );
371 	clearMechanismInfo( &mechanismInfo );
372 	zeroise( &queryInfo, sizeof( QUERY_INFO ) );
373 
374 	return( status );
375 	}
376 
377 /* Import a public-key encrypted session key */
378 
379 CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
importPublicKey(IN_BUFFER (encryptedKeyLength)const void * encryptedKey,IN_DATALENGTH const int encryptedKeyLength,IN_HANDLE_OPT const CRYPT_CONTEXT iSessionKeyContext,IN_HANDLE const CRYPT_CONTEXT iImportContext,OUT_OPT_HANDLE_OPT CRYPT_CONTEXT * iReturnedContext,IN_ENUM (KEYEX)const KEYEX_TYPE keyexType)380 int importPublicKey( IN_BUFFER( encryptedKeyLength ) const void *encryptedKey,
381 					 IN_DATALENGTH const int encryptedKeyLength,
382 					 IN_HANDLE_OPT const CRYPT_CONTEXT iSessionKeyContext,
383 					 IN_HANDLE const CRYPT_CONTEXT iImportContext,
384 					 OUT_OPT_HANDLE_OPT CRYPT_CONTEXT *iReturnedContext,
385 					 IN_ENUM( KEYEX ) const KEYEX_TYPE keyexType )
386 	{
387 	MECHANISM_WRAP_INFO mechanismInfo;
388 	const READKEYTRANS_FUNCTION readKeytransFunction = getReadKeytransFunction( keyexType );
389 	QUERY_INFO queryInfo;
390 	MESSAGE_DATA msgData;
391 	STREAM stream;
392 	int compareType, status;
393 
394 	assert( isReadPtr( encryptedKey, encryptedKeyLength ) );
395 	assert( ( keyexType == KEYEX_PGP && \
396 			  isWritePtr( iReturnedContext, sizeof( CRYPT_CONTEXT ) ) ) || \
397 			( keyexType != KEYEX_PGP && iReturnedContext == NULL ) );
398 
399 	REQUIRES( encryptedKeyLength > MIN_CRYPT_OBJECTSIZE && \
400 			  encryptedKeyLength < MAX_BUFFER_SIZE );
401 	REQUIRES( ( keyexType == KEYEX_PGP && \
402 				iSessionKeyContext == CRYPT_UNUSED ) || \
403 			  ( keyexType != KEYEX_PGP && \
404 				isHandleRangeValid( iSessionKeyContext ) ) );
405 	REQUIRES( isHandleRangeValid( iImportContext ) );
406 	REQUIRES( ( keyexType == KEYEX_PGP && iReturnedContext != NULL ) || \
407 			  ( keyexType != KEYEX_PGP && iReturnedContext == NULL ) );
408 	REQUIRES( keyexType > KEYEX_NONE && keyexType < KEYEX_LAST );
409 
410 	/* Clear return value */
411 	if( iReturnedContext != NULL )
412 		*iReturnedContext = CRYPT_ERROR;
413 
414 	/* Make sure that the requested key exchange format is available */
415 	if( readKeytransFunction == NULL )
416 		return( CRYPT_ERROR_NOTAVAIL );
417 
418 	/* Read and check the encrypted key record */
419 	sMemConnect( &stream, encryptedKey, encryptedKeyLength );
420 	status = readKeytransFunction( &stream, &queryInfo );
421 	sMemDisconnect( &stream );
422 	if( cryptStatusError( status ) )
423 		{
424 		zeroise( &queryInfo, sizeof( QUERY_INFO ) );
425 		return( status );
426 		}
427 
428 	/* Make sure that we've been given the correct key */
429 	setMessageData( &msgData, queryInfo.keyID, queryInfo.keyIDlength );
430 	switch( keyexType )
431 		{
432 		case KEYEX_CMS:
433 			setMessageData( &msgData, \
434 					( BYTE * ) encryptedKey + queryInfo.iAndSStart, \
435 					queryInfo.iAndSLength );
436 			compareType = MESSAGE_COMPARE_ISSUERANDSERIALNUMBER;
437 			break;
438 
439 		case KEYEX_CRYPTLIB:
440 			compareType = MESSAGE_COMPARE_KEYID;
441 			break;
442 
443 		case KEYEX_PGP:
444 			compareType = ( queryInfo.version == PGP_VERSION_2 ) ? \
445 						  MESSAGE_COMPARE_KEYID_PGP : \
446 						  MESSAGE_COMPARE_KEYID_OPENPGP;
447 			break;
448 
449 		default:
450 			retIntError();
451 		}
452 	status = krnlSendMessage( iImportContext, IMESSAGE_COMPARE, &msgData,
453 							  compareType );
454 	if( cryptStatusError( status ) && \
455 		compareType == MESSAGE_COMPARE_KEYID )
456 		{
457 		/* Checking for the keyID gets a bit complicated, in theory it's the
458 		   subjectKeyIdentifier from a certificate but in practice this form
459 		   is mostly used for certificateless public keys.  Because of this we
460 		   check for the keyID first and if that fails fall back to the
461 		   sKID */
462 		status = krnlSendMessage( iImportContext, IMESSAGE_COMPARE,
463 								  &msgData,
464 								  MESSAGE_COMPARE_SUBJECTKEYIDENTIFIER );
465 		}
466 	if( cryptStatusError( status ) && \
467 		compareType == MESSAGE_COMPARE_KEYID_OPENPGP )
468 		{
469 		/* Some broken PGP implementations put PGP 2.x IDs in packets marked
470 		   as OpenPGP packets so if we were doing a check for an OpenPGP ID
471 		   and it failed, fall back to a PGP 2.x one */
472 		status = krnlSendMessage( iImportContext, IMESSAGE_COMPARE,
473 								  &msgData, MESSAGE_COMPARE_KEYID_PGP );
474 		}
475 	if( cryptStatusError( status ) )
476 		{
477 		/* A failed comparison is reported as a generic CRYPT_ERROR, convert
478 		   it into a wrong-key error */
479 		zeroise( &queryInfo, sizeof( QUERY_INFO ) );
480 		return( CRYPT_ERROR_WRONGKEY );
481 		}
482 
483 	/* Decrypt the encrypted key and load it into the context */
484 	if( keyexType != KEYEX_PGP )
485 		{
486 		setMechanismWrapInfo( &mechanismInfo,
487 							  ( BYTE * ) encryptedKey + queryInfo.dataStart,
488 							  queryInfo.dataLength, NULL, 0,
489 							  iSessionKeyContext, iImportContext );
490 		status = krnlSendMessage( iImportContext, IMESSAGE_DEV_IMPORT,
491 								  &mechanismInfo, MECHANISM_ENC_PKCS1 );
492 		}
493 	else
494 		{
495 		/* PGP doesn't provide separate session key information with the
496 		   encrypted data but wraps it up alongside the encrypted key, so we
497 		   can't import the wrapped key into a context via the standard key
498 		   import functions but instead have to create the context as part
499 		   of the unwrap process */
500 		setMechanismWrapInfo( &mechanismInfo,
501 							  ( BYTE * ) encryptedKey + queryInfo.dataStart,
502 							  queryInfo.dataLength, NULL, 0,
503 							  CRYPT_UNUSED, iImportContext );
504 		status = krnlSendMessage( iImportContext, IMESSAGE_DEV_IMPORT,
505 								  &mechanismInfo, MECHANISM_ENC_PKCS1_PGP );
506 		if( cryptStatusOK( status ) )
507 			*iReturnedContext = mechanismInfo.keyContext;
508 		}
509 	clearMechanismInfo( &mechanismInfo );
510 	zeroise( &queryInfo, sizeof( QUERY_INFO ) );
511 
512 	return( status );
513 	}
514 #endif /* USE_INT_CMS */
515