2 * Copyright (c) 2000-2001,2011-2012,2014 Apple Inc. All Rights Reserved.
4 * The contents of this file constitute Original Code as defined in and are
5 * subject to the Apple Public Source License Version 1.2 (the 'License').
6 * You may not use this file except in compliance with the License. Please obtain
7 * a copy of the License at http://www.apple.com/publicsource and read it before
10 * This Original Code and all software distributed under the License are
11 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS
12 * OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, INCLUDING WITHOUT
13 * LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
14 * PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. Please see the License for the
15 * specific language governing rights and limitations under the License.
20 // wrapKeyCms.cpp - wrap/unwrap key, CMS format
23 #include "AppleCSPSession.h"
24 #include "AppleCSPUtils.h"
25 #include "AppleCSPKeys.h"
26 #include "cspdebugging.h"
30 * Here is the algorithm implemented in this module:
32 * Note that DEK is the wrapping key,
34 * 1. PRIVATE_KEY_BYTES is the private data to be wrapped. It consists of the
35 * following concatenation:
37 * 4-byte length of Descriptive Data, big-endian |
41 * 2. Encrypt PRIVATE_KEY_BYTES using DEK (3DES) and IV in CBC mode with
42 * PKCS1 padding. Call the ciphertext TEMP1
44 * 3. Let TEMP2 = IV || TEMP1.
46 * 4. Reverse the order of the octets in TEMP2 call the result TEMP3.
48 * 5. Encrypt TEMP3 using DEK with an IV of 0x4adda22c79e82105 in CBC mode
49 * with PKCS1 padding call the result TEMP4.
51 * TEMP4 is wrappedKey.KeyData.
54 /* true: cook up second CCHandle via a new HandleObject
55 * false - OK to reuse a CCHandle */
56 #define USE_SECOND_CCHAND 0
58 /* false : make copy of incoming context before changing IV
60 #define REUSE_CONTEXT 1
62 /* lots'o'printfs in lieu of a debugger which works */
63 #define VERBOSE_DEBUG 0
65 static const uint8 magicCmsIv
[] =
66 { 0x4a, 0xdd, 0xa2, 0x2c, 0x79, 0xe8, 0x21, 0x05 };
78 printf("%s: ", title
);
84 printf("Total Length: %d\n ", d
->Length
);
89 for(i
=0; i
<len
; i
++) {
90 printf("%02X ", d
->Data
[i
]);
98 #define dumpBuf(t, d, m)
99 #endif /* VERBOSE_DEBUG */
102 /* serialize/deserialize uint32, big-endian. */
103 static void serializeUint32(uint32 i
, uint8
*buf
)
105 *buf
++ = (uint8
)(i
>> 24);
106 *buf
++ = (uint8
)(i
>> 16);
107 *buf
++ = (uint8
)(i
>> 8);
111 static uint32
deserializeUint32(const uint8
*buf
) {
114 result
= ((uint32
)buf
[0] << 24) |
115 ((uint32
)buf
[1] << 16) |
116 ((uint32
)buf
[2] << 8) |
121 void AppleCSPSession::WrapKeyCms(
122 CSSM_CC_HANDLE CCHandle
,
123 const Context
&context
,
124 const AccessCredentials
&AccessCred
,
125 const CssmKey
&UnwrappedKey
,
127 bool allocdRawBlob
, // callee has to free rawBlob
128 const CssmData
*DescriptiveData
,
130 CSSM_PRIVILEGE Privilege
)
133 CssmData PRIVATE_KEY_BYTES
;
135 Context
secondCtx(context
.ContextType
, context
.AlgorithmType
);
136 secondCtx
.copyFrom(context
, privAllocator
);
137 #endif /* REUSE_CONTEXT */
140 * 1. PRIVATE_KEY_BYTES is the private data to be wrapped. It consists of the
141 * following concatenation:
143 * 4-byte length of Descriptive Data, big-endian |
147 dumpBuf("wrap rawBlob", &rawBlob
, 24);
148 dumpBuf("wrap DescriptiveData", DescriptiveData
, 24);
150 if(DescriptiveData
== NULL
) {
154 ddLen
= (uint32
) DescriptiveData
->Length
;
156 size_t pkbLen
= 4 + ddLen
+ rawBlob
.Length
;
157 setUpCssmData(PRIVATE_KEY_BYTES
, pkbLen
, privAllocator
);
158 uint8
*cp
= PRIVATE_KEY_BYTES
.Data
;
159 serializeUint32(ddLen
, cp
);
162 memcpy(cp
, DescriptiveData
->Data
, ddLen
);
165 memcpy(cp
, rawBlob
.Data
, rawBlob
.Length
);
166 dumpBuf("wrap PRIVATE_KEY_BYTES", &PRIVATE_KEY_BYTES
, 48);
168 /* 2. Encrypt PRIVATE_KEY_BYTES using DEK (3DES) and IV in CBC mode with
169 * PKCS1 padding. Call the ciphertext TEMP1
171 * We'll just use the caller's context for this. Maybe we should
172 * validate mode, padding, IV?
175 CSSM_SIZE bytesEncrypted
;
177 EncryptData(CCHandle
,
179 &PRIVATE_KEY_BYTES
, // ClearBufs[]
181 &TEMP1
, // CipherBufs[],
182 1, // CipherBufCount,
187 // I'm not 100% sure about this....
188 assert(remData
.Length
== 0);
189 TEMP1
.Length
= bytesEncrypted
;
190 dumpBuf("wrap TEMP1", &TEMP1
, 48);
193 * 3. Let TEMP2 = IV || TEMP1.
196 CssmData
&IV
= context
.get
<CssmData
>(CSSM_ATTRIBUTE_INIT_VECTOR
,
197 CSSMERR_CSP_MISSING_ATTR_INIT_VECTOR
);
198 setUpCssmData(TEMP2
, IV
.Length
+ TEMP1
.Length
, privAllocator
);
199 memcpy(TEMP2
.Data
, IV
.Data
, IV
.Length
);
200 memcpy(TEMP2
.Data
+ IV
.Length
, TEMP1
.Data
, TEMP1
.Length
);
201 dumpBuf("wrap TEMP2", &TEMP2
, 56);
205 * 4. Reverse the order of the octets in TEMP2 call the result
209 setUpCssmData(TEMP3
, TEMP2
.Length
, privAllocator
);
210 uint8
*cp2
= TEMP2
.Data
+ TEMP2
.Length
- 1;
212 for(uint32 i
=0; i
<TEMP2
.Length
; i
++) {
215 dumpBuf("wrap TEMP3", &TEMP3
, 64);
218 * 5. Encrypt TEMP3 using DEK with an IV of 0x4adda22c79e82105 in CBC mode
219 * with PKCS1 padding call the result TEMP4.
221 * TEMP4 is wrappedKey.KeyData.
223 * This is the tricky part - we're going to use the caller's context
224 * again, but we're going to modify the IV.
225 * We're assuming here that the IV we got via context.get<CssmData>
226 * actually is in the context and not a copy!
229 CssmData
&IV2
= context
.get
<CssmData
>(CSSM_ATTRIBUTE_INIT_VECTOR
,
230 CSSMERR_CSP_MISSING_ATTR_INIT_VECTOR
);
232 CssmData
&IV2
= secondCtx
.get
<CssmData
>(CSSM_ATTRIBUTE_INIT_VECTOR
,
233 CSSMERR_CSP_MISSING_ATTR_INIT_VECTOR
);
234 #endif /* REUSE_CONTEXT */
236 uint8
*savedIV
= IV2
.Data
;
237 CSSM_SIZE savedIVLen
= IV2
.Length
;
238 IV2
.Data
= (uint8
*)magicCmsIv
;
240 CssmData
&outBlob
= CssmData::overlay(WrappedKey
.KeyData
);
244 EncryptData(CCHandle
,
249 #endif /* REUSE_CONTEXT */
251 &TEMP3
, // ClearBufs[]
253 &outBlob
, // CipherBufs[],
254 1, // CipherBufCount,
261 IV2
.Length
= savedIVLen
;
265 IV2
.Length
= savedIVLen
;
267 // I'm not 100% sure about this....
268 assert(remData
.Length
== 0);
269 outBlob
.Length
= bytesEncrypted
;
270 dumpBuf("wrap outBlob", &outBlob
, 64);
272 /* outgoing header */
273 WrappedKey
.KeyHeader
.BlobType
= CSSM_KEYBLOB_WRAPPED
;
274 // OK to be zero or not present
275 WrappedKey
.KeyHeader
.WrapMode
= context
.getInt(CSSM_ATTRIBUTE_MODE
);
276 WrappedKey
.KeyHeader
.Format
= CSSM_KEYBLOB_WRAPPED_FORMAT_APPLE_CUSTOM
;
279 freeCssmData(PRIVATE_KEY_BYTES
, privAllocator
);
280 freeCssmData(TEMP1
, normAllocator
); // alloc via encrypt
281 freeCssmData(TEMP2
, privAllocator
);
282 freeCssmData(TEMP3
, privAllocator
);
284 /* our caller mallocd this when dereferencing a ref key */
285 freeCssmData(rawBlob
, privAllocator
);
289 /* note we expect an IV present in the context though we don't use it
290 * FIXME - we should figure out how to add this attribute at this level
293 /* safety trap - don't try to malloc anything bigger than this - we get
294 * sizes from the processed bit stream.... */
295 #define MAX_MALLOC_SIZE 0x10000
297 void AppleCSPSession::UnwrapKeyCms(
298 CSSM_CC_HANDLE CCHandle
,
299 const Context
&Context
,
300 const CssmKey
&WrappedKey
,
301 const CSSM_RESOURCE_CONTROL_CONTEXT
*CredAndAclEntry
,
302 CssmKey
&UnwrappedKey
,
303 CssmData
&DescriptiveData
,
304 CSSM_PRIVILEGE Privilege
,
305 cspKeyStorage keyStorage
)
308 * In reverse order, the steps from wrap...
310 * 5. Encrypt TEMP3 using DEK with an IV of 0x4adda22c79e82105 in CBC mode
311 * with PKCS1 padding call the result TEMP4.
313 * TEMP4 is wrappedKey.KeyData.
315 const CssmData
&wrappedBlob
= CssmData::overlay(WrappedKey
.KeyData
);
316 dumpBuf("unwrap inBlob", &wrappedBlob
, 64);
317 CssmData
&IV1
= Context
.get
<CssmData
>(CSSM_ATTRIBUTE_INIT_VECTOR
,
318 CSSMERR_CSP_MISSING_ATTR_INIT_VECTOR
);
319 uint8
*savedIV
= IV1
.Data
;
320 CSSM_SIZE savedIvLen
= IV1
.Length
;
321 IV1
.Data
= (uint8
*)magicCmsIv
;
324 CSSM_SIZE bytesDecrypted
;
328 DecryptData(CCHandle
,
330 &wrappedBlob
, // CipherBufs[],
331 1, // CipherBufCount,
332 &TEMP3
, // ClearBufs[]
340 IV1
.Length
= savedIvLen
;
344 IV1
.Length
= savedIvLen
;
345 // I'm not 100% sure about this....
346 assert(remData
.Length
== 0);
347 TEMP3
.Length
= bytesDecrypted
;
348 dumpBuf("unwrap TEMP3", &TEMP3
, 64);
351 * 4. Reverse the order of the octets in TEMP2 call the result
354 * i.e., TEMP2 := reverse(TEMP3)
357 setUpCssmData(TEMP2
, TEMP3
.Length
, privAllocator
);
358 uint8
*src
= TEMP3
.Data
+ TEMP3
.Length
- 1;
359 uint8
*dst
= TEMP2
.Data
;
360 for(uint32 i
=0; i
<TEMP2
.Length
; i
++) {
363 dumpBuf("unwrap TEMP2", &TEMP2
, 64);
366 * 3. Let TEMP2 = IV || TEMP1.
368 * IV2 is first 8 bytes of TEMP2, remainder is TEMP1
370 if(TEMP2
.Length
<= 8) {
371 dprintf0("UnwrapKeyCms: short TEMP2\n");
372 CssmError::throwMe(CSSMERR_CSP_INVALID_KEY
);
376 setUpCssmData(IV2
, 8, privAllocator
);
377 setUpCssmData(TEMP1
, TEMP2
.Length
- 8, privAllocator
);
378 memcpy(IV2
.Data
, TEMP2
.Data
, 8);
379 memcpy(TEMP1
.Data
, TEMP2
.Data
+ 8, TEMP1
.Length
);
380 dumpBuf("unwrap TEMP1", &TEMP1
, 48);
383 * 2. Encrypt PRIVATE_KEY_BYTES using DEK (3DES) and IV in CBC mode with
384 * PKCS1 padding. Call the ciphertext TEMP1
386 * i.e., decrypt TEMP1 to get PRIVATE_KEY_BYTES. Use IV2, not caller's
387 * IV. We already saved caller's IV in savediV and savedIvLen.
390 CssmData PRIVATE_KEY_BYTES
;
392 DecryptData(CCHandle
,
394 &TEMP1
, // CipherBufs[],
395 1, // CipherBufCount,
396 &PRIVATE_KEY_BYTES
, // ClearBufs[]
404 IV1
.Length
= savedIvLen
;
408 // I'm not 100% sure about this....
409 assert(remData
.Length
== 0);
410 PRIVATE_KEY_BYTES
.Length
= bytesDecrypted
;
411 dumpBuf("unwrap PRIVATE_KEY_BYTES", &PRIVATE_KEY_BYTES
, 64);
414 * 1. PRIVATE_KEY_BYTES is the private data to be wrapped. It consists of the
415 * following concatenation:
417 * 4-byte length of Descriptive Data, big-endian |
421 if(PRIVATE_KEY_BYTES
.Length
< 4) {
422 dprintf0("UnwrapKeyCms: short PRIVATE_KEY_BYTES\n");
423 CssmError::throwMe(CSSMERR_CSP_INVALID_KEY
);
425 uint8
*cp1
= PRIVATE_KEY_BYTES
.Data
;
426 uint32 ddLen
= deserializeUint32(cp1
);
428 if(ddLen
> MAX_MALLOC_SIZE
) {
429 dprintf0("UnwrapKeyCms: preposterous ddLen in PRIVATE_KEY_BYTES\n");
430 CssmError::throwMe(CSSMERR_CSP_INVALID_KEY
);
432 setUpCssmData(DescriptiveData
, ddLen
, normAllocator
);
433 memcpy(DescriptiveData
.Data
, cp1
, ddLen
);
435 size_t outBlobLen
= PRIVATE_KEY_BYTES
.Length
- ddLen
- 4;
436 if(ddLen
> MAX_MALLOC_SIZE
) {
437 dprintf0("UnwrapKeyCms: preposterous outBlobLen in PRIVATE_KEY_BYTES\n");
438 CssmError::throwMe(CSSMERR_CSP_INVALID_KEY
);
440 CssmData
&outBlob
= CssmData::overlay(UnwrappedKey
.KeyData
);
441 setUpCssmData(outBlob
, outBlobLen
, normAllocator
);
442 memcpy(outBlob
.Data
, cp1
, outBlobLen
);
444 /* set up outgoing header */
445 UnwrappedKey
.KeyHeader
.BlobType
= CSSM_KEYBLOB_RAW
;
446 UnwrappedKey
.KeyHeader
.Format
= inferFormat(UnwrappedKey
);
449 * Cook up a BinaryKey if caller wants a reference key.
451 if(keyStorage
== CKS_Ref
) {
452 BinaryKey
*binKey
= NULL
;
453 CSPKeyInfoProvider
*provider
= infoProvider(UnwrappedKey
);
454 /* optional parameter-bearing key */
455 CssmKey
*paramKey
= Context
.get
<CssmKey
>(CSSM_ATTRIBUTE_PARAM_KEY
);
456 provider
->CssmKeyToBinary(paramKey
, UnwrappedKey
.KeyHeader
.KeyAttr
, &binKey
);
457 addRefKey(*binKey
, UnwrappedKey
);
461 freeCssmData(PRIVATE_KEY_BYTES
, normAllocator
); // alloc via decrypt
462 freeCssmData(TEMP1
, privAllocator
);
463 freeCssmData(IV2
, privAllocator
);
464 freeCssmData(TEMP2
, privAllocator
);
465 freeCssmData(TEMP3
, normAllocator
); // via decrypt