cryptlib  3.4.1
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Macros
pkcs15_getp.c
Go to the documentation of this file.
1 /****************************************************************************
2 * *
3 * cryptlib PKCS #15 Get Public/Private Key *
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 /* Define the following to enable a workaround for a MAC keysize bug in
23  authEnc private key data in cryptlib 3.4.0 */
24 
25 #define MAC_KEYSIZE_BUG
26 
27 #ifdef USE_PKCS15
28 
29 /* OID information used to read a PKCS #15 keyset */
30 
31 static const OID_INFO FAR_BSS dataOIDinfo[] = {
33  { NULL, 0 }, { NULL, 0 }
34  };
35 
36 /****************************************************************************
37 * *
38 * Utility Functions *
39 * *
40 ****************************************************************************/
41 
42 /* Translate the PKCS #15 usage flags into cryptlib permitted actions. The
43  PKCS #11 use of the 'derive' flag to mean 'allow key agreement' is a bit
44  of a kludge, we map it to allowing key-agreement export and import if
45  it's a key-agreement algorithm, if there are further constraints then
46  they'll be handled by the attached certificate. The PKCS #15
47  nonRepudiation flag doesn't have any definition so we can't do anything
48  with it, although we may need to translate it to allowing signing and/or
49  verification if implementations appear that expect it to be used this
50  way */
51 
53 static int getPermittedActions( IN_FLAGS( PKCS15_USAGE ) const int usageFlags,
55  OUT_FLAGS_Z( ACTION ) int *usage )
56  {
57  int actionFlags = ACTION_PERM_NONE_ALL;
58 
59  REQUIRES( usageFlags >= PKSC15_USAGE_FLAG_NONE && \
60  usageFlags < PKCS15_USAGE_FLAG_MAX );
61  REQUIRES( isPkcAlgo( cryptAlgo ) );
62 
63  /* Clear return value */
64  *usage = ACTION_PERM_NONE;
65 
66  if( usageFlags & ( PKCS15_USAGE_ENCRYPT | PKCS15_USAGE_WRAP ) )
68  if( usageFlags & ( PKCS15_USAGE_DECRYPT | PKCS15_USAGE_UNWRAP ) )
70  if( usageFlags & PKCS15_USAGE_SIGN )
72  if( usageFlags & PKCS15_USAGE_VERIFY )
74  if( isKeyxAlgo( cryptAlgo ) && ( usageFlags & PKCS15_USAGE_DERIVE ) )
76  MK_ACTION_PERM( MESSAGE_CTX_DECRYPT, ACTION_PERM_ALL );
77  if( cryptAlgo == CRYPT_ALGO_RSA )
78  {
79  /* If there are any restrictions on the key usage then we have to
80  make it internal-only because of RSA's signature/encryption
81  duality */
82  if( !( ( usageFlags & ( PKCS15_USAGE_ENCRYPT | PKCS15_USAGE_WRAP | \
84  ( usageFlags & ( PKCS15_USAGE_SIGN | PKCS15_USAGE_VERIFY ) ) ) )
85  actionFlags = MK_ACTION_PERM_NONE_EXTERNAL( actionFlags );
86  }
87  else
88  {
89  /* Because of the special-case data formatting requirements for DLP
90  algorithms we make the usage internal-only */
91  actionFlags = MK_ACTION_PERM_NONE_EXTERNAL( actionFlags );
92  }
93  if( actionFlags <= ACTION_PERM_NONE_ALL )
94  return( CRYPT_ERROR_PERMISSION );
95  *usage = actionFlags;
96 
97  return( CRYPT_OK );
98  }
99 
100 /****************************************************************************
101 * *
102 * Read Public Key Components *
103 * *
104 ****************************************************************************/
105 
106 /* Read public-key components from a PKCS #15 object entry */
107 
108 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 4, 8, 9, 10, 11, 12 ) ) \
109 int readPublicKeyComponents( const PKCS15_INFO *pkcs15infoPtr,
112  IN_BUFFER( keyIDlength ) const void *keyID,
113  IN_LENGTH_KEYID const int keyIDlength,
118  OUT_FLAGS_Z( ACTION ) int *pubkeyActionFlags,
119  OUT_FLAGS_Z( ACTION ) int *privkeyActionFlags,
121  {
123  CRYPT_CERTIFICATE iDataCert = CRYPT_ERROR;
124  STREAM stream;
125  int pkcAlgo, status;
126 
127  assert( isReadPtr( pkcs15infoPtr, sizeof( PKCS15_INFO ) ) );
128  assert( isReadPtr( keyID, keyIDlength ) );
129  assert( isWritePtr( iCryptContextPtr, sizeof( CRYPT_CONTEXT ) ) );
130  assert( isWritePtr( iDataCertPtr, sizeof( CRYPT_CERTIFICATE ) ) );
131  assert( isWritePtr( pubkeyActionFlags, sizeof( int ) ) );
132  assert( isWritePtr( privkeyActionFlags, sizeof( int ) ) );
133 
134  REQUIRES( isHandleRangeValid( iCryptKeysetCallback ) );
135  REQUIRES( keyIDtype == CRYPT_KEYID_NAME || \
136  keyIDtype == CRYPT_KEYID_URI || \
137  keyIDtype == CRYPT_IKEYID_KEYID || \
138  keyIDtype == CRYPT_IKEYID_PGPKEYID || \
139  keyIDtype == CRYPT_IKEYID_ISSUERID );
140  REQUIRES( keyIDlength >= MIN_NAME_LENGTH && \
141  keyIDlength < MAX_ATTRIBUTE_SIZE );
142  REQUIRES( iDeviceObject == SYSTEM_OBJECT_HANDLE || \
143  isHandleRangeValid( iDeviceObject ) );
144  REQUIRES( errorInfo != NULL );
145 
146  /* Clear return values */
147  *iCryptContextPtr = CRYPT_ERROR;
148  *iDataCertPtr = CRYPT_ERROR;
149  *pubkeyActionFlags = *privkeyActionFlags = ACTION_PERM_NONE;
150 
151  /* If we're creating a public-key context we create the certificate or
152  PKC context normally, if we're creating a private-key context we
153  create a data-only certificate (if there's certificate information
154  present) and a partial PKC context ready to accept the private key
155  components. If there's a certificate present then we take all of the
156  information that we need from the certificate, otherwise we use the
157  public-key data */
158 #ifdef USE_CERTIFICATES
159  if( pkcs15infoPtr->certData != NULL )
160  {
161  /* There's a certificate present, import it and reconstruct the
162  public-key information from it if we're creating a partial PKC
163  context */
164  status = iCryptImportCertIndirect( &iCryptContext,
165  iCryptKeysetCallback, keyIDtype, keyID,
166  keyIDlength, publicComponentsOnly ? \
169  if( cryptStatusError( status ) )
170  {
171  retExt( status,
172  ( status, errorInfo,
173  "Couldn't recreate certificate from stored "
174  "certificate data" ) );
175  }
176  if( !publicComponentsOnly )
177  {
178  DYNBUF pubKeyDB;
179 
180  /* We got the certificate, now create the public part of the
181  context from the certificate's encoded public-key
182  components */
183  iDataCert = iCryptContext;
184  status = dynCreate( &pubKeyDB, iDataCert,
185  CRYPT_IATTRIBUTE_SPKI );
186  if( cryptStatusError( status ) )
187  return( status );
188  sMemConnect( &stream, dynData( pubKeyDB ),
189  dynLength( pubKeyDB ) );
190  status = iCryptReadSubjectPublicKey( &stream, &iCryptContext,
191  iDeviceObject, TRUE );
192  sMemDisconnect( &stream );
193  dynDestroy( &pubKeyDB );
194  if( cryptStatusError( status ) )
195  {
197  retExt( status,
198  ( status, errorInfo,
199  "Couldn't recreate public key from "
200  "certificate" ) );
201  }
202  }
203  }
204  else
205 #endif /* USE_CERTIFICATES */
206  {
207  const int pubKeyStartOffset = pkcs15infoPtr->pubKeyOffset;
208  const int pubKeyTotalSize = pkcs15infoPtr->pubKeyDataSize;
209 
210  /* There's no certificate present, create the public-key context
211  directly */
212  REQUIRES( rangeCheck( pubKeyStartOffset,
213  pubKeyTotalSize - pubKeyStartOffset,
214  pubKeyTotalSize ) );
215  sMemConnect( &stream,
216  ( BYTE * ) pkcs15infoPtr->pubKeyData + pubKeyStartOffset,
217  pubKeyTotalSize - pubKeyStartOffset );
218  status = iCryptReadSubjectPublicKey( &stream, &iCryptContext,
219  iDeviceObject,
220  !publicComponentsOnly );
221  sMemDisconnect( &stream );
222  if( cryptStatusError( status ) )
223  {
224  retExt( status,
225  ( status, errorInfo,
226  "Couldn't recreate public key from stored public key "
227  "data" ) );
228  }
229  }
230 
231  /* Get the permitted usage flags for each object type that we'll be
232  instantiating. If there's a public key present we apply its usage
233  flags to whichever PKC context we create, even if it's done indirectly
234  via the certificate import. Since the private key can also perform
235  the actions of the public key we set its action flags to the union of
236  the two */
237  status = krnlSendMessage( iCryptContext, IMESSAGE_GETATTRIBUTE,
238  &pkcAlgo, CRYPT_CTXINFO_ALGO );
239  if( cryptStatusOK( status ) && pkcs15infoPtr->pubKeyData != NULL )
240  {
241  status = getPermittedActions( pkcs15infoPtr->pubKeyUsage, pkcAlgo,
242  pubkeyActionFlags );
243  }
244  if( cryptStatusOK( status ) && !publicComponentsOnly )
245  {
246  status = getPermittedActions( pkcs15infoPtr->privKeyUsage, pkcAlgo,
247  privkeyActionFlags );
248  }
249  if( cryptStatusError( status ) )
250  {
251  krnlSendNotifier( iCryptContext, IMESSAGE_DECREFCOUNT );
252  if( iDataCert != CRYPT_ERROR )
254  retExt( status,
255  ( status, errorInfo,
256  "Public/private key usage flags don't allow any type of "
257  "key usage" ) );
258  }
259 
260  /* Return the newly-created objects to the caller */
261  *iCryptContextPtr = iCryptContext;
262  *iDataCertPtr = iDataCert;
263 
264  return( CRYPT_OK );
265  }
266 
267 /****************************************************************************
268 * *
269 * Read Private Key Components *
270 * *
271 ****************************************************************************/
272 
273 /* Import the session key (either a direct session key or a generic-secret
274  key used to derive the decryption and MAC contexts) using the supplied
275  password */
276 
277 CHECK_RETVAL STDC_NONNULL_ARG( ( 2, 4, 6 ) ) \
278 static int importSessionKey( IN_HANDLE const CRYPT_CONTEXT iSessionKey,
279  IN_BUFFER( encryptedKeyDataSize ) \
280  const void *encryptedKeyData,
281  IN_LENGTH_SHORT const int encryptedKeyDataSize,
282  IN_BUFFER( passwordLength ) const void *password,
283  IN_LENGTH_NAME const int passwordLength,
284  const QUERY_INFO *queryInfo )
285  {
286  CRYPT_CONTEXT iKeyWrapContext;
287  MESSAGE_CREATEOBJECT_INFO createInfo;
289  int mode, status;
290 
291  assert( isReadPtr( encryptedKeyData, encryptedKeyDataSize ) );
292  assert( isReadPtr( password, passwordLength ) );
293  assert( isReadPtr( queryInfo, sizeof( QUERY_INFO ) ) );
294 
295  REQUIRES( isHandleRangeValid( iSessionKey ) );
296  REQUIRES( encryptedKeyDataSize >= 16 && \
297  encryptedKeyDataSize < MAX_INTLENGTH_SHORT );
298  REQUIRES( passwordLength >= MIN_NAME_LENGTH && \
299  passwordLength < MAX_ATTRIBUTE_SIZE );
300 
301  /* Create the context used to import the session key and derive the user
302  password into it */
303  setMessageCreateObjectInfo( &createInfo, queryInfo->cryptAlgo );
305  &createInfo, OBJECT_TYPE_CONTEXT );
306  if( cryptStatusError( status ) )
307  return( status );
308  iKeyWrapContext = createInfo.cryptHandle;
309  mode = queryInfo->cryptMode; /* int vs.enum */
310  status = krnlSendMessage( iKeyWrapContext, IMESSAGE_SETATTRIBUTE,
311  &mode, CRYPT_CTXINFO_MODE );
312  if( cryptStatusOK( status ) && \
313  queryInfo->keySetupAlgo != CRYPT_ALGO_NONE )
314  {
315  const int algorithm = queryInfo->keySetupAlgo; /* int vs.enum */
316  status = krnlSendMessage( iKeyWrapContext, IMESSAGE_SETATTRIBUTE,
317  ( MESSAGE_CAST ) &algorithm,
319  }
320  if( cryptStatusOK( status ) )
321  status = krnlSendMessage( iKeyWrapContext, IMESSAGE_SETATTRIBUTE,
322  ( MESSAGE_CAST ) &queryInfo->keySetupIterations,
324  if( cryptStatusOK( status ) && queryInfo->keySize > 0 )
325  status = krnlSendMessage( iKeyWrapContext, IMESSAGE_SETATTRIBUTE,
326  ( MESSAGE_CAST ) &queryInfo->keySize,
328  if( cryptStatusOK( status ) )
329  {
330  setMessageData( &msgData, ( MESSAGE_CAST ) queryInfo->salt,
331  queryInfo->saltLength );
332  status = krnlSendMessage( iKeyWrapContext, IMESSAGE_SETATTRIBUTE_S,
333  &msgData, CRYPT_CTXINFO_KEYING_SALT );
334  }
335  if( cryptStatusOK( status ) )
336  {
337  setMessageData( &msgData, ( MESSAGE_CAST ) password, passwordLength );
338  status = krnlSendMessage( iKeyWrapContext, IMESSAGE_SETATTRIBUTE_S,
339  &msgData, CRYPT_CTXINFO_KEYING_VALUE );
340  }
341  if( cryptStatusError( status ) )
342  {
343  krnlSendNotifier( iKeyWrapContext, IMESSAGE_DECREFCOUNT );
344 
345  /* If there's an error in the parameters supplied via the query
346  information (which were stored with the exported key) we'll get
347  an argument or attribute error when we try to set the attribute
348  so we translate it into an error code which is appropriate for
349  the situation */
350  return( cryptArgError( status ) ? CRYPT_ERROR_BADDATA : status );
351  }
352 
353  /* Import the session key, either an actual session key context for PKCS
354  #15 v1.1 using direct-protected content or a generic-secret context
355  for PKCS #15 v1.2 using direct-protected-ext content */
356  status = iCryptImportKey( encryptedKeyData, encryptedKeyDataSize,
357  CRYPT_FORMAT_CRYPTLIB, iKeyWrapContext,
358  iSessionKey, NULL );
359  krnlSendNotifier( iKeyWrapContext, IMESSAGE_DECREFCOUNT );
360  if( cryptStatusError( status ) )
361  {
362  /* Translate any arg/attribute errors as before */
363  return( cryptArgError( status ) ? CRYPT_ERROR_BADDATA : status );
364  }
365 
366  return( CRYPT_OK );
367  }
368 
369 /* Initialise decryption and MAC contexts and keys from a generic-secret
370  context */
371 
372 CHECK_RETVAL_SPECIAL STDC_NONNULL_ARG( ( 2, 3, 4 ) ) \
373 static int initKeys( IN_HANDLE const CRYPT_CONTEXT iGenericContext,
374  OUT_HANDLE_OPT CRYPT_CONTEXT *iCryptContext,
375  OUT_HANDLE_OPT CRYPT_CONTEXT *iMacContext,
376  OUT_HANDLE_OPT CRYPT_CONTEXT *iMacAltContext,
377  const QUERY_INFO *queryInfo )
378  {
379  CRYPT_CONTEXT iAuthEncCryptContext, iAuthEncMacContext;
380 #ifdef MAC_KEYSIZE_BUG
381  CRYPT_CONTEXT iAuthEncMacAltContext = DUMMY_INIT;
382 #endif /* MAC_KEYSIZE_BUG */
383  MESSAGE_CREATEOBJECT_INFO createInfo;
385  STREAM stream;
386  int status;
387 
388  assert( isWritePtr( iCryptContext, sizeof( CRYPT_CONTEXT ) ) );
389  assert( isWritePtr( iMacContext, sizeof( CRYPT_CONTEXT ) ) );
390  assert( isWritePtr( iMacAltContext, sizeof( CRYPT_CONTEXT ) ) );
391  assert( isReadPtr( queryInfo, sizeof( QUERY_INFO ) ) );
392 
393  REQUIRES( isHandleRangeValid( iGenericContext ) );
394 
395  /* Clear return values */
396  *iCryptContext = *iMacContext = *iMacAltContext = CRYPT_ERROR;
397 
398  /* Recreate the encryption and MAC contexts used for the authenticated
399  encryption from the algorithm parameter data that was stored
400  alongside the generic-secret context parameter data */
401  sMemConnect( &stream,
402  queryInfo->authEncParamData + queryInfo->encParamStart,
403  queryInfo->encParamLength );
404  status = readContextAlgoID( &stream, &iAuthEncCryptContext, NULL,
406  sMemDisconnect( &stream );
407  if( cryptStatusError( status ) )
408  return( status );
409  sMemConnect( &stream,
410  queryInfo->authEncParamData + queryInfo->macParamStart,
411  queryInfo->macParamLength );
412  status = readContextAlgoID( &stream, &iAuthEncMacContext, NULL,
414  sMemDisconnect( &stream );
415  if( cryptStatusError( status ) )
416  {
417  krnlSendNotifier( iAuthEncCryptContext, IMESSAGE_DECREFCOUNT );
418  return( status );
419  }
420 
421  /* Derive the encryption and MAC keys from the generic-secret key */
422  setMechanismKDFInfo( &mechanismInfo, iAuthEncCryptContext,
423  iGenericContext, CRYPT_ALGO_HMAC_SHA1,
424  "encryption", 10 );
426  &mechanismInfo, MECHANISM_DERIVE_PKCS5 );
427  if( cryptStatusOK( status ) )
428  {
429  setMechanismKDFInfo( &mechanismInfo, iAuthEncMacContext,
430  iGenericContext, CRYPT_ALGO_HMAC_SHA1,
431  "authentication", 14 );
433  &mechanismInfo, MECHANISM_DERIVE_PKCS5 );
434  }
435  if( cryptStatusError( status ) )
436  {
437  krnlSendNotifier( iAuthEncCryptContext, IMESSAGE_DECREFCOUNT );
438  krnlSendNotifier( iAuthEncMacContext, IMESSAGE_DECREFCOUNT );
439  return( status );
440  }
441 
442  /* MAC the EncryptedContentInfo.ContentEncryptionAlgorithmIdentifier
443  information alongside the payload data to prevent an attacker from
444  manipulating the algorithm parameters to cause corruption that won't
445  be detected by the MAC on the payload data */
446  status = krnlSendMessage( iAuthEncMacContext, IMESSAGE_CTX_HASH,
447  ( MESSAGE_CAST ) queryInfo->authEncParamData,
448  queryInfo->authEncParamLength );
449  if( cryptStatusError( status ) )
450  {
451  krnlSendNotifier( iAuthEncCryptContext, IMESSAGE_DECREFCOUNT );
452  krnlSendNotifier( iAuthEncMacContext, IMESSAGE_DECREFCOUNT );
453  return( status );
454  }
455 
456 #ifdef MAC_KEYSIZE_BUG
457  /* Because of an error in setting the key size for the 3.4.0 cryptlib
458  release, private-key files created by this version use a 128-bit
459  HMAC-SHA1 key instead of a 160-bit one. To work around this we
460  create a second MAC context with a 128-bit key and try that if the
461  initial check with the 160-bit key fails */
464  &createInfo, OBJECT_TYPE_CONTEXT );
465  if( cryptStatusError( status ) )
466  {
467  krnlSendNotifier( iAuthEncCryptContext, IMESSAGE_DECREFCOUNT );
468  krnlSendNotifier( iAuthEncMacContext, IMESSAGE_DECREFCOUNT );
469  return( status );
470  }
471  else
472  {
473  static const int macKeySize = bitsToBytes( 128 );
474 
475  iAuthEncMacAltContext = createInfo.cryptHandle;
476  status = krnlSendMessage( iAuthEncMacAltContext, IMESSAGE_SETATTRIBUTE,
477  ( MESSAGE_CAST ) &macKeySize,
479  }
480  if( cryptStatusOK( status ) )
481  {
482  setMechanismKDFInfo( &mechanismInfo, iAuthEncMacAltContext,
483  iGenericContext, CRYPT_ALGO_HMAC_SHA1,
484  "authentication", 14 );
486  &mechanismInfo, MECHANISM_DERIVE_PKCS5 );
487  }
488  if( cryptStatusOK( status ) )
489  {
490  status = krnlSendMessage( iAuthEncMacAltContext, IMESSAGE_CTX_HASH,
491  ( MESSAGE_CAST ) queryInfo->authEncParamData,
492  queryInfo->authEncParamLength );
493  }
494  if( cryptStatusError( status ) )
495  {
496  krnlSendNotifier( iAuthEncCryptContext, IMESSAGE_DECREFCOUNT );
497  krnlSendNotifier( iAuthEncMacContext, IMESSAGE_DECREFCOUNT );
498  krnlSendNotifier( iAuthEncMacAltContext, IMESSAGE_DECREFCOUNT );
499  return( status );
500  }
501  *iMacAltContext = iAuthEncMacAltContext;
502 #endif /* MAC_KEYSIZE_BUG */
503 
504  *iCryptContext = iAuthEncCryptContext;
505  *iMacContext = iAuthEncMacContext;
506 
507  return( CRYPT_OK );
508  }
509 
510 /* Read private-key components from a PKCS #15 object entry */
511 
512 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 6 ) ) \
513 int readPrivateKeyComponents( const PKCS15_INFO *pkcs15infoPtr,
515  IN_BUFFER_OPT( passwordLength ) \
516  const void *password,
517  IN_LENGTH_NAME_Z const int passwordLength,
518  const BOOLEAN isStorageObject,
519  INOUT ERROR_INFO *errorInfo )
520  {
522 #ifdef MAC_KEYSIZE_BUG
523  CRYPT_CONTEXT iMacAltContext = CRYPT_UNUSED;
524 #endif /* MAC_KEYSIZE_BUG */
527  QUERY_INFO queryInfo = DUMMY_INIT_STRUCT, contentQueryInfo;
528  STREAM stream;
529  BYTE macValue[ CRYPT_MAX_HASHSIZE + 8 ];
530  BOOLEAN isAuthEnc = FALSE;
531  const int privKeyStartOffset = pkcs15infoPtr->privKeyOffset;
532  const int privKeyTotalSize = pkcs15infoPtr->privKeyDataSize;
533  void *encryptedKey, *encryptedContent = DUMMY_INIT_PTR;
534  int encryptedContentLength = DUMMY_INIT;
535  int tag, macValueLength = DUMMY_INIT, status;
536 
537  assert( isReadPtr( pkcs15infoPtr, sizeof( PKCS15_INFO ) ) );
538  assert( ( isStorageObject && \
539  password == NULL && passwordLength == 0 ) || \
540  ( !isStorageObject && \
541  isReadPtr( password, passwordLength ) ) );
542 
543  REQUIRES( isHandleRangeValid( iPrivKeyContext ) );
544  REQUIRES( ( isStorageObject && \
545  password == NULL && passwordLength == 0 ) || \
546  ( !isStorageObject && \
547  passwordLength >= MIN_NAME_LENGTH && \
548  passwordLength < MAX_ATTRIBUTE_SIZE ) );
549  REQUIRES( errorInfo != NULL );
550 
551  /* Skip the outer wrapper, version number, and header for the SET OF
552  EncryptionInfo, and query the exported key information to determine
553  the parameters required to reconstruct the decryption key */
554  REQUIRES( rangeCheck( privKeyStartOffset,
555  privKeyTotalSize - privKeyStartOffset,
556  privKeyTotalSize ) );
557  sMemConnect( &stream,
558  ( BYTE * ) pkcs15infoPtr->privKeyData + privKeyStartOffset,
559  privKeyTotalSize - privKeyStartOffset );
560  status = tag = peekTag( &stream );
561  if( cryptStatusError( status ) )
562  return( status );
563  if( isStorageObject )
564  {
565  BYTE storageID[ KEYID_SIZE + 8 ];
566  int length;
567 
568  /* If this is a PKCS #15 storage object then it'll contain only
569  private-key metadata with the content being merely a reference
570  to external hardware, so we just read the storage object
571  reference and save it to the dummy context */
572  if( tag != BER_SEQUENCE )
573  {
574  sMemDisconnect( &stream );
576  ( CRYPT_ERROR_BADDATA, errorInfo,
577  "Expected device storage ID, not item type %02X",
578  tag ) );
579  }
580  readSequence( &stream, NULL );
581  status = readOctetString( &stream, storageID, &length,
583  sMemDisconnect( &stream );
584  if( cryptStatusError( status ) )
585  return( status );
586  setMessageData( &msgData, storageID, KEYID_SIZE );
587  return( krnlSendMessage( iPrivKeyContext, IMESSAGE_SETATTRIBUTE_S,
588  &msgData, CRYPT_IATTRIBUTE_DEVICESTORAGEID ) );
589  }
590  if( tag == MAKE_CTAG( CTAG_OV_DIRECTPROTECTED_EXT ) )
591  isAuthEnc = TRUE;
592  else
593  {
594  if( tag != MAKE_CTAG( CTAG_OV_DIRECTPROTECTED ) )
595  {
597  ( CRYPT_ERROR_NOTAVAIL, errorInfo,
598  "Unrecognised private-key protection type %02X",
599  tag ) );
600  }
601  }
602  readConstructed( &stream, NULL,
603  isAuthEnc ? CTAG_OV_DIRECTPROTECTED_EXT : \
605  readShortInteger( &stream, NULL );
606  status = readSet( &stream, NULL );
607  if( cryptStatusOK( status ) )
608  status = queryAsn1Object( &stream, &queryInfo );
609  if( cryptStatusOK( status ) && \
610  queryInfo.type != CRYPT_OBJECT_ENCRYPTED_KEY )
611  status = CRYPT_ERROR_BADDATA;
612  if( cryptStatusError( status ) )
613  {
614  sMemDisconnect( &stream );
615  zeroise( &queryInfo, sizeof( QUERY_INFO ) );
616  retExt( status,
617  ( status, errorInfo,
618  "Invalid encrypted private key data header" ) );
619  }
620  status = sMemGetDataBlock( &stream, &encryptedKey, queryInfo.size );
621  if( cryptStatusOK( status ) )
622  status = readUniversal( &stream ); /* Skip the exported key */
623  if( cryptStatusError( status ) )
624  {
625  sMemDisconnect( &stream );
626  zeroise( &queryInfo, sizeof( QUERY_INFO ) );
627  return( status );
628  }
629 
630  /* Read the header for the encrypted key and make sure that all of the
631  encrypted key data (and the trailing MAC value if we're using
632  authenticated encryption) is present in the stream */
633  status = readCMSencrHeader( &stream, dataOIDinfo,
634  FAILSAFE_ARRAYSIZE( dataOIDinfo, OID_INFO ), &iCryptContext,
635  &contentQueryInfo, isAuthEnc ? \
638  if( cryptStatusOK( status ) )
639  {
640  encryptedContentLength = contentQueryInfo.size;
641  status = sMemGetDataBlock( &stream, &encryptedContent,
642  encryptedContentLength );
643  if( cryptStatusOK( status ) )
644  status = sSkip( &stream, encryptedContentLength );
645  if( cryptStatusOK( status ) && \
646  ( encryptedContentLength < MIN_OBJECT_SIZE || \
647  encryptedContentLength > MAX_INTLENGTH_SHORT ) )
648  {
649  /* Too-small object */
650  status = CRYPT_ERROR_BADDATA;
651  }
652  }
653  if( cryptStatusOK( status ) && isAuthEnc )
654  {
655  /* If we're using authenticated encryption then the encrypted key
656  data is followed by a MAC value */
657  status = readOctetString( &stream, macValue, &macValueLength, 16,
659  }
660  sMemDisconnect( &stream );
661  if( cryptStatusError( status ) )
662  {
663  zeroise( &queryInfo, sizeof( QUERY_INFO ) );
664  zeroise( &contentQueryInfo, sizeof( QUERY_INFO ) );
665  retExt( status,
666  ( status, errorInfo,
667  "Invalid encrypted private key data" ) );
668  }
669 
670  /* Import the session key using the user password */
671  status = importSessionKey( iCryptContext, encryptedKey, queryInfo.size,
672  password, passwordLength, &queryInfo );
673  zeroise( &queryInfo, sizeof( QUERY_INFO ) );
674  if( cryptStatusError( status ) )
675  {
676  krnlSendNotifier( iCryptContext, IMESSAGE_DECREFCOUNT );
677  zeroise( &contentQueryInfo, sizeof( QUERY_INFO ) );
678  retExt( status,
679  ( status, errorInfo,
680  "Couldn't import the session key used to protect the "
681  "private key" ) );
682  }
683 
684  /* If we're using authenticated encryption then we have to use an
685  intermediate step that transforms the generic-secret context into
686  distinct decryption and MAC contexts */
687  if( isAuthEnc )
688  {
689  const CRYPT_CONTEXT iGenericContext = iCryptContext;
690 
691  status = initKeys( iGenericContext, &iCryptContext, &iMacContext,
692  &iMacAltContext, &contentQueryInfo );
693  krnlSendNotifier( iGenericContext, IMESSAGE_DECREFCOUNT );
694  if( cryptStatusError( status ) )
695  {
696  zeroise( &contentQueryInfo, sizeof( QUERY_INFO ) );
697  retExt( status,
698  ( status, errorInfo,
699  "Couldn't recreate encryption and MAC keys needed to "
700  "unwrap the private key" ) );
701  }
702  }
703  zeroise( &contentQueryInfo, sizeof( QUERY_INFO ) );
704 
705  /* If we're using authenticated encryption, verify the integrity of the
706  encrypted private key before trying to process it */
707  if( isAuthEnc )
708  {
709  status = krnlSendMessage( iMacContext, IMESSAGE_CTX_HASH,
710  encryptedContent, encryptedContentLength );
711  if( cryptStatusOK( status ) )
712  status = krnlSendMessage( iMacContext, IMESSAGE_CTX_HASH, "", 0 );
713  if( cryptStatusOK( status ) )
714  {
715  setMessageData( &msgData, macValue, macValueLength );
716  status = krnlSendMessage( iMacContext, IMESSAGE_COMPARE,
717  &msgData, MESSAGE_COMPARE_HASH );
718  if( cryptStatusError( status ) )
719  {
720  /* A failed MAC check is reported as a CRYPT_ERROR
721  comparison result */
722  status = CRYPT_ERROR_SIGNATURE;
723  }
724  }
725 #ifdef MAC_KEYSIZE_BUG
726  if( status == CRYPT_ERROR_SIGNATURE )
727  {
728  /* If we got a failed MAC check, try again with a MAC context
729  with a 128-bit MAC key, a bug in cryptlib version 3.4.0 */
730  status = krnlSendMessage( iMacAltContext, IMESSAGE_CTX_HASH,
731  encryptedContent,
732  encryptedContentLength );
733  if( cryptStatusOK( status ) )
734  status = krnlSendMessage( iMacAltContext, IMESSAGE_CTX_HASH,
735  "", 0 );
736  if( cryptStatusOK( status ) )
737  {
738  setMessageData( &msgData, macValue, macValueLength );
739  status = krnlSendMessage( iMacAltContext, IMESSAGE_COMPARE,
740  &msgData, MESSAGE_COMPARE_HASH );
741  if( cryptStatusError( status ) )
742  {
743  /* A failed MAC check is reported as a CRYPT_ERROR
744  comparison result */
745  status = CRYPT_ERROR_SIGNATURE;
746  }
747  }
748  }
749  krnlSendNotifier( iMacAltContext, IMESSAGE_DECREFCOUNT );
750 #endif /* MAC_KEYSIZE_BUG */
751  krnlSendNotifier( iMacContext, IMESSAGE_DECREFCOUNT );
752  if( cryptStatusError( status ) )
753  {
754  /* We convert any failure status encountered at this point into
755  a generic signature-check failure, which makes explicit
756  what's going on */
757  krnlSendNotifier( iCryptContext, IMESSAGE_DECREFCOUNT );
759  ( CRYPT_ERROR_SIGNATURE, errorInfo,
760  "Private-key integrity check failed" ) );
761  }
762  }
763 
764  /* Import the encrypted key into the PKC context */
765  setMechanismWrapInfo( &mechanismInfo, ( MESSAGE_CAST ) encryptedContent,
766  encryptedContentLength, NULL, 0, iPrivKeyContext,
767  iCryptContext );
769  &mechanismInfo, MECHANISM_PRIVATEKEYWRAP );
770  clearMechanismInfo( &mechanismInfo );
771  krnlSendNotifier( iCryptContext, IMESSAGE_DECREFCOUNT );
772  if( cryptStatusError( status ) )
773  {
774  /* We can end up here due to a whole range of possible low-level
775  problems, to make things easier on the caller we provide a
776  somewhat more detailed breakdown of possible causes */
777  switch( status )
778  {
780  retExt( status,
781  ( status, errorInfo,
782  "Couldn't unwrap private key, probably due to "
783  "incorrect decryption key being used" ) );
784 
785  case CRYPT_ERROR_BADDATA:
786  retExt( status,
787  ( status, errorInfo,
788  "Private key data corrupted or invalid" ) );
789 
790  case CRYPT_ERROR_INVALID:
791  retExt( status,
792  ( status, errorInfo,
793  "Private key components failed validity check" ) );
794 
795  default:
796  retExt( status,
797  ( status, errorInfo,
798  "Couldn't unwrap/import private key" ) );
799  }
800  }
801 
802  return( CRYPT_OK );
803  }
804 #endif /* USE_PKCS15 */