cryptlib  3.4.1
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Macros
keyex_int.c
Go to the documentation of this file.
1 /****************************************************************************
2 * *
3 * Internal Key Exchange Routines *
4 * Copyright Peter Gutmann 1993-2007 *
5 * *
6 ****************************************************************************/
7 
8 #if defined( INC_ALL )
9  #include "crypt.h"
10  #include "asn1.h"
11  #include "mech.h"
12  #include "pgp.h"
13 #else
14  #include "crypt.h"
15  #include "enc_dec/asn1.h"
16  #include "mechs/mech.h"
17  #include "misc/pgp.h"
18 #endif /* Compiler-specific includes */
19 
20 /****************************************************************************
21 * *
22 * Low-level Key Export Functions *
23 * *
24 ****************************************************************************/
25 
26 /* Export a conventionally encrypted session key */
27 
29 int exportConventionalKey( OUT_BUFFER_OPT( encryptedKeyMaxLength, *encryptedKeyLength ) \
30  void *encryptedKey,
35  IN_ENUM( KEYEX ) const KEYEX_TYPE keyexType )
36  {
38  const WRITEKEK_FUNCTION writeKeyexFunction = getWriteKekFunction( keyexType );
39  BYTE buffer[ CRYPT_MAX_KEYSIZE + 16 + 8 ];
40  BYTE *bufPtr = ( encryptedKey == NULL ) ? NULL : buffer;
41  const int bufSize = ( encryptedKey == NULL ) ? 0 : CRYPT_MAX_KEYSIZE + 16;
42  int keySize, ivSize, status;
43 
44  assert( ( encryptedKey == NULL && encryptedKeyMaxLength == 0 ) || \
45  isWritePtr( encryptedKey, encryptedKeyMaxLength ) );
46  assert( isWritePtr( encryptedKeyLength, sizeof( int ) ) );
47 
48  REQUIRES( ( encryptedKey == NULL && encryptedKeyMaxLength == 0 ) || \
49  ( encryptedKey != NULL && \
50  encryptedKeyMaxLength > MIN_CRYPT_OBJECTSIZE && \
51  encryptedKeyMaxLength < MAX_INTLENGTH ) );
52  REQUIRES( ( keyexType == KEYEX_PGP && \
53  iSessionKeyContext == CRYPT_UNUSED ) || \
54  ( keyexType != KEYEX_PGP && \
55  isHandleRangeValid( iSessionKeyContext ) ) );
56  REQUIRES( isHandleRangeValid( iExportContext ) );
57  REQUIRES( keyexType > KEYEX_NONE && keyexType < KEYEX_LAST );
58 
59  /* Clear return value */
60  *encryptedKeyLength = 0;
61 
62  /* Make sure that the requested key exchange format is available */
63  if( writeKeyexFunction == NULL )
64  return( CRYPT_ERROR_NOTAVAIL );
65 
66 #ifdef USE_PGP
67  /* PGP doesn't actually wrap up a key but derives the session key
68  directly from the password. Because of this there isn't any key
69  wrapping to be done so we just write the key derivation parameters
70  and exit */
71  if( keyexType == KEYEX_PGP )
72  {
73  STREAM stream;
74 
75  sMemOpenOpt( &stream, encryptedKey, encryptedKeyMaxLength );
76  status = writeKeyexFunction( &stream, iExportContext, NULL, 0 );
77  if( cryptStatusOK( status ) )
78  *encryptedKeyLength = stell( &stream );
79  sMemDisconnect( &stream );
80 
81  return( status );
82  }
83 #endif /* USE_PGP */
84 
85  /* Get the export parameters */
86  status = krnlSendMessage( iSessionKeyContext, IMESSAGE_GETATTRIBUTE,
87  &keySize, CRYPT_CTXINFO_KEYSIZE );
88  if( cryptStatusError( status ) )
89  return( cryptArgError( status ) ? CRYPT_ARGERROR_NUM1 : status );
90  if( cryptStatusError( krnlSendMessage( iExportContext,
91  IMESSAGE_GETATTRIBUTE, &ivSize,
93  ivSize = 0;
94 
95  /* Load an IV into the exporting context. This is somewhat nasty in that
96  a side-effect of exporting a key is to load an IV, which isn't really
97  part of the function's job description. The alternative would be to
98  require the user to explicitly load an IV before exporting the key,
99  but this is equally nasty because they'll never remember. The lesser
100  of the two evils is to load the IV here and assume that anyone
101  loading the IV themselves will read the docs, which warn about the
102  side-effects of exporting a key.
103 
104  Note that we always load a new IV when we export a key because the
105  caller may be using the context to exchange multiple keys. Since each
106  exported key requires its own IV we perform an unconditional reload.
107  In addition because we don't want another thread coming along and
108  changing the IV while we're in the process of encrypting with it, we
109  lock the exporting key object until the encryption has completed and
110  the IV is written to the output */
111  status = krnlSendMessage( iExportContext, IMESSAGE_SETATTRIBUTE,
112  MESSAGE_VALUE_TRUE, CRYPT_IATTRIBUTE_LOCKED );
113  if( cryptStatusError( status ) )
114  return( status );
115  if( ivSize > 0 )
116  {
117  status = krnlSendNotifier( iExportContext, IMESSAGE_CTX_GENIV );
118  if( cryptStatusError( status ) )
119  return( status );
120  }
121 
122  /* Encrypt the session key and write the result to the output stream */
123  setMechanismWrapInfo( &mechanismInfo, bufPtr, bufSize, NULL, 0,
124  iSessionKeyContext, iExportContext );
126  &mechanismInfo, MECHANISM_ENC_CMS );
127  if( cryptStatusOK( status ) )
128  {
129  STREAM stream;
130 
131  /* If we're perfoming a dummy export for a length check, set up a
132  dummy value to write */
133  if( encryptedKey == NULL )
134  memset( buffer, 0x01, mechanismInfo.wrappedDataLength );
135 
136  sMemOpenOpt( &stream, encryptedKey, encryptedKeyMaxLength );
137  status = writeKeyexFunction( &stream, iExportContext,
138  ( encryptedKey != NULL ) ? \
139  mechanismInfo.wrappedData : buffer,
140  mechanismInfo.wrappedDataLength );
141  if( cryptStatusOK( status ) )
142  *encryptedKeyLength = stell( &stream );
143  sMemDisconnect( &stream );
144  }
145  ( void ) krnlSendMessage( iExportContext, IMESSAGE_SETATTRIBUTE,
146  MESSAGE_VALUE_FALSE, CRYPT_IATTRIBUTE_LOCKED );
147  clearMechanismInfo( &mechanismInfo );
148  zeroise( buffer, CRYPT_MAX_KEYSIZE + 16 );
149  return( status );
150  }
151 
152 /* Export a public-key encrypted session key */
153 
155 int exportPublicKey( OUT_BUFFER_OPT( encryptedKeyMaxLength, *encryptedKeyLength ) \
156  void *encryptedKey,
157  IN_LENGTH const int encryptedKeyMaxLength,
158  OUT_LENGTH_Z int *encryptedKeyLength,
159  IN_HANDLE const CRYPT_CONTEXT iSessionKeyContext,
160  IN_HANDLE const CRYPT_CONTEXT iExportContext,
161  IN_BUFFER_OPT( auxInfoLength ) const void *auxInfo,
163  IN_ENUM( KEYEX ) const KEYEX_TYPE keyexType )
164  {
166  const WRITEKEYTRANS_FUNCTION writeKetransFunction = getWriteKeytransFunction( keyexType );
168  BYTE *bufPtr = ( encryptedKey == NULL ) ? NULL : buffer;
169  const int bufSize = ( encryptedKey == NULL ) ? 0 : MAX_PKCENCRYPTED_SIZE;
170  int keySize, status;
171 
172  assert( ( encryptedKey == NULL && encryptedKeyMaxLength == 0 ) || \
173  isWritePtr( encryptedKey, encryptedKeyMaxLength ) );
174  assert( isWritePtr( encryptedKeyLength, sizeof( int ) ) );
175  assert( ( auxInfo == NULL && auxInfoLength == 0 ) || \
176  isReadPtr( auxInfo, auxInfoLength ) );
177 
178  REQUIRES( ( encryptedKey == NULL && encryptedKeyMaxLength == 0 ) || \
179  ( encryptedKey != NULL && \
180  encryptedKeyMaxLength > MIN_CRYPT_OBJECTSIZE && \
181  encryptedKeyMaxLength < MAX_INTLENGTH ) );
182  REQUIRES( isHandleRangeValid( iSessionKeyContext ) );
183  REQUIRES( isHandleRangeValid( iExportContext ) );
184  REQUIRES( ( auxInfo == NULL && auxInfoLength == 0 ) || \
185  ( auxInfo != NULL && \
186  auxInfoLength > 0 && auxInfoLength < MAX_INTLENGTH_SHORT ) );
187  REQUIRES( keyexType > KEYEX_NONE && keyexType < KEYEX_LAST );
188 
189  /* Clear return value */
190  *encryptedKeyLength = 0;
191 
192  /* Make sure that the requested key exchange format is available */
193  if( writeKetransFunction == NULL )
194  return( CRYPT_ERROR_NOTAVAIL );
195 
196  /* Get the export parameters */
197  status = krnlSendMessage( iSessionKeyContext, IMESSAGE_GETATTRIBUTE,
198  &keySize, CRYPT_CTXINFO_KEYSIZE );
199  if( cryptStatusError( status ) )
200  return( cryptArgError( status ) ? CRYPT_ARGERROR_NUM1 : status );
201 
202  /* Encrypt the session key and write the result to the output stream */
203  setMechanismWrapInfo( &mechanismInfo, bufPtr, bufSize, NULL, 0,
204  iSessionKeyContext, iExportContext );
205  status = krnlSendMessage( iExportContext, IMESSAGE_DEV_EXPORT,
206  &mechanismInfo, ( keyexType == KEYEX_PGP ) ? \
209  if( cryptStatusOK( status ) )
210  {
211  STREAM stream;
212 
213  /* If we're perfoming a dummy export for a length check, set up a
214  dummy value to write */
215  if( encryptedKey == NULL )
216  memset( buffer, 0x01, mechanismInfo.wrappedDataLength );
217 
218  sMemOpenOpt( &stream, encryptedKey, encryptedKeyMaxLength );
219  status = writeKetransFunction ( &stream, iExportContext,
220  ( encryptedKey != NULL ) ? \
221  mechanismInfo.wrappedData : buffer,
222  mechanismInfo.wrappedDataLength,
223  auxInfo, auxInfoLength );
224  if( cryptStatusOK( status ) )
225  *encryptedKeyLength = stell( &stream );
226  sMemDisconnect( &stream );
227  }
228  clearMechanismInfo( &mechanismInfo );
229 
230  /* Clean up */
231  zeroise( buffer, MAX_PKCENCRYPTED_SIZE );
232  return( status );
233  }
234 
235 /****************************************************************************
236 * *
237 * Low-level Key Import Functions *
238 * *
239 ****************************************************************************/
240 
241 /* Import a conventionally encrypted session key */
242 
244 int importConventionalKey( IN_BUFFER( encryptedKeyLength ) \
245  const void *encryptedKey,
246  IN_LENGTH_SHORT const int encryptedKeyLength,
247  IN_HANDLE const CRYPT_CONTEXT iSessionKeyContext,
249  IN_ENUM( KEYEX ) const KEYEX_TYPE keyexType )
250  {
252  const READKEK_FUNCTION readKeyexFunction = getReadKekFunction( keyexType );
255  STREAM stream;
256  int importAlgo, importMode = DUMMY_INIT, status; /* int vs.enum */
257 
258  assert( isReadPtr( encryptedKey, encryptedKeyLength ) );
259 
260  REQUIRES( encryptedKeyLength > MIN_CRYPT_OBJECTSIZE && \
261  encryptedKeyLength < MAX_INTLENGTH_SHORT );
262  REQUIRES( isHandleRangeValid( iSessionKeyContext ) );
263  REQUIRES( isHandleRangeValid( iImportContext ) );
264  REQUIRES( keyexType > KEYEX_NONE && keyexType < KEYEX_LAST );
265 
266  /* Make sure that the requested key exchange format is available */
267  if( readKeyexFunction == NULL )
268  return( CRYPT_ERROR_NOTAVAIL );
269 
270  /* Get the import parameters */
271  status = krnlSendMessage( iImportContext, IMESSAGE_GETATTRIBUTE,
272  &importAlgo, CRYPT_CTXINFO_ALGO );
273  if( cryptStatusOK( status ) )
274  status = krnlSendMessage( iImportContext, IMESSAGE_GETATTRIBUTE,
275  &importMode, CRYPT_CTXINFO_MODE );
276  if( cryptStatusError( status ) )
277  return( cryptArgError( status ) ? CRYPT_ARGERROR_NUM2 : status );
278 
279  /* Read and check the encrypted key record and make sure that we'll be
280  using the correct type of encryption context to decrypt it */
281  sMemConnect( &stream, encryptedKey, encryptedKeyLength );
282  status = readKeyexFunction( &stream, &queryInfo );
283  sMemDisconnect( &stream );
284  if( cryptStatusOK( status ) && \
285  ( importAlgo != queryInfo.cryptAlgo || \
286  importMode != queryInfo.cryptMode ) )
287  status = CRYPT_ARGERROR_NUM1;
288  if( cryptStatusError( status ) )
289  {
290  zeroise( &queryInfo, sizeof( QUERY_INFO ) );
291  return( status );
292  }
293 
294  /* Extract the encrypted key from the buffer and decrypt it. Since we
295  don't want another thread changing the IV while we're using the import
296  context, we lock it for the duration */
297  status = krnlSendMessage( iImportContext, IMESSAGE_SETATTRIBUTE,
298  MESSAGE_VALUE_TRUE, CRYPT_IATTRIBUTE_LOCKED );
299  if( cryptStatusError( status ) )
300  {
301  zeroise( &queryInfo, sizeof( QUERY_INFO ) );
302  return( status );
303  }
304  if( needsIV( importMode ) && importAlgo != CRYPT_ALGO_RC4 )
305  {
306  setMessageData( &msgData, queryInfo.iv, queryInfo.ivLength );
307  status = krnlSendMessage( iImportContext, IMESSAGE_SETATTRIBUTE_S,
308  &msgData, CRYPT_CTXINFO_IV );
309  if( cryptStatusError( status ) )
310  {
311  ( void ) krnlSendMessage( iImportContext, IMESSAGE_SETATTRIBUTE,
313  CRYPT_IATTRIBUTE_LOCKED );
314  zeroise( &queryInfo, sizeof( QUERY_INFO ) );
315  return( status );
316  }
317  }
318  ENSURES( rangeCheck( queryInfo.dataStart, queryInfo.dataLength,
319  encryptedKeyLength ) );
320  setMechanismWrapInfo( &mechanismInfo,
321  ( BYTE * ) encryptedKey + queryInfo.dataStart,
322  queryInfo.dataLength, NULL, 0,
323  iSessionKeyContext, iImportContext );
325  &mechanismInfo, MECHANISM_ENC_CMS );
326  ( void ) krnlSendMessage( iImportContext, IMESSAGE_SETATTRIBUTE,
328  CRYPT_IATTRIBUTE_LOCKED );
329  clearMechanismInfo( &mechanismInfo );
330  zeroise( &queryInfo, sizeof( QUERY_INFO ) );
331 
332  return( status );
333  }
334 
335 /* Import a public-key encrypted session key */
336 
338 int importPublicKey( IN_BUFFER( encryptedKeyLength ) const void *encryptedKey,
339  IN_LENGTH_SHORT const int encryptedKeyLength,
340  IN_HANDLE_OPT const CRYPT_CONTEXT iSessionKeyContext,
341  IN_HANDLE const CRYPT_CONTEXT iImportContext,
343  IN_ENUM( KEYEX ) const KEYEX_TYPE keyexType )
344  {
346  const READKEYTRANS_FUNCTION readKetransFunction = getReadKeytransFunction( keyexType );
349  STREAM stream;
350  int compareType, status;
351 
352  assert( isReadPtr( encryptedKey, encryptedKeyLength ) );
353  assert( ( keyexType == KEYEX_PGP && \
354  isWritePtr( iReturnedContext, sizeof( CRYPT_CONTEXT ) ) ) || \
355  ( keyexType != KEYEX_PGP && iReturnedContext == NULL ) );
356 
357  REQUIRES( encryptedKeyLength > MIN_CRYPT_OBJECTSIZE && \
358  encryptedKeyLength < MAX_INTLENGTH );
359  REQUIRES( ( keyexType == KEYEX_PGP && \
360  iSessionKeyContext == CRYPT_UNUSED ) || \
361  ( keyexType != KEYEX_PGP && \
362  isHandleRangeValid( iSessionKeyContext ) ) );
363  REQUIRES( isHandleRangeValid( iImportContext ) );
364  REQUIRES( ( keyexType == KEYEX_PGP && iReturnedContext != NULL ) || \
365  ( keyexType != KEYEX_PGP && iReturnedContext == NULL ) );
366  REQUIRES( keyexType > KEYEX_NONE && keyexType < KEYEX_LAST );
367 
368  /* Clear return value */
369  if( iReturnedContext != NULL )
370  *iReturnedContext = CRYPT_ERROR;
371 
372  /* Make sure that the requested key exchange format is available */
373  if( readKetransFunction == NULL )
374  return( CRYPT_ERROR_NOTAVAIL );
375 
376  /* Read and check the encrypted key record */
377  sMemConnect( &stream, encryptedKey, encryptedKeyLength );
378  status = readKetransFunction( &stream, &queryInfo );
379  sMemDisconnect( &stream );
380  if( cryptStatusError( status ) )
381  {
382  zeroise( &queryInfo, sizeof( QUERY_INFO ) );
383  return( status );
384  }
385 
386  /* Make sure that we've been given the correct key */
387  setMessageData( &msgData, queryInfo.keyID, queryInfo.keyIDlength );
388  switch( keyexType )
389  {
390  case KEYEX_CMS:
391  setMessageData( &msgData, \
392  ( BYTE * ) encryptedKey + queryInfo.iAndSStart, \
393  queryInfo.iAndSLength );
395  break;
396 
397  case KEYEX_CRYPTLIB:
398  compareType = MESSAGE_COMPARE_KEYID;
399  break;
400 
401  case KEYEX_PGP:
402  compareType = ( queryInfo.version == PGP_VERSION_2 ) ? \
405  break;
406 
407  default:
408  retIntError();
409  }
410  status = krnlSendMessage( iImportContext, IMESSAGE_COMPARE, &msgData,
411  compareType );
412  if( cryptStatusError( status ) && \
413  compareType == MESSAGE_COMPARE_KEYID_OPENPGP )
414  {
415  /* Some broken PGP implementations put PGP 2.x IDs in packets marked
416  as OpenPGP packets so if we were doing a check for an OpenPGP ID
417  and it failed, fall back to a PGP 2.x one */
418  status = krnlSendMessage( iImportContext, IMESSAGE_COMPARE,
419  &msgData, MESSAGE_COMPARE_KEYID_PGP );
420  }
421  if( cryptStatusError( status ) )
422  {
423  /* A failed comparison is reported as a generic CRYPT_ERROR, convert
424  it into a wrong-key error */
425  zeroise( &queryInfo, sizeof( QUERY_INFO ) );
426  return( CRYPT_ERROR_WRONGKEY );
427  }
428 
429  /* Decrypt the encrypted key and load it into the context */
430  if( keyexType != KEYEX_PGP )
431  {
432  setMechanismWrapInfo( &mechanismInfo,
433  ( BYTE * ) encryptedKey + queryInfo.dataStart,
434  queryInfo.dataLength, NULL, 0,
435  iSessionKeyContext, iImportContext );
436  status = krnlSendMessage( iImportContext, IMESSAGE_DEV_IMPORT,
437  &mechanismInfo, MECHANISM_ENC_PKCS1 );
438  }
439  else
440  {
441  /* PGP doesn't provide separate session key information with the
442  encrypted data but wraps it up alongside the encrypted key, so we
443  can't import the wrapped key into a context via the standard key
444  import functions but instead have to create the context as part
445  of the unwrap process */
446  setMechanismWrapInfo( &mechanismInfo,
447  ( BYTE * ) encryptedKey + queryInfo.dataStart,
448  queryInfo.dataLength, NULL, 0,
449  CRYPT_UNUSED, iImportContext );
450  status = krnlSendMessage( iImportContext, IMESSAGE_DEV_IMPORT,
451  &mechanismInfo, MECHANISM_ENC_PKCS1_PGP );
452  if( cryptStatusOK( status ) )
453  *iReturnedContext = mechanismInfo.keyContext;
454  }
455  clearMechanismInfo( &mechanismInfo );
456  zeroise( &queryInfo, sizeof( QUERY_INFO ) );
457 
458  return( status );
459  }