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