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