]> git.saurik.com Git - apple/security.git/blob - OSX/libsecurity_apple_csp/lib/wrapKeyCms.cpp
Security-59754.41.1.tar.gz
[apple/security.git] / OSX / libsecurity_apple_csp / lib / wrapKeyCms.cpp
1 /*
2 * Copyright (c) 2000-2001,2011-2012,2014 Apple Inc. All Rights Reserved.
3 *
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
8 * using this file.
9 *
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.
16 */
17
18
19 //
20 // wrapKeyCms.cpp - wrap/unwrap key, CMS format
21 //
22
23 #include "AppleCSPSession.h"
24 #include "AppleCSPUtils.h"
25 #include "AppleCSPKeys.h"
26 #include "cspdebugging.h"
27
28 /*
29 *
30 * Here is the algorithm implemented in this module:
31 *
32 * Note that DEK is the wrapping key,
33 *
34 * 1. PRIVATE_KEY_BYTES is the private data to be wrapped. It consists of the
35 * following concatenation:
36 *
37 * 4-byte length of Descriptive Data, big-endian |
38 * Descriptive Data |
39 * rawBlob.Data bytes
40 *
41 * 2. Encrypt PRIVATE_KEY_BYTES using DEK (3DES) and IV in CBC mode with
42 * PKCS1 padding. Call the ciphertext TEMP1
43 *
44 * 3. Let TEMP2 = IV || TEMP1.
45 *
46 * 4. Reverse the order of the octets in TEMP2 call the result TEMP3.
47 *
48 * 5. Encrypt TEMP3 using DEK with an IV of 0x4adda22c79e82105 in CBC mode
49 * with PKCS1 padding call the result TEMP4.
50 *
51 * TEMP4 is wrappedKey.KeyData.
52 */
53
54 /* true: cook up second CCHandle via a new HandleObject
55 * false - OK to reuse a CCHandle */
56 #define USE_SECOND_CCHAND 0
57
58 /* false : make copy of incoming context before changing IV
59 * true : resuse OK */
60 #define REUSE_CONTEXT 1
61
62 /* lots'o'printfs in lieu of a debugger which works */
63 #define VERBOSE_DEBUG 0
64
65 static const uint8 magicCmsIv[] =
66 { 0x4a, 0xdd, 0xa2, 0x2c, 0x79, 0xe8, 0x21, 0x05 };
67
68 #if VERBOSE_DEBUG
69 static void dumpBuf(
70 char *title,
71 const CSSM_DATA *d,
72 uint32 maxLen)
73 {
74 unsigned i;
75 uint32 len;
76
77 if(title) {
78 printf("%s: ", title);
79 }
80 if(d == NULL) {
81 printf("NO DATA\n");
82 return;
83 }
84 printf("Total Length: %d\n ", d->Length);
85 len = maxLen;
86 if(d->Length < len) {
87 len = d->Length;
88 }
89 for(i=0; i<len; i++) {
90 printf("%02X ", d->Data[i]);
91 if((i % 16) == 15) {
92 printf("\n ");
93 }
94 }
95 printf("\n");
96 }
97 #else
98 #define dumpBuf(t, d, m)
99 #endif /* VERBOSE_DEBUG */
100
101
102 /* serialize/deserialize uint32, big-endian. */
103 static void serializeUint32(uint32 i, uint8 *buf)
104 {
105 *buf++ = (uint8)(i >> 24);
106 *buf++ = (uint8)(i >> 16);
107 *buf++ = (uint8)(i >> 8);
108 *buf = (uint8)i;
109 }
110
111 static uint32 deserializeUint32(const uint8 *buf) {
112 uint32 result;
113
114 result = ((uint32)buf[0] << 24) |
115 ((uint32)buf[1] << 16) |
116 ((uint32)buf[2] << 8) |
117 (uint32)buf[3];
118 return result;
119 }
120
121 void AppleCSPSession::WrapKeyCms(
122 CSSM_CC_HANDLE CCHandle,
123 const Context &context,
124 const AccessCredentials &AccessCred,
125 const CssmKey &UnwrappedKey,
126 CssmData &rawBlob,
127 bool allocdRawBlob, // callee has to free rawBlob
128 const CssmData *DescriptiveData,
129 CssmKey &WrappedKey,
130 CSSM_PRIVILEGE Privilege)
131 {
132 uint32 ddLen;
133 CssmData PRIVATE_KEY_BYTES;
134 #if !REUSE_CONTEXT
135 Context secondCtx(context.ContextType, context.AlgorithmType);
136 secondCtx.copyFrom(context, privAllocator);
137 #endif /* REUSE_CONTEXT */
138
139 /*
140 * 1. PRIVATE_KEY_BYTES is the private data to be wrapped. It consists of the
141 * following concatenation:
142 *
143 * 4-byte length of Descriptive Data, big-endian |
144 * Descriptive Data |
145 * rawBlob.Data bytes
146 */
147 dumpBuf("wrap rawBlob", &rawBlob, 24);
148 dumpBuf("wrap DescriptiveData", DescriptiveData, 24);
149
150 if(DescriptiveData == NULL) {
151 ddLen = 0;
152 }
153 else {
154 ddLen = (uint32) DescriptiveData->Length;
155 }
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);
160 cp += 4;
161 if(ddLen != 0) {
162 memcpy(cp, DescriptiveData->Data, ddLen);
163 cp += ddLen;
164 }
165 memcpy(cp, rawBlob.Data, rawBlob.Length);
166 dumpBuf("wrap PRIVATE_KEY_BYTES", &PRIVATE_KEY_BYTES, 48);
167
168 /* 2. Encrypt PRIVATE_KEY_BYTES using DEK (3DES) and IV in CBC mode with
169 * PKCS1 padding. Call the ciphertext TEMP1
170 *
171 * We'll just use the caller's context for this. Maybe we should
172 * validate mode, padding, IV?
173 */
174 CssmData TEMP1;
175 CSSM_SIZE bytesEncrypted;
176 CssmData remData;
177 EncryptData(CCHandle,
178 context,
179 &PRIVATE_KEY_BYTES, // ClearBufs[]
180 1, // ClearBufCount
181 &TEMP1, // CipherBufs[],
182 1, // CipherBufCount,
183 bytesEncrypted,
184 remData,
185 Privilege);
186
187 // I'm not 100% sure about this....
188 assert(remData.Length == 0);
189 TEMP1.Length = bytesEncrypted;
190 dumpBuf("wrap TEMP1", &TEMP1, 48);
191
192 /*
193 * 3. Let TEMP2 = IV || TEMP1.
194 */
195 CssmData TEMP2;
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);
202
203
204 /*
205 * 4. Reverse the order of the octets in TEMP2 call the result
206 * TEMP3.
207 */
208 CssmData TEMP3;
209 setUpCssmData(TEMP3, TEMP2.Length, privAllocator);
210 uint8 *cp2 = TEMP2.Data + TEMP2.Length - 1;
211 cp = TEMP3.Data;
212 for(uint32 i=0; i<TEMP2.Length; i++) {
213 *cp++ = *cp2--;
214 }
215 dumpBuf("wrap TEMP3", &TEMP3, 64);
216
217 /*
218 * 5. Encrypt TEMP3 using DEK with an IV of 0x4adda22c79e82105 in CBC mode
219 * with PKCS1 padding call the result TEMP4.
220 *
221 * TEMP4 is wrappedKey.KeyData.
222 *
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!
227 */
228 #if REUSE_CONTEXT
229 CssmData &IV2 = context.get<CssmData>(CSSM_ATTRIBUTE_INIT_VECTOR,
230 CSSMERR_CSP_MISSING_ATTR_INIT_VECTOR);
231 #else
232 CssmData &IV2 = secondCtx.get<CssmData>(CSSM_ATTRIBUTE_INIT_VECTOR,
233 CSSMERR_CSP_MISSING_ATTR_INIT_VECTOR);
234 #endif /* REUSE_CONTEXT */
235
236 uint8 *savedIV = IV2.Data;
237 CSSM_SIZE savedIVLen = IV2.Length;
238 IV2.Data = (uint8 *)magicCmsIv;
239 IV2.Length = 8;
240 CssmData &outBlob = CssmData::overlay(WrappedKey.KeyData);
241 outBlob.Length = 0;
242 outBlob.Data = NULL;
243 try {
244 EncryptData(CCHandle,
245 #if REUSE_CONTEXT
246 context,
247 #else
248 secondCtx,
249 #endif /* REUSE_CONTEXT */
250
251 &TEMP3, // ClearBufs[]
252 1, // ClearBufCount
253 &outBlob, // CipherBufs[],
254 1, // CipherBufCount,
255 bytesEncrypted,
256 remData,
257 Privilege);
258 }
259 catch (...) {
260 IV2.Data = savedIV;
261 IV2.Length = savedIVLen;
262 throw; // and leak
263 }
264 IV2.Data = savedIV;
265 IV2.Length = savedIVLen;
266
267 // I'm not 100% sure about this....
268 assert(remData.Length == 0);
269 outBlob.Length = bytesEncrypted;
270 dumpBuf("wrap outBlob", &outBlob, 64);
271
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;
277
278 /* free resources */
279 freeCssmData(PRIVATE_KEY_BYTES, privAllocator);
280 freeCssmData(TEMP1, normAllocator); // alloc via encrypt
281 freeCssmData(TEMP2, privAllocator);
282 freeCssmData(TEMP3, privAllocator);
283 if(allocdRawBlob) {
284 /* our caller mallocd this when dereferencing a ref key */
285 freeCssmData(rawBlob, privAllocator);
286 }
287 }
288
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
291 */
292
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
296
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)
306 {
307 /*
308 * In reverse order, the steps from wrap...
309 *
310 * 5. Encrypt TEMP3 using DEK with an IV of 0x4adda22c79e82105 in CBC mode
311 * with PKCS1 padding call the result TEMP4.
312 *
313 * TEMP4 is wrappedKey.KeyData.
314 */
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;
322 IV1.Length = 8;
323 CssmData TEMP3;
324 CSSM_SIZE bytesDecrypted;
325 CssmData remData;
326
327 try {
328 DecryptData(CCHandle,
329 Context,
330 &wrappedBlob, // CipherBufs[],
331 1, // CipherBufCount,
332 &TEMP3, // ClearBufs[]
333 1, // ClearBufCount
334 bytesDecrypted,
335 remData,
336 Privilege);
337 }
338 catch(...) {
339 IV1.Data = savedIV;
340 IV1.Length = savedIvLen;
341 throw;
342 }
343 IV1.Data = savedIV;
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);
349
350 /*
351 * 4. Reverse the order of the octets in TEMP2 call the result
352 * TEMP3.
353 *
354 * i.e., TEMP2 := reverse(TEMP3)
355 */
356 CssmData TEMP2;
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++) {
361 *dst++ = *src--;
362 }
363 dumpBuf("unwrap TEMP2", &TEMP2, 64);
364
365 /*
366 * 3. Let TEMP2 = IV || TEMP1.
367 *
368 * IV2 is first 8 bytes of TEMP2, remainder is TEMP1
369 */
370 if(TEMP2.Length <= 8) {
371 dprintf0("UnwrapKeyCms: short TEMP2\n");
372 CssmError::throwMe(CSSMERR_CSP_INVALID_KEY);
373 }
374 CssmData IV2;
375 CssmData TEMP1;
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);
381
382 /*
383 * 2. Encrypt PRIVATE_KEY_BYTES using DEK (3DES) and IV in CBC mode with
384 * PKCS1 padding. Call the ciphertext TEMP1
385 *
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.
388 */
389 IV1 = IV2;
390 CssmData PRIVATE_KEY_BYTES;
391 try {
392 DecryptData(CCHandle,
393 Context,
394 &TEMP1, // CipherBufs[],
395 1, // CipherBufCount,
396 &PRIVATE_KEY_BYTES, // ClearBufs[]
397 1, // ClearBufCount
398 bytesDecrypted,
399 remData,
400 Privilege);
401 }
402 catch(...) {
403 IV1.Data = savedIV;
404 IV1.Length = savedIvLen;
405 throw;
406 }
407 IV1.Data = savedIV;
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);
412
413 /*
414 * 1. PRIVATE_KEY_BYTES is the private data to be wrapped. It consists of the
415 * following concatenation:
416 *
417 * 4-byte length of Descriptive Data, big-endian |
418 * Descriptive Data |
419 * rawBlob.Data bytes
420 */
421 if(PRIVATE_KEY_BYTES.Length < 4) {
422 dprintf0("UnwrapKeyCms: short PRIVATE_KEY_BYTES\n");
423 CssmError::throwMe(CSSMERR_CSP_INVALID_KEY);
424 }
425 uint8 *cp1 = PRIVATE_KEY_BYTES.Data;
426 uint32 ddLen = deserializeUint32(cp1);
427 cp1 += 4;
428 if(ddLen > MAX_MALLOC_SIZE) {
429 dprintf0("UnwrapKeyCms: preposterous ddLen in PRIVATE_KEY_BYTES\n");
430 CssmError::throwMe(CSSMERR_CSP_INVALID_KEY);
431 }
432 setUpCssmData(DescriptiveData, ddLen, normAllocator);
433 memcpy(DescriptiveData.Data, cp1, ddLen);
434 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);
439 }
440 CssmData &outBlob = CssmData::overlay(UnwrappedKey.KeyData);
441 setUpCssmData(outBlob, outBlobLen, normAllocator);
442 memcpy(outBlob.Data, cp1, outBlobLen);
443
444 /* set up outgoing header */
445 UnwrappedKey.KeyHeader.BlobType = CSSM_KEYBLOB_RAW;
446 UnwrappedKey.KeyHeader.Format = inferFormat(UnwrappedKey);
447
448 /*
449 * Cook up a BinaryKey if caller wants a reference key.
450 */
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);
458 delete provider;
459 }
460 /* free resources */
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
466
467 }
468