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