]>
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 | ||
6b200bc3 | 28 | #include <Security/SecImportExport.h> |
b1ab9ed8 A |
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> | |
e3d460c9 | 44 | #include <security_keychain/KeyItem.h> |
b1ab9ed8 A |
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 | /* | |
d8f41ccd A |
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; | |
b1ab9ed8 A |
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 | |
d8f41ccd | 76 | CSSM_KEY_PTR cssmKey, |
b1ab9ed8 A |
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}; | |
d8f41ccd | 84 | |
b1ab9ed8 A |
85 | crtn = impExpKeyDigest(cspHand, cssmKey, &keyDigest); |
86 | if(crtn) { | |
87 | return crtn; | |
88 | } | |
d8f41ccd | 89 | |
b1ab9ed8 A |
90 | /* caller needs this for subsequent DL lookup */ |
91 | newLabel.copy(keyDigest); | |
92 | ||
93 | /* Find this key as a SecKeychainItem */ | |
d8f41ccd A |
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 | } | |
427c49bc | 108 | SecKeychainAttribute kcAttr = {kSecKeyLabel, (UInt32)existKeyLabel->Length, existKeyLabel->Data}; |
b1ab9ed8 A |
109 | SecKeychainAttributeList kcAttrList = {1, &kcAttr}; |
110 | SecKeychainSearchRef srchRef = NULL; | |
111 | OSStatus ortn; | |
112 | SecKeychainItemRef itemRef = NULL; | |
d8f41ccd | 113 | |
b1ab9ed8 A |
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); | |
427c49bc | 129 | if(ortn == errSecSuccess) { |
b1ab9ed8 | 130 | SecImpExpDbg("impExpSetKeyLabel: found second key with same label!"); |
427c49bc | 131 | crtn = errSecInternalComponent; |
b1ab9ed8 A |
132 | goto errOut; |
133 | } | |
134 | #endif /* NDEBUG */ | |
d8f41ccd | 135 | |
b1ab9ed8 A |
136 | /* modify two attributes... */ |
137 | SecKeychainAttribute modAttrs[2]; | |
138 | modAttrs[0].tag = kSecKeyLabel; | |
427c49bc | 139 | modAttrs[0].length = (UInt32)keyDigest.Length; |
b1ab9ed8 A |
140 | modAttrs[0].data = keyDigest.Data; |
141 | modAttrs[1].tag = kSecKeyPrintName; | |
427c49bc | 142 | modAttrs[1].length = (UInt32)newPrintName->Length; |
b1ab9ed8 A |
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 | } | |
d8f41ccd | 164 | |
b1ab9ed8 | 165 | /* |
d8f41ccd A |
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 | |
b1ab9ed8 A |
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, | |
d8f41ccd | 178 | const SecKeyImportExportParameters *keyParams, // optional |
b1ab9ed8 | 179 | const char *printName, // optional |
d8f41ccd | 180 | CFMutableArrayRef outArray) // optional, append here |
b1ab9ed8 A |
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; | |
d8f41ccd | 189 | |
b1ab9ed8 A |
190 | /* First convert external format and types to CSSM style. */ |
191 | crtn = impExpKeyForm(externForm, itemType, keyAlg, &format, &keyClass); | |
d8f41ccd | 192 | |
b1ab9ed8 A |
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); | |
d8f41ccd | 197 | |
b1ab9ed8 A |
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; | |
d8f41ccd A |
208 | |
209 | /* | |
b1ab9ed8 | 210 | * Get key size in bits from raw CSP. Doing this right now is a good |
d8f41ccd | 211 | * optimization for the "guessing" case; getting the key size from the |
b1ab9ed8 | 212 | * raw CSP involves a full decode on an alg- and format-specific manner. |
d8f41ccd | 213 | * If we've been given the wrong params, we'll fail right here without |
b1ab9ed8 A |
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; | |
d8f41ccd | 227 | |
b1ab9ed8 A |
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; | |
d8f41ccd | 234 | |
b1ab9ed8 A |
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 | ||
d8f41ccd | 248 | /* |
b1ab9ed8 A |
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 | |
d8f41ccd | 251 | * existing key in order to get a KeychainCore::Item, by all means have at it. |
b1ab9ed8 A |
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 | { | |
d8f41ccd | 258 | /* |
b1ab9ed8 A |
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; | |
d8f41ccd | 263 | |
b1ab9ed8 A |
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: | |
427c49bc | 275 | return errSecParam; |
b1ab9ed8 A |
276 | } |
277 | assert(importKeychain != NULL); | |
278 | Keychain keychain = KeychainImpl::required(importKeychain); | |
d8f41ccd | 279 | |
b1ab9ed8 A |
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 | } | |
d8f41ccd | 285 | |
b1ab9ed8 | 286 | CssmClient::SSDb ssDb(impl); |
d8f41ccd | 287 | |
b1ab9ed8 A |
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 | } | |
d8f41ccd A |
300 | |
301 | /* | |
302 | * Get a Keychain-style Item, post notification. | |
b1ab9ed8 A |
303 | */ |
304 | Item keyItem = keychain->item(recordType, uniqueId); | |
305 | keychain->postEvent(kSecAddEvent, keyItem); | |
306 | ||
427c49bc | 307 | return errSecSuccess; |
b1ab9ed8 A |
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 | /* | |
d8f41ccd | 319 | * Common code to unwrap a key, used for raw keys (which do a NULL unwrap) and |
b1ab9ed8 A |
320 | * wrapped keys. |
321 | */ | |
322 | OSStatus impExpImportKeyCommon( | |
323 | const CSSM_KEY *wrappedKey, | |
324 | SecKeychainRef importKeychain, // optional | |
d8f41ccd | 325 | CSSM_CSP_HANDLE cspHand, // required, if importKeychain is |
b1ab9ed8 A |
326 | // present, must be from there |
327 | SecItemImportExportFlags flags, | |
d8f41ccd | 328 | const SecKeyImportExportParameters *keyParams, // optional |
b1ab9ed8 A |
329 | const impExpKeyUnwrapParams *unwrapParams, |
330 | const char *printName, // optional | |
d8f41ccd | 331 | CFMutableArrayRef outArray) // optional, append here |
b1ab9ed8 A |
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; | |
d8f41ccd | 353 | |
b1ab9ed8 A |
354 | assert(unwrapParams != NULL); |
355 | assert(cspHand != 0); | |
d8f41ccd | 356 | |
b1ab9ed8 A |
357 | if(importKeychain) { |
358 | ortn = SecKeychainGetDLDBHandle(importKeychain, &dlDbHandle); | |
359 | if(ortn) { | |
360 | return ortn; | |
361 | } | |
362 | dlDbPtr = &dlDbHandle; | |
363 | } | |
d8f41ccd | 364 | |
b1ab9ed8 A |
365 | memset(&unwrappedKey, 0, sizeof(CSSM_KEY)); |
366 | memset(&nullCreds, 0, sizeof(CSSM_ACCESS_CREDENTIALS)); | |
d8f41ccd | 367 | |
b1ab9ed8 A |
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 */ | |
d8f41ccd | 383 | crtn = impExpAddContextAttribute(ccHand, |
b1ab9ed8 A |
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 */ | |
d8f41ccd | 395 | char *randAscii = (char *)randLabel; |
b1ab9ed8 A |
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 | } | |
d8f41ccd | 414 | |
b1ab9ed8 A |
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 | } | |
d8f41ccd | 441 | |
b1ab9ed8 A |
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 | ) { | |
d8f41ccd | 448 | /* |
b1ab9ed8 A |
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 | ||
d8f41ccd A |
457 | /* |
458 | * Additional optional parameters: block size, rounds, | |
b1ab9ed8 A |
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); | |
d8f41ccd | 468 | crtn = impExpAddContextAttribute(ccHand, |
b1ab9ed8 A |
469 | CSSM_ATTRIBUTE_EFFECTIVE_BITS, |
470 | sizeof(uint32), | |
427c49bc | 471 | (void *)((size_t) unwrapParams->effectiveKeySizeInBits)); |
b1ab9ed8 A |
472 | if(crtn) { |
473 | SecImpExpDbg("impExpImportKeyCommon: CSSM_UpdateContextAttributes error"); | |
474 | goto errOut; | |
475 | } | |
476 | } | |
d8f41ccd | 477 | |
b1ab9ed8 A |
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); | |
d8f41ccd | 483 | crtn = impExpAddContextAttribute(ccHand, |
b1ab9ed8 A |
484 | CSSM_ATTRIBUTE_ROUNDS, |
485 | sizeof(uint32), | |
427c49bc | 486 | (void *)((size_t)unwrapParams->rounds)); |
b1ab9ed8 A |
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); | |
d8f41ccd A |
498 | /* |
499 | * With the current CSP this will actually be ignored | |
b1ab9ed8 | 500 | */ |
d8f41ccd | 501 | crtn = impExpAddContextAttribute(ccHand, |
b1ab9ed8 A |
502 | CSSM_ATTRIBUTE_BLOCK_SIZE, |
503 | sizeof(uint32), | |
427c49bc | 504 | (void *)((size_t)unwrapParams->blockSizeInBits)); |
b1ab9ed8 A |
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 | } | |
d8f41ccd | 530 | |
b1ab9ed8 A |
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 | |
d8f41ccd | 549 | crtn = impExpSetKeyLabel(cspHand, *dlDbPtr, &unwrappedKey, |
b1ab9ed8 A |
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 | } | |
d8f41ccd | 559 | |
b1ab9ed8 A |
560 | /* Private key: adjust ACL as appropriate */ |
561 | if(rccPtr != NULL) { | |
d8f41ccd A |
562 | SecPointer<KeychainCore::Access> theAccess(accessRef ? |
563 | KeychainCore::Access::required(accessRef) : | |
b1ab9ed8 A |
564 | new KeychainCore::Access("Imported Private Key")); |
565 | try { | |
e3d460c9 A |
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 | } | |
b1ab9ed8 A |
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 | } | |
d8f41ccd | 587 | |
b1ab9ed8 A |
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 | } | |
d8f41ccd | 607 | |
b1ab9ed8 A |
608 | if(importKeychain) { |
609 | impExpKeyNotify(importKeychain, keyLabel.get(), unwrappedKey); | |
610 | } | |
d8f41ccd | 611 | |
b1ab9ed8 A |
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 | } | |
d8f41ccd A |
625 | |
626 | /* | |
b1ab9ed8 A |
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; | |
d8f41ccd | 645 | |
b1ab9ed8 A |
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 | /* | |
d8f41ccd | 658 | * Creds are needed for wrapping private and session keys. |
b1ab9ed8 A |
659 | */ |
660 | CSSM_ACCESS_CREDENTIALS nullCreds; | |
661 | memset(&nullCreds, 0, sizeof(nullCreds)); | |
662 | const CSSM_ACCESS_CREDENTIALS *creds = &nullCreds; // default | |
d8f41ccd | 663 | |
b1ab9ed8 | 664 | CSSM_KEYCLASS keyClass = unwrappedKey->KeyHeader.KeyClass; |
d8f41ccd | 665 | if(keyClass == CSSM_KEYCLASS_PRIVATE_KEY || keyClass == CSSM_KEYCLASS_SESSION_KEY) { |
b1ab9ed8 | 666 | ortn = SecKeyGetCredentials(secKey, |
d8f41ccd | 667 | CSSM_ACL_AUTHORIZATION_DECRYPT, |
b1ab9ed8 A |
668 | kSecCredentialTypeDefault, |
669 | &creds); | |
670 | if(ortn) { | |
671 | SecImpExpDbg("impExpExportKeyCommon SecKeyGetCredentials error"); | |
672 | return ortn; | |
673 | } | |
674 | } | |
d8f41ccd | 675 | |
b1ab9ed8 A |
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 | } | |
d8f41ccd | 690 | |
b1ab9ed8 A |
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, | |
d8f41ccd | 695 | sizeof(uint32), |
427c49bc | 696 | (void *)((size_t)wrapFormat)); |
b1ab9ed8 A |
697 | if(crtn) { |
698 | SecImpExpDbg("impExpExportKeyCommon AddContextAttribute error (1)"); | |
699 | CSSM_DeleteContext(ccHand); | |
700 | return crtn; | |
701 | } | |
702 | } | |
d8f41ccd | 703 | |
b1ab9ed8 A |
704 | if(blobAttrType != CSSM_ATTRIBUTE_NONE) { |
705 | crtn = impExpAddContextAttribute(ccHand, | |
706 | blobAttrType, | |
d8f41ccd | 707 | sizeof(uint32), |
427c49bc | 708 | (void *)((size_t)blobForm)); |
b1ab9ed8 A |
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 | } | |
b1ab9ed8 A |
719 | |
720 | crtn = CSSM_WrapKey(ccHand, | |
721 | creds, | |
722 | unwrappedKey, | |
d8f41ccd | 723 | &dData, |
b1ab9ed8 A |
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 | |
d8f41ccd | 733 | * key, or when we null wrap a sensitive key. Give the caller |
b1ab9ed8 A |
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 | } |