]> git.saurik.com Git - apple/security.git/blob - OSX/libsecurity_keychain/lib/SecImportExportCrypto.cpp
Security-57740.1.18.tar.gz
[apple/security.git] / OSX / libsecurity_keychain / lib / SecImportExportCrypto.cpp
1 /*
2 * Copyright (c) 2000-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 /*
24 * SecImportExportCrypto.cpp - low-level crypto routines for wrapping and unwrapping
25 * keys.
26 */
27
28 #include "SecImportExport.h"
29 #include "SecImportExportCrypto.h"
30 #include "SecImportExportUtils.h"
31 #include "Keychains.h"
32 #include "Access.h"
33 #include "Item.h"
34 #include "SecKeyPriv.h"
35 #include "KCEventNotifier.h"
36 #include <security_cdsa_utilities/cssmacl.h>
37 #include <security_cdsa_utilities/KeySchema.h>
38 #include <security_cdsa_utilities/cssmdata.h>
39 #include <security_cdsa_utils/cuCdsaUtils.h>
40 #include <security_utilities/devrandom.h>
41 #include <security_cdsa_client/securestorage.h>
42 #include <security_cdsa_client/dlclient.h>
43 #include <Security/cssmapi.h>
44 #include <security_keychain/KeyItem.h>
45
46 /*
47 * Key attrribute names and values.
48 *
49 * This is where the public key hash goes.
50 */
51 #define SEC_KEY_HASH_ATTR_NAME "Label"
52
53 /*
54 * This is where the publicly visible name goes.
55 */
56 #define SEC_KEY_PRINT_NAME_ATTR_NAME "PrintName"
57
58 /*
59 * Default values we ultimately assign to the PrintName attr.
60 */
61 #define SEC_PRIVKEY_PRINT_NAME_ATTR_VALUE "Imported Private Key"
62 #define SEC_PUBKEY_PRINT_NAME_ATTR_VALUE "Imported Public Key"
63 #define SEC_SESSIONKEY_PRINT_NAME_ATTR_VALUE "Imported Key"
64
65 /*
66 * Set private key's Label and PrintName attributes. On entry Label
67 * is typically a random string to faciliate finding the key in a DL;
68 * the PrintName is currently set to the same value by the DL. We
69 * replace the Label attr with the public key hash and the PrintName
70 * attr with a caller-supplied value.
71 */
72 static CSSM_RETURN impExpSetKeyLabel(
73 CSSM_CSP_HANDLE cspHand, // where the key lives
74 CSSM_DL_DB_HANDLE dlDbHand, // ditto
75 SecKeychainRef kcRef, // ditto
76 CSSM_KEY_PTR cssmKey,
77 const CSSM_DATA *existKeyLabel, // existing label, a random string
78 const CSSM_DATA *newPrintName,
79 CssmOwnedData &newLabel, // RETURNED as what we set
80 SecKeyRef *secKey) // RETURNED
81 {
82 CSSM_RETURN crtn;
83 CSSM_DATA keyDigest = {0, NULL};
84
85 crtn = impExpKeyDigest(cspHand, cssmKey, &keyDigest);
86 if(crtn) {
87 return crtn;
88 }
89
90 /* caller needs this for subsequent DL lookup */
91 newLabel.copy(keyDigest);
92
93 /* Find this key as a SecKeychainItem */
94 SecItemClass itemClass;
95 switch (cssmKey->KeyHeader.KeyClass) {
96 case CSSM_KEYCLASS_PRIVATE_KEY:
97 itemClass = kSecPrivateKeyItemClass;
98 break;
99 case CSSM_KEYCLASS_PUBLIC_KEY:
100 itemClass = kSecPublicKeyItemClass;
101 break;
102 case CSSM_KEYCLASS_SESSION_KEY:
103 itemClass = kSecSymmetricKeyItemClass;
104 break;
105 default:
106 itemClass = (SecItemClass) 0;
107 }
108 SecKeychainAttribute kcAttr = {kSecKeyLabel, (UInt32)existKeyLabel->Length, existKeyLabel->Data};
109 SecKeychainAttributeList kcAttrList = {1, &kcAttr};
110 SecKeychainSearchRef srchRef = NULL;
111 OSStatus ortn;
112 SecKeychainItemRef itemRef = NULL;
113
114 ortn = SecKeychainSearchCreateFromAttributes(kcRef, itemClass,
115 &kcAttrList, &srchRef);
116 if(ortn) {
117 SecImpExpDbg("SecKeychainSearchCreateFromAttributes error");
118 crtn = ortn;
119 goto errOut;
120 }
121 ortn = SecKeychainSearchCopyNext(srchRef, &itemRef);
122 if(ortn) {
123 SecImpExpDbg("SecKeychainSearchCopyNext error");
124 crtn = ortn;
125 goto errOut;
126 }
127 #ifndef NDEBUG
128 ortn = SecKeychainSearchCopyNext(srchRef, &itemRef);
129 if(ortn == errSecSuccess) {
130 SecImpExpDbg("impExpSetKeyLabel: found second key with same label!");
131 crtn = errSecInternalComponent;
132 goto errOut;
133 }
134 #endif /* NDEBUG */
135
136 /* modify two attributes... */
137 SecKeychainAttribute modAttrs[2];
138 modAttrs[0].tag = kSecKeyLabel;
139 modAttrs[0].length = (UInt32)keyDigest.Length;
140 modAttrs[0].data = keyDigest.Data;
141 modAttrs[1].tag = kSecKeyPrintName;
142 modAttrs[1].length = (UInt32)newPrintName->Length;
143 modAttrs[1].data = newPrintName->Data;
144 kcAttrList.count = 2;
145 kcAttrList.attr = modAttrs;
146 ortn = SecKeychainItemModifyAttributesAndData(itemRef, &kcAttrList,
147 0, NULL);
148 if(ortn) {
149 SecImpExpDbg("SecKeychainItemModifyAttributesAndData error");
150 crtn = ortn;
151 goto errOut;
152 }
153 *secKey = (SecKeyRef)itemRef;
154 errOut:
155 if(keyDigest.Data) {
156 /* mallocd by CSP */
157 impExpFreeCssmMemory(cspHand, keyDigest.Data);
158 }
159 if(srchRef) {
160 CFRelease(srchRef);
161 }
162 return crtn;
163 }
164
165 /*
166 * Import a raw key. This can be used as a lightweight "guess" evaluator
167 * if a handle to the raw CSP is passed in (with no keychain), or as
168 * the real thing which does full keychain import.
169 */
170 OSStatus impExpImportRawKey(
171 CFDataRef inData,
172 SecExternalFormat externForm,
173 SecExternalItemType itemType,
174 CSSM_ALGORITHMS keyAlg,
175 SecKeychainRef importKeychain, // optional
176 CSSM_CSP_HANDLE cspHand, // required
177 SecItemImportExportFlags flags,
178 const SecKeyImportExportParameters *keyParams, // optional
179 const char *printName, // optional
180 CFMutableArrayRef outArray) // optional, append here
181 {
182 CSSM_RETURN crtn;
183 CSSM_KEY wrappedKey;
184 CSSM_KEYHEADER &hdr = wrappedKey.KeyHeader;
185 CSSM_CSP_HANDLE rawCspHand = 0;
186 CSSM_KEY_SIZE keySize;
187 CSSM_KEYBLOB_FORMAT format;
188 CSSM_KEYCLASS keyClass;
189
190 /* First convert external format and types to CSSM style. */
191 crtn = impExpKeyForm(externForm, itemType, keyAlg, &format, &keyClass);
192
193 /* cook up key to be null-unwrapped */
194 memset(&wrappedKey, 0, sizeof(CSSM_KEY));
195 wrappedKey.KeyData.Length = CFDataGetLength(inData);
196 wrappedKey.KeyData.Data = (uint8 *)CFDataGetBytePtr(inData);
197
198 hdr.HeaderVersion = CSSM_KEYHEADER_VERSION;
199 /* CspId don't care */
200 hdr.BlobType = CSSM_KEYBLOB_RAW;
201 hdr.Format = format;
202 hdr.AlgorithmId = keyAlg;
203 hdr.KeyClass = keyClass;
204 /* LogicalKeySizeInBits calculated below */
205 /* attr and usage are for the incoming unwrapped key... */
206 hdr.KeyAttr = CSSM_KEYATTR_EXTRACTABLE;
207 hdr.KeyUsage = CSSM_KEYUSE_ANY;
208
209 /*
210 * Get key size in bits from raw CSP. Doing this right now is a good
211 * optimization for the "guessing" case; getting the key size from the
212 * raw CSP involves a full decode on an alg- and format-specific manner.
213 * If we've been given the wrong params, we'll fail right here without
214 * the complication of a full UnwrapKey op.
215 */
216 rawCspHand = cuCspStartup(CSSM_TRUE);
217 if(rawCspHand == 0) {
218 return CSSMERR_CSSM_ADDIN_LOAD_FAILED;
219 }
220 crtn = CSSM_QueryKeySizeInBits(rawCspHand, CSSM_INVALID_HANDLE, &wrappedKey, &keySize);
221 cuCspDetachUnload(rawCspHand, CSSM_TRUE);
222 if(crtn) {
223 SecImpExpDbg("CSSM_QueryKeySizeInBits error");
224 return crtn;
225 }
226 hdr.LogicalKeySizeInBits = keySize.LogicalKeySizeInBits;
227
228 impExpKeyUnwrapParams unwrapParams;
229 memset(&unwrapParams, 0, sizeof(unwrapParams));
230 unwrapParams.encrAlg = CSSM_ALGID_NONE;
231 unwrapParams.encrMode = CSSM_ALGMODE_NONE;
232 unwrapParams.unwrappingKey = NULL;
233 unwrapParams.encrPad = CSSM_PADDING_NONE;
234
235 return impExpImportKeyCommon(
236 &wrappedKey,
237 importKeychain,
238 cspHand,
239 flags,
240 keyParams,
241 &unwrapParams,
242 printName,
243 outArray);
244 }
245
246 using namespace KeychainCore;
247
248 /*
249 * Post notification of a "new key added" event.
250 * If you know of another way to do this, other than a dlclient-based lookup of the
251 * existing key in order to get a KeychainCore::Item, by all means have at it.
252 */
253 OSStatus impExpKeyNotify(
254 SecKeychainRef importKeychain,
255 const CssmData &keyLabel, // stored with this, we use it to do a lookup
256 const CSSM_KEY &cssmKey) // unwrapped key in CSSM format
257 {
258 /*
259 * Look up key in the DLDB by label, key class, algorithm, and key size.
260 */
261 CSSM_DB_RECORDTYPE recordType;
262 const CSSM_KEYHEADER &hdr = cssmKey.KeyHeader;
263
264 switch(hdr.KeyClass) {
265 case CSSM_KEYCLASS_PUBLIC_KEY:
266 recordType = CSSM_DL_DB_RECORD_PUBLIC_KEY;
267 break;
268 case CSSM_KEYCLASS_PRIVATE_KEY:
269 recordType = CSSM_DL_DB_RECORD_PRIVATE_KEY;
270 break;
271 case CSSM_KEYCLASS_SESSION_KEY:
272 recordType = CSSM_DL_DB_RECORD_SYMMETRIC_KEY;
273 break;
274 default:
275 return errSecParam;
276 }
277 assert(importKeychain != NULL);
278 Keychain keychain = KeychainImpl::required(importKeychain);
279
280 SSDbImpl* impl = dynamic_cast<CssmClient::SSDbImpl *>(&(*keychain->database()));
281 if (impl == NULL) // did we go bad?
282 {
283 CssmError::throwMe(CSSMERR_CSSM_INVALID_POINTER);
284 }
285
286 CssmClient::SSDb ssDb(impl);
287
288 CssmClient::DbAttributes dbAttributes;
289 CssmClient::DbUniqueRecord uniqueId;
290 CssmClient::SSDbCursor dbCursor(ssDb, 3); // three attributes
291 dbCursor->recordType(recordType);
292 dbCursor->add(CSSM_DB_EQUAL, KeySchema::Label, keyLabel);
293 dbCursor->add(CSSM_DB_EQUAL, KeySchema::KeyType, hdr.AlgorithmId);
294 dbCursor->add(CSSM_DB_EQUAL, KeySchema::KeySizeInBits, hdr.LogicalKeySizeInBits);
295 CssmClient::Key key;
296 if (!dbCursor->nextKey(&dbAttributes, key, uniqueId)) {
297 SecImpExpDbg("impExpKeyNotify: key not found");
298 return errSecItemNotFound;
299 }
300
301 /*
302 * Get a Keychain-style Item, post notification.
303 */
304 Item keyItem = keychain->item(recordType, uniqueId);
305 keychain->postEvent(kSecAddEvent, keyItem);
306
307 return errSecSuccess;
308 }
309
310 /*
311 * Size of random label string in ASCII chars to facilitate DL lookup.
312 */
313 #define SEC_RANDOM_LABEL_LEN 16
314
315 #define SEC_KEYATTR_RETURN_MASK \
316 (CSSM_KEYATTR_RETURN_DATA | CSSM_KEYATTR_RETURN_REF | CSSM_KEYATTR_RETURN_NONE)
317
318 /*
319 * Common code to unwrap a key, used for raw keys (which do a NULL unwrap) and
320 * wrapped keys.
321 */
322 OSStatus impExpImportKeyCommon(
323 const CSSM_KEY *wrappedKey,
324 SecKeychainRef importKeychain, // optional
325 CSSM_CSP_HANDLE cspHand, // required, if importKeychain is
326 // present, must be from there
327 SecItemImportExportFlags flags,
328 const SecKeyImportExportParameters *keyParams, // optional
329 const impExpKeyUnwrapParams *unwrapParams,
330 const char *printName, // optional
331 CFMutableArrayRef outArray) // optional, append here
332 {
333 CSSM_CC_HANDLE ccHand = 0;
334 CSSM_RETURN crtn;
335 CSSM_DATA labelData;
336 CSSM_KEY unwrappedKey;
337 CSSM_DL_DB_HANDLE dlDbHandle;
338 CSSM_DL_DB_HANDLE *dlDbPtr = NULL;
339 OSStatus ortn;
340 CSSM_ACCESS_CREDENTIALS nullCreds;
341 uint8 randLabel[SEC_RANDOM_LABEL_LEN + 1];
342 CSSM_KEYUSE keyUsage = 0; // default
343 CSSM_KEYATTR_FLAGS keyAttributes = 0; // default
344 const CSSM_KEYHEADER &hdr = wrappedKey->KeyHeader;
345 CSSM_DATA descrData = {0, NULL};
346 ResourceControlContext rcc;
347 Security::KeychainCore::Access::Maker maker;
348 ResourceControlContext *rccPtr = NULL;
349 SecAccessRef accessRef = keyParams ? keyParams->accessRef : NULL;
350 CssmAutoData keyLabel(Allocator::standard());
351 SecKeyRef secKeyRef = NULL;
352 bool usedSecKeyCreate = false;
353
354 assert(unwrapParams != NULL);
355 assert(cspHand != 0);
356
357 if(importKeychain) {
358 ortn = SecKeychainGetDLDBHandle(importKeychain, &dlDbHandle);
359 if(ortn) {
360 return ortn;
361 }
362 dlDbPtr = &dlDbHandle;
363 }
364
365 memset(&unwrappedKey, 0, sizeof(CSSM_KEY));
366 memset(&nullCreds, 0, sizeof(CSSM_ACCESS_CREDENTIALS));
367
368 /* context for unwrap */
369 crtn = CSSM_CSP_CreateSymmetricContext(cspHand,
370 unwrapParams->encrAlg,
371 unwrapParams->encrMode,
372 &nullCreds,
373 unwrapParams->unwrappingKey,
374 unwrapParams->iv.Data ? &unwrapParams->iv : NULL,
375 unwrapParams->encrPad,
376 0, // Params
377 &ccHand);
378 if(crtn) {
379 goto errOut;
380 }
381 if(dlDbPtr) {
382 /* Importing to a keychain - add DLDB to context */
383 crtn = impExpAddContextAttribute(ccHand,
384 CSSM_ATTRIBUTE_DL_DB_HANDLE,
385 sizeof(CSSM_ATTRIBUTE_DL_DB_HANDLE),
386 dlDbPtr);
387 if(crtn) {
388 SecImpExpDbg("impExpImportKeyCommon: CSSM_UpdateContextAttributes error");
389 goto errOut;
390 }
391 }
392
393 if((hdr.KeyClass != CSSM_KEYCLASS_SESSION_KEY) && (dlDbPtr != NULL)) {
394 /* Generate random 16-char label to facilitate DL lookup */
395 char *randAscii = (char *)randLabel;
396 uint8 randBinary[SEC_RANDOM_LABEL_LEN / 2];
397 unsigned randBinaryLen = SEC_RANDOM_LABEL_LEN / 2;
398 DevRandomGenerator rng;
399
400 rng.random(randBinary, randBinaryLen);
401 for(unsigned i=0; i<randBinaryLen; i++) {
402 sprintf(randAscii, "%02X", randBinary[i]);
403 randAscii += 2;
404 }
405 labelData.Data = randLabel;
406 labelData.Length = SEC_RANDOM_LABEL_LEN;
407 /* actual keyLabel value set later */
408 }
409 else {
410 labelData.Data = (uint8 *)SEC_SESSIONKEY_PRINT_NAME_ATTR_VALUE;
411 labelData.Length = strlen(SEC_SESSIONKEY_PRINT_NAME_ATTR_VALUE);
412 keyLabel.copy(labelData);
413 }
414
415 /*
416 * key attr flags and usage. First the defaults.
417 */
418 if(keyParams) {
419 keyUsage = keyParams->keyUsage;
420 keyAttributes = keyParams->keyAttributes;
421 }
422 if(keyUsage == 0) {
423 /* default */
424 keyUsage = CSSM_KEYUSE_ANY;
425 }
426 if(keyAttributes == 0) {
427 /* default */
428 keyAttributes = CSSM_KEYATTR_RETURN_REF | CSSM_KEYATTR_EXTRACTABLE;
429 if(dlDbPtr) {
430 keyAttributes |= CSSM_KEYATTR_PERMANENT;
431 }
432 if(hdr.KeyClass == CSSM_KEYCLASS_PRIVATE_KEY) {
433 keyAttributes |= (CSSM_KEYATTR_SENSITIVE | CSSM_KEYATTR_EXTRACTABLE);
434 }
435 }
436 else {
437 /* caller-supplied; ensure we're generating a reference key */
438 keyAttributes &= ~SEC_KEYATTR_RETURN_MASK;
439 keyAttributes |= CSSM_KEYATTR_RETURN_REF;
440 }
441
442 if( (dlDbPtr != NULL) && // not permanent, no ACL
443 (hdr.KeyClass == CSSM_KEYCLASS_PRIVATE_KEY) && // ACLs only for private key
444 ( (keyParams == NULL) || // NULL --> default ACL
445 !(keyParams->flags & kSecKeyNoAccessControl) // explicity request no ACL
446 )
447 ) {
448 /*
449 * Prepare to set up either a default ACL or one provided by caller via
450 * keyParams->accessRef.
451 */
452 memset(&rcc, 0, sizeof(rcc));
453 maker.initialOwner(rcc);
454 rccPtr = &rcc;
455 }
456
457 /*
458 * Additional optional parameters: block size, rounds,
459 * effectiveKeySize.
460 * WARNING: block size and rounds, used for RC5, have not been tested.
461 * OpenSSL, as of Panther ship, did not support RC5 encryption.
462 */
463 if(unwrapParams->effectiveKeySizeInBits != 0) {
464 assert(unwrapParams->unwrappingKey->KeyHeader.AlgorithmId ==
465 CSSM_ALGID_RC2);
466 SecImpExpDbg("impExpImportKeyCommon: setting effectiveKeySizeInBits to %lu",
467 (unsigned long)unwrapParams->effectiveKeySizeInBits);
468 crtn = impExpAddContextAttribute(ccHand,
469 CSSM_ATTRIBUTE_EFFECTIVE_BITS,
470 sizeof(uint32),
471 (void *)((size_t) unwrapParams->effectiveKeySizeInBits));
472 if(crtn) {
473 SecImpExpDbg("impExpImportKeyCommon: CSSM_UpdateContextAttributes error");
474 goto errOut;
475 }
476 }
477
478 if(unwrapParams->rounds != 0) {
479 assert(unwrapParams->unwrappingKey->KeyHeader.AlgorithmId ==
480 CSSM_ALGID_RC5);
481 SecImpExpDbg("impExpImportKeyCommon: setting rounds to %lu",
482 (unsigned long)unwrapParams->rounds);
483 crtn = impExpAddContextAttribute(ccHand,
484 CSSM_ATTRIBUTE_ROUNDS,
485 sizeof(uint32),
486 (void *)((size_t)unwrapParams->rounds));
487 if(crtn) {
488 SecImpExpDbg("impExpImportKeyCommon: CSSM_UpdateContextAttributes error");
489 goto errOut;
490 }
491 }
492
493 if(unwrapParams->blockSizeInBits != 0) {
494 /* Our RC5 implementation has a fixed block size */
495 if(unwrapParams->blockSizeInBits != 64) {
496 SecImpExpDbg("WARNING impExpImportKeyCommon: setting block size to %lu",
497 (unsigned long)unwrapParams->blockSizeInBits);
498 /*
499 * With the current CSP this will actually be ignored
500 */
501 crtn = impExpAddContextAttribute(ccHand,
502 CSSM_ATTRIBUTE_BLOCK_SIZE,
503 sizeof(uint32),
504 (void *)((size_t)unwrapParams->blockSizeInBits));
505 if(crtn) {
506 SecImpExpDbg("impExpImportKeyCommon: CSSM_UpdateContextAttributes error");
507 goto errOut;
508 }
509 }
510 }
511
512 /* Here we go */
513 crtn = CSSM_UnwrapKey(ccHand,
514 NULL, // public key
515 (const CSSM_WRAP_KEY *)wrappedKey,
516 keyUsage,
517 keyAttributes,
518 &labelData,
519 rccPtr, // CredAndAclEntry
520 &unwrappedKey,
521 &descrData); // required
522 if(crtn != CSSM_OK) {
523 SecImpExpDbg("CSSM_UnwrapKey failure");
524 if(crtn == CSSMERR_DL_INVALID_UNIQUE_INDEX_DATA) {
525 /* report in a keychain-friendly way */
526 crtn = errSecDuplicateItem;
527 }
528 goto errOut;
529 }
530
531 /* Private and public keys: update Label as public key hash */
532 if((hdr.KeyClass != CSSM_KEYCLASS_SESSION_KEY) && (dlDbPtr != NULL)) {
533 CSSM_DATA newPrintName;
534 if(printName) {
535 /* caller specified */
536 newPrintName.Data = (uint8 *)printName;
537 }
538 else {
539 /* use defaults */
540 if(hdr.KeyClass == CSSM_KEYCLASS_PRIVATE_KEY) {
541 newPrintName.Data = (uint8 *)SEC_PRIVKEY_PRINT_NAME_ATTR_VALUE;
542 }
543 else {
544 newPrintName.Data = (uint8 *)SEC_PUBKEY_PRINT_NAME_ATTR_VALUE;
545 }
546 }
547 newPrintName.Length = strlen((char *)newPrintName.Data);
548 #if old_way
549 crtn = impExpSetKeyLabel(cspHand, *dlDbPtr, &unwrappedKey,
550 &labelData, &newPrintName, keyLabel);
551 #else
552 crtn = impExpSetKeyLabel(cspHand, *dlDbPtr, importKeychain,
553 &unwrappedKey, &labelData, &newPrintName, keyLabel, &secKeyRef);
554 #endif
555 if(crtn) {
556 goto errOut;
557 }
558 }
559
560 /* Private key: adjust ACL as appropriate */
561 if(rccPtr != NULL) {
562 SecPointer<KeychainCore::Access> theAccess(accessRef ?
563 KeychainCore::Access::required(accessRef) :
564 new KeychainCore::Access("Imported Private Key"));
565 try {
566 if(secKeyRef != NULL) {
567 // setAccess using the new secKeyRef, not the old unwrappedKey.
568 // At this point, we might have duplicate keys registered with securityd. Use the newest one.
569 theAccess->setAccess(*KeyItem::required(secKeyRef)->key(), maker);
570 } else {
571 CssmClient::KeyAclBearer bearer(cspHand, unwrappedKey, Allocator::standard());
572 theAccess->setAccess(bearer, maker);
573 }
574 }
575 catch (const CssmError &e) {
576 /* not implemented means we're talking to the raw CSP which does
577 * not implement ACLs */
578 if(e.error != CSSMERR_CSP_FUNCTION_NOT_IMPLEMENTED) {
579 crtn = e.error;
580 }
581 }
582 catch(...) {
583 SecImpExpDbg("keyImport: exception on setAccess\n");
584 crtn = errSecAuthFailed; /* ??? */
585 }
586 }
587
588 /*
589 * If importKeychain is non-NULL we've already added the key to the keychain.
590 * If importKeychain is NULL, and outArray is non-NULL, we have to use the
591 * half-baked SecKeyCreateWithCSSMKey to give the caller *something*.
592 */
593 if(outArray) {
594 if(secKeyRef == NULL) {
595 assert(importKeychain == NULL);
596 ortn = SecKeyCreateWithCSSMKey(&unwrappedKey, &secKeyRef);
597 if(ortn) {
598 SecImpExpDbg("SecKeyCreateWithCSSMKey failure");
599 crtn = ortn;
600 goto errOut;
601 }
602 /* don't CSSM_FreeKey() this key */
603 usedSecKeyCreate = true;
604 }
605 CFArrayAppendValue(outArray, secKeyRef);
606 }
607
608 if(importKeychain) {
609 impExpKeyNotify(importKeychain, keyLabel.get(), unwrappedKey);
610 }
611
612 errOut:
613 if(ccHand != 0) {
614 CSSM_DeleteContext(ccHand);
615 }
616 if(secKeyRef) {
617 CFRelease(secKeyRef);
618 }
619 if((unwrappedKey.KeyData.Data != NULL) && !usedSecKeyCreate) {
620 /* skip this free if we used SecKeyCreateWithCSSMKey() */
621 CSSM_FreeKey(cspHand, NULL, &unwrappedKey, CSSM_FALSE);
622 }
623 return crtn;
624 }
625
626 /*
627 * Common code to wrap a key for export.
628 */
629 CSSM_RETURN impExpExportKeyCommon(
630 CSSM_CSP_HANDLE cspHand, // for all three keys
631 SecKeyRef secKey,
632 CSSM_KEY_PTR wrappingKey,
633 CSSM_KEY_PTR wrappedKey, // RETURNED
634 CSSM_ALGORITHMS wrapAlg,
635 CSSM_ENCRYPT_MODE wrapMode,
636 CSSM_PADDING wrapPad,
637 CSSM_KEYBLOB_FORMAT wrapFormat, // NONE, PKCS7, PKCS8, OPENSSL
638 CSSM_ATTRIBUTE_TYPE blobAttrType, // optional raw key format attr
639 CSSM_KEYBLOB_FORMAT blobForm, // ditto
640 const CSSM_DATA *descData, // optional descriptive data
641 const CSSM_DATA *iv)
642 {
643 OSStatus ortn;
644 CSSM_RETURN crtn;
645
646 const CSSM_KEY *unwrappedKey;
647 ortn = SecKeyGetCSSMKey(secKey, &unwrappedKey);
648 if(ortn) {
649 SecImpExpDbg("impExpExportKeyCommon SecKeyGetCSSMKey error");
650 return ortn;
651 }
652 else if(!(unwrappedKey->KeyHeader.KeyAttr & CSSM_KEYATTR_EXTRACTABLE)) {
653 SecImpExpDbg("impExpExportKeyCommon: CSSM key is non-extractable");
654 return errSecDataNotAvailable;
655 }
656
657 /*
658 * Creds are needed for wrapping private and session keys.
659 */
660 CSSM_ACCESS_CREDENTIALS nullCreds;
661 memset(&nullCreds, 0, sizeof(nullCreds));
662 const CSSM_ACCESS_CREDENTIALS *creds = &nullCreds; // default
663
664 CSSM_KEYCLASS keyClass = unwrappedKey->KeyHeader.KeyClass;
665 if(keyClass == CSSM_KEYCLASS_PRIVATE_KEY || keyClass == CSSM_KEYCLASS_SESSION_KEY) {
666 ortn = SecKeyGetCredentials(secKey,
667 CSSM_ACL_AUTHORIZATION_DECRYPT,
668 kSecCredentialTypeDefault,
669 &creds);
670 if(ortn) {
671 SecImpExpDbg("impExpExportKeyCommon SecKeyGetCredentials error");
672 return ortn;
673 }
674 }
675
676 CSSM_CC_HANDLE ccHand;
677 crtn = CSSM_CSP_CreateSymmetricContext(cspHand,
678 wrapAlg,
679 wrapMode,
680 &nullCreds, // creds for wrapping key, never a private key here
681 wrappingKey,
682 iv,
683 wrapPad,
684 0, // Params
685 &ccHand);
686 if(ortn) {
687 SecImpExpDbg("impExpExportKeyCommon CSSM_CSP_CreateSymmetricContext error");
688 return crtn;
689 }
690
691 /* a couple of optional caller-specified attributes */
692 if(wrapFormat != CSSM_KEYBLOB_WRAPPED_FORMAT_NONE) {
693 crtn = impExpAddContextAttribute(ccHand,
694 CSSM_ATTRIBUTE_WRAPPED_KEY_FORMAT,
695 sizeof(uint32),
696 (void *)((size_t)wrapFormat));
697 if(crtn) {
698 SecImpExpDbg("impExpExportKeyCommon AddContextAttribute error (1)");
699 CSSM_DeleteContext(ccHand);
700 return crtn;
701 }
702 }
703
704 if(blobAttrType != CSSM_ATTRIBUTE_NONE) {
705 crtn = impExpAddContextAttribute(ccHand,
706 blobAttrType,
707 sizeof(uint32),
708 (void *)((size_t)blobForm));
709 if(crtn) {
710 SecImpExpDbg("impExpExportKeyCommon AddContextAttribute error");
711 return crtn;
712 }
713 }
714
715 CSSM_DATA dData = {0, 0};
716 if(descData) {
717 dData = *descData;
718 }
719
720 crtn = CSSM_WrapKey(ccHand,
721 creds,
722 unwrappedKey,
723 &dData,
724 wrappedKey);
725 CSSM_DeleteContext(ccHand);
726 switch(crtn) {
727 case CSSM_OK:
728 break;
729 case CSSMERR_CSP_INVALID_KEYATTR_MASK:
730 {
731 /*
732 * This is what comes back when we try to wrap an unextractable
733 * key, or when we null wrap a sensitive key. Give the caller
734 * some useful info.
735 */
736 CSSM_KEYATTR_FLAGS attr = unwrappedKey->KeyHeader.KeyAttr;
737 if(!(attr & CSSM_KEYATTR_EXTRACTABLE)) {
738 SecImpExpDbg("impExpExportKeyCommon !EXTRACTABLE");
739 return errSecDataNotAvailable;
740 }
741 if((attr & CSSM_KEYATTR_SENSITIVE) && (wrappingKey == NULL)) {
742 SecImpExpDbg("impExpExportKeyCommon !SENSITIVE, NULL wrap");
743 return errSecPassphraseRequired;
744 }
745
746 }
747 default:
748 SecImpExpDbg("impExpExportKeyCommon CSSM_WrapKey error");
749 }
750 return crtn;
751 }