cryptlib  3.4.1
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Macros
mech_cwrap.c
Go to the documentation of this file.
1 /****************************************************************************
2 * *
3 * cryptlib Conventional Key Wrap Mechanism Routines *
4 * Copyright Peter Gutmann 1992-2008 *
5 * *
6 ****************************************************************************/
7 
8 #ifdef INC_ALL
9  #include "crypt.h"
10  #include "mech_int.h"
11 #else
12  #include "crypt.h"
13  #include "mechs/mech_int.h"
14 #endif /* Compiler-specific includes */
15 
16 /* The size of the key block header, an 8-bit length followed by a 24-bit
17  check value */
18 
19 #define CMS_KEYBLOCK_HEADERSIZE 4
20 
21 /****************************************************************************
22 * *
23 * CMS Wrap/Unwrap Mechanisms *
24 * *
25 ****************************************************************************/
26 
27 /* Determine how many padding bytes are required for a CMS conventional key
28  wrap */
29 
31 static int getPadSize( IN_HANDLE const CRYPT_CONTEXT iExportContext,
33  const int payloadSize,
34  OUT_LENGTH_SHORT_Z int *padSize )
35  {
36  int blockSize, totalSize, status;
37 
38  assert( isWritePtr( padSize, sizeof( int ) ) );
39 
40  REQUIRES( isHandleRangeValid( iExportContext ) );
41  REQUIRES( payloadSize >= MIN_KEYSIZE && \
42  payloadSize <= CRYPT_MAX_KEYSIZE );
43 
44  /* Clear return value */
45  *padSize = 0;
46 
47  status = krnlSendMessage( iExportContext, IMESSAGE_GETATTRIBUTE,
48  &blockSize, CRYPT_CTXINFO_IVSIZE );
49  if( cryptStatusError( status ) )
50  return( status );
51 
52  /* Determine the padding size, which is the amount of padding required to
53  bring the total data size up to a multiple of the block size with a
54  minimum size of two blocks. Unlike PKCS #5 padding, the total may be
55  zero */
56  totalSize = roundUp( payloadSize, blockSize );
57  if( totalSize < blockSize * 2 )
58  totalSize = blockSize * 2;
59  ENSURES( !( totalSize & ( blockSize - 1 ) ) );
60  *padSize = totalSize - payloadSize;
61 
62  return( CRYPT_OK );
63  }
64 
65 /****************************************************************************
66 * *
67 * CMS Wrap/Unwrap Mechanisms *
68 * *
69 ****************************************************************************/
70 
71 /* Perform CMS data wrapping */
72 
74 int exportCMS( STDC_UNUSED void *dummy,
76  {
78  BYTE *keyBlockPtr = ( BYTE * ) mechanismInfo->wrappedData;
79  BYTE dataSample[ 16 + 8 ];
80  int keySize, padSize, status = CRYPT_OK;
81 
82  UNUSED_ARG( dummy );
83  assert( isWritePtr( mechanismInfo, sizeof( MECHANISM_WRAP_INFO ) ) );
84 
85  /* Clear the return value */
86  if( mechanismInfo->wrappedData != NULL )
87  {
88  memset( mechanismInfo->wrappedData, 0,
89  mechanismInfo->wrappedDataLength );
90  }
91 
92  /* Get the key payload details from the key contexts */
93  status = krnlSendMessage( mechanismInfo->keyContext,
94  IMESSAGE_GETATTRIBUTE, &keySize,
96  if( cryptStatusError( status ) )
97  return( status );
98  status = getPadSize( mechanismInfo->wrapContext,
99  CMS_KEYBLOCK_HEADERSIZE + keySize, &padSize );
100  if( cryptStatusError( status ) )
101  return( status );
102  ENSURES( padSize >= 0 && padSize <= CRYPT_MAX_IVSIZE );
103 
104  /* If this is just a length check, we're done */
105  if( mechanismInfo->wrappedData == NULL )
106  {
107  mechanismInfo->wrappedDataLength = \
108  CMS_KEYBLOCK_HEADERSIZE + keySize + padSize;
109  return( CRYPT_OK );
110  }
111  ANALYSER_HINT( mechanismInfo->wrappedDataLength > ( 2 * 8 ) && \
112  mechanismInfo->wrappedDataLength < MAX_INTLENGTH_SHORT );
113 
114  /* Make sure that the wrapped key data fits in the output */
116  keySize + padSize > mechanismInfo->wrappedDataLength )
117  return( CRYPT_ERROR_OVERFLOW );
118 
119  /* Pad the payload out with a random nonce if required */
120  if( padSize > 0 )
121  {
122  setMessageData( &msgData,
123  keyBlockPtr + CMS_KEYBLOCK_HEADERSIZE + keySize,
124  padSize );
126  IMESSAGE_GETATTRIBUTE_S, &msgData,
127  CRYPT_IATTRIBUTE_RANDOM_NONCE );
128  if( cryptStatusError( status ) )
129  return( status );
130  }
131 
132  /* Format the key block:
133 
134  |<--- C_K_HDRSIZE + keySize --->|<- padSz ->|
135  +-------+-----------+-----------+-----------+
136  | length| chk.value | key | padding |
137  +-------+-----------+-----------+-----------+
138  |<- 1 ->|<--- 3 --->|
139 
140  then copy the payload in at the last possible moment and perform two
141  passes of encryption, retaining the IV from the first pass for the
142  second pass */
143  keyBlockPtr[ 0 ] = intToByte( keySize );
144  status = extractKeyData( mechanismInfo->keyContext,
145  keyBlockPtr + CMS_KEYBLOCK_HEADERSIZE,
146  keySize, "keydata", 7 );
147  keyBlockPtr[ 1 ] = intToByte( keyBlockPtr[ CMS_KEYBLOCK_HEADERSIZE ] ^ 0xFF );
148  keyBlockPtr[ 2 ] = intToByte( keyBlockPtr[ CMS_KEYBLOCK_HEADERSIZE + 1 ] ^ 0xFF );
149  keyBlockPtr[ 3 ] = intToByte( keyBlockPtr[ CMS_KEYBLOCK_HEADERSIZE + 2 ] ^ 0xFF );
150  memcpy( dataSample, keyBlockPtr, 16 );
151  if( cryptStatusOK( status ) )
152  {
153  status = krnlSendMessage( mechanismInfo->wrapContext,
155  mechanismInfo->wrappedData,
156  CMS_KEYBLOCK_HEADERSIZE + keySize + \
157  padSize );
158  }
159  if( cryptStatusOK( status ) )
160  {
161  status = krnlSendMessage( mechanismInfo->wrapContext,
163  mechanismInfo->wrappedData,
164  CMS_KEYBLOCK_HEADERSIZE + keySize + \
165  padSize );
166  }
167  if( cryptStatusOK( status ) && !memcmp( dataSample, keyBlockPtr, 16 ) )
168  {
169  /* The data to wrap is unchanged, there's been a catastrophic
170  failure of the encryption. We don't do a retIntError() at this
171  point because we want to at least continue and zeroise the data
172  first */
173  assert( FALSE );
175  }
176  zeroise( dataSample, 16 );
177  if( cryptStatusError( status ) )
178  {
179  zeroise( mechanismInfo->wrappedData,
180  mechanismInfo->wrappedDataLength );
181  return( status );
182  }
183  mechanismInfo->wrappedDataLength = CMS_KEYBLOCK_HEADERSIZE + \
184  keySize + padSize;
185 
186  return( CRYPT_OK );
187  }
188 
189 /* Perform CMS data unwrapping */
190 
192 int importCMS( STDC_UNUSED void *dummy,
194  {
197  BYTE ivBuffer[ CRYPT_MAX_IVSIZE + 8 ];
198  BYTE *dataEndPtr = buffer + mechanismInfo->wrappedDataLength;
199  int blockSize, value, status;
200 
201  UNUSED_ARG( dummy );
202  assert( isWritePtr( mechanismInfo, sizeof( MECHANISM_WRAP_INFO ) ) );
203 
204  /* Make sure that the data is a multiple of the cipher block size and
205  contains at least two encrypted blocks */
206  status = krnlSendMessage( mechanismInfo->wrapContext,
207  IMESSAGE_GETATTRIBUTE, &blockSize,
209  if( cryptStatusError( status ) )
210  return( status );
211  if( mechanismInfo->wrappedDataLength & ( blockSize - 1 ) )
212  return( CRYPT_ERROR_BADDATA );
213  if( mechanismInfo->wrappedDataLength < 2 * blockSize )
214  return( CRYPT_ERROR_UNDERFLOW );
215  if( mechanismInfo->wrappedDataLength > CRYPT_MAX_KEYSIZE + blockSize )
216  {
217  /* This has already been checked in general via a kernel ACL but we
218  can perform a more specific check here because we now know the
219  encryption block size */
220  return( CRYPT_ERROR_OVERFLOW );
221  }
222  ANALYSER_HINT( mechanismInfo->wrappedDataLength >= 8 && \
223  mechanismInfo->wrappedDataLength < CRYPT_MAX_KEYSIZE + 16 );
224 
225  /* Save the current IV for the inner decryption */
226  setMessageData( &msgData, ivBuffer, CRYPT_MAX_IVSIZE );
227  status = krnlSendMessage( mechanismInfo->wrapContext,
228  IMESSAGE_GETATTRIBUTE_S, &msgData,
230  if( cryptStatusError( status ) )
231  return( status );
232 
233  /* Decrypt the n'th block using the n-1'th ciphertext block as the new
234  IV. Then decrypt the remainder of the ciphertext blocks using the
235  decrypted n'th ciphertext block as the IV. Technically some of the
236  checks of the return value aren't necessary because the CBC-MAC that
237  we're performing at the end will detect any failure to set a
238  parameter but we perform them anyway just to be sure */
239  memcpy( buffer, mechanismInfo->wrappedData,
240  mechanismInfo->wrappedDataLength );
241  setMessageData( &msgData, dataEndPtr - ( 2 * blockSize ), blockSize );
242  status = krnlSendMessage( mechanismInfo->wrapContext,
243  IMESSAGE_SETATTRIBUTE_S, &msgData,
245  if( cryptStatusOK( status ) )
246  {
247  status = krnlSendMessage( mechanismInfo->wrapContext,
249  dataEndPtr - blockSize, blockSize );
250  }
251  if( cryptStatusOK( status ) )
252  {
253  setMessageData( &msgData, dataEndPtr - blockSize, blockSize );
254  status = krnlSendMessage( mechanismInfo->wrapContext,
255  IMESSAGE_SETATTRIBUTE_S, &msgData,
257  }
258  if( cryptStatusOK( status ) )
259  {
260  status = krnlSendMessage( mechanismInfo->wrapContext,
261  IMESSAGE_CTX_DECRYPT, buffer,
262  mechanismInfo->wrappedDataLength - blockSize );
263  }
264  if( cryptStatusError( status ) )
265  {
267  return( status );
268  }
269 
270  /* Decrypt the inner data using the original IV */
271  setMessageData( &msgData, ivBuffer, blockSize );
272  status = krnlSendMessage( mechanismInfo->wrapContext,
273  IMESSAGE_SETATTRIBUTE_S, &msgData,
275  if( cryptStatusOK( status ) )
276  {
277  status = krnlSendMessage( mechanismInfo->wrapContext,
278  IMESSAGE_CTX_DECRYPT, buffer,
279  mechanismInfo->wrappedDataLength );
280  }
281  if( cryptStatusError( status ) )
282  {
284  return( status );
285  }
286 
287  /* Make sure that everything is in order and load the decrypted keying
288  information into the session key context. This uses a somewhat
289  odd checking mechanism in order to avoid timing attacks (although it's
290  not really certain what an attacker would gain by an ability to do
291  this). The checks being performed are:
292 
293  if( buffer[ 0 ] < MIN_KEYSIZE || \
294  buffer[ 0 ] > MAX_WORKING_KEYSIZE || \
295  buffer[ 0 ] > mechanismInfo->wrappedDataLength - \
296  CMS_KEYBLOCK_HEADERSIZE || \
297  buffer[ 1 ] != ( buffer[ CMS_KEYBLOCK_HEADERSIZE ] ^ 0xFF ) || \
298  buffer[ 2 ] != ( buffer[ CMS_KEYBLOCK_HEADERSIZE + 1 ] ^ 0xFF ) || \
299  buffer[ 3 ] != ( buffer[ CMS_KEYBLOCK_HEADERSIZE + 2 ] ^ 0xFF ) )
300  error;
301 
302  If this check fails then it could be due to corruption of the wrapped
303  data but is far more likely to be because the incorrect unwrap key was
304  used, so we return a CRYPT_ERROR_WRONGKEY instead of a
305  CRYPT_ERROR_BADDATA */
306  value = ( buffer[ 0 ] < MIN_KEYSIZE ) | \
307  ( buffer[ 0 ] > MAX_WORKING_KEYSIZE ) | \
308  ( buffer[ 0 ] > mechanismInfo->wrappedDataLength - \
309  CMS_KEYBLOCK_HEADERSIZE );
310  value |= buffer[ 1 ] ^ ( buffer[ CMS_KEYBLOCK_HEADERSIZE ] ^ 0xFF );
311  value |= buffer[ 2 ] ^ ( buffer[ CMS_KEYBLOCK_HEADERSIZE + 1 ] ^ 0xFF );
312  value |= buffer[ 3 ] ^ ( buffer[ CMS_KEYBLOCK_HEADERSIZE + 2 ] ^ 0xFF );
313  if( value != 0 )
314  {
316  return( CRYPT_ERROR_WRONGKEY );
317  }
318 
319  /* Load the recovered key into the session key context */
320  setMessageData( &msgData, buffer + CMS_KEYBLOCK_HEADERSIZE, buffer[ 0 ] );
321  status = krnlSendMessage( mechanismInfo->keyContext,
322  IMESSAGE_SETATTRIBUTE_S, &msgData,
324  if( cryptArgError( status ) )
325  {
326  /* If there was an error with the key value or size, convert the
327  return value into something more appropriate */
328  status = CRYPT_ERROR_BADDATA;
329  }
331 
332  return( status );
333  }