1 /****************************************************************************
2 *																			*
3 *							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 "asn1_ext.h"
12   #include "misc_rw.h"
13   #include "pgp_rw.h"
14   #include "mech.h"
15 #else
16   #include "crypt.h"
17   #include "enc_dec/asn1.h"
18   #include "enc_dec/asn1_ext.h"
19   #include "enc_dec/misc_rw.h"
20   #include "enc_dec/pgp_rw.h"
21   #include "mechs/mech.h"
22 #endif /* Compiler-specific includes */
23 
24 #ifdef USE_INT_CMS
25 
26 /****************************************************************************
27 *																			*
28 *								Utility Functions							*
29 *																			*
30 ****************************************************************************/
31 
32 /* Try and determine the format of the encrypted data */
33 
34 CHECK_RETVAL_ENUM( CRYPT_FORMAT ) STDC_NONNULL_ARG( ( 1 ) ) \
getFormatType(IN_BUFFER (dataLength)const void * data,IN_DATALENGTH const int dataLength)35 static CRYPT_FORMAT_TYPE getFormatType( IN_BUFFER( dataLength ) const void *data,
36 										IN_DATALENGTH const int dataLength )
37 	{
38 	STREAM stream;
39 	long value;
40 	int status;
41 
42 	assert( isReadPtr( data, dataLength ) );
43 
44 	REQUIRES_EXT( dataLength > MIN_CRYPT_OBJECTSIZE && \
45 				  dataLength < MAX_BUFFER_SIZE, CRYPT_FORMAT_NONE );
46 
47 	sMemConnect( &stream, data, min( 16, dataLength ) );
48 
49 	/* Figure out what we've got.  PKCS #7/CMS/SMIME keyTrans begins:
50 
51 		keyTransRecipientInfo ::= SEQUENCE {
52 			version		INTEGER (0|2),
53 
54 	   while a kek begins:
55 
56 		kekRecipientInfo ::= [3] IMPLICIT SEQUENCE {
57 			version		INTEGER (0),
58 
59 	   which allows us to determine which type of object we have.  Note that
60 	   we use sPeek() rather than peekTag() because we want to continue
61 	   processing (or at least checking for) PGP data if it's no ASN.1 */
62 	if( sPeek( &stream ) == BER_SEQUENCE )
63 		{
64 		CRYPT_FORMAT_TYPE formatType;
65 
66 		readSequence( &stream, NULL );
67 		status = readShortInteger( &stream, &value );
68 		if( cryptStatusError( status ) )
69 			{
70 			sMemDisconnect( &stream );
71 			return( CRYPT_FORMAT_NONE );
72 			}
73 		switch( value )
74 			{
75 			case KEYTRANS_VERSION:
76 				formatType = CRYPT_FORMAT_CMS;
77 				break;
78 
79 			case KEYTRANS_EX_VERSION:
80 				formatType = CRYPT_FORMAT_CRYPTLIB;
81 				break;
82 
83 			default:
84 				formatType = CRYPT_FORMAT_NONE;
85 			}
86 		sMemDisconnect( &stream );
87 
88 		return( formatType );
89 		}
90 	if( sPeek( &stream ) == MAKE_CTAG( CTAG_RI_PWRI ) )
91 		{
92 		readConstructed( &stream, NULL, CTAG_RI_PWRI );
93 		status = readShortInteger( &stream, &value );
94 		if( cryptStatusError( status ) )
95 			{
96 			sMemDisconnect( &stream );
97 			return( CRYPT_FORMAT_NONE );
98 			}
99 		sMemDisconnect( &stream );
100 
101 		return( ( value == PWRI_VERSION ) ? \
102 				CRYPT_FORMAT_CRYPTLIB : CRYPT_FORMAT_NONE );
103 		}
104 
105 #ifdef USE_PGP
106 	/* It's not ASN.1 data, check for PGP data */
107 	status = pgpReadPacketHeader( &stream, NULL, &value, 30, 8192 );
108 	if( cryptStatusOK( status ) && value > 30 && value < 8192 )
109 		{
110 		sMemDisconnect( &stream );
111 		return( CRYPT_FORMAT_PGP );
112 		}
113 #endif /* USE_PGP */
114 
115 	sMemDisconnect( &stream );
116 
117 	return( CRYPT_FORMAT_NONE );
118 	}
119 
120 /* Check the key wrap key being used to import/export a session key */
121 
122 CHECK_RETVAL STDC_NONNULL_ARG( ( 2 ) ) \
checkWrapKey(IN_HANDLE int importKey,OUT_ALGO_Z CRYPT_ALGO_TYPE * cryptAlgo,const BOOLEAN isImport)123 static int checkWrapKey( IN_HANDLE int importKey,
124 						 OUT_ALGO_Z CRYPT_ALGO_TYPE *cryptAlgo,
125 						 const BOOLEAN isImport )
126 	{
127 	int localCryptAlgo, status;
128 
129 	assert( isWritePtr( cryptAlgo, sizeof( CRYPT_ALGO_TYPE ) ) );
130 
131 	REQUIRES( isHandleRangeValid( importKey ) );
132 
133 	/* Clear return value */
134 	*cryptAlgo = CRYPT_ALGO_NONE;
135 
136 	/* Make sure that the context is valid and get the algorithm being
137 	   used */
138 	status = krnlSendMessage( importKey, MESSAGE_GETATTRIBUTE,
139 							  &localCryptAlgo, CRYPT_CTXINFO_ALGO );
140 	if( cryptStatusError( status ) )
141 		return( status );
142 	if( isPkcAlgo( localCryptAlgo ) )
143 		{
144 		/* The DLP algorithms have specialised data-formatting requirements
145 		   and can't normally be directly accessed via external messages,
146 		   and PKC operations in general may be restricted to internal access
147 		   only if they have certificates that restrict their use associated
148 		   with them.  However if we're performing a high-level key import
149 		   (rather than a low-level raw context operation) this is OK since
150 		   the low-level operation is being performed via these higher-level
151 		   routines which handle the formatting requirements.  Doing the
152 		   check via an internal message is safe at this point since we've
153 		   already checked the context's external accessibility when we got
154 		   the algorithm info */
155 		status = krnlSendMessage( importKey, IMESSAGE_CHECK, NULL,
156 								  isImport ? MESSAGE_CHECK_PKC_DECRYPT : \
157 											 MESSAGE_CHECK_PKC_ENCRYPT );
158 		}
159 	else
160 		{
161 		status = krnlSendMessage( importKey, MESSAGE_CHECK, NULL,
162 								  MESSAGE_CHECK_CRYPT );
163 		}
164 	if( cryptStatusError( status ) )
165 		return( status );
166 
167 	*cryptAlgo = localCryptAlgo;
168 	return( CRYPT_OK );
169 	}
170 
171 /* Check that the context data is encodable using the chosen format */
172 
173 CHECK_RETVAL \
checkContextsEncodable(IN_HANDLE const CRYPT_HANDLE exportKey,IN_ALGO const CRYPT_ALGO_TYPE exportAlgo,IN_HANDLE const CRYPT_CONTEXT sessionKeyContext,IN_ENUM (CRYPT_FORMAT)const CRYPT_FORMAT_TYPE formatType)174 static int checkContextsEncodable( IN_HANDLE const CRYPT_HANDLE exportKey,
175 								   IN_ALGO const CRYPT_ALGO_TYPE exportAlgo,
176 								   IN_HANDLE const CRYPT_CONTEXT sessionKeyContext,
177 								   IN_ENUM( CRYPT_FORMAT ) \
178 									const CRYPT_FORMAT_TYPE formatType )
179 	{
180 	const BOOLEAN exportIsPKC = isPkcAlgo( exportAlgo ) ? TRUE : FALSE;
181 	BOOLEAN sessionIsMAC = FALSE;
182 	int sessionKeyAlgo, sessionKeyMode DUMMY_INIT, status;
183 
184 	REQUIRES( isHandleRangeValid( exportKey ) );
185 	REQUIRES( exportAlgo > CRYPT_ALGO_NONE && exportAlgo < CRYPT_ALGO_LAST );
186 	REQUIRES( isHandleRangeValid( sessionKeyContext ) );
187 	REQUIRES( formatType > CRYPT_FORMAT_NONE && \
188 			  formatType < CRYPT_FORMAT_LAST_EXTERNAL );
189 
190 	/* Get any required context information */
191 	status = krnlSendMessage( sessionKeyContext, MESSAGE_GETATTRIBUTE,
192 							  &sessionKeyAlgo, CRYPT_CTXINFO_ALGO );
193 	if( cryptStatusError( status ) )
194 		return( CRYPT_ERROR_PARAM3 );
195 	if( isMacAlgo( sessionKeyAlgo ) )
196 		sessionIsMAC = TRUE;
197 	else
198 		{
199 		status = krnlSendMessage( sessionKeyContext, MESSAGE_GETATTRIBUTE,
200 								  &sessionKeyMode, CRYPT_CTXINFO_MODE );
201 		if( cryptStatusError( status ) )
202 			return( CRYPT_ERROR_PARAM3 );
203 		}
204 
205 	switch( formatType )
206 		{
207 		case CRYPT_FORMAT_CRYPTLIB:
208 		case CRYPT_FORMAT_CMS:
209 		case CRYPT_FORMAT_SMIME:
210 			/* Check that the export algorithm is encodable */
211 			if( exportIsPKC )
212 				{
213 				if( cryptStatusError( sizeofAlgoID( exportAlgo ) ) )
214 					return( CRYPT_ERROR_PARAM1 );
215 				}
216 			else
217 				{
218 				int exportMode;	/* int vs.enum */
219 
220 				/* If it's a conventional key export, the key wrap mechanism
221 				   requires the use of CBC mode for the wrapping */
222 				status = krnlSendMessage( exportKey, MESSAGE_GETATTRIBUTE,
223 										  &exportMode, CRYPT_CTXINFO_MODE );
224 				if( cryptStatusError( status ) )
225 					return( CRYPT_ERROR_PARAM1 );
226 				if( exportMode != CRYPT_MODE_CBC || \
227 					cryptStatusError( \
228 						sizeofAlgoIDex( exportAlgo, exportMode, 0 ) ) )
229 					return( CRYPT_ERROR_PARAM1 );
230 				}
231 
232 			/* Check that the session-key algorithm is encodable */
233 			if( sessionIsMAC )
234 				status = sizeofAlgoID( sessionKeyAlgo );
235 			else
236 				status = checkAlgoID( sessionKeyAlgo, sessionKeyMode );
237 			if( cryptStatusError( status ) )
238 				return( CRYPT_ERROR_PARAM3 );
239 
240 			return( CRYPT_OK );
241 
242 #ifdef USE_PGP
243 		case CRYPT_FORMAT_PGP:
244 			{
245 			int dummy;
246 
247 			/* Check that the export algorithm is encodable */
248 			if( cryptStatusError( \
249 					cryptlibToPgpAlgo( exportAlgo, &dummy ) ) )
250 				return( CRYPT_ERROR_PARAM1 );
251 
252 			/* Check that the session-key algorithm is encodable */
253 			if( exportIsPKC )
254 				{
255 				if( cryptStatusError( \
256 						cryptlibToPgpAlgo( sessionKeyAlgo, &dummy ) ) )
257 					return( CRYPT_ERROR_PARAM3 );
258 				if( sessionKeyMode != CRYPT_MODE_CFB )
259 					return( CRYPT_ERROR_PARAM3 );
260 				}
261 			else
262 				{
263 				int exportMode;	/* int vs.enum */
264 
265 				/* If it's a conventional key export there's no key wrap as
266 				   in CMS (the session-key context isn't used), so the
267 				   "export context" mode must be CFB */
268 				status = krnlSendMessage( exportKey, MESSAGE_GETATTRIBUTE,
269 										  &exportMode, CRYPT_CTXINFO_MODE );
270 				if( cryptStatusError( status ) || \
271 					exportMode != CRYPT_MODE_CFB )
272 					return( CRYPT_ERROR_PARAM1 );
273 				}
274 
275 			return( CRYPT_OK );
276 			}
277 #endif /* USE_PGP */
278 		}
279 
280 	/* It's an invalid/unknown format, we can't check the encodability of
281 	   the context data */
282 	return( CRYPT_ERROR_PARAM4 );
283 	}
284 
285 /****************************************************************************
286 *																			*
287 *								Import a Session Key						*
288 *																			*
289 ****************************************************************************/
290 
291 /* Import an extended encrypted key, either a cryptlib key or a CMS key */
292 
293 C_CHECK_RETVAL C_NONNULL_ARG( ( 1 ) ) \
cryptImportKeyEx(C_IN void C_PTR encryptedKey,C_IN int encryptedKeyLength,C_IN CRYPT_CONTEXT importKey,C_IN CRYPT_CONTEXT sessionKeyContext,C_OUT_OPT CRYPT_CONTEXT C_PTR returnedContext)294 C_RET cryptImportKeyEx( C_IN void C_PTR encryptedKey,
295 						C_IN int encryptedKeyLength,
296 						C_IN CRYPT_CONTEXT importKey,
297 						C_IN CRYPT_CONTEXT sessionKeyContext,
298 						C_OUT_OPT CRYPT_CONTEXT C_PTR returnedContext )
299 	{
300 	CRYPT_CONTEXT iReturnedContext DUMMY_INIT;
301 	CRYPT_FORMAT_TYPE formatType;
302 	CRYPT_ALGO_TYPE importAlgo;
303 	int owner = CRYPT_ERROR, originalOwner = CRYPT_ERROR, status;
304 
305 	/* Perform basic error checking */
306 	if( encryptedKeyLength <= MIN_CRYPT_OBJECTSIZE || \
307 		encryptedKeyLength >= MAX_INTLENGTH_SHORT )
308 		return( CRYPT_ERROR_PARAM2 );
309 	if( !isReadPtr( encryptedKey, encryptedKeyLength ) )
310 		return( CRYPT_ERROR_PARAM1 );
311 	if( ( formatType = \
312 			getFormatType( encryptedKey, encryptedKeyLength ) ) == CRYPT_FORMAT_NONE )
313 		return( CRYPT_ERROR_BADDATA );
314 	if( !isHandleRangeValid( importKey ) )
315 		return( CRYPT_ERROR_PARAM3 );
316 	if( sessionKeyContext != CRYPT_UNUSED && \
317 		!isHandleRangeValid( sessionKeyContext ) )
318 		return( CRYPT_ERROR_PARAM4 );
319 
320 	/* Check the importing key */
321 	status = checkWrapKey( importKey, &importAlgo, TRUE );
322 	if( cryptStatusError( status ) )
323 		return( cryptArgError( status ) ? CRYPT_ERROR_PARAM3 : status );
324 
325 	/* Check the session key */
326 	if( formatType == CRYPT_FORMAT_PGP )
327 		{
328 		/* PGP stores the session key information with the encrypted key
329 		   data, so the user can't provide a context */
330 		if( sessionKeyContext != CRYPT_UNUSED )
331 			return( CRYPT_ERROR_PARAM4 );
332 		if( !isWritePtrConst( returnedContext, sizeof( CRYPT_CONTEXT ) ) )
333 			return( CRYPT_ERROR_PARAM5 );
334 		*returnedContext = CRYPT_ERROR;
335 		}
336 	else
337 		{
338 		int sessionKeyAlgo;	/* int vs.enum */
339 
340 		if( !isHandleRangeValid( sessionKeyContext ) )
341 			return( CRYPT_ERROR_PARAM4 );
342 		status = krnlSendMessage( sessionKeyContext, MESSAGE_GETATTRIBUTE,
343 								  &sessionKeyAlgo, CRYPT_CTXINFO_ALGO );
344 		if( cryptStatusOK( status ) )
345 			{
346 			status = krnlSendMessage( sessionKeyContext, MESSAGE_CHECK, NULL,
347 									  isMacAlgo( sessionKeyAlgo ) ? \
348 										MESSAGE_CHECK_MAC_READY : \
349 										MESSAGE_CHECK_CRYPT_READY );
350 			}
351 		if( cryptStatusError( status ) )
352 			return( cryptArgError( status ) ? CRYPT_ERROR_PARAM4 : status );
353 		if( returnedContext != NULL )
354 			return( CRYPT_ERROR_PARAM5 );
355 		}
356 
357 	/* If the importing key is owned, bind the session key context to the same
358 	   owner before we load a key into it.  We also need to save the original
359 	   owner so that we can undo the binding later if things fail */
360 	status = krnlSendMessage( importKey, MESSAGE_GETATTRIBUTE, &owner,
361 							  CRYPT_PROPERTY_OWNER );
362 	if( cryptStatusOK( status ) && sessionKeyContext != CRYPT_UNUSED )
363 		{
364 		/* The importing key is owned, set the imported key's owner */
365 		status = krnlSendMessage( sessionKeyContext, MESSAGE_GETATTRIBUTE,
366 								  &originalOwner, CRYPT_PROPERTY_OWNER );
367 		if( cryptStatusOK( status ) )
368 			{
369 			status = krnlSendMessage( sessionKeyContext, MESSAGE_SETATTRIBUTE,
370 									  &owner, CRYPT_PROPERTY_OWNER );
371 			if( cryptStatusError( status ) )
372 				return( status );
373 			}
374 		else
375 			{
376 			/* Don't try and reset the session key ownership on error */
377 			originalOwner = CRYPT_ERROR;
378 			}
379 		}
380 
381 	/* Import it as appropriate */
382 	status = iCryptImportKey( encryptedKey, encryptedKeyLength, formatType,
383 							  importKey, sessionKeyContext,
384 							  ( formatType == CRYPT_FORMAT_PGP ) ? \
385 								&iReturnedContext : NULL );
386 	if( cryptStatusError( status ) )
387 		{
388 		/* The import failed, return the session key context to its
389 		   original owner.  If this fails there's not much that we can do
390 		   to recover so we don't do anything with the return value */
391 		if( originalOwner != CRYPT_ERROR )
392 			{
393 			( void ) krnlSendMessage( sessionKeyContext,
394 									  MESSAGE_SETATTRIBUTE,
395 									  &originalOwner, CRYPT_PROPERTY_OWNER );
396 			}
397 		if( cryptArgError( status ) )
398 			{
399 			/* If we get an argument error from the lower-level code, map the
400 			   parameter number to the function argument number */
401 			status = ( status == CRYPT_ARGERROR_NUM1 ) ? \
402 					 CRYPT_ERROR_PARAM4 : CRYPT_ERROR_PARAM3;
403 			}
404 		return( status );
405 		}
406 
407 #ifdef USE_PGP
408 	/* If it's a PGP key import then the session key was recreated from
409 	   information stored with the wrapped key so we have to make it
410 	   externally visible before it can be used by the caller */
411 	if( formatType == CRYPT_FORMAT_PGP && isPkcAlgo( importAlgo ) )
412 		{
413 		/* If the importing key is owned, set the imported key's owner */
414 		if( owner != CRYPT_ERROR )
415 			{
416 			status = krnlSendMessage( iReturnedContext,
417 									  IMESSAGE_SETATTRIBUTE,
418 									  &owner, CRYPT_PROPERTY_OWNER );
419 			if( cryptStatusError( status ) )
420 				{
421 				krnlSendNotifier( iReturnedContext, IMESSAGE_DECREFCOUNT );
422 				return( status );
423 				}
424 			}
425 
426 		/* Make the newly-created context externally visible */
427 		status = krnlSendMessage( iReturnedContext, IMESSAGE_SETATTRIBUTE,
428 								  MESSAGE_VALUE_FALSE,
429 								  CRYPT_IATTRIBUTE_INTERNAL );
430 		if( cryptStatusError( status ) )
431 			{
432 			krnlSendNotifier( iReturnedContext, IMESSAGE_DECREFCOUNT );
433 			return( status );
434 			}
435 		*returnedContext = iReturnedContext;
436 		}
437 #endif /* USE_PGP */
438 
439 	return( CRYPT_OK );
440 	}
441 
442 C_CHECK_RETVAL C_NONNULL_ARG( ( 1 ) ) \
cryptImportKey(C_IN void C_PTR encryptedKey,C_IN int encryptedKeyLength,C_IN CRYPT_CONTEXT importKey,C_IN CRYPT_CONTEXT sessionKeyContext)443 C_RET cryptImportKey( C_IN void C_PTR encryptedKey,
444 					  C_IN int encryptedKeyLength,
445 					  C_IN CRYPT_CONTEXT importKey,
446 					  C_IN CRYPT_CONTEXT sessionKeyContext )
447 	{
448 	return( cryptImportKeyEx( encryptedKey, encryptedKeyLength, importKey,
449 							  sessionKeyContext, NULL ) );
450 	}
451 
452 /****************************************************************************
453 *																			*
454 *								Export a Session Key						*
455 *																			*
456 ****************************************************************************/
457 
458 /* Export an extended encrypted key, either a cryptlib key or a CMS key */
459 
460 C_CHECK_RETVAL C_NONNULL_ARG( ( 3 ) ) \
cryptExportKeyEx(C_OUT_OPT void C_PTR encryptedKey,C_IN int encryptedKeyMaxLength,C_OUT int C_PTR encryptedKeyLength,C_IN CRYPT_FORMAT_TYPE formatType,C_IN CRYPT_HANDLE exportKey,C_IN CRYPT_CONTEXT sessionKeyContext)461 C_RET cryptExportKeyEx( C_OUT_OPT void C_PTR encryptedKey,
462 						C_IN int encryptedKeyMaxLength,
463 						C_OUT int C_PTR encryptedKeyLength,
464 						C_IN CRYPT_FORMAT_TYPE formatType,
465 						C_IN CRYPT_HANDLE exportKey,
466 						C_IN CRYPT_CONTEXT sessionKeyContext )
467 	{
468 	CRYPT_ALGO_TYPE exportAlgo;
469 	int sessionKeyAlgo, status;	/* int vs.enum */
470 
471 	/* Perform basic error checking */
472 	if( encryptedKey != NULL )
473 		{
474 		if( encryptedKeyMaxLength <= MIN_CRYPT_OBJECTSIZE || \
475 			encryptedKeyMaxLength >= MAX_BUFFER_SIZE )
476 			return( CRYPT_ERROR_PARAM2 );
477 		if( !isWritePtr( encryptedKey, encryptedKeyMaxLength ) )
478 			return( CRYPT_ERROR_PARAM1 );
479 		memset( encryptedKey, 0, MIN_CRYPT_OBJECTSIZE );
480 		}
481 	else
482 		{
483 		if( encryptedKeyMaxLength != 0 )
484 			return( CRYPT_ERROR_PARAM2 );
485 		}
486 	if( !isWritePtrConst( encryptedKeyLength, sizeof( int ) ) )
487 		return( CRYPT_ERROR_PARAM3 );
488 	*encryptedKeyLength = 0;
489 	if( formatType != CRYPT_FORMAT_CRYPTLIB && \
490 		formatType != CRYPT_FORMAT_CMS && \
491 		formatType != CRYPT_FORMAT_SMIME && \
492 		formatType != CRYPT_FORMAT_PGP )
493 		return( CRYPT_ERROR_PARAM4 );
494 	if( !isHandleRangeValid( exportKey ) )
495 		return( CRYPT_ERROR_PARAM5 );
496 	if( !isHandleRangeValid( sessionKeyContext ) )
497 		return( CRYPT_ERROR_PARAM6 );
498 
499 	/* Check the exporting key */
500 	status = checkWrapKey( exportKey, &exportAlgo, FALSE );
501 	if( cryptStatusError( status ) )
502 		return( cryptArgError( status ) ? CRYPT_ERROR_PARAM5 : status );
503 	status = checkContextsEncodable( exportKey, exportAlgo,
504 									 sessionKeyContext, formatType );
505 	if( cryptStatusError( status ) )
506 		{
507 		return( ( status == CRYPT_ERROR_PARAM1 ) ? CRYPT_ERROR_PARAM5 : \
508 				( status == CRYPT_ERROR_PARAM3 ) ? CRYPT_ERROR_PARAM6 : \
509 				CRYPT_ERROR_PARAM4 );
510 		}
511 
512 	/* Check the exported key */
513 	status = krnlSendMessage( sessionKeyContext, MESSAGE_GETATTRIBUTE,
514 							  &sessionKeyAlgo, CRYPT_CTXINFO_ALGO );
515 	if( cryptStatusError( status ) )
516 		return( CRYPT_ERROR_PARAM6 );
517 	status = krnlSendMessage( sessionKeyContext, MESSAGE_CHECK, NULL,
518 							  isMacAlgo( sessionKeyAlgo ) ? \
519 								MESSAGE_CHECK_MAC : MESSAGE_CHECK_CRYPT );
520 	if( cryptStatusError( status ) )
521 		return( cryptArgError( status ) ? CRYPT_ERROR_PARAM6 : status );
522 
523 	/* Export the key via the shared export function */
524 	status = iCryptExportKey( encryptedKey, encryptedKeyMaxLength,
525 							  encryptedKeyLength, formatType,
526 							  sessionKeyContext, exportKey );
527 	if( cryptArgError( status ) )
528 		{
529 		/* If we get an argument error from the lower-level code, map the
530 		   parameter number to the function argument number */
531 		status = ( status == CRYPT_ARGERROR_NUM1 ) ? \
532 				 CRYPT_ERROR_PARAM6 : CRYPT_ERROR_PARAM5;
533 		}
534 	return( status );
535 	}
536 
537 C_CHECK_RETVAL C_NONNULL_ARG( ( 3 ) ) \
cryptExportKey(C_OUT_OPT void C_PTR encryptedKey,C_IN int encryptedKeyMaxLength,C_OUT int C_PTR encryptedKeyLength,C_IN CRYPT_HANDLE exportKey,C_IN CRYPT_CONTEXT sessionKeyContext)538 C_RET cryptExportKey( C_OUT_OPT void C_PTR encryptedKey,
539 					  C_IN int encryptedKeyMaxLength,
540 					  C_OUT int C_PTR encryptedKeyLength,
541 					  C_IN CRYPT_HANDLE exportKey,
542 					  C_IN CRYPT_CONTEXT sessionKeyContext )
543 	{
544 	int status;
545 
546 	status = cryptExportKeyEx( encryptedKey, encryptedKeyMaxLength,
547 							   encryptedKeyLength, CRYPT_FORMAT_CRYPTLIB,
548 							   exportKey, sessionKeyContext );
549 	return( ( status == CRYPT_ERROR_PARAM5 ) ? CRYPT_ERROR_PARAM4 : \
550 			( status == CRYPT_ERROR_PARAM6 ) ? CRYPT_ERROR_PARAM5 : status );
551 	}
552 
553 /****************************************************************************
554 *																			*
555 *						Internal Import/Export Functions					*
556 *																			*
557 ****************************************************************************/
558 
559 /* Internal versions of the above.  These skip a lot of the explicit
560    checking done by the external versions (e.g. "Is this value really a
561    handle to a valid PKC context?") since they're only called by cryptlib
562    internal functions rather than being passed untrusted user data */
563 
564 CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
iCryptImportKey(IN_BUFFER (encryptedKeyLength)const void * encryptedKey,IN_LENGTH_SHORT const int encryptedKeyLength,IN_ENUM (CRYPT_FORMAT)const CRYPT_FORMAT_TYPE formatType,IN_HANDLE const CRYPT_CONTEXT iImportKey,IN_HANDLE_OPT const CRYPT_CONTEXT iSessionKeyContext,OUT_OPT_HANDLE_OPT CRYPT_CONTEXT * iReturnedContext)565 int iCryptImportKey( IN_BUFFER( encryptedKeyLength ) const void *encryptedKey,
566 					 IN_LENGTH_SHORT const int encryptedKeyLength,
567 					 IN_ENUM( CRYPT_FORMAT ) const CRYPT_FORMAT_TYPE formatType,
568 					 IN_HANDLE const CRYPT_CONTEXT iImportKey,
569 					 IN_HANDLE_OPT const CRYPT_CONTEXT iSessionKeyContext,
570 					 OUT_OPT_HANDLE_OPT CRYPT_CONTEXT *iReturnedContext )
571 	{
572 	const KEYEX_TYPE keyexType = \
573 			( formatType == CRYPT_FORMAT_AUTO || \
574 			  formatType == CRYPT_FORMAT_CRYPTLIB ) ? KEYEX_CRYPTLIB : \
575 			( formatType == CRYPT_FORMAT_PGP ) ? KEYEX_PGP : KEYEX_CMS;
576 	int importAlgo,	status;	/* int vs.enum */
577 
578 	assert( isReadPtr( encryptedKey, encryptedKeyLength ) );
579 	assert( ( formatType == CRYPT_FORMAT_PGP && \
580 			  isWritePtr( iReturnedContext, sizeof( CRYPT_CONTEXT ) ) ) || \
581 			( formatType != CRYPT_FORMAT_PGP && \
582 			  iReturnedContext == NULL ) );
583 
584 	REQUIRES( encryptedKeyLength > MIN_CRYPT_OBJECTSIZE && \
585 			  encryptedKeyLength < MAX_INTLENGTH_SHORT );
586 	REQUIRES( formatType > CRYPT_FORMAT_NONE && \
587 			  formatType < CRYPT_FORMAT_LAST );
588 	REQUIRES( isHandleRangeValid( iImportKey ) );
589 	REQUIRES( ( formatType == CRYPT_FORMAT_PGP && \
590 				iSessionKeyContext == CRYPT_UNUSED ) || \
591 			  ( formatType != CRYPT_FORMAT_PGP && \
592 				isHandleRangeValid( iSessionKeyContext ) ) );
593 	REQUIRES( ( formatType == CRYPT_FORMAT_PGP && \
594 				iReturnedContext != NULL ) || \
595 			  ( formatType != CRYPT_FORMAT_PGP && \
596 				iReturnedContext == NULL ) );
597 
598 	/* Clear return values */
599 	if( iReturnedContext != NULL )
600 		*iReturnedContext = CRYPT_ERROR;
601 
602 	/* Import it as appropriate.  We don't handle key agreement at this
603 	   level since it's a protocol-specific mechanism used by SSH and SSL,
604 	   which are internal-only formats */
605 	status = krnlSendMessage( iImportKey, IMESSAGE_GETATTRIBUTE, &importAlgo,
606 							  CRYPT_CTXINFO_ALGO );
607 	if( cryptStatusError( status ) )
608 		return( status );
609 	if( isConvAlgo( importAlgo ) )
610 		{
611 		if( !isHandleRangeValid( iSessionKeyContext ) )
612 			return( CRYPT_ARGERROR_NUM2 );
613 		return( importConventionalKey( encryptedKey, encryptedKeyLength,
614 									   iSessionKeyContext, iImportKey,
615 									   keyexType ) );
616 		}
617 	return( importPublicKey( encryptedKey, encryptedKeyLength,
618 							 iSessionKeyContext, iImportKey,
619 							 iReturnedContext, keyexType ) );
620 	}
621 
622 CHECK_RETVAL STDC_NONNULL_ARG( ( 3 ) ) \
iCryptExportKey(OUT_BUFFER_OPT (encryptedKeyMaxLength,* encryptedKeyLength)void * encryptedKey,IN_DATALENGTH_Z const int encryptedKeyMaxLength,OUT_DATALENGTH_Z int * encryptedKeyLength,IN_ENUM (CRYPT_FORMAT)const CRYPT_FORMAT_TYPE formatType,IN_HANDLE_OPT const CRYPT_CONTEXT iSessionKeyContext,IN_HANDLE const CRYPT_CONTEXT iExportKey)623 int iCryptExportKey( OUT_BUFFER_OPT( encryptedKeyMaxLength, *encryptedKeyLength ) \
624 						void *encryptedKey,
625 					 IN_DATALENGTH_Z const int encryptedKeyMaxLength,
626 					 OUT_DATALENGTH_Z int *encryptedKeyLength,
627 					 IN_ENUM( CRYPT_FORMAT ) const CRYPT_FORMAT_TYPE formatType,
628 					 IN_HANDLE_OPT const CRYPT_CONTEXT iSessionKeyContext,
629 					 IN_HANDLE const CRYPT_CONTEXT iExportKey )
630 	{
631 	const KEYEX_TYPE keyexType = \
632 			( formatType == CRYPT_FORMAT_CRYPTLIB ) ? KEYEX_CRYPTLIB : \
633 			( formatType == CRYPT_FORMAT_PGP ) ? KEYEX_PGP : KEYEX_CMS;
634 	DYNBUF auxDB;
635 	const int encKeyMaxLength = ( encryptedKey == NULL ) ? \
636 								0 : encryptedKeyMaxLength;
637 	int exportAlgo, status;	/* int vs.enum */
638 
639 	assert( ( encryptedKey == NULL && encryptedKeyMaxLength == 0 ) || \
640 			( encryptedKeyMaxLength >= MIN_CRYPT_OBJECTSIZE && \
641 			  isWritePtr( encryptedKey, encryptedKeyMaxLength ) ) );
642 	assert( isWritePtr( encryptedKeyLength, sizeof( int ) ) );
643 
644 	REQUIRES( ( encryptedKey == NULL && encryptedKeyMaxLength == 0 ) || \
645 			  ( encryptedKeyMaxLength > MIN_CRYPT_OBJECTSIZE && \
646 				encryptedKeyMaxLength < MAX_BUFFER_SIZE ) );
647 	REQUIRES( formatType > CRYPT_FORMAT_NONE && \
648 			  formatType < CRYPT_FORMAT_LAST );
649 	REQUIRES( ( formatType == CRYPT_FORMAT_PGP && \
650 				iSessionKeyContext == CRYPT_UNUSED ) || \
651 			  isHandleRangeValid( iSessionKeyContext ) );
652 	REQUIRES( isHandleRangeValid( iExportKey ) );
653 
654 	ANALYSER_HINT( encryptedKeyLength != NULL );
655 
656 	/* Clear return value */
657 	*encryptedKeyLength = 0;
658 
659 	/* Perform simplified error checking */
660 	status = krnlSendMessage( iExportKey, IMESSAGE_GETATTRIBUTE, &exportAlgo,
661 							  CRYPT_CTXINFO_ALGO );
662 	if( cryptStatusError( status ) )
663 		return( cryptArgError( status ) ? CRYPT_ARGERROR_NUM2 : status );
664 
665 	/* If it's a non-PKC export, pass the call down to the low-level export
666 	   function */
667 	if( !isPkcAlgo( exportAlgo ) )
668 		{
669 		return( exportConventionalKey( encryptedKey, encKeyMaxLength,
670 									   encryptedKeyLength, iSessionKeyContext,
671 									   iExportKey, keyexType ) );
672 		}
673 
674 	REQUIRES( isHandleRangeValid( iSessionKeyContext ) );
675 
676 	/* If it's a non-CMS/SMIME PKC export, pass the call down to the low-
677 	   level export function */
678 	if( formatType != CRYPT_FORMAT_CMS && formatType != CRYPT_FORMAT_SMIME )
679 		{
680 		return( exportPublicKey( encryptedKey, encKeyMaxLength,
681 								 encryptedKeyLength, iSessionKeyContext,
682 								 iExportKey, NULL, 0, keyexType ) );
683 		}
684 
685 	/* We're exporting a key in CMS format we need to obtain recipient
686 	   information from the certificate associated with the export context.
687 	   First we lock the certificate for our exclusive use and in case it's
688 	   a certificate chain select the first certificate in the chain */
689 	status = krnlSendMessage( iExportKey, IMESSAGE_SETATTRIBUTE,
690 							  MESSAGE_VALUE_TRUE, CRYPT_IATTRIBUTE_LOCKED );
691 	if( cryptStatusError( status ) )
692 		return( CRYPT_ERROR_PARAM5 );
693 	status = krnlSendMessage( iExportKey, IMESSAGE_SETATTRIBUTE,
694 							  MESSAGE_VALUE_CURSORFIRST,
695 							  CRYPT_CERTINFO_CURRENT_CERTIFICATE );
696 	if( cryptStatusError( status ) )
697 		{
698 		/* Unlock the chain before we exit.  If this fails there's not much
699 		   that we can do to recover so we don't do anything with the return
700 		   value */
701 		( void ) krnlSendMessage( iExportKey, IMESSAGE_SETATTRIBUTE,
702 								  MESSAGE_VALUE_FALSE,
703 								  CRYPT_IATTRIBUTE_LOCKED );
704 		return( CRYPT_ERROR_PARAM5 );
705 		}
706 
707 	/* Next we get the recipient information from the certificate into a
708 	   dynbuf */
709 	status = dynCreate( &auxDB, iExportKey,
710 						CRYPT_IATTRIBUTE_ISSUERANDSERIALNUMBER );
711 	if( cryptStatusError( status ) )
712 		{
713 		( void ) krnlSendMessage( iExportKey, IMESSAGE_SETATTRIBUTE,
714 								  MESSAGE_VALUE_FALSE,
715 								  CRYPT_IATTRIBUTE_LOCKED );
716 		return( CRYPT_ERROR_PARAM5 );
717 		}
718 
719 	/* We're ready to export the key alongside the key ID as auxiliary
720 	   data */
721 	status = exportPublicKey( encryptedKey, encKeyMaxLength,
722 							  encryptedKeyLength, iSessionKeyContext,
723 							  iExportKey, dynData( auxDB ),
724 							  dynLength( auxDB ), keyexType );
725 
726 	/* Clean up.  If the unlock fails there's not much that we can do to
727 	   recover so we don't do anything with the return value */
728 	( void ) krnlSendMessage( iExportKey, IMESSAGE_SETATTRIBUTE,
729 							  MESSAGE_VALUE_FALSE, CRYPT_IATTRIBUTE_LOCKED );
730 	dynDestroy( &auxDB );
731 
732 	return( status );
733 	}
734 
735 #else
736 
737 /****************************************************************************
738 *																			*
739 *						Stub Functions for non-CMS/PGP Use					*
740 *																			*
741 ****************************************************************************/
742 
cryptImportKeyEx(C_IN void C_PTR encryptedKey,C_IN int encryptedKeyLength,C_IN CRYPT_CONTEXT importKey,C_IN CRYPT_CONTEXT sessionKeyContext,C_OUT CRYPT_CONTEXT C_PTR returnedContext)743 C_RET cryptImportKeyEx( C_IN void C_PTR encryptedKey,
744 						C_IN int encryptedKeyLength,
745 						C_IN CRYPT_CONTEXT importKey,
746 						C_IN CRYPT_CONTEXT sessionKeyContext,
747 						C_OUT CRYPT_CONTEXT C_PTR returnedContext )
748 	{
749 	UNUSED_ARG( encryptedKey );
750 	UNUSED_ARG( returnedContext );
751 
752 	return( CRYPT_ERROR_NOTAVAIL );
753 	}
754 
cryptImportKey(C_IN void C_PTR encryptedKey,C_IN int encryptedKeyLength,C_IN CRYPT_CONTEXT importKey,C_IN CRYPT_CONTEXT sessionKeyContext)755 C_RET cryptImportKey( C_IN void C_PTR encryptedKey,
756 					  C_IN int encryptedKeyLength,
757 					  C_IN CRYPT_CONTEXT importKey,
758 					  C_IN CRYPT_CONTEXT sessionKeyContext )
759 	{
760 	UNUSED_ARG( encryptedKey );
761 
762 	return( CRYPT_ERROR_NOTAVAIL );
763 	}
764 
cryptExportKeyEx(C_OUT_OPT void C_PTR encryptedKey,C_IN int encryptedKeyMaxLength,C_OUT int C_PTR encryptedKeyLength,C_IN CRYPT_FORMAT_TYPE formatType,C_IN CRYPT_HANDLE exportKey,C_IN CRYPT_CONTEXT sessionKeyContext)765 C_RET cryptExportKeyEx( C_OUT_OPT void C_PTR encryptedKey,
766 						C_IN int encryptedKeyMaxLength,
767 						C_OUT int C_PTR encryptedKeyLength,
768 						C_IN CRYPT_FORMAT_TYPE formatType,
769 						C_IN CRYPT_HANDLE exportKey,
770 						C_IN CRYPT_CONTEXT sessionKeyContext )
771 	{
772 	UNUSED_ARG( encryptedKey );
773 	UNUSED_ARG( encryptedKeyLength );
774 
775 	return( CRYPT_ERROR_NOTAVAIL );
776 	}
777 
cryptExportKey(C_OUT_OPT void C_PTR encryptedKey,C_IN int encryptedKeyMaxLength,C_OUT int C_PTR encryptedKeyLength,C_IN CRYPT_HANDLE exportKey,C_IN CRYPT_CONTEXT sessionKeyContext)778 C_RET cryptExportKey( C_OUT_OPT void C_PTR encryptedKey,
779 					  C_IN int encryptedKeyMaxLength,
780 					  C_OUT int C_PTR encryptedKeyLength,
781 					  C_IN CRYPT_HANDLE exportKey,
782 					  C_IN CRYPT_CONTEXT sessionKeyContext )
783 	{
784 	UNUSED_ARG( encryptedKey );
785 	UNUSED_ARG( encryptedKeyLength );
786 
787 	return( CRYPT_ERROR_NOTAVAIL );
788 	}
789 #endif /* USE_INT_CMS */
790