1/*
2 * Copyright (c) 2004,2011-2014 Apple Inc. All Rights Reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 *
23 * SecWrappedKeys.cpp - SecExportRep and SecImportRep methods dealing with
24 *		wrapped private keys (other than PKCS8 format).
25 */
26
27#include "SecExternalRep.h"
28#include "SecImportExportUtils.h"
29#include "SecImportExportPem.h"
30#include "SecImportExportCrypto.h"
31#include <Security/cssmtype.h>
32#include <Security/cssmapi.h>
33#include <Security/SecKeyPriv.h>
34#include <security_asn1/SecNssCoder.h>
35#include <security_cdsa_utils/cuCdsaUtils.h>
36#include <security_utilities/devrandom.h>
37
38#include <assert.h>
39
40using namespace Security;
41using namespace KeychainCore;
42
43static int hexToDigit(
44	char digit,
45	uint8 *rtn)		// RETURNED
46{
47	if((digit >= '0') && (digit <= '9')) {
48		*rtn = digit - '0';
49		return 0;
50	}
51	if((digit >= 'a') && (digit <= 'f')) {
52		*rtn = digit - 'a' + 10;
53		return 0;
54	}
55	if((digit >= 'A') && (digit <= 'F')) {
56		*rtn = digit - 'A' + 10;
57		return 0;
58	}
59	return -1;
60}
61
62/*
63 * Convert two ascii characters starting at cp to an unsigned char.
64 * Returns nonzero on error.
65 */
66static int hexToUchar(
67	const char *cp,
68	uint8 *rtn)		// RETURNED
69{
70	uint8 rtnc = 0;
71	uint8 c;
72	if(hexToDigit(*cp++, &c)) {
73		return -1;
74	}
75	rtnc = c << 4;
76	if(hexToDigit(*cp, &c)) {
77		return -1;
78	}
79	rtnc |= c;
80	*rtn = rtnc;
81	return 0;
82}
83
84/*
85 * Given an array of PEM parameter lines, infer parameters for key derivation and
86 * encryption.
87 */
88static OSStatus opensslPbeParams(
89	CFArrayRef			paramLines,			// elements are CFStrings
90	SecNssCoder			&coder,				// IV allocd with this
91	/* remaining arguments RETURNED */
92	CSSM_ALGORITHMS		&pbeAlg,
93	CSSM_ALGORITHMS		&keyAlg,
94	CSSM_ALGORITHMS		&encrAlg,
95	CSSM_ENCRYPT_MODE	&encrMode,
96	CSSM_PADDING		&encrPad,
97	uint32				&keySizeInBits,
98	unsigned			&blockSizeInBytes,
99	CSSM_DATA			&iv)
100{
101	/*
102	 * This format requires PEM parameter lines. We could have gotten here
103	 * without them if caller specified wrong format.
104	 */
105	 if(paramLines == NULL) {
106		SecImpExpDbg("importWrappedKeyOpenssl: no PEM parameter lines");
107		return errSecUnknownFormat;
108	 }
109	 CFStringRef dekInfo = NULL;
110	 CFIndex numLines = CFArrayGetCount(paramLines);
111	 for(CFIndex dex=0; dex<numLines; dex++) {
112		CFStringRef str = (CFStringRef)CFArrayGetValueAtIndex(paramLines, dex);
113		CFRange range;
114		range = CFStringFind(str, CFSTR("DEK-Info: "), 0);
115		if(range.length != 0) {
116			dekInfo = str;
117			break;
118		}
119	 }
120	 if(dekInfo == NULL) {
121		SecImpExpDbg("importWrappedKeyOpenssl: no DEK-Info lines");
122		return errSecUnknownFormat;
123	 }
124
125	 /* drop down to C strings for low level grunging */
126	 char cstr[1024];
127	 if(!CFStringGetCString(dekInfo, cstr, sizeof(cstr), kCFStringEncodingASCII)) {
128		SecImpExpDbg("importWrappedKeyOpenssl: bad DEK-Info line (1)");
129		return errSecUnknownFormat;
130	 }
131
132	/*
133	 * This line looks like this:
134	 * DEK-Info: DES-CBC,A22977A0A6A6F696
135	 *
136	 * Now parse, getting the cipher spec and the IV.
137	 */
138	char *cp = strchr(cstr, ':');
139	if(cp == NULL) {
140		SecImpExpDbg("importWrappedKeyOpenssl: bad DEK-Info line (2)");
141		return errSecUnknownFormat;
142	}
143	if((cp[1] == ' ') && (cp[2] != '\0')) {
144		/* as it normally does... */
145		cp += 2;
146	}
147
148	/* We only support DES and 3DES here */
149	if(!strncmp(cp, "DES-EDE3-CBC", 12)) {
150		keyAlg = CSSM_ALGID_3DES_3KEY;
151		encrAlg = CSSM_ALGID_3DES_3KEY_EDE;
152		keySizeInBits = 64 * 3;
153		blockSizeInBytes = 8;
154	}
155	else if(!strncmp(cp, "DES-CBC", 7)) {
156		keyAlg = CSSM_ALGID_DES;
157		encrAlg = CSSM_ALGID_DES;
158		keySizeInBits = 64;
159		blockSizeInBytes = 8;
160	}
161	else {
162		SecImpExpDbg("importWrappedKeyOpenssl: unrecognized wrap alg (%s)",
163			cp);
164		return errSecUnknownFormat;
165	}
166
167	/* these are more or less fixed */
168	pbeAlg   = CSSM_ALGID_PBE_OPENSSL_MD5;
169	encrMode = CSSM_ALGMODE_CBCPadIV8;
170	encrPad  = CSSM_PADDING_PKCS7;
171
172	/* now get the ASCII hex version of the IV */
173	cp = strchr(cp, ',');
174	if(cp == NULL) {
175		SecImpExpDbg("importWrappedKeyOpenssl: No IV in DEK-Info line");
176		return errSecUnknownFormat;
177	}
178	if(cp[1] != '\0') {
179		cp++;
180	}
181
182	/* remainder should be just the IV */
183	if(strlen(cp) != (blockSizeInBytes * 2)) {
184		SecImpExpDbg("importWrappedKeyOpenssl: bad IV in DEK-Info line (1)");
185		return errSecUnknownFormat;
186	}
187
188	coder.allocItem(iv, blockSizeInBytes);
189	for(unsigned dex=0; dex<blockSizeInBytes; dex++) {
190		if(hexToUchar(cp + (dex * 2), &iv.Data[dex])) {
191			SecImpExpDbg("importWrappedKeyOpenssl: bad IV in DEK-Info line (2)");
192			return errSecUnknownFormat;
193		}
194	}
195	return errSecSuccess;
196}
197
198/*
199 * Common code to derive an openssl-wrap style wrap/unwrap key.
200 */
201static OSStatus deriveKeyOpensslWrap(
202	const SecKeyImportExportParameters	*keyParams,		// required
203	CSSM_CSP_HANDLE						cspHand,		// required
204	impExpVerifyPhrase					vp,				// import/export
205	CSSM_ALGORITHMS						pbeAlg,
206	CSSM_ALGORITHMS						keyAlg,
207	uint32								keySizeInBits,
208	const CSSM_DATA						&salt,
209	CSSM_KEY_PTR						derivedKey)
210{
211	CFDataRef	cfPhrase = NULL;
212	CSSM_KEY	*passKey = NULL;
213	OSStatus	ortn;
214
215	/* passphrase or passkey? */
216	ortn = impExpPassphraseCommon(keyParams, cspHand, SPF_Data, vp,
217		(CFTypeRef *)&cfPhrase, &passKey);
218	if(ortn) {
219		return ortn;
220	}
221	/* subsequent errors to errOut: */
222
223	CSSM_CRYPTO_DATA		seed;
224	CSSM_CC_HANDLE			ccHand = 0;
225	CSSM_ACCESS_CREDENTIALS	creds;
226	SecNssCoder				coder;
227	CSSM_DATA				param = {0, NULL};
228	CSSM_DATA				dummyLabel;
229
230	memset(&seed, 0, sizeof(seed));
231	if(cfPhrase != NULL) {
232		size_t len = CFDataGetLength(cfPhrase);
233		coder.allocItem(seed.Param, len);
234		memmove(seed.Param.Data, CFDataGetBytePtr(cfPhrase), len);
235		CFRelease(cfPhrase);
236	}
237
238	memset(&creds, 0, sizeof(CSSM_ACCESS_CREDENTIALS));
239	ortn = CSSM_CSP_CreateDeriveKeyContext(cspHand,
240		pbeAlg,
241		keyAlg,
242		keySizeInBits,
243		&creds,
244		passKey,				// BaseKey
245		1,						// iterCount - yup, this is what openssl does
246		&salt,
247		&seed,
248		&ccHand);
249	if(ortn) {
250		SecImpExpDbg("deriveKeyOpensslWrap: CSSM_CSP_CreateDeriveKeyContext error");
251		goto errOut;
252	}
253
254	memset(derivedKey, 0, sizeof(CSSM_KEY));
255
256	dummyLabel.Data = (uint8 *)"temp unwrap key";
257	dummyLabel.Length = strlen((char *)dummyLabel.Data);
258
259	ortn = CSSM_DeriveKey(ccHand,
260		&param,					// i.e., derived IV - don't want one
261		CSSM_KEYUSE_ANY,
262		/* not extractable even for the short time this key lives */
263		CSSM_KEYATTR_RETURN_REF | CSSM_KEYATTR_SENSITIVE,
264		&dummyLabel,
265		NULL,			// cred and acl
266		derivedKey);
267	if(ortn) {
268		SecImpExpDbg("importWrappedKeyOpenssl: PKCS5 v1.5 CSSM_DeriveKey failure");
269	}
270
271errOut:
272	if(ccHand != 0) {
273		CSSM_DeleteContext(ccHand);
274	}
275	if(passKey != NULL) {
276		CSSM_FreeKey(cspHand, NULL, passKey, CSSM_FALSE);
277		free(passKey);
278	}
279	return ortn;
280}
281
282OSStatus SecImportRep::importWrappedKeyOpenssl(
283	SecKeychainRef						importKeychain, // optional
284	CSSM_CSP_HANDLE						cspHand,		// required
285	SecItemImportExportFlags			flags,
286	const SecKeyImportExportParameters	*keyParams,		// optional
287	CFMutableArrayRef					outArray)		// optional, append here
288{
289	assert(mExternFormat == kSecFormatWrappedOpenSSL);
290
291	/* I think this is an assert - only private keys are wrapped in opensssl format */
292	assert(mExternType == kSecItemTypePrivateKey);
293	assert(cspHand != 0);
294
295	if(keyParams == NULL) {
296		return errSecParam;
297	}
298
299	OSStatus				ortn;
300	SecNssCoder				coder;
301	impExpKeyUnwrapParams   unwrapParams;
302	CSSM_ALGORITHMS			pbeAlg = CSSM_ALGID_NONE;
303	CSSM_ALGORITHMS			keyAlg = CSSM_ALGID_NONE;
304	uint32					keySizeInBits;
305	unsigned				blockSizeInBytes;
306
307	memset(&unwrapParams, 0, sizeof(unwrapParams));
308
309	/* parse PEM header lines */
310	ortn = opensslPbeParams(mPemParamLines, coder,
311		pbeAlg, keyAlg,
312		unwrapParams.encrAlg,
313		unwrapParams.encrMode,
314		unwrapParams.encrPad,
315		keySizeInBits,
316		blockSizeInBytes,
317		unwrapParams.iv);
318	if(ortn) {
319		return ortn;
320	}
321
322	/* derive unwrapping key */
323	CSSM_KEY unwrappingKey;
324
325	ortn = deriveKeyOpensslWrap(keyParams, cspHand, VP_Import, pbeAlg, keyAlg,
326		keySizeInBits,
327		unwrapParams.iv,		/* salt = IV for these algs */
328		&unwrappingKey);
329	if(ortn) {
330		return ortn;
331	}
332
333	/* set up key to unwrap */
334	CSSM_KEY				wrappedKey;
335	CSSM_KEYHEADER			&hdr = wrappedKey.KeyHeader;
336	memset(&wrappedKey, 0, sizeof(CSSM_KEY));
337	hdr.HeaderVersion = CSSM_KEYHEADER_VERSION;
338	/* CspId : don't care */
339	hdr.BlobType = CSSM_KEYBLOB_WRAPPED;
340	hdr.Format = CSSM_KEYBLOB_WRAPPED_FORMAT_OPENSSL;
341	hdr.AlgorithmId = mKeyAlg;
342	hdr.KeyClass = CSSM_KEYCLASS_PRIVATE_KEY;
343	/* LogicalKeySizeInBits : calculated by CSP during unwrap */
344	hdr.KeyAttr = CSSM_KEYATTR_EXTRACTABLE;
345	hdr.KeyUsage = CSSM_KEYUSE_ANY;
346
347	wrappedKey.KeyData.Data = (uint8 *)CFDataGetBytePtr(mExternal);
348	wrappedKey.KeyData.Length = CFDataGetLength(mExternal);
349
350	unwrapParams.unwrappingKey = &unwrappingKey;
351
352	/* GO */
353	ortn =  impExpImportKeyCommon(&wrappedKey, importKeychain, cspHand,
354		flags, keyParams, &unwrapParams, NULL, outArray);
355
356	if(unwrappingKey.KeyData.Data != NULL) {
357		CSSM_FreeKey(cspHand, NULL, &unwrappingKey, CSSM_FALSE);
358	}
359	return ortn;
360}
361
362/*
363 * Hard coded parameters for export, we only do one flavor.
364 */
365#define OPENSSL_WRAP_KEY_ALG	CSSM_ALGID_3DES_3KEY
366#define OPENSSL_WRAP_PBE_ALG	CSSM_ALGID_PBE_OPENSSL_MD5
367#define OPENSSL_WRAP_KEY_SIZE   (64 * 3)
368#define OPENSSL_WRAP_ENCR_ALG   CSSM_ALGID_3DES_3KEY_EDE
369#define OPENSSL_WRAP_ENCR_MODE  CSSM_ALGMODE_CBCPadIV8
370#define OPENSSL_WRAP_ENCR_PAD   CSSM_PADDING_PKCS7
371
372OSStatus impExpWrappedKeyOpenSslExport(
373	SecKeyRef							secKey,
374	SecItemImportExportFlags			flags,
375	const SecKeyImportExportParameters	*keyParams,		// optional
376	CFMutableDataRef					outData,		// output appended here
377	const char							**pemHeader,	// RETURNED
378	CFArrayRef							*pemParamLines) // RETURNED
379{
380	DevRandomGenerator		rng;
381	SecNssCoder				coder;
382	CSSM_CSP_HANDLE			cspHand = 0;
383	OSStatus				ortn;
384	bool					releaseCspHand = false;
385	CFMutableArrayRef		paramLines;
386	CFStringRef				cfStr;
387	char					dekStr[100];
388	char					ivStr[3];
389
390	if(keyParams == NULL) {
391		return errSecParam;
392	}
393
394	/* we need a CSPDL handle - try to get it from the key */
395	ortn = SecKeyGetCSPHandle(secKey, &cspHand);
396	if(ortn) {
397		cspHand = cuCspStartup(CSSM_FALSE);
398		if(cspHand == 0) {
399			return CSSMERR_CSSM_ADDIN_LOAD_FAILED;
400		}
401		releaseCspHand = true;
402	}
403	/* subsequent errors to errOut: */
404
405	/* 8 bytes of random IV/salt */
406	uint8 saltIv[8];
407	CSSM_DATA saltIvData = { 8, saltIv} ;
408	rng.random(saltIv, 8);
409
410	/* derive wrapping key */
411	CSSM_KEY	wrappingKey;
412	wrappingKey.KeyData.Data = NULL;
413	wrappingKey.KeyData.Length = 0;
414	ortn = deriveKeyOpensslWrap(keyParams, cspHand, VP_Export,
415		OPENSSL_WRAP_PBE_ALG, OPENSSL_WRAP_KEY_ALG,
416		OPENSSL_WRAP_KEY_SIZE,
417		saltIvData,		// IV == salt for this wrapping alg
418		&wrappingKey);
419	if(ortn) {
420		goto errOut;
421	}
422
423	/* wrap the outgoing key */
424	CSSM_KEY wrappedKey;
425	memset(&wrappedKey, 0, sizeof(CSSM_KEY));
426
427	ortn = impExpExportKeyCommon(cspHand, secKey, &wrappingKey, &wrappedKey,
428		OPENSSL_WRAP_ENCR_ALG, OPENSSL_WRAP_ENCR_MODE, OPENSSL_WRAP_ENCR_PAD,
429		CSSM_KEYBLOB_WRAPPED_FORMAT_OPENSSL,
430		CSSM_ATTRIBUTE_NONE, CSSM_KEYBLOB_RAW_FORMAT_NONE,
431		NULL, &saltIvData);
432	if(ortn) {
433		goto errOut;
434	}
435
436	/*
437	 * That wrapped key's KeyData is our output
438	 */
439	CFDataAppendBytes(outData, wrappedKey.KeyData.Data, wrappedKey.KeyData.Length);
440
441	/* PEM header depends on key algorithm */
442	switch(wrappedKey.KeyHeader.AlgorithmId) {
443		case CSSM_ALGID_RSA:
444			*pemHeader = PEM_STRING_RSA;
445			break;
446		case CSSM_ALGID_DH:
447			*pemHeader = PEM_STRING_DH_PRIVATE;
448			break;
449		case CSSM_ALGID_DSA:
450			*pemHeader = PEM_STRING_DSA;
451			break;
452		case CSSM_ALGID_ECDSA:
453			*pemHeader = PEM_STRING_ECDSA_PRIVATE;
454			break;
455		default:
456			SecImpExpDbg("impExpWrappedKeyOpenSslExport unknown private key alg "
457				"%lu", (unsigned long)wrappedKey.KeyHeader.AlgorithmId);
458			/* punt though I think something is seriously hosed */
459			*pemHeader = "Private Key";
460	}
461	CSSM_FreeKey(cspHand, NULL, &wrappedKey, CSSM_FALSE);
462
463	/*
464	 * Last thing: set up outgoing PEM parameter lines
465	 */
466	assert(pemParamLines != NULL);
467	paramLines = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
468	cfStr = CFStringCreateWithCString(NULL,
469		"Proc-Type: 4,ENCRYPTED", kCFStringEncodingASCII);
470	CFArrayAppendValue(paramLines, cfStr);
471	CFRelease(cfStr);		// owned by array now */
472	strcpy(dekStr, "DEK-Info: DES-EDE3-CBC,");
473	/* next goes the IV */
474	for(unsigned dex=0; dex<8; dex++) {
475		sprintf(ivStr, "%02X", saltIv[dex]);
476		strcat(dekStr, ivStr);
477	}
478	cfStr = CFStringCreateWithCString(NULL, dekStr, kCFStringEncodingASCII);
479	CFArrayAppendValue(paramLines, cfStr);
480	CFRelease(cfStr);		// owned by array now */
481	/* and an empty line */
482	cfStr = CFStringCreateWithCString(NULL, "", kCFStringEncodingASCII);
483	CFArrayAppendValue(paramLines, cfStr);
484	CFRelease(cfStr);		// owned by array now */
485	*pemParamLines = paramLines;
486
487errOut:
488	if(wrappingKey.KeyData.Data != NULL) {
489		CSSM_FreeKey(cspHand, NULL, &wrappingKey, CSSM_FALSE);
490	}
491	return ortn;
492
493}
494
495