]> git.saurik.com Git - apple/security.git/blob - OSX/libsecurity_keychain/lib/SecWrappedKeys.cpp
Security-59306.11.20.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
37 #include <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