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