]> git.saurik.com Git - apple/security.git/blob - OSX/libsecurity_keychain/lib/SecWrappedKeys.cpp
Security-57337.20.44.tar.gz
[apple/security.git] / OSX / libsecurity_keychain / lib / 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 #include <security_utilities/devrandom.h>
37
38 #include <assert.h>
39
40 using namespace Security;
41 using namespace KeychainCore;
42
43 static 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 */
66 static 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 */
88 static 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 */
201 static 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
271 errOut:
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
282 OSStatus 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
372 OSStatus 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
487 errOut:
488 if(wrappingKey.KeyData.Data != NULL) {
489 CSSM_FreeKey(cspHand, NULL, &wrappingKey, CSSM_FALSE);
490 }
491 return ortn;
492
493 }
494