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
65static const uint8 magicCmsIv[] =
66	{ 0x4a, 0xdd, 0xa2, 0x2c, 0x79, 0xe8, 0x21, 0x05 };
67
68#if		VERBOSE_DEBUG
69static 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. */
103static 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
111static 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
121void 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
297void 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
469