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