cryptlib  3.4.1
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Macros
pnppki.c
Go to the documentation of this file.
1 /****************************************************************************
2 * *
3 * cryptlib Plug-and-play PKI Routines *
4 * Copyright Peter Gutmann 1999-2008 *
5 * *
6 ****************************************************************************/
7 
8 #if defined( INC_ALL )
9  #include "crypt.h"
10  #include "session.h"
11  #include "cmp.h"
12 #else
13  #include "crypt.h"
14  #include "session/session.h"
15  #include "session/cmp.h"
16 #endif /* Compiler-specific includes */
17 
18 #if defined( USE_CMP ) || defined( USE_SCEP )
19 
20 /* When we generate a new key there are a variety of different key types
21  (meaning key usages) that we can generate it for, constrained to some
22  extent by what the underlying certificate management protocol supports.
23  The following values identify the key type that we need to generate */
24 
25 typedef enum {
26  KEY_TYPE_NONE, /* No key type */
27  KEY_TYPE_ENCRYPTION, /* Encryption key */
28  KEY_TYPE_SIGNATURE, /* Signature key */
29  KEY_TYPE_BOTH, /* Dual encryption/signature key */
30  KEY_TYPE_LAST /* Last possible key type */
31  } KEY_TYPE;
32 
33 /* A structure to store key type-related information, indexed by the KEY_TYPE
34  value */
35 
36 static const struct {
37  const char *label; /* Label for private key */
38  const int labelLength;
39  const int actionPerms; /* Context action perms */
40  const int keyUsage; /* Certificate key usage */
41  } keyInfo[] = {
42  { NULL, 0, /* KEY_TYPE_NONE */
44  { "Encryption key", 14, /* KEY_TYPE_ENCRYPTION */
48  { "Signature key", 13, /* KEY_TYPE_SIGNATURE */
52  { "Private key", 11, /* KEY_TYPE_BOTH */
54  MK_ACTION_PERM( MESSAGE_CTX_DECRYPT, ACTION_PERM_NONE_EXTERNAL ) | \
55  MK_ACTION_PERM( MESSAGE_CTX_SIGN, ACTION_PERM_NONE_EXTERNAL ) | \
58  { NULL, 0, 0, CRYPT_KEYUSAGE_NONE }, { NULL, 0, 0, CRYPT_KEYUSAGE_NONE }
59  };
60 
61 /****************************************************************************
62 * *
63 * Utility Routines *
64 * *
65 ****************************************************************************/
66 
67 /* Clean up an object if the PnP operation fails. This is required when
68  working with devices since we need to explicitly delete anything that
69  was created in the device as well as just deleting the cryptlib object */
70 
71 static void cleanupObject( IN_HANDLE const CRYPT_CONTEXT iPrivateKey,
72  IN_ENUM( KEY_TYPE ) const KEY_TYPE keyType )
73  {
74  CRYPT_DEVICE iCryptDevice;
75  MESSAGE_KEYMGMT_INFO deletekeyInfo;
76  int status;
77 
78  REQUIRES_V( isHandleRangeValid( iPrivateKey ) );
79  REQUIRES_V( keyType > KEY_TYPE_NONE && keyType < KEY_TYPE_LAST );
80 
81  /* Delete the cryptlib object. If it's a native object, we're done */
82  status = krnlSendMessage( iPrivateKey, IMESSAGE_GETDEPENDENT,
83  &iCryptDevice, OBJECT_TYPE_DEVICE );
84  krnlSendNotifier( iPrivateKey, IMESSAGE_DECREFCOUNT );
85  if( cryptStatusError( status ) )
86  return;
87 
88  /* Delete the key from the device. We set the item type to delete to
89  public key since the device object will interpret this correctly
90  to mean that it should also delete the associated private key. We
91  don't bother checking the return code since there's not much that
92  we can do to recover if this fails */
93  setMessageKeymgmtInfo( &deletekeyInfo, CRYPT_KEYID_NAME,
94  keyInfo[ keyType ].label,
95  keyInfo[ keyType ].labelLength, NULL, 0,
97  ( void ) krnlSendMessage( iCryptDevice, IMESSAGE_KEY_DELETEKEY,
98  &deletekeyInfo, KEYMGMT_ITEM_PUBLICKEY );
99  }
100 
101 /* Check whether a network connection is still open, used when performing
102  multiple transactions in a single session */
103 
105 static BOOLEAN isConnectionOpen( INOUT SESSION_INFO *sessionInfoPtr )
106  {
107  int streamState, status;
108 
109  assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
110 
111  status = sioctlGet( &sessionInfoPtr->stream, STREAM_IOCTL_CONNSTATE,
112  &streamState, sizeof( int ) );
113  return( cryptStatusError( status ) ? FALSE : streamState );
114  }
115 
116 /* Check for the presence of a named object in a keyset/device */
117 
118 CHECK_RETVAL_BOOL \
119 static BOOLEAN isNamedObjectPresent( IN_HANDLE const CRYPT_HANDLE iCryptHandle,
120  IN_ENUM( KEY_TYPE ) const KEY_TYPE keyType )
121  {
122  MESSAGE_KEYMGMT_INFO getkeyInfo;
123  const char *keyLabel = keyInfo[ keyType ].label;
124  const int keyLabelLength = keyInfo[ keyType ].labelLength;
125  int status;
126 
127  REQUIRES_B( isHandleRangeValid( iCryptHandle ) );
128  REQUIRES_B( keyType > KEY_TYPE_NONE && keyType < KEY_TYPE_LAST );
129 
130  setMessageKeymgmtInfo( &getkeyInfo, CRYPT_KEYID_NAME, keyLabel,
131  keyLabelLength, NULL, 0,
133  status = krnlSendMessage( iCryptHandle, IMESSAGE_KEY_GETKEY,
134  &getkeyInfo, KEYMGMT_ITEM_PUBLICKEY );
135  if( cryptStatusError( status ) )
136  {
137  setMessageKeymgmtInfo( &getkeyInfo, CRYPT_KEYID_NAME, keyLabel,
138  keyLabelLength, NULL, 0,
140  status = krnlSendMessage( iCryptHandle, IMESSAGE_KEY_GETKEY,
141  &getkeyInfo, KEYMGMT_ITEM_PRIVATEKEY );
142  }
143  return( cryptStatusOK( status ) ? TRUE : FALSE );
144  }
145 
146 /* Get the identified CA/RA certificate from a CTL */
147 
149 static int getCACert( OUT_HANDLE_OPT CRYPT_CERTIFICATE *iNewCert,
150  IN_HANDLE const CRYPT_CERTIFICATE iCTL,
151  IN_BUFFER_OPT( certIDlength ) const void *certID,
152  IN_LENGTH_KEYID_Z const int certIDlength )
153  {
154  int status;
155 
156  assert( isWritePtr( iNewCert, sizeof( CRYPT_CERTIFICATE ) ) );
157  assert( ( certID == NULL && certIDlength == 0 ) || \
158  ( isReadPtr( certID, certIDlength ) ) );
159 
160  REQUIRES( isHandleRangeValid( iCTL ) );
161  REQUIRES( ( certID == NULL && certIDlength == 0 ) || \
162  ( certID != NULL && certIDlength == KEYID_SIZE ) );
163 
164  /* Clear return value */
165  *iNewCert = CRYPT_ERROR;
166 
167  /* Step through the certificate trust list checking each certificate in
168  turn to see if it's the identified CA/RA certificate. Some CAs may
169  only send a single certificate in the CTL and not explicitly identify
170  it so if there's no certificate ID present we just use the first
171  one. Note that the limit is given as FAILSAFE_ITERATIONS_MED since
172  we're using it as a fallback check on the maximum chain length allowed
173  by the certificate-import code. In other words anything over the
174  certificate-handling code's maximum chain length is handled as a
175  normal error and it's only if we exceed this that we have an internal
176  error */
177  status = krnlSendMessage( iCTL, IMESSAGE_SETATTRIBUTE,
180  if( cryptStatusError( status ) )
181  return( status );
182  if( certIDlength > 0 )
183  {
185  int iterationCount;
186 
187  setMessageData( &msgData, ( MESSAGE_CAST ) certID, KEYID_SIZE );
188  for( iterationCount = 0; iterationCount < FAILSAFE_ITERATIONS_MED;
189  iterationCount++ )
190  {
191  status = krnlSendMessage( iCTL, IMESSAGE_COMPARE, &msgData,
193  if( cryptStatusOK( status ) )
194  {
195  /* We've found the identified certificate, we're done */
196  break;
197  }
198  status = krnlSendMessage( iCTL, IMESSAGE_SETATTRIBUTE,
201  if( cryptStatusError( status ) )
202  {
203  /* We've run out of certificates without finding a match,
204  exit */
205  return( CRYPT_ERROR_NOTFOUND );
206  }
207  }
208  ENSURES( iterationCount < FAILSAFE_ITERATIONS_MED );
209  }
210 
211  /* We've found the identified certificate, convert it from the data-only
212  form in the CTL to a full certificate that can be used to verify
213  returned data. This is easier than trying to disconnect and re-
214  connect certificate and context objects directly (ex duobus malis
215  minimum eligendum est) */
216  return( krnlSendMessage( iCTL, IMESSAGE_GETATTRIBUTE, iNewCert,
217  CRYPT_IATTRIBUTE_CERTCOPY ) );
218  }
219 
220 /****************************************************************************
221 * *
222 * Certificate Creation/Update Routines *
223 * *
224 ****************************************************************************/
225 
226 /* Generate a new key of the appropriate type. For CMP we typically
227  generate first a signature key and then an encryption key (unless it's
228  a CA key, for which we only generate a certificate-signing key), for
229  SCEP we generate a dual encryption/signature key */
230 
232 static int generateKey( OUT_HANDLE_OPT CRYPT_CONTEXT *iPrivateKey,
233  IN_HANDLE const CRYPT_USER iCryptUser,
234  IN_HANDLE const CRYPT_DEVICE iCryptDevice,
235  IN_ENUM( KEY_TYPE ) const KEY_TYPE keyType )
236  {
237  MESSAGE_CREATEOBJECT_INFO createInfo;
239  BOOLEAN substitutedAlgorithm = FALSE;
240  int pkcAlgo, keySize, status;
241 
242  assert( isWritePtr( iPrivateKey, sizeof( CRYPT_CONTEXT ) ) );
243 
244  REQUIRES( iCryptUser == DEFAULTUSER_OBJECT_HANDLE || \
245  isHandleRangeValid( iCryptUser ) );
246  REQUIRES( iCryptDevice == SYSTEM_OBJECT_HANDLE || \
247  isHandleRangeValid( iCryptDevice ) );
248  REQUIRES( keyType > KEY_TYPE_NONE && keyType < KEY_TYPE_LAST );
249 
250  /* Clear return value */
251  *iPrivateKey = CRYPT_ERROR;
252 
253  /* Get the algorithm to use for the key. We try and use the given
254  default PKC algorithm, however some devices don't support all
255  algorithm types so if this isn't available we try and fall back to
256  other choices */
257  status = krnlSendMessage( iCryptUser, IMESSAGE_GETATTRIBUTE, &pkcAlgo,
259  if( cryptStatusError( status ) )
260  return( status );
261  if( !algoAvailable( pkcAlgo ) )
262  {
263  /* The default algorithm type isn't available for this device, try
264  and fall back to an alternative */
265  switch( pkcAlgo )
266  {
267  case CRYPT_ALGO_RSA:
268  pkcAlgo = CRYPT_ALGO_DSA;
269  break;
270 
271  case CRYPT_ALGO_DSA:
272  pkcAlgo = CRYPT_ALGO_RSA;
273  break;
274 
275  default:
276  return( CRYPT_ERROR_NOTAVAIL );
277  }
278  if( !algoAvailable( pkcAlgo ) )
279  return( CRYPT_ERROR_NOTAVAIL );
280 
281  /* Remember that we've switched to a fallback algorithm */
282  substitutedAlgorithm = TRUE;
283  }
284 
285  /* Make sure that the chosen algorithm is compatible with what we're
286  trying to do */
287  switch( keyType )
288  {
289  case KEY_TYPE_ENCRYPTION:
290  /* If we're being asked for an encryption key, which in normal
291  operation implies that we've already successfully completed
292  the process of acquiring a signature key, and only a non-
293  encryption algorithm is available, we return OK_SPECIAL to
294  tell the caller that the failure is non-fatal. However if
295  we've substituted an algorithm (for example DSA when RSA was
296  requested) then we genuinely can't go any further and exit
297  with an error */
298  if( !isCryptAlgo( pkcAlgo ) )
299  return( substitutedAlgorithm ? \
301  break;
302 
303  case KEY_TYPE_SIGNATURE:
304  if( !isSigAlgo( pkcAlgo ) )
305  return( CRYPT_ERROR_NOTAVAIL );
306  break;
307 
308  case KEY_TYPE_BOTH:
309  if( !isCryptAlgo( pkcAlgo ) || !isSigAlgo( pkcAlgo ) )
310  return( CRYPT_ERROR_NOTAVAIL );
311  break;
312 
313  default:
314  retIntError();
315  }
316 
317  /* Create a new key using the given PKC algorithm and of the default
318  size */
319  setMessageCreateObjectInfo( &createInfo, pkcAlgo );
320  status = krnlSendMessage( iCryptDevice, IMESSAGE_DEV_CREATEOBJECT,
321  &createInfo, OBJECT_TYPE_CONTEXT );
322  if( cryptStatusError( status ) )
323  return( status );
324  status = krnlSendMessage( iCryptUser, IMESSAGE_GETATTRIBUTE, &keySize,
326  if( cryptStatusOK( status ) )
327  {
328  status = krnlSendMessage( createInfo.cryptHandle,
329  IMESSAGE_SETATTRIBUTE, &keySize,
331  }
332  if( cryptStatusOK( status ) )
333  {
334  setMessageData( &msgData, ( MESSAGE_CAST ) keyInfo[ keyType ].label,
335  keyInfo[ keyType ].labelLength );
336  status = krnlSendMessage( createInfo.cryptHandle,
337  IMESSAGE_SETATTRIBUTE_S, &msgData,
339  }
340  if( cryptStatusError( status ) )
341  {
343  return( status );
344  }
345 
346  /* Generate the key */
347  status = krnlSendNotifier( createInfo.cryptHandle, IMESSAGE_CTX_GENKEY );
348  if( cryptStatusOK( status ) )
349  {
350  status = krnlSendMessage( createInfo.cryptHandle,
352  ( int * ) &keyInfo[ keyType ].actionPerms,
353  CRYPT_IATTRIBUTE_ACTIONPERMS );
354  }
355  if( cryptStatusError( status ) )
356  {
358  return( status );
359  }
360  *iPrivateKey = createInfo.cryptHandle;
361 
362  return( CRYPT_OK );
363  }
364 
365 /* Create a certificate request for a key. If a certificate with a subject
366  DN template is provided we copy this into the request, otherwise we
367  create a minimal key-only request */
368 
370 static int createCertRequest( OUT_HANDLE_OPT CRYPT_CERTIFICATE *iCertReq,
371  IN_HANDLE const CRYPT_CONTEXT iPrivateKey,
372  IN_HANDLE_OPT const CRYPT_CERTIFICATE iSubjDNCert,
373  IN_ENUM( KEY_TYPE ) const KEY_TYPE keyType )
374  {
375  MESSAGE_CREATEOBJECT_INFO createInfo;
376  const BOOLEAN isPKCS10 = ( keyType == KEY_TYPE_BOTH ) ? TRUE : FALSE;
377  int status;
378 
379  assert( isWritePtr( iCertReq, sizeof( CRYPT_CONTEXT ) ) );
380 
381  REQUIRES( isHandleRangeValid( iPrivateKey ) );
382  REQUIRES( iSubjDNCert == CRYPT_UNUSED || \
383  isHandleRangeValid( iSubjDNCert ) );
384  REQUIRES( keyType > KEY_TYPE_NONE && keyType < KEY_TYPE_LAST );
385 
386  /* Clear return value */
387  *iCertReq = CRYPT_ERROR;
388 
389  /* Create the signing key certificate request */
390  setMessageCreateObjectInfo( &createInfo, isPKCS10 ? \
394  IMESSAGE_DEV_CREATEOBJECT, &createInfo,
396  if( cryptStatusError( status ) )
397  return( status );
398 
399  /* Add the key information to the request and sign it if it's a CMP
400  request. We can't sign PKCS #10 requests (for SCEP) because the
401  client session has to add further information required by the server
402  to the request before it submits it */
403  status = krnlSendMessage( createInfo.cryptHandle, IMESSAGE_SETATTRIBUTE,
404  ( int * ) &iPrivateKey,
406  if( cryptStatusOK( status ) )
407  status = krnlSendMessage( createInfo.cryptHandle, IMESSAGE_SETATTRIBUTE,
408  ( int * ) &keyInfo[ keyType ].keyUsage,
410  if( cryptStatusOK( status ) && iSubjDNCert != CRYPT_UNUSED )
411  status = krnlSendMessage( createInfo.cryptHandle, IMESSAGE_SETATTRIBUTE,
412  ( int * ) &iSubjDNCert,
414  if( cryptStatusOK( status ) && !isPKCS10 )
415  status = krnlSendMessage( createInfo.cryptHandle, IMESSAGE_CRT_SIGN,
416  NULL, iPrivateKey );
417  if( cryptStatusError( status ) )
418  {
420  return( status );
421  }
422  *iCertReq = createInfo.cryptHandle;
423 
424  return( CRYPT_OK );
425  }
426 
427 /* Update a keyset/device with a newly-created key and certificate */
428 
430 static int updateKeys( IN_HANDLE const CRYPT_HANDLE iCryptHandle,
431  IN_HANDLE const CRYPT_CONTEXT iPrivateKey,
433  IN_BUFFER( passwordLength ) const char *password,
434  IN_LENGTH_NAME const int passwordLength )
435  {
436  MESSAGE_KEYMGMT_INFO setkeyInfo;
437  int objectType, status;
438 
439  assert( isReadPtr( password, passwordLength ) );
440 
441  REQUIRES( isHandleRangeValid( iCryptHandle ) );
442  REQUIRES( isHandleRangeValid( iPrivateKey ) );
443  REQUIRES( isHandleRangeValid( iCryptCert ) );
444  REQUIRES( passwordLength >= MIN_NAME_LENGTH && \
445  passwordLength < MAX_ATTRIBUTE_SIZE );
446 
447  /* Find out whether the storage object is a keyset or a device. If it's
448  a device there's no need to add the private key since it'll have been
449  created inside the device */
450  status = krnlSendMessage( iCryptHandle, IMESSAGE_GETATTRIBUTE,
451  &objectType, CRYPT_IATTRIBUTE_TYPE );
452  if( cryptStatusError( status ) )
453  return( status );
454 
455  /* Add the private key and certificate to the keyset/device */
456  if( objectType == OBJECT_TYPE_KEYSET )
457  {
458  setMessageKeymgmtInfo( &setkeyInfo, CRYPT_KEYID_NONE, NULL, 0,
459  ( MESSAGE_CAST ) password, passwordLength,
461  setkeyInfo.cryptHandle = iPrivateKey;
462  status = krnlSendMessage( iCryptHandle, IMESSAGE_KEY_SETKEY,
463  &setkeyInfo, KEYMGMT_ITEM_PRIVATEKEY );
464  if( cryptStatusError( status ) )
465  return( status );
466  }
467  setMessageKeymgmtInfo( &setkeyInfo, CRYPT_KEYID_NONE, NULL, 0,
468  NULL, 0, KEYMGMT_FLAG_NONE );
469  setkeyInfo.cryptHandle = iCryptCert;
470  return( krnlSendMessage( iCryptHandle, IMESSAGE_KEY_SETKEY,
471  &setkeyInfo, KEYMGMT_ITEM_PUBLICKEY ) );
472  }
473 
474 /* Update the keyset/device with any required trusted certificates up to the
475  root. This ensures that we can still build a full certificate chain even
476  if the PKIBoot trusted certificates aren't preserved */
477 
478 static int updateTrustedCerts( IN_HANDLE const CRYPT_HANDLE iCryptHandle,
479  IN_HANDLE const CRYPT_HANDLE iLeafCert )
480  {
481  CRYPT_CERTIFICATE iCertCursor = iLeafCert;
482  int iterationCount, status;
483 
484  REQUIRES( isHandleRangeValid( iCryptHandle ) );
485  REQUIRES( isHandleRangeValid( iLeafCert ) );
486 
487  for( status = CRYPT_OK, iterationCount = 0;
488  cryptStatusOK( status ) && iterationCount < FAILSAFE_ITERATIONS_MED;
489  iterationCount++ )
490  {
491  MESSAGE_KEYMGMT_INFO setkeyInfo;
492 
493  /* Get the trusted issuer certificate for the current certificate
494  and send it to the keyset/device */
495  status = krnlSendMessage( iCertCursor, IMESSAGE_USER_TRUSTMGMT,
496  &iCertCursor, MESSAGE_TRUSTMGMT_GETISSUER );
497  if( cryptStatusError( status ) )
498  break;
499  setMessageKeymgmtInfo( &setkeyInfo, CRYPT_KEYID_NONE, NULL, 0,
500  NULL, 0, KEYMGMT_FLAG_NONE );
501  setkeyInfo.cryptHandle = iCertCursor;
502  status = krnlSendMessage( iCryptHandle, IMESSAGE_KEY_SETKEY,
503  &setkeyInfo, KEYMGMT_ITEM_PUBLICKEY );
504  }
505  ENSURES( iterationCount < FAILSAFE_ITERATIONS_MED );
506 
507  return( CRYPT_OK );
508  }
509 
510 /****************************************************************************
511 * *
512 * PnP PKI Session Management *
513 * *
514 ****************************************************************************/
515 
516 /* Run a plug-and-play PKI session */
517 
519 int pnpPkiSession( INOUT SESSION_INFO *sessionInfoPtr )
520  {
521  CRYPT_DEVICE iCryptDevice = SYSTEM_OBJECT_HANDLE;
522  CRYPT_CONTEXT iPrivateKey1, iPrivateKey2 ;
523  CRYPT_CERTIFICATE iCertReq, iCACert = DUMMY_INIT;
525  const ATTRIBUTE_LIST *passwordPtr = \
526  findSessionInfo( sessionInfoPtr->attributeList,
528  const KEY_TYPE keyType = ( sessionInfoPtr->type == CRYPT_SESSION_CMP ) ? \
529  KEY_TYPE_SIGNATURE : KEY_TYPE_BOTH;
530  const char *storageObjectName = "keyset";
531  BOOLEAN isCAcert;
532  int objectType, status;
533 
534  assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) );
535 
536  REQUIRES( passwordPtr != NULL );
537 
538  /* If we've been passed a device as the private-key storage location,
539  create the key in the device instead of as a local object */
540  status = krnlSendMessage( sessionInfoPtr->privKeyset,
541  IMESSAGE_GETATTRIBUTE, &objectType,
542  CRYPT_IATTRIBUTE_TYPE );
543  if( cryptStatusError( status ) )
544  return( status );
545  if( objectType == OBJECT_TYPE_DEVICE )
546  {
547  iCryptDevice = sessionInfoPtr->privKeyset;
548  storageObjectName = "device";
549  }
550 
551  /* Make sure that the named objects that are about to be created aren't
552  already present in the keyset/device */
553  if( isNamedObjectPresent( sessionInfoPtr->privKeyset, keyType ) )
554  {
557  "%s is already present in %s",
558  ( keyType == KEY_TYPE_SIGNATURE ) ? \
559  "Signature key" : "Key", storageObjectName ) );
560  }
561  if( sessionInfoPtr->type == CRYPT_SESSION_CMP )
562  {
563  if( isNamedObjectPresent( sessionInfoPtr->privKeyset,
564  KEY_TYPE_ENCRYPTION ) )
565  {
568  "Encryption key is already present in %s",
569  storageObjectName ) );
570  }
571  }
572 
573  /* Perform the PKIBoot exchange (done as part of an ir) to get the
574  initial trusted certificate set. We also set the retain-connection
575  flag since we're going to follow this with another transaction */
576  if( sessionInfoPtr->type == CRYPT_SESSION_CMP )
577  {
578  sessionInfoPtr->sessionCMP->requestType = CRYPT_REQUESTTYPE_PKIBOOT;
579  sessionInfoPtr->protocolFlags |= CMP_PFLAG_RETAINCONNECTION;
580  }
581  status = sessionInfoPtr->transactFunction( sessionInfoPtr );
582  if( cryptStatusError( status ) )
583  return( status );
584  if( !isConnectionOpen( sessionInfoPtr ) )
585  {
586  /* If the connection was shut down by the other side, signal an
587  error. This is possibly a bit excessive since we could always
588  try reactivating the session, but there's no good reason for the
589  other side to simply close the connection and requiring it to
590  remain open simplifies the implementation */
591  krnlSendNotifier( sessionInfoPtr->iCertResponse,
593  sessionInfoPtr->iCertResponse = CRYPT_ERROR;
596  "Server closed connection after PKIBoot phase before any "
597  "certificates could be issued" ) );
598  }
599 
600  /* Get the CA/RA certificate from the returned CTL and set it as the
601  certificate to use for authenticating server responses */
602  attributeListPtr = findSessionInfo( sessionInfoPtr->attributeList,
604  if( attributeListPtr != NULL )
605  {
606  status = getCACert( &iCACert, sessionInfoPtr->iCertResponse,
607  attributeListPtr->value,
608  attributeListPtr->valueLength );
609  }
610  else
611  {
612  status = getCACert( &iCACert, sessionInfoPtr->iCertResponse,
613  NULL, 0 );
614  }
615  krnlSendNotifier( sessionInfoPtr->iCertResponse, IMESSAGE_DECREFCOUNT );
616  sessionInfoPtr->iCertResponse = CRYPT_ERROR;
617  if( cryptStatusError( status ) )
618  {
619  retExt( status,
620  ( status, SESSION_ERRINFO,
621  "Couldn't read CA/RA certificate from returned "
622  "certificate trust list" ) );
623  }
624  sessionInfoPtr->iAuthInContext = iCACert;
625 
626  /* Create a private key and a certificate request for it */
627  status = generateKey( &iPrivateKey1, sessionInfoPtr->ownerHandle,
628  iCryptDevice, keyType );
629  if( cryptStatusError( status ) )
630  {
631  ENSURES( status != OK_SPECIAL );
632  retExt( status,
633  ( status, SESSION_ERRINFO, "Couldn't create %s key",
634  ( keyType == KEY_TYPE_SIGNATURE ) ? \
635  "signature" : "private" ) );
636  }
637  status = createCertRequest( &iCertReq, iPrivateKey1, CRYPT_UNUSED,
638  keyType );
639  if( cryptStatusError( status ) )
640  {
641  cleanupObject( iPrivateKey1, keyType );
642  retExt( status,
643  ( status, SESSION_ERRINFO,
644  "Couldn't create %skey certificate request",
645  ( keyType == KEY_TYPE_SIGNATURE ) ? \
646  "signature " : "" ) );
647  }
648 
649  /* Set up the request information and activate the session */
650  if( sessionInfoPtr->type == CRYPT_SESSION_CMP )
651  {
652  /* If it's CMP, start with an ir. The second certificate will be
653  fetched with a cr */
654  sessionInfoPtr->sessionCMP->requestType = \
655  CRYPT_REQUESTTYPE_INITIALISATION;
656  }
657  sessionInfoPtr->iCertRequest = iCertReq;
658  status = sessionInfoPtr->transactFunction( sessionInfoPtr );
659  krnlSendNotifier( sessionInfoPtr->iCertRequest, IMESSAGE_DECREFCOUNT );
660  sessionInfoPtr->iCertRequest = CRYPT_ERROR;
661  if( cryptStatusError( status ) )
662  {
663  cleanupObject( iPrivateKey1, keyType );
664  return( status );
665  }
666 
667  /* Check whether we've been issued a standalone CA certificate rather
668  than a standard signature certificate to be followed by an encryption
669  certificate or a standard signature + encryption certificate */
670  status = krnlSendMessage( sessionInfoPtr->iCertResponse,
671  IMESSAGE_GETATTRIBUTE, &isCAcert,
673  if( cryptStatusError( status ) )
674  isCAcert = FALSE;
675 
676  /* If the connection was shut down by the other side and we're
677  performing a multi-part operation that requires it to remain open,
678  signal an error. This is possibly a bit excessive since we could
679  always try reactivating the session, but there's no good reason for
680  the other side to simply close the connection and requiring it to
681  remain open simplifies the implementation */
682  if( sessionInfoPtr->type == CRYPT_SESSION_CMP && \
683  !isConnectionOpen( sessionInfoPtr ) && !isCAcert )
684  {
685  cleanupObject( iPrivateKey1, keyType );
686  krnlSendNotifier( sessionInfoPtr->iCertResponse,
688  sessionInfoPtr->iCertResponse = CRYPT_ERROR;
691  "Server closed connection before second (encryption) "
692  "certificate could be issued" ) );
693  }
694 
695  /* We've got the first certificate, update the keyset/device */
696  status = updateKeys( sessionInfoPtr->privKeyset, iPrivateKey1,
697  sessionInfoPtr->iCertResponse,
698  passwordPtr->value, passwordPtr->valueLength );
699  if( cryptStatusOK( status ) )
700  {
701  CRYPT_CERTIFICATE iNewCert;
702 
703  /* Recreate the certificate as a data-only certificate and attach it
704  to the signing key so that we can use it to authenticate a
705  request for an encryption key. We need to recreate the
706  certificate because we're about to attach it to the private-key
707  context for further operations, and attaching a certificate with
708  a public-key context already attached isn't possible. Even if
709  we're not getting a second certificate, we still need the current
710  certificate attached so that we can use it as the base
711  certificate for the trusted certificate update that we perform
712  before we exit */
713  status = krnlSendMessage( sessionInfoPtr->iCertResponse,
714  IMESSAGE_GETATTRIBUTE, &iNewCert,
715  CRYPT_IATTRIBUTE_CERTCOPY_DATAONLY );
716  if( cryptStatusOK( status ) )
717  krnlSendMessage( iPrivateKey1, IMESSAGE_SETDEPENDENT, &iNewCert,
719  }
720  krnlSendNotifier( sessionInfoPtr->iCertResponse, IMESSAGE_DECREFCOUNT );
721  sessionInfoPtr->iCertResponse = CRYPT_ERROR;
722  if( cryptStatusError( status ) )
723  {
724  cleanupObject( iPrivateKey1, keyType );
725  retExt( ( status == CRYPT_ARGERROR_NUM1 ) ? \
726  CRYPT_ERROR_INVALID : status,
727  ( ( status == CRYPT_ARGERROR_NUM1 ) ? \
729  "Couldn't update %s with %skey/certificate",
730  storageObjectName, isCAcert ? "CA " : \
731  ( keyType == KEY_TYPE_SIGNATURE ) ? "signature " : "" ) );
732  }
733 
734  /* If it's a combined encryption/signature key or a standalone CA key,
735  we're done. See the comment at the end of this function for the
736  details of the trusted-certificates update process */
737  if( keyType == KEY_TYPE_BOTH || isCAcert )
738  {
739  updateTrustedCerts( sessionInfoPtr->privKeyset, iPrivateKey1 );
740  krnlSendNotifier( iPrivateKey1, IMESSAGE_DECREFCOUNT );
741  return( CRYPT_OK );
742  }
743 
744  /* We're running a CMP session from this point on */
745  ENSURES( sessionInfoPtr->type == CRYPT_SESSION_CMP );
746 
747  /* Create the second, encryption private key and a certificate request
748  for it */
749  status = generateKey( &iPrivateKey2, sessionInfoPtr->ownerHandle,
750  iCryptDevice, KEY_TYPE_ENCRYPTION );
751  if( status == OK_SPECIAL )
752  {
753  /* Encryption isn't available via this device, exit without going
754  through the second phase of the exchange, leaving only the
755  signature key and certificates set up */
756  updateTrustedCerts( sessionInfoPtr->privKeyset, iPrivateKey1 );
757  krnlSendNotifier( iPrivateKey1, IMESSAGE_DECREFCOUNT );
758  return( CRYPT_OK );
759  }
760  if( cryptStatusError( status ) )
761  {
762  cleanupObject( iPrivateKey1, KEY_TYPE_SIGNATURE );
763  retExt( status,
764  ( status, SESSION_ERRINFO,
765  "Couldn't create encryption key" ) );
766  }
767  status = createCertRequest( &iCertReq, iPrivateKey2, iPrivateKey1,
768  KEY_TYPE_ENCRYPTION );
769  if( cryptStatusError( status ) )
770  {
771  cleanupObject( iPrivateKey1, KEY_TYPE_SIGNATURE );
772  cleanupObject( iPrivateKey2, KEY_TYPE_ENCRYPTION );
773  retExt( status,
774  ( status, SESSION_ERRINFO,
775  "Couldn't create encryption key certificate request" ) );
776  }
777 
778  /* Set up the request information and activate the session. This
779  request is slightly different to the previous one since we now have a
780  signature certificate that we can use to authenticate the request (in
781  fact we have to use this since we can't authenticate the message with
782  an encryption-only key). In addition since this is the last
783  transaction we turn off the retain-connection flag */
784  sessionInfoPtr->protocolFlags &= ~CMP_PFLAG_RETAINCONNECTION;
785  sessionInfoPtr->sessionCMP->requestType = CRYPT_REQUESTTYPE_CERTIFICATE;
786  sessionInfoPtr->iCertRequest = iCertReq;
787  sessionInfoPtr->privateKey = iPrivateKey2;
788  sessionInfoPtr->iAuthOutContext = iPrivateKey1;
789  status = sessionInfoPtr->transactFunction( sessionInfoPtr );
790  sessionInfoPtr->privateKey = CRYPT_ERROR;
791  sessionInfoPtr->iAuthOutContext = CRYPT_ERROR;
792  krnlSendNotifier( sessionInfoPtr->iCertRequest, IMESSAGE_DECREFCOUNT );
793  sessionInfoPtr->iCertRequest = CRYPT_ERROR;
794  if( cryptStatusError( status ) )
795  {
796  cleanupObject( iPrivateKey1, KEY_TYPE_SIGNATURE );
797  cleanupObject( iPrivateKey2, KEY_TYPE_ENCRYPTION );
798  return( status );
799  }
800 
801  /* We've got the second certificate, update the keyset/device */
802  status = updateKeys( sessionInfoPtr->privKeyset, iPrivateKey2,
803  sessionInfoPtr->iCertResponse,
804  passwordPtr->value, passwordPtr->valueLength );
805  krnlSendNotifier( sessionInfoPtr->iCertResponse, IMESSAGE_DECREFCOUNT );
806  sessionInfoPtr->iCertResponse = CRYPT_ERROR;
807  if( cryptStatusError( status ) )
808  {
809  cleanupObject( iPrivateKey1, KEY_TYPE_SIGNATURE );
810  cleanupObject( iPrivateKey2, KEY_TYPE_ENCRYPTION );
811  retExt( status,
812  ( status, SESSION_ERRINFO,
813  "Couldn't update %s with encryption key/certificate",
814  storageObjectName ) );
815  }
816 
817  /* Finally, update the keyset/device with any required trusted
818  certificates up to the root. This ensures that we can still build a
819  full certificate chain even if the PKIBoot trusted certificates
820  aren't preserved. We don't check for errors from this function since
821  it's not worth aborting the process for some minor CA certificate
822  update problem, the user keys and certificates will still function
823  without them */
824  updateTrustedCerts( sessionInfoPtr->privKeyset, iPrivateKey1 );
825 
826  /* Both keys were certified and the keys and certificates sent to the
827  keyset/device, we're done */
828  krnlSendNotifier( iPrivateKey1, IMESSAGE_DECREFCOUNT );
829  krnlSendNotifier( iPrivateKey2, IMESSAGE_DECREFCOUNT );
830  return( CRYPT_OK );
831  }
832 #endif /* USE_CMP || USE_SCEP */