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