cryptlib  3.4.1
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Macros
pkcs15_adpr.c
Go to the documentation of this file.
1 /****************************************************************************
2 * *
3 * cryptlib PKCS #15 Private-key Add Interface *
4 * Copyright Peter Gutmann 1996-2009 *
5 * *
6 ****************************************************************************/
7 
8 #if defined( INC_ALL )
9  #include "crypt.h"
10  #include "asn1.h"
11  #include "asn1_ext.h"
12  #include "keyset.h"
13  #include "pkcs15.h"
14 #else
15  #include "crypt.h"
16  #include "enc_dec/asn1.h"
17  #include "enc_dec/asn1_ext.h"
18  #include "keyset/keyset.h"
19  #include "keyset/pkcs15.h"
20 #endif /* Compiler-specific includes */
21 
22 #ifdef USE_PKCS15
23 
24 /* Define the following to write the pre-PKCS #15 v1.2 exponent-size value
25  for RSA private keys */
26 
27 /* #define USE_RSA_EXTRAPARAM */
28 
29 /* The minimum number of keying iterations to use when deriving a key wrap
30  key from a password */
31 
32 #ifdef CONFIG_SLOW_CPU
33  #define MIN_KEYING_ITERATIONS 800
34 #else
35  #define MIN_KEYING_ITERATIONS 5000
36 #endif /* CONFIG_SLOW_CPU */
37 
38 /****************************************************************************
39 * *
40 * Utility Functions *
41 * *
42 ****************************************************************************/
43 
44 /* Replace existing private-key data with updated information */
45 
46 STDC_NONNULL_ARG( ( 1, 2 ) ) \
47 static void replacePrivkeyData( INOUT PKCS15_INFO *pkcs15infoPtr,
49  const void *newPrivKeyData,
50  IN_LENGTH_SHORT_MIN( 16 ) \
51  const int newPrivKeyDataSize,
52  IN_LENGTH_SHORT const int newPrivKeyOffset )
53  {
54  assert( isWritePtr( pkcs15infoPtr, sizeof( PKCS15_INFO ) ) );
55  assert( isReadPtr( newPrivKeyData, newPrivKeyDataSize ) );
56 
57  REQUIRES_V( newPrivKeyDataSize >= 16 && \
58  newPrivKeyDataSize < MAX_INTLENGTH_SHORT );
59  REQUIRES_V( newPrivKeyOffset > 0 && \
60  newPrivKeyOffset < newPrivKeyDataSize && \
61  newPrivKeyOffset < MAX_INTLENGTH_SHORT );
62 
63  /* If we've allocated new storage for the data rather than directly
64  replacing the existing entry, free the existing one and replace it
65  with the new one */
66  if( newPrivKeyData != pkcs15infoPtr->privKeyData )
67  {
68  if( pkcs15infoPtr->privKeyData != NULL )
69  {
70  zeroise( pkcs15infoPtr->privKeyData,
71  pkcs15infoPtr->privKeyDataSize );
72  clFree( "replacePrivkeyData", pkcs15infoPtr->privKeyData );
73  }
74  pkcs15infoPtr->privKeyData = ( void * ) newPrivKeyData;
75  }
76 
77  /* Update the size information */
78  pkcs15infoPtr->privKeyDataSize = newPrivKeyDataSize;
79  pkcs15infoPtr->privKeyOffset = newPrivKeyOffset;
80  }
81 
82 /* Calculate the size of and if necessary allocate storage for private-key
83  data. This function has to be accessible externally because adding or
84  changing a certificate for a private key can change the private-key
85  attributes */
86 
87 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
88 int calculatePrivkeyStorage( OUT_BUFFER_ALLOC_OPT( *newPrivKeyDataSize ) \
89  void **newPrivKeyDataPtr,
90  OUT_LENGTH_SHORT_Z int *newPrivKeyDataSize,
92  const void *origPrivKeyData,
94  IN_LENGTH_SHORT const int privKeySize,
96  IN_LENGTH_SHORT const int extraDataSize )
97  {
98  void *newPrivKeyData;
99 
100  assert( isWritePtr( newPrivKeyDataPtr, sizeof( void * ) ) );
101  assert( isWritePtr( newPrivKeyDataSize, sizeof( int ) ) );
102  assert( ( origPrivKeyData == NULL && origPrivKeyDataSize == 0 ) || \
103  isReadPtr( origPrivKeyData, origPrivKeyDataSize ) );
104 
105  REQUIRES( ( origPrivKeyData == NULL && origPrivKeyDataSize == 0 ) || \
106  ( origPrivKeyData != NULL && origPrivKeyDataSize > 0 && \
107  origPrivKeyDataSize < MAX_INTLENGTH_SHORT ) );
108  REQUIRES( privKeySize > 0 && privKeySize < MAX_INTLENGTH_SHORT );
109  REQUIRES( privKeyAttributeSize > 0 && \
110  privKeyAttributeSize < MAX_INTLENGTH_SHORT );
111  REQUIRES( extraDataSize >= 0 && extraDataSize < MAX_INTLENGTH_SHORT );
112 
113  /* Clear return values */
114  *newPrivKeyDataPtr = NULL;
115  *newPrivKeyDataSize = 0;
116 
117  /* Calculate the new private-key data size */
118  *newPrivKeyDataSize = sizeofObject( privKeyAttributeSize + \
119  sizeofObject( \
120  sizeofObject( privKeySize ) + \
121  extraDataSize ) );
122  ENSURES( *newPrivKeyDataSize > 0 && \
123  *newPrivKeyDataSize < MAX_INTLENGTH );
124 
125  /* If the new data will fit into the existing storage, we're done */
126  if( *newPrivKeyDataSize <= origPrivKeyDataSize )
127  {
128  *newPrivKeyDataPtr = ( void * ) origPrivKeyData;
129 
130  return( CRYPT_OK );
131  }
132 
133  /* Allocate storage for the new data */
134  newPrivKeyData = clAlloc( "calculatePrivkeyStorage", *newPrivKeyDataSize );
135  if( newPrivKeyData == NULL )
136  return( CRYPT_ERROR_MEMORY );
137  *newPrivKeyDataPtr = newPrivKeyData;
138 
139  return( CRYPT_OK );
140  }
141 
142 /* Update the private-key attributes while leaving the private key itself
143  untouched. This is necessary after updating a certificate associated
144  with a private key, which can affect the key's attributes */
145 
146 STDC_NONNULL_ARG( ( 1, 2, 4 ) ) \
147 void updatePrivKeyAttributes( INOUT PKCS15_INFO *pkcs15infoPtr,
148  OUT_BUFFER_FIXED( newPrivKeyDataSize ) \
149  void *newPrivKeyData,
150  IN_LENGTH_SHORT_MIN( 16 ) \
151  const int newPrivKeyDataSize,
152  IN_BUFFER( privKeyAttributeSize ) \
153  const void *privKeyAttributes,
154  IN_LENGTH_SHORT const int privKeyAttributeSize,
155  IN_LENGTH_SHORT const int privKeyInfoSize,
156  IN_TAG const int keyTypeTag )
157  {
158  STREAM stream;
159  BYTE keyBuffer[ MAX_PRIVATE_KEYSIZE + 8 ];
160  int newPrivKeyOffset = DUMMY_INIT, status;
161 
162  assert( isWritePtr( pkcs15infoPtr, sizeof( PKCS15_INFO ) ) );
163  assert( isWritePtr( newPrivKeyData, newPrivKeyDataSize ) );
164  assert( isReadPtr( privKeyAttributes, privKeyAttributeSize ) );
165 
166  REQUIRES_V( newPrivKeyDataSize >= 16 && \
167  newPrivKeyDataSize < MAX_INTLENGTH_SHORT );
168  REQUIRES_V( privKeyAttributeSize > 0 && \
169  privKeyAttributeSize < MAX_INTLENGTH_SHORT );
170  REQUIRES_V( privKeyInfoSize > 0 && \
171  privKeyInfoSize < MAX_PRIVATE_KEYSIZE );
172  REQUIRES_V( keyTypeTag == DEFAULT_TAG || \
173  ( keyTypeTag >= 0 && keyTypeTag < MAX_TAG_VALUE ) );
174 
175  /* Since we may be doing an in-place update of the private-key
176  information we copy the wrapped key data out to a temporary buffer
177  while we make the changes */
178  ENSURES_V( rangeCheck( pkcs15infoPtr->privKeyOffset, privKeyInfoSize,
179  pkcs15infoPtr->privKeyDataSize ) );
180  memcpy( keyBuffer, ( BYTE * ) pkcs15infoPtr->privKeyData +
181  pkcs15infoPtr->privKeyOffset,
182  privKeyInfoSize );
183 
184  /* The corresponding key is already present, we need to update the key
185  attributes since adding the certificate may have changed them. The
186  key data itself is unchanged so we just memcpy() it across verbatim */
187  sMemOpen( &stream, newPrivKeyData, newPrivKeyDataSize );
188  writeConstructed( &stream, privKeyAttributeSize + \
189  sizeofObject( \
190  sizeofObject( privKeyInfoSize ) ),
191  keyTypeTag );
192  swrite( &stream, privKeyAttributes, privKeyAttributeSize );
193  writeConstructed( &stream, ( int ) sizeofObject( privKeyInfoSize ),
195  status = writeSequence( &stream, privKeyInfoSize );
196  if( cryptStatusOK( status ) )
197  {
198  newPrivKeyOffset = stell( &stream );
199  status = swrite( &stream, keyBuffer, privKeyInfoSize );
200  }
201  sMemDisconnect( &stream );
202  zeroise( keyBuffer, MAX_PRIVATE_KEYSIZE );
203  ENSURES_V( cryptStatusOK( status ) && \
204  !cryptStatusError( checkObjectEncoding( newPrivKeyData, \
205  newPrivKeyDataSize ) ) );
206 
207  /* Replace the old data with the newly-written data */
208  replacePrivkeyData( pkcs15infoPtr, newPrivKeyData, newPrivKeyDataSize,
209  newPrivKeyOffset );
210  }
211 
212 /****************************************************************************
213 * *
214 * Encryption Context Management *
215 * *
216 ****************************************************************************/
217 
218 /* Create a strong encryption/MAC context to protect a private key */
219 
221 static int createStrongAlgorithmContext( OUT_HANDLE_OPT \
224  IN_HANDLE_OPT \
225  const CRYPT_CONTEXT iMasterKeyContext,
226  const BOOLEAN isCryptContext )
227  {
228  CRYPT_CONTEXT iLocalContext;
229  MESSAGE_CREATEOBJECT_INFO createInfo;
231  int algorithm, status;
232 
233  assert( isWritePtr( iCryptContext, sizeof( CRYPT_CONTEXT ) ) );
234 
235  REQUIRES( iCryptOwner == DEFAULTUSER_OBJECT_HANDLE || \
236  isHandleRangeValid( iCryptOwner ) );
237  REQUIRES( iMasterKeyContext == CRYPT_UNUSED || \
238  isHandleRangeValid( iMasterKeyContext ) );
239 
240  /* Clear return value */
241  *iCryptContext = CRYPT_ERROR;
242 
243  /* In the interests of luser-proofing we're rather paranoid and force
244  the use of non-weak algorithms and modes of operation. In addition
245  since OIDs are only defined for a limited subset of algorithms we
246  also default to a guaranteed available algorithm if no OID is defined
247  for the algorithm that was requested */
248  if( isCryptContext )
249  {
250  status = krnlSendMessage( iCryptOwner, IMESSAGE_GETATTRIBUTE,
251  &algorithm, CRYPT_OPTION_ENCR_ALGO );
252  if( cryptStatusError( status ) || isWeakCryptAlgo( algorithm ) || \
253  cryptStatusError( sizeofAlgoIDex( algorithm, CRYPT_MODE_CBC, 0 ) ) )
254  algorithm = CRYPT_ALGO_3DES;
255  }
256  else
257  {
258  status = krnlSendMessage( iCryptOwner, IMESSAGE_GETATTRIBUTE,
259  &algorithm, CRYPT_OPTION_ENCR_MAC );
260  if( cryptStatusError( status ) || isWeakMacAlgo( algorithm ) || \
261  cryptStatusError( sizeofAlgoID( algorithm ) ) )
262  algorithm = CRYPT_ALGO_HMAC_SHA1;
263  }
264 
265  /* Create the context */
266  setMessageCreateObjectInfo( &createInfo, algorithm );
268  &createInfo, OBJECT_TYPE_CONTEXT );
269  if( cryptStatusError( status ) )
270  return( status );
271  iLocalContext = createInfo.cryptHandle;
272 
273  /* Perform any additional initialisation that may be required. In
274  particular we need to generate an IV at this point so that it can be
275  copied to the generic-secret context */
276  if( isCryptContext )
277  {
278  status = krnlSendNotifier( iLocalContext, IMESSAGE_CTX_GENIV );
279  if( cryptStatusError( status ) )
280  {
281  krnlSendNotifier( iLocalContext, IMESSAGE_DECREFCOUNT );
282  return( status );
283  }
284  }
285 
286  /* If we're using standard encryption, we're done */
287  if( iMasterKeyContext == CRYPT_UNUSED )
288  {
289  *iCryptContext = iLocalContext;
290 
291  return( CRYPT_OK );
292  }
293 
294  /* Derive the key for the context from the generic-secret context */
295  if( isCryptContext )
296  {
297  setMechanismKDFInfo( &mechanismInfo, iLocalContext,
298  iMasterKeyContext, CRYPT_ALGO_HMAC_SHA1,
299  "encryption", 10 );
300  }
301  else
302  {
303  setMechanismKDFInfo( &mechanismInfo, iLocalContext,
304  iMasterKeyContext, CRYPT_ALGO_HMAC_SHA1,
305  "authentication", 14 );
306  }
308  &mechanismInfo, MECHANISM_DERIVE_PKCS5 );
309  if( cryptStatusError( status ) )
310  {
311  krnlSendNotifier( iLocalContext, IMESSAGE_DECREFCOUNT );
312  return( status );
313  }
314  *iCryptContext = iLocalContext;
315 
316  return( CRYPT_OK );
317  }
318 
319 /* Send algorithm parameters from the encryption or MAC context to the
320  generic-secret context */
321 
322 CHECK_RETVAL \
323 static int setAlgoParams( IN_HANDLE const CRYPT_CONTEXT iGenericSecret,
324  IN_HANDLE const CRYPT_CONTEXT iCryptContext,
326  {
328  STREAM stream;
329  BYTE algorithmParamData[ CRYPT_MAX_TEXTSIZE + 8 ];
330  int algorithmParamDataSize = DUMMY_INIT, status;
331 
332  REQUIRES( isHandleRangeValid( iGenericSecret ) );
333  REQUIRES( isHandleRangeValid( iCryptContext ) );
334  REQUIRES( attribute == CRYPT_IATTRIBUTE_ENCPARAMS || \
335  attribute == CRYPT_IATTRIBUTE_MACPARAMS );
336 
337  /* Get the algorithm parameter data from the encryption or MAC
338  context */
339  sMemOpen( &stream, algorithmParamData, CRYPT_MAX_TEXTSIZE );
340  if( attribute == CRYPT_IATTRIBUTE_ENCPARAMS )
341  status = writeCryptContextAlgoID( &stream, iCryptContext );
342  else
343  status = writeContextAlgoID( &stream, iCryptContext,
344  CRYPT_ALGO_NONE );
345  if( cryptStatusOK( status ) )
346  algorithmParamDataSize = stell( &stream );
347  sMemDisconnect( &stream );
348  if( cryptStatusError( status ) )
349  return( status );
350 
351  /* Send the encoded parameter information to the generic-secret
352  context */
353  setMessageData( &msgData, algorithmParamData, algorithmParamDataSize );
354  return( krnlSendMessage( iGenericSecret, IMESSAGE_SETATTRIBUTE_S,
355  &msgData, attribute ) );
356  }
357 
358 /* Create encryption contexts to protect the private key data */
359 
360 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2, 3 ) ) \
361 static int createContexts( OUT_HANDLE_OPT CRYPT_CONTEXT *iGenericSecret,
362  OUT_HANDLE_OPT CRYPT_CONTEXT *iCryptContext,
363  OUT_HANDLE_OPT CRYPT_CONTEXT *iMacContext,
364  IN_HANDLE const CRYPT_HANDLE iCryptOwner )
365  {
366  MESSAGE_CREATEOBJECT_INFO createInfo;
367  int status;
368 
369  assert( isWritePtr( iGenericSecret, sizeof( CRYPT_CONTEXT ) ) );
370  assert( isWritePtr( iCryptContext, sizeof( CRYPT_CONTEXT ) ) );
371  assert( isWritePtr( iMacContext, sizeof( CRYPT_CONTEXT ) ) );
372 
373  REQUIRES( iCryptOwner == DEFAULTUSER_OBJECT_HANDLE || \
374  isHandleRangeValid( iCryptOwner ) );
375 
376  /* Clear return values */
377  *iGenericSecret = *iCryptContext = *iMacContext = CRYPT_ERROR;
378 
379  /* Create the generic-secret context from which the encryption and MAC
380  keys will be derived and generate a key into it */
381  setMessageCreateObjectInfo( &createInfo, CRYPT_IALGO_GENERIC_SECRET );
383  IMESSAGE_DEV_CREATEOBJECT, &createInfo,
385  if( cryptStatusError( status ) )
386  return( status );
387  *iGenericSecret = createInfo.cryptHandle;
388  status = krnlSendNotifier( *iGenericSecret, IMESSAGE_CTX_GENKEY );
389  if( cryptStatusError( status ) )
390  {
391  krnlSendNotifier( *iGenericSecret, IMESSAGE_DECREFCOUNT );
392  return( status );
393  }
394 
395  /* Create the encryption and MAC contexts and derive the keys for them
396  from the generic-secret context */
397  status = createStrongAlgorithmContext( iCryptContext, iCryptOwner,
398  *iGenericSecret, TRUE );
399  if( cryptStatusError( status ) )
400  {
401  krnlSendNotifier( *iGenericSecret, IMESSAGE_DECREFCOUNT );
402  return( status );
403  }
404  status = createStrongAlgorithmContext( iMacContext, iCryptOwner,
405  *iGenericSecret, FALSE );
406  if( cryptStatusError( status ) )
407  {
408  krnlSendNotifier( *iGenericSecret, IMESSAGE_DECREFCOUNT );
409  krnlSendNotifier( *iCryptContext, IMESSAGE_DECREFCOUNT );
410  return( status );
411  }
412 
413  /* Send the algorithm parameters for the encryption and MAC contexts to
414  the generic-secret context */
415  status = setAlgoParams( *iGenericSecret, *iCryptContext,
416  CRYPT_IATTRIBUTE_ENCPARAMS );
417  if( cryptStatusOK( status ) )
418  status = setAlgoParams( *iGenericSecret, *iMacContext,
419  CRYPT_IATTRIBUTE_MACPARAMS );
420  if( cryptStatusError( status ) )
421  {
422  krnlSendNotifier( *iGenericSecret, IMESSAGE_DECREFCOUNT );
423  krnlSendNotifier( *iCryptContext, IMESSAGE_DECREFCOUNT );
424  krnlSendNotifier( *iMacContext, IMESSAGE_DECREFCOUNT );
425  }
426 
427  return( CRYPT_OK );
428  }
429 
430 /****************************************************************************
431 * *
432 * Private-key Wrap Routines *
433 * *
434 ****************************************************************************/
435 
436 /* Write a wrapped content-protection key in the form
437  SET OF { [ 0 ] (EncryptedKey) } */
438 
439 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 4 ) ) \
440 static int writeWrappedSessionKey( INOUT STREAM *stream,
441  IN_HANDLE \
443  IN_HANDLE const CRYPT_USER iCryptOwner,
445  const char *password,
446  IN_LENGTH_NAME const int passwordLength )
447  {
449  int iterations, exportedKeySize, status;
450 
451  assert( isWritePtr( stream, sizeof( STREAM ) ) );
452  assert( isReadPtr( password, passwordLength ) );
453 
454  REQUIRES( isHandleRangeValid( iSessionKeyContext ) );
455  REQUIRES( iCryptOwner == DEFAULTUSER_OBJECT_HANDLE || \
456  isHandleRangeValid( iCryptOwner ) );
457  REQUIRES( passwordLength >= MIN_NAME_LENGTH && \
458  passwordLength < MAX_ATTRIBUTE_SIZE );
459 
460  /* In the interests of luser-proofing we force the use of a safe minimum
461  number of iterations */
462  status = krnlSendMessage( iCryptOwner, IMESSAGE_GETATTRIBUTE, &iterations,
464  if( cryptStatusError( status ) || iterations < MIN_KEYING_ITERATIONS )
465  iterations = MIN_KEYING_ITERATIONS;
466 
467  /* Create an encryption context and derive the user password into it */
468  status = createStrongAlgorithmContext( &iCryptContext, iCryptOwner,
469  CRYPT_UNUSED, TRUE );
470  if( cryptStatusError( status ) )
471  return( status );
472  status = krnlSendMessage( iCryptContext, IMESSAGE_SETATTRIBUTE,
473  &iterations, CRYPT_CTXINFO_KEYING_ITERATIONS );
474  if( cryptStatusOK( status ) )
475  {
477 
478  setMessageData( &msgData, ( MESSAGE_CAST ) password,
479  passwordLength );
480  status = krnlSendMessage( iCryptContext, IMESSAGE_SETATTRIBUTE_S,
481  &msgData, CRYPT_CTXINFO_KEYING_VALUE );
482  }
483  if( cryptStatusError( status ) )
484  {
485  krnlSendNotifier( iCryptContext, IMESSAGE_DECREFCOUNT );
486  return( status );
487  }
488 
489  /* Determine the size of the exported key and write the encrypted data
490  content field */
491  status = iCryptExportKey( NULL, 0, &exportedKeySize, CRYPT_FORMAT_CMS,
492  iSessionKeyContext, iCryptContext );
493  if( cryptStatusOK( status ) )
494  {
495  void *dataPtr;
496  int length;
497 
498  writeSet( stream, exportedKeySize );
499  status = sMemGetDataBlockRemaining( stream, &dataPtr, &length );
500  if( cryptStatusOK( status ) )
501  {
502  status = iCryptExportKey( dataPtr, length, &exportedKeySize,
503  CRYPT_FORMAT_CMS, iSessionKeyContext,
504  iCryptContext );
505  }
506  if( cryptStatusOK( status ) )
507  status = sSkip( stream, exportedKeySize );
508  }
509 
510  /* Clean up */
511  krnlSendNotifier( iCryptContext, IMESSAGE_DECREFCOUNT );
512  return( status );
513  }
514 
515 /* Write the private key wrapped using the session key */
516 
517 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 3 ) ) \
518 static int writeWrappedPrivateKey( OUT_BUFFER( wrappedKeyMaxLength, \
519  *wrappedKeyLength ) \
520  void *wrappedKey,
521  IN_LENGTH_SHORT_MIN( 16 ) \
522  const int wrappedKeyMaxLength,
523  OUT_LENGTH_SHORT_Z int *wrappedKeyLength,
525  IN_HANDLE const CRYPT_CONTEXT iCryptContext,
526  IN_HANDLE_OPT const CRYPT_CONTEXT iMacContext,
528  {
530  STREAM encDataStream;
531  int length = DUMMY_INIT, status;
532 
533  assert( isWritePtr( wrappedKey, wrappedKeyMaxLength ) );
534  assert( isWritePtr( wrappedKeyLength, sizeof( int ) ) );
535 
536  REQUIRES( wrappedKeyMaxLength >= 16 && \
537  wrappedKeyMaxLength < MAX_INTLENGTH_SHORT );
538  REQUIRES( isHandleRangeValid( iPrivKeyContext ) );
539  REQUIRES( isHandleRangeValid( iCryptContext ) );
540  REQUIRES( iMacContext == CRYPT_UNUSED || \
541  isHandleRangeValid( iMacContext ) );
542  REQUIRES( isPkcAlgo( pkcAlgo ) );
543 
544  /* Clear return values */
545  memset( wrappedKey, 0, min( 16, wrappedKeyMaxLength ) );
546  *wrappedKeyLength = 0;
547 
548  /* Export the wrapped private key */
549  setMechanismWrapInfo( &mechanismInfo, wrappedKey, wrappedKeyMaxLength,
550  NULL, 0, iPrivKeyContext, iCryptContext );
552  &mechanismInfo, MECHANISM_PRIVATEKEYWRAP );
553  if( cryptStatusOK( status ) )
554  length = mechanismInfo.wrappedDataLength;
555  clearMechanismInfo( &mechanismInfo );
556  if( cryptStatusError( status ) )
557  return( status );
558  *wrappedKeyLength = length;
559 
560  /* MAC the wrapped key data if necessary */
561  if( iMacContext != CRYPT_UNUSED )
562  {
563  status = krnlSendMessage( iMacContext, IMESSAGE_CTX_HASH,
564  wrappedKey, length );
565  if( cryptStatusOK( status ) )
566  status = krnlSendMessage( iMacContext, IMESSAGE_CTX_HASH, "", 0 );
567  if( cryptStatusError( status ) )
568  return( status );
569  }
570 
571  /* Try and check that the wrapped key data no longer contains
572  identifiable structured data. We can only do this for RSA keys
573  because the amount of information present for DLP keys (a single
574  short integer) is too small to reliably check. This check is
575  performed in addition to checks already performed by the encryption
576  code and the key wrap code */
577  if( pkcAlgo != CRYPT_ALGO_RSA )
578  return( CRYPT_OK );
579 
580  /* For RSA keys the data would be:
581 
582  SEQUENCE {
583  [3] INTEGER,
584  ...
585  }
586 
587  99.9% of all wrapped keys will fail the initial valid-SEQUENCE check
588  so we provide an early-out for it */
589  sMemConnect( &encDataStream, wrappedKey, *wrappedKeyLength );
590  status = readSequence( &encDataStream, &length );
591  if( cryptStatusError( status ) )
592  {
593  sMemDisconnect( &encDataStream );
594  return( CRYPT_OK );
595  }
596 
597  /* The data must contain at least p and q, or at most all key
598  components */
599  if( length < MIN_PKCSIZE * 2 || length > MAX_PRIVATE_KEYSIZE )
600  {
601  sMemDisconnect( &encDataStream );
602  return( CRYPT_OK );
603  }
604 
605  /* The first key component is p, encoded as '[3] INTEGER' */
606  status = readIntegerTag( &encDataStream, NULL, CRYPT_MAX_PKCSIZE,
607  &length, 3 );
608  if( cryptStatusOK( status ) && \
609  ( length < MIN_PKCSIZE || length > CRYPT_MAX_PKCSIZE ) )
610  status = CRYPT_ERROR;
611  sMemDisconnect( &encDataStream );
612  if( cryptStatusError( status ) )
613  return( CRYPT_OK );
614 
615  /* We appear to have plaintext data still present in the buffer, clear
616  it and warn the user */
617  zeroise( wrappedKey, wrappedKeyMaxLength );
618  DEBUG_DIAG(( "Private key data wasn't encrypted" ));
619  assert( DEBUG_WARN );
620  return( CRYPT_ERROR_FAILED );
621  }
622 
623 /****************************************************************************
624 * *
625 * Add a Private Key *
626 * *
627 ****************************************************************************/
628 
629 /* A structure to store various parameters needed when writing a private
630  key */
631 
632 typedef struct {
633  /* Encryption contexts used to secure the private key */
634  CRYPT_CONTEXT iGenericContext, iCryptContext, iMacContext;
635 
636  /* The encoded private-key attributes */
637  BUFFER_FIXED( privKeyAttributeSize ) \
638  const void *privKeyAttributes;
640 
641  /* Miscellaneous information */
642  CRYPT_ALGO_TYPE pkcCryptAlgo; /* Private-key algorithm */
643 #ifdef USE_RSA_EXTRAPARAM
644  int modulusSize; /* Optional parameter for RSA keys */
645 #endif /* USE_RSA_EXTRAPARAM */
646  int keyTypeTag; /* ASN.1 tag for encoding key data */
647  } PRIVKEY_WRITE_PARAMS;
648 
649 #define initPrivKeyParams( params, genericCtx, cryptCtx, macCtx, keyAttr, keyAttrSize, pkcAlgo, modSize, keyTag ) \
650  memset( params, 0, sizeof( PRIVKEY_WRITE_PARAMS ) ); \
651  ( params )->iGenericContext = genericCtx; \
652  ( params )->iCryptContext = cryptCtx; \
653  ( params )->iMacContext = macCtx; \
654  ( params )->privKeyAttributes = keyAttr; \
655  ( params )->privKeyAttributeSize = keyAttrSize; \
656  ( params )->pkcCryptAlgo = pkcAlgo; \
657  ( params )->keyTypeTag = keyTag;
658 #ifdef USE_RSA_EXTRAPARAM
659  ( params )->modulusSize = modSize;
660 #endif /* USE_RSA_EXTRAPARAM */
661 
662 /* Add private-key metadata to a PKCS #15 storage object using a simplified
663  version if the code in the standard writePrivateKey() to store just the
664  attributes and the external storage reference */
665 
666 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 3 ) ) \
667 static int addPrivateKeyMetadata( INOUT PKCS15_INFO *pkcs15infoPtr,
668  IN_HANDLE const CRYPT_HANDLE iCryptContext,
669  const PRIVKEY_WRITE_PARAMS *privKeyParams )
670  {
671  STREAM stream;
673  BYTE storageID[ KEYID_SIZE + 8 ];
674  void *newPrivKeyData = pkcs15infoPtr->privKeyData;
675  const int privKeySize = sizeofObject( KEYID_SIZE );
676  int newPrivKeyDataSize, newPrivKeyOffset = DUMMY_INIT;
677  int extraDataSize = 0;
678  int status;
679 
680  assert( isWritePtr( pkcs15infoPtr, sizeof( PKCS15_INFO ) ) );
681  assert( isReadPtr( privKeyParams, sizeof( PRIVKEY_WRITE_PARAMS ) ) );
682 
683  REQUIRES( isHandleRangeValid( iCryptContext ) );
684 
685  REQUIRES( privKeyParams->iGenericContext == CRYPT_UNUSED && \
686  privKeyParams->iCryptContext == CRYPT_UNUSED && \
687  privKeyParams->iMacContext == CRYPT_UNUSED );
688  REQUIRES( privKeyParams->privKeyAttributeSize > 0 && \
689  privKeyParams->privKeyAttributeSize < MAX_INTLENGTH_SHORT );
690  REQUIRES( isPkcAlgo( privKeyParams->pkcCryptAlgo ) );
691 #ifdef USE_RSA_EXTRAPARAM
692  REQUIRES( ( isEccAlgo( privKeyParams->pkcCryptAlgo ) && \
693  privKeyParams->modulusSize >= MIN_PKCSIZE_ECC && \
694  privKeyParams->modulusSize <= CRYPT_MAX_PKCSIZE_ECC ) || \
695  ( !isEccAlgo( privKeyParams->pkcCryptAlgo ) && \
696  privKeyParams->modulusSize >= MIN_PKCSIZE && \
697  privKeyParams->modulusSize <= CRYPT_MAX_PKCSIZE ) );
698 #endif /* USE_RSA_EXTRAPARAM */
699  REQUIRES( privKeyParams->keyTypeTag == DEFAULT_TAG || \
700  ( privKeyParams->keyTypeTag >= 0 && \
701  privKeyParams->keyTypeTag < MAX_TAG_VALUE ) );
702 
703  /* Get the storage ID used to link the metadata to the actual keying
704  data held in external hardware */
705  setMessageData( &msgData, storageID, KEYID_SIZE );
706  status = krnlSendMessage( iCryptContext, IMESSAGE_GETATTRIBUTE_S,
707  &msgData, CRYPT_IATTRIBUTE_DEVICESTORAGEID );
708  if( cryptStatusError( status ) )
709  return( status );
710 
711  /* Calculate the private-key storage size */
712 #ifdef USE_RSA_EXTRAPARAM
713  if( privKeyParams->pkcCryptAlgo == CRYPT_ALGO_RSA )
714  {
715  /* RSA keys have an extra element for PKCS #11 compatibility */
716  extraDataSize = sizeofShortInteger( \
717  bytesToBits( privKeyParams->modulusSize ) );
718  }
719 #endif /* USE_RSA_EXTRAPARAM */
720  status = calculatePrivkeyStorage( &newPrivKeyData, &newPrivKeyDataSize,
721  pkcs15infoPtr->privKeyData,
722  pkcs15infoPtr->privKeyDataSize,
723  sizeofObject( privKeySize ),
724  privKeyParams->privKeyAttributeSize,
725  extraDataSize );
726  if( cryptStatusError( status ) )
727  return( status );
728 
729  sMemOpen( &stream, newPrivKeyData, newPrivKeyDataSize );
730 
731  /* Write the outer header, attributes, and storage reference */
732  writeConstructed( &stream, privKeyParams->privKeyAttributeSize + \
733  sizeofObject( \
734  sizeofObject( \
735  sizeofObject( privKeySize ) + \
736  extraDataSize ) ),
737  privKeyParams->keyTypeTag );
738  swrite( &stream, privKeyParams->privKeyAttributes,
739  privKeyParams->privKeyAttributeSize );
740  writeConstructed( &stream,
741  sizeofObject( \
742  sizeofObject( privKeySize + extraDataSize ) ),
744  status = writeSequence( &stream,
745  sizeofObject( privKeySize + extraDataSize ) );
746  if( cryptStatusOK( status ) )
747  newPrivKeyOffset = stell( &stream );
748  if( cryptStatusOK( status ) )
749  {
750  writeSequence( &stream, privKeySize );
751  status = writeOctetString( &stream, storageID, KEYID_SIZE,
752  DEFAULT_TAG );
753 #ifdef USE_RSA_EXTRAPARAM
754  if( cryptStatusOK( status ) && \
755  privKeyParams->pkcCryptAlgo == CRYPT_ALGO_RSA )
756  {
757  /* RSA keys have an extra element for PKCS #11 compability that
758  we need to kludge onto the end of the private-key data */
759  status = writeShortInteger( &stream,
760  bytesToBits( privKeyParams->modulusSize ),
761  DEFAULT_TAG );
762  }
763 #endif /* USE_RSA_EXTRAPARAM */
764  }
765  if( cryptStatusError( status ) )
766  {
767  sMemClose( &stream );
768  return( status );
769  }
770  assert( newPrivKeyDataSize == stell( &stream ) );
771  sMemDisconnect( &stream );
772  ENSURES( !cryptStatusError( checkObjectEncoding( newPrivKeyData, \
773  newPrivKeyDataSize ) ) );
774 
775  /* Replace the old data with the newly-written data */
776  replacePrivkeyData( pkcs15infoPtr, newPrivKeyData,
777  newPrivKeyDataSize, newPrivKeyOffset );
778  return( CRYPT_OK );
779  }
780 
781 /* Write an encrypted and MACd private key */
782 
783 CHECK_RETVAL STDC_NONNULL_ARG( ( 3, 5, 8, 9, 10, 11 ) ) \
784 static int writePrivateKey( IN_HANDLE const CRYPT_HANDLE iPrivKeyContext,
785  IN_HANDLE const CRYPT_HANDLE iCryptOwner,
786  IN_BUFFER( passwordLength ) const char *password,
787  IN_LENGTH_NAME const int passwordLength,
788  const PRIVKEY_WRITE_PARAMS *privKeyParams,
789  IN_BUFFER_OPT( origPrivKeyDataSize ) \
790  const void *origPrivKeyData,
791  IN_LENGTH_SHORT_Z const int origPrivKeyDataSize,
792  OUT_BUFFER_ALLOC_OPT( *newPrivKeyDataSize ) \
793  void **newPrivKeyData,
794  OUT_LENGTH_Z int *newPrivKeyDataSize,
795  OUT_LENGTH_Z int *newPrivKeyOffset,
797  {
800  STREAM stream;
801  BYTE envelopeHeaderBuffer[ 256 + 8 ], macValue[ CRYPT_MAX_HASHSIZE + 8 ];
802  void *encryptedKeyDataPtr, *macDataPtr = DUMMY_INIT_PTR;
803  int privKeySize = DUMMY_INIT, extraDataSize = 0, macSize;
804  int envelopeHeaderSize, envelopeContentSize;
805  int macDataOffset = DUMMY_INIT, macDataLength = DUMMY_INIT;
806  int encryptedKeyDataLength, status;
807 
808  assert( isReadPtr( password, passwordLength ) );
809  assert( isReadPtr( privKeyParams, sizeof( PRIVKEY_WRITE_PARAMS ) ) );
810  assert( ( origPrivKeyData == NULL && origPrivKeyDataSize == 0 ) || \
811  isReadPtr( origPrivKeyData, origPrivKeyDataSize ) );
812  assert( isWritePtr( newPrivKeyData, sizeof( void * ) ) );
813  assert( isWritePtr( newPrivKeyDataSize, sizeof( int ) ) );
814  assert( isWritePtr( newPrivKeyOffset, sizeof( int ) ) );
815 
816  REQUIRES( isHandleRangeValid( iPrivKeyContext ) );
817  REQUIRES( iCryptOwner == DEFAULTUSER_OBJECT_HANDLE || \
818  isHandleRangeValid( iCryptOwner ) );
819  REQUIRES( passwordLength >= MIN_NAME_LENGTH && \
820  passwordLength < MAX_ATTRIBUTE_SIZE );
821  REQUIRES( ( origPrivKeyData == NULL && origPrivKeyDataSize == 0 ) || \
822  ( origPrivKeyData != NULL && origPrivKeyDataSize > 0 && \
823  origPrivKeyDataSize < MAX_INTLENGTH_SHORT ) );
824 
825  REQUIRES( isHandleRangeValid( privKeyParams->iGenericContext ) );
826  REQUIRES( isHandleRangeValid( privKeyParams->iCryptContext ) );
827  REQUIRES( isHandleRangeValid( privKeyParams->iMacContext ) );
828  REQUIRES( privKeyParams->privKeyAttributeSize > 0 && \
829  privKeyParams->privKeyAttributeSize < MAX_INTLENGTH_SHORT );
830  REQUIRES( isPkcAlgo( privKeyParams->pkcCryptAlgo ) );
831 #ifdef USE_RSA_EXTRAPARAM
832  REQUIRES( ( isEccAlgo( privKeyParams->pkcCryptAlgo ) && \
833  privKeyParams->modulusSize >= MIN_PKCSIZE_ECC && \
834  privKeyParams->modulusSize <= CRYPT_MAX_PKCSIZE_ECC ) || \
835  ( !isEccAlgo( privKeyParams->pkcCryptAlgo ) && \
836  privKeyParams->modulusSize >= MIN_PKCSIZE && \
837  privKeyParams->modulusSize <= CRYPT_MAX_PKCSIZE ) );
838 #endif /* USE_RSA_EXTRAPARAM */
839  REQUIRES( privKeyParams->keyTypeTag == DEFAULT_TAG || \
840  ( privKeyParams->keyTypeTag >= 0 && \
841  privKeyParams->keyTypeTag < MAX_TAG_VALUE ) );
842 
843  /* Clear return values */
844  *newPrivKeyData = NULL;
845  *newPrivKeyDataSize = *newPrivKeyOffset = 0;
846 
847  /* Calculate the eventual encrypted key size */
848  setMechanismWrapInfo( &mechanismInfo, NULL, 0, NULL, 0, iPrivKeyContext,
849  privKeyParams->iCryptContext );
851  &mechanismInfo, MECHANISM_PRIVATEKEYWRAP );
852  if( cryptStatusOK( status ) )
853  privKeySize = mechanismInfo.wrappedDataLength;
854  clearMechanismInfo( &mechanismInfo );
855  if( cryptStatusError( status ) )
856  return( status );
857  ENSURES( privKeySize >= 16 && privKeySize <= 256 + MAX_PRIVATE_KEYSIZE );
858 
859  /* Determine the size of the MAC value */
860  status = krnlSendMessage( privKeyParams->iMacContext,
861  IMESSAGE_GETATTRIBUTE, &macSize,
863  if( cryptStatusError( status ) )
864  return( status );
865  macSize = sizeofObject( macSize );
866 
867  /* Write the CMS envelope header for the wrapped private key except for
868  the outermost wrapper, which we have to defer writing until later
869  because we won't know the size of the encryption context information
870  or inner CMS header until we've written them. Since we're using
871  KEKRecipientInfo we use a version of 2 rather than 0 */
872  sMemOpen( &stream, envelopeHeaderBuffer, 256 );
873  writeShortInteger( &stream, 2, DEFAULT_TAG );
874  status = writeWrappedSessionKey( &stream, privKeyParams->iGenericContext,
875  iCryptOwner, password, passwordLength );
876  if( cryptStatusOK( status ) )
877  {
878  macDataOffset = stell( &stream );
879  status = writeCMSencrHeader( &stream, OID_CMS_DATA,
880  sizeofOID( OID_CMS_DATA ), privKeySize,
881  privKeyParams->iGenericContext );
882  }
883  if( cryptStatusError( status ) )
884  {
885  sMemClose( &stream );
886  retExt( status,
887  ( status, errorInfo,
888  "Couldn't write envelope header for wrapping private "
889  "key" ) );
890  }
891  envelopeHeaderSize = stell( &stream );
892  envelopeContentSize = envelopeHeaderSize + privKeySize + macSize;
893  sMemDisconnect( &stream );
894 
895  /* Since we haven't been able to write the outer CMS envelope wrapper
896  yet we need to adjust the overall size for the additional level of
897  encapsulation */
898  privKeySize = ( int ) sizeofObject( envelopeContentSize );
899 
900  /* Calculate the private-key storage size */
901 #ifdef USE_RSA_EXTRAPARAM
902  if( privKeyParams->pkcCryptAlgo == CRYPT_ALGO_RSA )
903  {
904  /* RSA keys have an extra element for PKCS #11 compatibility */
905  extraDataSize = sizeofShortInteger( privKeyParams->modulusSize );
906  }
907 #endif /* USE_RSA_EXTRAPARAM */
908  status = calculatePrivkeyStorage( newPrivKeyData, newPrivKeyDataSize,
909  origPrivKeyData, origPrivKeyDataSize,
910  privKeySize,
911  privKeyParams->privKeyAttributeSize,
912  extraDataSize );
913  if( cryptStatusError( status ) )
914  return( status );
915 
916  /* MAC the EncryptedContentInfo.ContentEncryptionAlgorithmIdentifier
917  information alongside the payload data to prevent an attacker from
918  manipulating the algorithm parameters to cause corruption that won't
919  be detected by the MAC on the payload data */
920  sMemConnect( &stream, envelopeHeaderBuffer + macDataOffset,
921  envelopeHeaderSize - macDataOffset );
922  readSequenceI( &stream, NULL ); /* Outer encapsulation */
923  status = readUniversal( &stream ); /* Content-type OID */
924  if( cryptStatusOK( status ) )
925  status = getStreamObjectLength( &stream, &macDataLength );
926  if( cryptStatusOK( status ) ) /* AlgoID */
927  {
928  status = sMemGetDataBlock( &stream, &macDataPtr,
929  macDataLength );
930  }
931  if( cryptStatusOK( status ) )
932  {
933  status = krnlSendMessage( privKeyParams->iMacContext,
934  IMESSAGE_CTX_HASH, macDataPtr,
935  macDataLength );
936  }
937  sMemDisconnect( &stream );
938  if( cryptStatusError( status ) )
939  {
940  if( newPrivKeyData != origPrivKeyData )
941  clFree( "writePrivateKey", newPrivKeyData );
942  retExt( status,
943  ( status, errorInfo,
944  "Couldn't MAC encryption attributes" ) );
945  }
946 
947  sMemOpen( &stream, *newPrivKeyData, *newPrivKeyDataSize );
948 
949  /* Write the outer header and attributes */
950  writeConstructed( &stream, privKeyParams->privKeyAttributeSize + \
951  sizeofObject( sizeofObject( privKeySize ) + \
952  extraDataSize ),
953  privKeyParams->keyTypeTag );
954  swrite( &stream, privKeyParams->privKeyAttributes,
955  privKeyParams->privKeyAttributeSize );
956  writeConstructed( &stream,
957  sizeofObject( privKeySize + extraDataSize ),
959  status = writeSequence( &stream, privKeySize + extraDataSize );
960  if( cryptStatusOK( status ) )
961  *newPrivKeyOffset = stell( &stream );
962  if( cryptStatusError( status ) )
963  {
964  sMemClose( &stream );
965  if( newPrivKeyData != origPrivKeyData )
966  clFree( "writePrivateKey", newPrivKeyData );
967  retExt( status,
968  ( status, errorInfo,
969  "Couldn't write private key attributes" ) );
970  }
971 
972  /* Write the previously-encoded CMS envelope header and key exchange
973  information. Since we now know the size of the envelope header
974  (which we couldn't write earlier) we can add this too */
975  writeConstructed( &stream, envelopeContentSize,
977  status = swrite( &stream, envelopeHeaderBuffer, envelopeHeaderSize );
978  if( cryptStatusError( status ) )
979  return( status );
980 
981  /* Write the encrypted private key by exporting it directly into the
982  stream buffer */
983  status = sMemGetDataBlockRemaining( &stream, &encryptedKeyDataPtr,
984  &encryptedKeyDataLength );
985  if( cryptStatusOK( status ) )
986  {
987  status = writeWrappedPrivateKey( encryptedKeyDataPtr,
988  encryptedKeyDataLength, &privKeySize,
989  iPrivKeyContext,
990  privKeyParams->iCryptContext,
991  privKeyParams->iMacContext,
992  privKeyParams->pkcCryptAlgo );
993  }
994  if( cryptStatusOK( status ) )
995  status = sSkip( &stream, privKeySize );
996  if( cryptStatusError( status ) )
997  {
998  sMemClose( &stream );
999  if( newPrivKeyData != origPrivKeyData )
1000  clFree( "writePrivateKey", newPrivKeyData );
1001  retExt( status,
1002  ( status, errorInfo,
1003  "Couldn't write wrapped private key" ) );
1004  }
1005 
1006  /* Get the MAC value and write it */
1007  setMessageData( &msgData, macValue, CRYPT_MAX_HASHSIZE );
1008  status = krnlSendMessage( privKeyParams->iMacContext,
1009  IMESSAGE_GETATTRIBUTE_S, &msgData,
1011  if( cryptStatusOK( status ) )
1012  status = writeOctetString( &stream, macValue, msgData.length,
1013  DEFAULT_TAG );
1014  if( cryptStatusError( status ) )
1015  {
1016  sMemClose( &stream );
1017  if( newPrivKeyData != origPrivKeyData )
1018  clFree( "writePrivateKey", newPrivKeyData );
1019  retExt( status,
1020  ( status, errorInfo,
1021  "Couldn't write integrity check value for wrapped private "
1022  "key" ) );
1023  }
1024 
1025 #ifdef USE_RSA_EXTRAPARAM
1026  /* RSA keys have an extra element for PKCS #11 compability that we need
1027  to kludge onto the end of the private-key data */
1028  if( privKeyParams->pkcCryptAlgo == CRYPT_ALGO_RSA )
1029  {
1030  status = writeShortInteger( &stream, privKeyParams->modulusSize,
1031  DEFAULT_TAG );
1032  if( cryptStatusError( status ) )
1033  {
1034  sMemClose( &stream );
1035  return( status );
1036  }
1037  }
1038 #endif /* USE_RSA_EXTRAPARAM */
1039  assert( *newPrivKeyDataSize == stell( &stream ) );
1040  sMemDisconnect( &stream );
1041  ENSURES( !cryptStatusError( checkObjectEncoding( *newPrivKeyData, \
1042  *newPrivKeyDataSize ) ) );
1043 
1044  return( CRYPT_OK );
1045  }
1046 
1047 /* Add a private key to a PKCS #15 collection */
1048 
1049 #if 1 /* New (3.4.0+) code to write the private key as AuthEnvData */
1050 
1051 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 6, 11 ) ) \
1052 int pkcs15AddPrivateKey( INOUT PKCS15_INFO *pkcs15infoPtr,
1053  IN_HANDLE const CRYPT_HANDLE iPrivKeyContext,
1054  IN_HANDLE const CRYPT_HANDLE iCryptOwner,
1055  IN_BUFFER_OPT( passwordLength ) const char *password,
1056  IN_LENGTH_NAME_Z const int passwordLength,
1057  IN_BUFFER( privKeyAttributeSize ) \
1058  const void *privKeyAttributes,
1059  IN_LENGTH_SHORT const int privKeyAttributeSize,
1061  IN_LENGTH_PKC const int modulusSize,
1062  const BOOLEAN isStorageObject,
1063  INOUT ERROR_INFO *errorInfo )
1064  {
1065  CRYPT_CONTEXT iGenericContext, iCryptContext, iMacContext;
1066  PRIVKEY_WRITE_PARAMS privKeyParams;
1067  void *newPrivKeyData;
1068  int newPrivKeyDataSize, newPrivKeyOffset, keyTypeTag, status;
1069 
1070  assert( isWritePtr( pkcs15infoPtr, sizeof( PKCS15_INFO ) ) );
1071  assert( ( isStorageObject && password == NULL && passwordLength == 0 ) || \
1072  ( !isStorageObject && isReadPtr( password, passwordLength ) ) );
1073  assert( isReadPtr( privKeyAttributes, privKeyAttributeSize ) );
1074 
1075  REQUIRES( isHandleRangeValid( iPrivKeyContext ) );
1076  REQUIRES( iCryptOwner == DEFAULTUSER_OBJECT_HANDLE || \
1077  isHandleRangeValid( iCryptOwner ) );
1078  REQUIRES( ( isStorageObject && password == NULL && \
1079  passwordLength == 0 ) || \
1080  ( !isStorageObject && password != NULL && \
1081  passwordLength >= MIN_NAME_LENGTH && \
1082  passwordLength < MAX_ATTRIBUTE_SIZE ) );
1083  REQUIRES( privKeyAttributeSize > 0 && \
1084  privKeyAttributeSize < MAX_INTLENGTH_SHORT );
1085  REQUIRES( isPkcAlgo( pkcCryptAlgo ) );
1086  REQUIRES( ( isEccAlgo( pkcCryptAlgo ) && \
1087  modulusSize >= MIN_PKCSIZE_ECC && \
1088  modulusSize <= CRYPT_MAX_PKCSIZE_ECC ) || \
1089  ( !isEccAlgo( pkcCryptAlgo ) && \
1090  modulusSize >= MIN_PKCSIZE && \
1091  modulusSize <= CRYPT_MAX_PKCSIZE ) );
1092  REQUIRES( errorInfo != NULL );
1093 
1094  /* Get the tag for encoding the key data */
1095  status = getKeyTypeTag( CRYPT_UNUSED, pkcCryptAlgo, &keyTypeTag );
1096  if( cryptStatusError( status ) )
1097  return( status );
1098 
1099  /* If this is a dummy object (in other words object metadata) being
1100  stored in a PKCS #15 object store then there's nothing present except
1101  key attributes and a reference to the key held in external hardware,
1102  in which case we use a simplified version of the key-write code */
1103  if( isStorageObject )
1104  {
1105  initPrivKeyParams( &privKeyParams, CRYPT_UNUSED, CRYPT_UNUSED,
1106  CRYPT_UNUSED, privKeyAttributes,
1107  privKeyAttributeSize, pkcCryptAlgo, modulusSize,
1108  keyTypeTag );
1109  status = addPrivateKeyMetadata( pkcs15infoPtr, iPrivKeyContext,
1110  &privKeyParams );
1111  if( cryptStatusError( status ) )
1112  {
1113  retExt( status,
1114  ( status, errorInfo,
1115  "Couldn't write private key metadata" ) );
1116  }
1117 
1118  return( CRYPT_OK );
1119  }
1120 
1121  /* Create the contexts needed to protect the private-key data */
1122  status = createContexts( &iGenericContext, &iCryptContext, &iMacContext,
1123  iCryptOwner );
1124  if( cryptStatusError( status ) )
1125  {
1126  retExt( status,
1127  ( status, errorInfo,
1128  "Couldn't create encryption contexts to protect the "
1129  "private key" ) );
1130  }
1131 
1132  /* Write the encrypted and MACd private key */
1133  initPrivKeyParams( &privKeyParams, iGenericContext, iCryptContext,
1134  iMacContext, privKeyAttributes,
1135  privKeyAttributeSize, pkcCryptAlgo, modulusSize,
1136  keyTypeTag );
1137  status = writePrivateKey( iPrivKeyContext, iCryptOwner, password,
1138  passwordLength, &privKeyParams,
1139  pkcs15infoPtr->privKeyData,
1140  pkcs15infoPtr->privKeyDataSize,
1141  &newPrivKeyData, &newPrivKeyDataSize,
1142  &newPrivKeyOffset, errorInfo );
1143  krnlSendNotifier( iGenericContext, IMESSAGE_DECREFCOUNT );
1144  krnlSendNotifier( iCryptContext, IMESSAGE_DECREFCOUNT );
1145  krnlSendNotifier( iMacContext, IMESSAGE_DECREFCOUNT );
1146  if( cryptStatusError( status ) )
1147  return( status );
1148 
1149  /* Replace the old data with the newly-written data */
1150  replacePrivkeyData( pkcs15infoPtr, newPrivKeyData,
1151  newPrivKeyDataSize, newPrivKeyOffset );
1152  return( CRYPT_OK );
1153  }
1154 
1155 #else /* Old (pre-3.4.0) code to write the encrypted private key as
1156  EnvelopedData rather than AuthEnv'd data */
1157 
1158 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 4, 6, 11 ) ) \
1159 int pkcs15AddPrivateKey( INOUT PKCS15_INFO *pkcs15infoPtr,
1160  IN_HANDLE const CRYPT_HANDLE iCryptContext,
1161  IN_HANDLE const CRYPT_HANDLE iCryptOwner,
1162  IN_BUFFER( passwordLength ) const char *password,
1163  IN_LENGTH_NAME const int passwordLength,
1164  IN_BUFFER( privKeyAttributeSize ) \
1165  const void *privKeyAttributes,
1166  IN_LENGTH_SHORT const int privKeyAttributeSize,
1167  IN_ALGO const CRYPT_ALGO_TYPE pkcCryptAlgo,
1168  IN_LENGTH_PKC const int modulusSize,
1169  const BOOLEAN isStorageObject,
1170  INOUT ERROR_INFO *errorInfo )
1171  {
1174  STREAM stream;
1175  BYTE envelopeHeaderBuffer[ 256 + 8 ];
1176  void *newPrivKeyData = pkcs15infoPtr->privKeyData;
1177  int newPrivKeyDataSize, newPrivKeyOffset = DUMMY_INIT;
1178  int privKeySize = DUMMY_INIT, extraDataSize = 0;
1179  int keyTypeTag, status;
1180 
1181  assert( isWritePtr( pkcs15infoPtr, sizeof( PKCS15_INFO ) ) );
1182  assert( ( isStorageObject && password == NULL && passwordLength == 0 ) || \
1183  ( !isStorageObject && isReadPtr( password, passwordLength ) ) );
1184  assert( isReadPtr( privKeyAttributes, privKeyAttributeSize ) );
1185 
1186  REQUIRES( isHandleRangeValid( iCryptContext ) );
1187  REQUIRES( iCryptOwner == DEFAULTUSER_OBJECT_HANDLE || \
1188  isHandleRangeValid( iCryptOwner ) );
1189  REQUIRES( ( isStorageObject && password == NULL && \
1190  passwordLength == 0 ) || \
1191  ( !isStorageObject && password != NULL && \
1192  passwordLength >= MIN_NAME_LENGTH && \
1193  passwordLength < MAX_ATTRIBUTE_SIZE ) );
1194  REQUIRES( privKeyAttributeSize > 0 && \
1195  privKeyAttributeSize < MAX_INTLENGTH_SHORT );
1196  REQUIRES( isPkcAlgo( pkcCryptAlgo ) );
1197  REQUIRES( ( isEccAlgo( pkcCryptAlgo ) && \
1198  modulusSize >= MIN_PKCSIZE_ECC && \
1199  modulusSize <= CRYPT_MAX_PKCSIZE_ECC ) || \
1200  ( !isEccAlgo( pkcCryptAlgo ) && \
1201  modulusSize >= MIN_PKCSIZE && \
1202  modulusSize <= CRYPT_MAX_PKCSIZE ) );
1203  REQUIRES( errorInfo != NULL );
1204 
1205  /* Get the tag for encoding the key data */
1206  status = getKeyTypeTag( CRYPT_UNUSED, pkcCryptAlgo, &keyTypeTag );
1207  if( cryptStatusError( status ) )
1208  return( status );
1209 
1210  /* If this is a dummy object (in other words object metadata) being
1211  stored in a PKCS #15 object store then there's nothing present except
1212  key attributes and a reference to the key held in external hardware,
1213  in which case we use a simplified version if the code that follows */
1214  if( isStorageObject )
1215  {
1216  status = addPrivateKeyMetadata( pkcs15infoPtr, iCryptContext,
1217  privKeyAttributes, privKeyAttributeSize,
1218  pkcCryptAlgo, modulusSize, keyTypeTag );
1219  if( cryptStatusError( status ) )
1220  {
1221  retExt( status,
1222  ( status, errorInfo,
1223  "Couldn't write private key metadata" ) );
1224  }
1225 
1226  return( CRYPT_OK );
1227  }
1228 
1229  /* Create a session key context and generate a key and IV into it. The
1230  IV would be generated automatically later on when we encrypt data for
1231  the first time but we do it explicitly here to catch any possible
1232  errors at a point where recovery is easier */
1233  status = createStrongEncryptionContext( &iSessionKeyContext, iCryptOwner );
1234  if( cryptStatusError( status ) )
1235  return( status );
1236  status = krnlSendNotifier( iSessionKeyContext, IMESSAGE_CTX_GENKEY );
1237  if( cryptStatusOK( status ) )
1238  status = krnlSendNotifier( iSessionKeyContext, IMESSAGE_CTX_GENIV );
1239  if( cryptStatusError( status ) )
1240  {
1241  krnlSendNotifier( iSessionKeyContext, IMESSAGE_DECREFCOUNT );
1242  retExt( status,
1243  ( status, errorInfo,
1244  "Couldn't create session key to wrap private key" ) );
1245  }
1246 
1247  /* Calculate the eventual encrypted key size */
1248  setMechanismWrapInfo( &mechanismInfo, NULL, 0, NULL, 0, iCryptContext,
1249  iSessionKeyContext );
1251  &mechanismInfo, MECHANISM_PRIVATEKEYWRAP );
1252  if( cryptStatusOK( status ) )
1253  privKeySize = mechanismInfo.wrappedDataLength;
1254  clearMechanismInfo( &mechanismInfo );
1255  if( cryptStatusError( status ) )
1256  {
1257  krnlSendNotifier( iSessionKeyContext, IMESSAGE_DECREFCOUNT );
1258  return( status );
1259  }
1260  ENSURES( privKeySize <= 256 + MAX_PRIVATE_KEYSIZE );
1261 
1262  /* Write the CMS envelope header for the wrapped private key except for
1263  the outermost wrapper, which we have to defer writing until later
1264  since we won't know the wrapped session key or inner CMS header size
1265  until we've written them. Since we're using KEKRecipientInfo we use
1266  a version of 2 rather than 0 */
1267  sMemOpen( &stream, envelopeHeaderBuffer, 256 );
1268  writeShortInteger( &stream, 2, DEFAULT_TAG );
1269  status = writeWrappedSessionKey( &stream, iSessionKeyContext,
1270  iCryptOwner, password, passwordLength );
1271  if( cryptStatusOK( status ) )
1272  status = writeCMSencrHeader( &stream, OID_CMS_DATA,
1273  sizeofOID( OID_CMS_DATA ), privKeySize,
1274  iSessionKeyContext );
1275  if( cryptStatusError( status ) )
1276  {
1277  sMemClose( &stream );
1278  krnlSendNotifier( iSessionKeyContext, IMESSAGE_DECREFCOUNT );
1279  retExt( status,
1280  ( status, errorInfo,
1281  "Couldn't write envelope header for wrapping private "
1282  "key" ) );
1283  }
1284  envelopeHeaderSize = stell( &stream );
1285  envelopeContentSize = envelopeHeaderSize + privKeySize;
1286  sMemDisconnect( &stream );
1287 
1288  /* Since we haven't been able to write the outer CMS envelope wrapper
1289  yet we need to adjust the overall size for the additional level of
1290  encapsulation */
1291  privKeySize = ( int ) sizeofObject( privKeySize + envelopeHeaderSize );
1292 
1293  /* Calculate the private-key storage size */
1294 #ifdef USE_RSA_EXTRAPARAM
1295  if( pkcCryptAlgo == CRYPT_ALGO_RSA )
1296  {
1297  /* RSA keys have an extra element for PKCS #11 compatibility */
1298  extraDataSize = sizeofShortInteger( modulusSize );
1299  }
1300 #endif /* USE_RSA_EXTRAPARAM */
1301  status = calculatePrivkeyStorage( pkcs15infoPtr, &newPrivKeyData,
1302  &newPrivKeyDataSize, privKeySize,
1303  privKeyAttributeSize,
1304  extraDataSize );
1305  if( cryptStatusError( status ) )
1306  {
1307  krnlSendNotifier( iSessionKeyContext, IMESSAGE_DECREFCOUNT );
1308  return( status );
1309  }
1310 
1311  sMemOpen( &stream, newPrivKeyData, newPrivKeyDataSize );
1312 
1313  /* Write the outer header and attributes */
1314  writeConstructed( &stream, privKeyAttributeSize + \
1315  sizeofObject( sizeofObject( privKeySize ) + \
1316  extraDataSize ),
1317  keyTypeTag );
1318  swrite( &stream, privKeyAttributes, privKeyAttributeSize );
1319  writeConstructed( &stream, sizeofObject( privKeySize + extraDataSize ),
1320  CTAG_OB_TYPEATTR );
1321  status = writeSequence( &stream, privKeySize + extraDataSize );
1322  if( cryptStatusOK( status ) )
1323  newPrivKeyOffset = stell( &stream );
1324  if( cryptStatusError( status ) )
1325  {
1326  sMemClose( &stream );
1327  krnlSendNotifier( iSessionKeyContext, IMESSAGE_DECREFCOUNT );
1328  if( newPrivKeyData != pkcs15infoPtr->privKeyData )
1329  clFree( "addPrivateKey", newPrivKeyData );
1330  retExt( status,
1331  ( status, errorInfo,
1332  "Couldn't write private key attributes" ) );
1333  }
1334 
1335  /* Write the previously-encoded CMS envelope header and key exchange
1336  information and follow it with the encrypted private key. Since we
1337  now know the size of the envelope header (which we couldn't write
1338  earlier) we can add this now too */
1339  writeConstructed( &stream, envelopeContentSize, CTAG_OV_DIRECTPROTECTED );
1340  status = swrite( &stream, envelopeHeaderBuffer, envelopeHeaderSize );
1341  if( cryptStatusOK( status ) )
1342  {
1343  void *dataPtr;
1344  int length;
1345 
1346  status = sMemGetDataBlockRemaining( &stream, &dataPtr, &length );
1347  if( cryptStatusOK( status ) )
1348  status = writeWrappedPrivateKey( dataPtr, length, &privKeySize,
1349  iCryptContext, iSessionKeyContext,
1350  pkcCryptAlgo );
1351  }
1352  if( cryptStatusOK( status ) )
1353  status = sSkip( &stream, privKeySize );
1354 #ifdef USE_RSA_EXTRAPARAM
1355  if( cryptStatusOK( status ) && pkcCryptAlgo == CRYPT_ALGO_RSA )
1356  {
1357  /* RSA keys have an extra element for PKCS #11 compability that we
1358  need to kludge onto the end of the private-key data */
1359  status = writeShortInteger( &stream, modulusSize, DEFAULT_TAG );
1360  }
1361 #endif /* USE_RSA_EXTRAPARAM */
1362  krnlSendNotifier( iSessionKeyContext, IMESSAGE_DECREFCOUNT );
1363  if( cryptStatusError( status ) )
1364  {
1365  sMemClose( &stream );
1366  retExt( status,
1367  ( status, errorInfo,
1368  "Couldn't wrap private key using session key" ) );
1369  }
1370  assert( newPrivKeyDataSize == stell( &stream ) );
1371  sMemDisconnect( &stream );
1372  ENSURES( !cryptStatusError( checkObjectEncoding( newPrivKeyData, \
1373  newPrivKeyDataSize ) ) );
1374 
1375  /* Replace the old data with the newly-written data */
1376  replacePrivkeyData( pkcs15infoPtr, newPrivKeyData,
1377  newPrivKeyDataSize, newPrivKeyOffset );
1378  return( CRYPT_OK );
1379  }
1380 #endif /* 0 */
1381 #endif /* USE_PKCS15 */