]>
Commit | Line | Data |
---|---|---|
b1ab9ed8 | 1 | /* |
d8f41ccd | 2 | * Copyright (c) 2004,2011-2014 Apple Inc. All Rights Reserved. |
427c49bc | 3 | * |
b1ab9ed8 | 4 | * @APPLE_LICENSE_HEADER_START@ |
d8f41ccd | 5 | * |
b1ab9ed8 A |
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. | |
d8f41ccd | 12 | * |
b1ab9ed8 A |
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. | |
d8f41ccd | 20 | * |
b1ab9ed8 A |
21 | * @APPLE_LICENSE_HEADER_END@ |
22 | * | |
23 | * SecImportExportUtils.cpp - misc. utilities for import/export module | |
24 | */ | |
25 | ||
26 | #include "SecImportExportUtils.h" | |
27 | #include "SecImportExportAgg.h" | |
28 | #include "SecImportExportCrypto.h" | |
29 | #include "SecIdentityPriv.h" | |
30 | #include "SecItem.h" | |
31 | #include <security_cdsa_utils/cuCdsaUtils.h> | |
427c49bc | 32 | #include <Security/SecBase.h> |
b1ab9ed8 A |
33 | #pragma mark --- Debug support --- |
34 | ||
35 | #ifndef NDEBUG | |
36 | ||
37 | const char *impExpExtFormatStr( | |
38 | SecExternalFormat format) | |
39 | { | |
40 | switch(format) { | |
41 | case kSecFormatUnknown: return "kSecFormatUnknown"; | |
42 | case kSecFormatOpenSSL: return "kSecFormatOpenSSL"; | |
43 | case kSecFormatSSH: return "kSecFormatSSH"; | |
44 | case kSecFormatBSAFE: return "kSecFormatBSAFE"; | |
45 | case kSecFormatRawKey: return "kSecFormatRawKey"; | |
46 | case kSecFormatWrappedPKCS8: return "kSecFormatWrappedPKCS8"; | |
47 | case kSecFormatWrappedOpenSSL: return "kSecFormatWrappedOpenSSL"; | |
48 | case kSecFormatWrappedSSH: return "kSecFormatWrappedSSH"; | |
49 | case kSecFormatWrappedLSH: return "kSecFormatWrappedLSH"; | |
50 | case kSecFormatX509Cert: return "kSecFormatX509Cert"; | |
51 | case kSecFormatPEMSequence: return "kSecFormatPEMSequence"; | |
52 | case kSecFormatPKCS7: return "kSecFormatPKCS7"; | |
53 | case kSecFormatPKCS12: return "kSecFormatPKCS12"; | |
54 | case kSecFormatNetscapeCertSequence: return "kSecFormatNetscapeCertSequence"; | |
55 | default: return "UNKNOWN FORMAT ENUM"; | |
56 | } | |
57 | } | |
58 | ||
59 | const char *impExpExtItemTypeStr( | |
60 | SecExternalItemType itemType) | |
61 | { | |
62 | switch(itemType) { | |
63 | case kSecItemTypeUnknown: return "kSecItemTypeUnknown"; | |
64 | case kSecItemTypePrivateKey: return "kSecItemTypePrivateKey"; | |
65 | case kSecItemTypePublicKey: return "kSecItemTypePublicKey"; | |
66 | case kSecItemTypeSessionKey: return "kSecItemTypeSessionKey"; | |
67 | case kSecItemTypeCertificate: return "kSecItemTypeCertificate"; | |
68 | case kSecItemTypeAggregate: return "kSecItemTypeAggregate"; | |
69 | default: return "UNKNOWN ITEM TYPE ENUM"; | |
70 | } | |
71 | } | |
72 | #endif /* NDEBUG */ | |
73 | ||
427c49bc A |
74 | /* |
75 | * Parse file extension and attempt to map it to format and type. Returns true | |
76 | * on success. | |
b1ab9ed8 A |
77 | */ |
78 | bool impExpImportParseFileExten( | |
79 | CFStringRef fstr, | |
80 | SecExternalFormat *inputFormat, // RETURNED | |
81 | SecExternalItemType *itemType) // RETURNED | |
82 | { | |
83 | if(fstr == NULL) { | |
84 | /* nothing to work with */ | |
85 | return false; | |
86 | } | |
427c49bc | 87 | if(CFStringHasSuffix(fstr, CFSTR(".cer")) || |
b1ab9ed8 A |
88 | CFStringHasSuffix(fstr, CFSTR(".crt"))) { |
89 | *inputFormat = kSecFormatX509Cert; | |
90 | *itemType = kSecItemTypeCertificate; | |
91 | SecImpInferDbg("Inferring kSecFormatX509Cert from file name"); | |
92 | return true; | |
93 | } | |
427c49bc | 94 | if(CFStringHasSuffix(fstr, CFSTR(".p12")) || |
b1ab9ed8 A |
95 | CFStringHasSuffix(fstr, CFSTR(".pfx"))) { |
96 | *inputFormat = kSecFormatPKCS12; | |
97 | *itemType = kSecItemTypeAggregate; | |
98 | SecImpInferDbg("Inferring kSecFormatPKCS12 from file name"); | |
99 | return true; | |
100 | } | |
101 | ||
102 | /* Get extension, look for key indicators as substrings */ | |
103 | CFURLRef url = CFURLCreateWithString(NULL, fstr, NULL); | |
104 | if(url == NULL) { | |
105 | SecImpInferDbg("impExpImportParseFileExten: error creating URL"); | |
106 | return false; | |
107 | } | |
108 | CFStringRef exten = CFURLCopyPathExtension(url); | |
109 | CFRelease(url); | |
110 | if(exten == NULL) { | |
111 | /* no extension, app probably passed in only an extension */ | |
112 | exten = fstr; | |
113 | CFRetain(exten); | |
114 | } | |
115 | bool ortn = false; | |
116 | CFRange cfr; | |
117 | cfr = CFStringFind(exten, CFSTR("p7"), kCFCompareCaseInsensitive); | |
118 | if(cfr.length != 0) { | |
119 | *inputFormat = kSecFormatPKCS7; | |
120 | *itemType = kSecItemTypeAggregate; | |
121 | SecImpInferDbg("Inferring kSecFormatPKCS7 from file name"); | |
122 | ortn = true; | |
123 | } | |
124 | if(!ortn) { | |
125 | cfr = CFStringFind(exten, CFSTR("p8"), kCFCompareCaseInsensitive); | |
126 | if(cfr.length != 0) { | |
127 | *inputFormat = kSecFormatWrappedPKCS8; | |
128 | *itemType = kSecItemTypePrivateKey; | |
129 | SecImpInferDbg("Inferring kSecFormatPKCS8 from file name"); | |
130 | ortn = true; | |
131 | } | |
132 | } | |
133 | CFRelease(exten); | |
134 | return ortn; | |
135 | } | |
427c49bc | 136 | |
b1ab9ed8 A |
137 | /* do a [NSString stringByDeletingPathExtension] equivalent */ |
138 | CFStringRef impExpImportDeleteExtension( | |
139 | CFStringRef fileStr) | |
140 | { | |
141 | CFDataRef fileStrData = CFStringCreateExternalRepresentation(NULL, fileStr, | |
142 | kCFStringEncodingUTF8, 0); | |
143 | if(fileStrData == NULL) { | |
144 | return NULL; | |
145 | } | |
427c49bc A |
146 | |
147 | CFURLRef urlRef = CFURLCreateFromFileSystemRepresentation(NULL, | |
b1ab9ed8 A |
148 | CFDataGetBytePtr(fileStrData), CFDataGetLength(fileStrData), false); |
149 | if(urlRef == NULL) { | |
150 | CFRelease(fileStrData); | |
151 | return NULL; | |
152 | } | |
153 | CFURLRef rtnUrl = CFURLCreateCopyDeletingPathExtension(NULL, urlRef); | |
154 | CFStringRef rtnStr = NULL; | |
155 | CFRelease(urlRef); | |
156 | if(rtnUrl) { | |
157 | rtnStr = CFURLGetString(rtnUrl); | |
158 | CFRetain(rtnStr); | |
159 | CFRelease(rtnUrl); | |
160 | } | |
161 | CFRelease(fileStrData); | |
162 | return rtnStr; | |
163 | } | |
164 | ||
165 | #pragma mark --- mapping of external format to CDSA formats --- | |
166 | ||
167 | /* | |
427c49bc A |
168 | * For the record, here is the mapping of SecExternalFormat, algorithm, and key |
169 | * class to CSSM-style key format (CSSM_KEYBLOB_FORMAT - | |
170 | * CSSM_KEYBLOB_RAW_FORMAT_X509, etc). The entries in the table are the | |
b1ab9ed8 | 171 | * last component of a CSSM_KEYBLOB_FORMAT. Format kSecFormatUnknown means |
427c49bc | 172 | * "default for specified class and algorithm", which is currently the |
b1ab9ed8 A |
173 | * same as kSecFormatOpenSSL. |
174 | * | |
175 | * algorithm/class | |
176 | * RSA DSA DH | |
177 | * ---------------- ---------------- ---------------- | |
178 | * SecExternalFormat priv pub priv pub priv pub | |
179 | * ----------------- ------- ------- ------- ------- ------- ------- | |
180 | * kSecFormatOpenSSL PKCS1 X509 OPENSSL X509 PKCS3 X509 | |
181 | * kSecFormatBSAFE PKCS8 PKCS1 FIPS186 FIPS186 PKCS8 not supported | |
182 | * kSecFormatUnknown PKCS1 X509 OPENSSL X509 PKCS3 X509 | |
183 | * kSecFormatSSH SSH SSH n/s n/s n/s n/s | |
184 | * kSecFormatSSHv2 n/s SSH2 n/s SSH2 n/s n/s | |
185 | * | |
186 | * The only external format supported for ECDSA and ECDH keys is kSecFormatOpenSSL, | |
187 | * which translates to OPENSSL for private keys and X509 for public keys. | |
188 | */ | |
427c49bc | 189 | |
b1ab9ed8 A |
190 | /* Arrays expressing the above table. */ |
191 | ||
192 | /* l.s. dimension is pub/priv for one alg */ | |
193 | typedef struct { | |
194 | CSSM_KEYBLOB_FORMAT priv; | |
195 | CSSM_KEYBLOB_FORMAT pub; | |
196 | } algForms; | |
197 | ||
427c49bc A |
198 | /* |
199 | * indices into array of algForms defining all algs' formats for a given | |
b1ab9ed8 A |
200 | * SecExternalFormat |
201 | */ | |
202 | #define SIE_ALG_RSA 0 | |
203 | #define SIE_ALG_DSA 1 | |
204 | #define SIE_ALG_DH 2 | |
205 | #define SIE_ALG_ECDSA 3 | |
206 | #define SIE_ALG_LAST SIE_ALG_ECDSA | |
207 | #define SIE_NUM_ALGS (SIE_ALG_LAST + 1) | |
208 | ||
209 | /* kSecFormatOpenSSL */ | |
427c49bc | 210 | static algForms opensslAlgForms[SIE_NUM_ALGS] = |
b1ab9ed8 A |
211 | { |
212 | { CSSM_KEYBLOB_RAW_FORMAT_PKCS1, CSSM_KEYBLOB_RAW_FORMAT_X509 }, // RSA | |
213 | { CSSM_KEYBLOB_RAW_FORMAT_OPENSSL, CSSM_KEYBLOB_RAW_FORMAT_X509 }, // DSA | |
214 | { CSSM_KEYBLOB_RAW_FORMAT_PKCS3, CSSM_KEYBLOB_RAW_FORMAT_X509 }, // D-H | |
215 | { CSSM_KEYBLOB_RAW_FORMAT_OPENSSL, CSSM_KEYBLOB_RAW_FORMAT_X509 }, // ECDSA | |
216 | }; | |
217 | ||
218 | /* kSecFormatBSAFE */ | |
427c49bc | 219 | static algForms bsafeAlgForms[SIE_NUM_ALGS] = |
b1ab9ed8 A |
220 | { |
221 | { CSSM_KEYBLOB_RAW_FORMAT_PKCS8, CSSM_KEYBLOB_RAW_FORMAT_PKCS1 }, // RSA | |
222 | { CSSM_KEYBLOB_RAW_FORMAT_FIPS186, CSSM_KEYBLOB_RAW_FORMAT_FIPS186 }, // DSA | |
223 | { CSSM_KEYBLOB_RAW_FORMAT_PKCS8, CSSM_KEYBLOB_RAW_FORMAT_NONE }, // D-H | |
224 | { CSSM_KEYBLOB_RAW_FORMAT_NONE, CSSM_KEYBLOB_RAW_FORMAT_NONE }, // ECDSA not supported | |
225 | }; | |
226 | ||
227 | /* kSecFormatSSH (v1) */ | |
427c49bc | 228 | static algForms ssh1AlgForms[SIE_NUM_ALGS] = |
b1ab9ed8 A |
229 | { |
230 | { CSSM_KEYBLOB_RAW_FORMAT_OPENSSH, CSSM_KEYBLOB_RAW_FORMAT_OPENSSH }, // RSA only | |
231 | { CSSM_KEYBLOB_RAW_FORMAT_NONE, CSSM_KEYBLOB_RAW_FORMAT_NONE }, // DSA not supported | |
232 | { CSSM_KEYBLOB_RAW_FORMAT_NONE, CSSM_KEYBLOB_RAW_FORMAT_NONE }, // D-H not supported | |
233 | { CSSM_KEYBLOB_RAW_FORMAT_NONE, CSSM_KEYBLOB_RAW_FORMAT_NONE }, // ECDSA not supported | |
234 | }; | |
235 | ||
236 | /* kSecFormatSSHv2 */ | |
427c49bc | 237 | static algForms ssh2AlgForms[SIE_NUM_ALGS] = |
b1ab9ed8 A |
238 | { |
239 | { CSSM_KEYBLOB_RAW_FORMAT_NONE, CSSM_KEYBLOB_RAW_FORMAT_OPENSSH2 }, // RSA - public only | |
240 | { CSSM_KEYBLOB_RAW_FORMAT_NONE, CSSM_KEYBLOB_RAW_FORMAT_OPENSSH2 }, // DSA - public only | |
241 | { CSSM_KEYBLOB_RAW_FORMAT_NONE, CSSM_KEYBLOB_RAW_FORMAT_NONE }, // D-H not supported | |
242 | { CSSM_KEYBLOB_RAW_FORMAT_NONE, CSSM_KEYBLOB_RAW_FORMAT_NONE }, // ECDSA not supported | |
243 | }; | |
244 | ||
245 | /* | |
246 | * This routine performs a lookup into the above 3-dimensional array to | |
247 | * map {algorithm, class, SecExternalFormat} to a CSSM_KEYBLOB_FORMAT. | |
248 | * Returns errSecUnsupportedFormat in the rare appropriate case. | |
249 | */ | |
250 | OSStatus impExpKeyForm( | |
251 | SecExternalFormat externForm, | |
252 | SecExternalItemType itemType, | |
253 | CSSM_ALGORITHMS alg, | |
254 | CSSM_KEYBLOB_FORMAT *cssmForm, // RETURNED | |
255 | CSSM_KEYCLASS *cssmClass) // RETRUNED | |
256 | { | |
257 | if(itemType == kSecItemTypeSessionKey) { | |
258 | /* special trivial case */ | |
259 | /* FIXME ensure caller hasn't specified bogus format */ | |
260 | *cssmForm = CSSM_KEYBLOB_RAW_FORMAT_NONE; | |
261 | *cssmClass = CSSM_KEYCLASS_SESSION_KEY; | |
427c49bc | 262 | return errSecSuccess; |
b1ab9ed8 A |
263 | } |
264 | if(externForm == kSecFormatUnknown) { | |
265 | /* default is openssl */ | |
266 | externForm = kSecFormatOpenSSL; | |
267 | } | |
427c49bc | 268 | |
b1ab9ed8 A |
269 | unsigned algDex; |
270 | switch(alg) { | |
271 | case CSSM_ALGID_RSA: | |
272 | algDex = SIE_ALG_RSA; | |
273 | break; | |
274 | case CSSM_ALGID_DSA: | |
275 | algDex = SIE_ALG_DSA; | |
276 | break; | |
277 | case CSSM_ALGID_DH: | |
278 | algDex = SIE_ALG_DH; | |
279 | break; | |
280 | case CSSM_ALGID_ECDSA: | |
281 | algDex = SIE_ALG_ECDSA; | |
282 | break; | |
283 | default: | |
284 | return CSSMERR_CSP_INVALID_ALGORITHM; | |
285 | } | |
286 | const algForms *forms; | |
287 | switch(externForm) { | |
288 | case kSecFormatOpenSSL: | |
289 | forms = opensslAlgForms; | |
290 | break; | |
291 | case kSecFormatBSAFE: | |
292 | forms = bsafeAlgForms; | |
293 | break; | |
294 | case kSecFormatSSH: | |
295 | forms = ssh1AlgForms; | |
296 | break; | |
297 | case kSecFormatSSHv2: | |
298 | forms = ssh2AlgForms; | |
299 | break; | |
300 | default: | |
301 | return errSecUnsupportedFormat; | |
302 | } | |
303 | CSSM_KEYBLOB_FORMAT form = CSSM_KEYBLOB_RAW_FORMAT_NONE; | |
304 | switch(itemType) { | |
305 | case kSecItemTypePrivateKey: | |
306 | form = forms[algDex].priv; | |
307 | *cssmClass = CSSM_KEYCLASS_PRIVATE_KEY; | |
308 | break; | |
309 | case kSecItemTypePublicKey: | |
310 | form = forms[algDex].pub; | |
311 | *cssmClass = CSSM_KEYCLASS_PUBLIC_KEY; | |
312 | break; | |
313 | default: | |
314 | return errSecUnsupportedFormat; | |
315 | } | |
316 | if(form == CSSM_KEYBLOB_RAW_FORMAT_NONE) { | |
317 | /* not in the tables - abort */ | |
318 | return errSecUnsupportedFormat; | |
319 | } | |
320 | else { | |
321 | *cssmForm = form; | |
427c49bc | 322 | return errSecSuccess; |
b1ab9ed8 A |
323 | } |
324 | } | |
325 | ||
326 | /* | |
427c49bc | 327 | * Given a raw key blob and zero to three known parameters (type, format, |
b1ab9ed8 A |
328 | * algorithm), figure out all parameters. Used for private and public keys. |
329 | */ | |
330 | static bool impExpGuessKeyParams( | |
331 | CFDataRef keyData, | |
332 | SecExternalFormat *externForm, // IN/OUT | |
333 | SecExternalItemType *itemType, // IN/OUT | |
334 | CSSM_ALGORITHMS *keyAlg) // IN/OUT | |
335 | { | |
336 | /* CSSM alg list: RSA, DSA, DH, ECDSA */ | |
427c49bc | 337 | CSSM_ALGORITHMS minAlg = CSSM_ALGID_RSA; |
b1ab9ed8 A |
338 | CSSM_ALGORITHMS maxAlg = CSSM_ALGID_ECDSA; |
339 | SecExternalFormat minForm = kSecFormatOpenSSL; // then SSH, BSAFE, then... | |
340 | SecExternalFormat maxForm = kSecFormatSSHv2; | |
341 | SecExternalItemType minType = kSecItemTypePrivateKey; // just two | |
342 | SecExternalItemType maxType = kSecItemTypePublicKey; | |
427c49bc | 343 | |
d8f41ccd A |
344 | |
345 | if(keyData == NULL || CFDataGetLength(keyData) == 0){ | |
346 | MacOSError::throwMe(errSecUnsupportedKeySize); | |
347 | } | |
348 | ||
b1ab9ed8 A |
349 | switch(*externForm) { |
350 | case kSecFormatUnknown: | |
351 | break; // run through all formats | |
352 | case kSecFormatOpenSSL: | |
353 | case kSecFormatSSH: | |
354 | case kSecFormatSSHv2: | |
355 | case kSecFormatBSAFE: | |
356 | minForm = maxForm = *externForm; // just test this one | |
357 | break; | |
358 | default: | |
359 | return false; | |
360 | } | |
361 | switch(*itemType) { | |
362 | case kSecItemTypeUnknown: | |
363 | break; | |
364 | case kSecItemTypePrivateKey: | |
365 | case kSecItemTypePublicKey: | |
366 | minType = maxType = *itemType; | |
367 | break; | |
368 | default: | |
369 | return false; | |
370 | } | |
371 | switch(*keyAlg) { | |
372 | case CSSM_ALGID_NONE: | |
373 | break; | |
374 | case CSSM_ALGID_RSA: | |
375 | case CSSM_ALGID_DSA: | |
376 | case CSSM_ALGID_DH: | |
377 | case CSSM_ALGID_ECDSA: | |
378 | minAlg = maxAlg = *keyAlg; | |
379 | break; | |
380 | default: | |
381 | return false; | |
382 | } | |
427c49bc | 383 | |
b1ab9ed8 A |
384 | CSSM_ALGORITHMS theAlg; |
385 | SecExternalFormat theForm; | |
386 | SecExternalItemType theType; | |
387 | CSSM_CSP_HANDLE cspHand = cuCspStartup(CSSM_TRUE); | |
388 | if(cspHand == 0) { | |
389 | return CSSMERR_CSSM_ADDIN_LOAD_FAILED; | |
390 | } | |
427c49bc | 391 | |
b1ab9ed8 A |
392 | /* |
393 | * Iterate through all set of enabled {alg, type, format}. | |
394 | * We do not assume that any of the enums are sequential hence this | |
395 | * odd iteration algorithm.... | |
396 | */ | |
397 | bool ourRtn = false; | |
398 | for(theAlg=minAlg; ; ) { | |
399 | for(theForm=minForm; ; ) { | |
400 | for(theType=minType; ; ) { | |
427c49bc | 401 | |
b1ab9ed8 A |
402 | /* do super lightweight null unwrap to parse */ |
403 | OSStatus ortn = impExpImportRawKey(keyData, | |
404 | theForm, theType, theAlg, | |
405 | NULL, // no keychain | |
406 | cspHand, | |
407 | 0, // no flags | |
408 | NULL, // no key params | |
409 | NULL, // no printName | |
410 | NULL); // no returned items | |
427c49bc | 411 | if(ortn == errSecSuccess) { |
b1ab9ed8 A |
412 | *externForm = theForm; |
413 | *itemType = theType; | |
414 | *keyAlg = theAlg; | |
415 | ourRtn = true; | |
416 | goto done; | |
417 | } | |
427c49bc | 418 | |
b1ab9ed8 A |
419 | /* next type or break if we're done */ |
420 | if(theType == maxType) { | |
421 | break; | |
422 | } | |
423 | else switch(theType) { | |
427c49bc A |
424 | case kSecItemTypePrivateKey: |
425 | theType = kSecItemTypePublicKey; | |
b1ab9ed8 A |
426 | break; |
427 | default: | |
428 | assert(0); | |
429 | ourRtn = false; | |
430 | goto done; | |
431 | } | |
432 | } /* for each class/type */ | |
433 | ||
434 | /* next format or break if we're done */ | |
435 | if(theForm == maxForm) { | |
436 | break; | |
437 | } | |
438 | else switch(theForm) { | |
427c49bc A |
439 | case kSecFormatOpenSSL: |
440 | theForm = kSecFormatSSH; | |
b1ab9ed8 | 441 | break; |
427c49bc A |
442 | case kSecFormatSSH: |
443 | theForm = kSecFormatBSAFE; | |
b1ab9ed8 A |
444 | break; |
445 | case kSecFormatBSAFE: | |
446 | theForm = kSecFormatSSHv2; | |
447 | break; | |
448 | default: | |
449 | assert(0); | |
450 | ourRtn = false; | |
451 | goto done; | |
452 | } | |
453 | } /* for each format */ | |
427c49bc | 454 | |
b1ab9ed8 A |
455 | /* next alg or break if we're done */ |
456 | if(theAlg == maxAlg) { | |
457 | break; | |
458 | } | |
459 | else switch(theAlg) { | |
427c49bc A |
460 | case CSSM_ALGID_RSA: |
461 | theAlg = CSSM_ALGID_DSA; | |
b1ab9ed8 | 462 | break; |
427c49bc A |
463 | case CSSM_ALGID_DSA: |
464 | theAlg = CSSM_ALGID_DH; | |
b1ab9ed8 A |
465 | break; |
466 | case CSSM_ALGID_DH: | |
467 | theAlg = CSSM_ALGID_ECDSA; | |
468 | break; | |
469 | default: | |
470 | /* i.e. CSSM_ALGID_ECDSA, we already broke at theAlg == maxAlg */ | |
471 | assert(0); | |
472 | ourRtn = false; | |
473 | goto done; | |
474 | } | |
475 | } /* for each alg */ | |
476 | done: | |
477 | cuCspDetachUnload(cspHand, CSSM_TRUE); | |
478 | return ourRtn; | |
479 | } | |
480 | ||
481 | /* | |
482 | * Guess an incoming blob's type, format and (for keys only) algorithm | |
427c49bc A |
483 | * by examining its contents. Returns true on success, in which case |
484 | * *inputFormat, *itemType, and *keyAlg are all valid. Caller optionally | |
b1ab9ed8 A |
485 | * passes in valid values any number of these as a clue. |
486 | */ | |
487 | bool impExpImportGuessByExamination( | |
488 | CFDataRef inData, | |
427c49bc A |
489 | SecExternalFormat *inputFormat, // may be kSecFormatUnknown on entry |
490 | SecExternalItemType *itemType, // may be kSecItemTypeUnknown on entry | |
b1ab9ed8 A |
491 | CSSM_ALGORITHMS *keyAlg) // CSSM_ALGID_NONE - unknown |
492 | { | |
493 | if( ( (*inputFormat == kSecFormatUnknown) || | |
427c49bc | 494 | (*inputFormat == kSecFormatX509Cert) |
b1ab9ed8 A |
495 | ) && |
496 | ( (*itemType == kSecItemTypeUnknown) || | |
497 | (*itemType == kSecItemTypeCertificate) ) ) { | |
427c49bc | 498 | /* |
b1ab9ed8 A |
499 | * See if it parses as a cert |
500 | */ | |
501 | CSSM_CL_HANDLE clHand = cuClStartup(); | |
502 | if(clHand == 0) { | |
503 | return CSSMERR_CSSM_ADDIN_LOAD_FAILED; | |
504 | } | |
505 | CSSM_HANDLE cacheHand; | |
506 | CSSM_RETURN crtn; | |
427c49bc | 507 | CSSM_DATA cdata = { CFDataGetLength(inData), |
b1ab9ed8 A |
508 | (uint8 *)CFDataGetBytePtr(inData) }; |
509 | crtn = CSSM_CL_CertCache(clHand, &cdata, &cacheHand); | |
510 | bool brtn = false; | |
511 | if(crtn == CSSM_OK) { | |
512 | *inputFormat = kSecFormatX509Cert; | |
513 | *itemType = kSecItemTypeCertificate; | |
514 | SecImpInferDbg("Inferred kSecFormatX509Cert via CL"); | |
515 | CSSM_CL_CertAbortCache(clHand, cacheHand); | |
516 | brtn = true; | |
517 | } | |
518 | cuClDetachUnload(clHand); | |
519 | if(brtn) { | |
520 | return true; | |
521 | } | |
522 | } | |
523 | /* TBD: need way to inquire of P12 lib if this is a valid-looking PFX */ | |
427c49bc | 524 | |
b1ab9ed8 | 525 | if( ( (*inputFormat == kSecFormatUnknown) || |
427c49bc | 526 | (*inputFormat == kSecFormatNetscapeCertSequence) |
b1ab9ed8 A |
527 | ) && |
528 | ( (*itemType == kSecItemTypeUnknown) || | |
529 | (*itemType == kSecItemTypeAggregate) ) ) { | |
530 | /* See if it's a netscape cert sequence */ | |
531 | CSSM_RETURN crtn = impExpNetscapeCertImport(inData, 0, NULL, NULL, NULL); | |
532 | if(crtn == CSSM_OK) { | |
533 | *inputFormat = kSecFormatNetscapeCertSequence; | |
534 | *itemType = kSecItemTypeAggregate; | |
535 | SecImpInferDbg("Inferred netscape-cert-sequence by decoding"); | |
536 | return true; | |
537 | } | |
538 | } | |
427c49bc | 539 | |
b1ab9ed8 A |
540 | /* See if it's a key */ |
541 | return impExpGuessKeyParams(inData, inputFormat, itemType, keyAlg); | |
542 | } | |
543 | ||
544 | #pragma mark --- Key Import support --- | |
545 | ||
546 | /* | |
547 | * Given a context specified via a CSSM_CC_HANDLE, add a new | |
548 | * CSSM_CONTEXT_ATTRIBUTE to the context as specified by AttributeType, | |
549 | * AttributeLength, and an untyped pointer. | |
550 | */ | |
551 | CSSM_RETURN impExpAddContextAttribute(CSSM_CC_HANDLE CCHandle, | |
552 | uint32 AttributeType, | |
553 | uint32 AttributeLength, | |
554 | const void *AttributePtr) | |
555 | { | |
427c49bc A |
556 | CSSM_CONTEXT_ATTRIBUTE newAttr; |
557 | ||
b1ab9ed8 A |
558 | newAttr.AttributeType = AttributeType; |
559 | newAttr.AttributeLength = AttributeLength; | |
560 | newAttr.Attribute.Data = (CSSM_DATA_PTR)AttributePtr; | |
561 | return CSSM_UpdateContextAttributes(CCHandle, 1, &newAttr); | |
562 | } | |
563 | ||
564 | /* | |
565 | * Free memory via specified plugin's app-level allocator | |
566 | */ | |
567 | void impExpFreeCssmMemory( | |
568 | CSSM_HANDLE hand, | |
569 | void *p) | |
570 | { | |
571 | CSSM_API_MEMORY_FUNCS memFuncs; | |
572 | CSSM_RETURN crtn = CSSM_GetAPIMemoryFunctions(hand, &memFuncs); | |
573 | if(crtn) { | |
574 | return; | |
575 | } | |
576 | memFuncs.free_func(p, memFuncs.AllocRef); | |
577 | } | |
578 | ||
427c49bc | 579 | /* |
b1ab9ed8 | 580 | * Calculate digest of any CSSM_KEY. Unlike older implementations |
427c49bc | 581 | * of this logic, you can actually calculate the public key hash |
b1ab9ed8 A |
582 | * on any class of key, any format, raw CSP or CSPDL (though if |
583 | * you're using the CSPDL, the key has to be a reference key | |
584 | * in that CSPDL session). | |
585 | * | |
427c49bc | 586 | * Caller must free keyDigest->Data using impExpFreeCssmMemory() since |
b1ab9ed8 A |
587 | * this is allocated by the CSP's app-specified allocator. |
588 | */ | |
589 | CSSM_RETURN impExpKeyDigest( | |
590 | CSSM_CSP_HANDLE cspHand, | |
591 | CSSM_KEY_PTR key, | |
592 | CSSM_DATA_PTR keyDigest) // contents allocd and RETURNED | |
593 | { | |
594 | CSSM_DATA_PTR localDigest; | |
595 | CSSM_CC_HANDLE ccHand; | |
427c49bc | 596 | |
b1ab9ed8 A |
597 | CSSM_RETURN crtn = CSSM_CSP_CreatePassThroughContext(cspHand, |
598 | key, | |
599 | &ccHand); | |
600 | if(crtn) { | |
601 | return crtn; | |
602 | } | |
603 | crtn = CSSM_CSP_PassThrough(ccHand, | |
604 | CSSM_APPLECSP_KEYDIGEST, | |
605 | NULL, | |
606 | (void **)&localDigest); | |
607 | if(crtn) { | |
608 | SecImpExpDbg("CSSM_CSP_PassThrough(KEY_DIGEST) failure"); | |
609 | } | |
610 | else { | |
427c49bc A |
611 | /* |
612 | * Give caller the Data referent and we'll free the | |
b1ab9ed8 A |
613 | * CSSM_DATA struct itswelf. |
614 | */ | |
615 | *keyDigest = *localDigest; | |
616 | impExpFreeCssmMemory(cspHand, localDigest); | |
617 | } | |
618 | CSSM_DeleteContext(ccHand); | |
619 | return crtn; | |
620 | } | |
427c49bc | 621 | |
b1ab9ed8 A |
622 | |
623 | /* | |
624 | * Given a CFTypeRef passphrase which may be a CFDataRef or a CFStringRef, | |
625 | * return a refcounted CFStringRef suitable for use with the PKCS12 library. | |
427c49bc | 626 | * PKCS12 passphrases in CFData format must be UTF8 encoded. |
b1ab9ed8 A |
627 | */ |
628 | OSStatus impExpPassphraseToCFString( | |
629 | CFTypeRef passin, | |
630 | CFStringRef *passout) // may be the same as passin, but refcounted | |
631 | { | |
632 | if(CFGetTypeID(passin) == CFStringGetTypeID()) { | |
633 | CFStringRef passInStr = (CFStringRef)passin; | |
634 | CFRetain(passInStr); | |
635 | *passout = passInStr; | |
427c49bc | 636 | return errSecSuccess; |
b1ab9ed8 A |
637 | } |
638 | else if(CFGetTypeID(passin) == CFDataGetTypeID()) { | |
639 | CFDataRef cfData = (CFDataRef)passin; | |
640 | CFIndex len = CFDataGetLength(cfData); | |
427c49bc | 641 | CFStringRef cfStr = CFStringCreateWithBytes(NULL, |
b1ab9ed8 A |
642 | CFDataGetBytePtr(cfData), len, kCFStringEncodingUTF8, true); |
643 | if(cfStr == NULL) { | |
644 | SecImpExpDbg("Passphrase not in UTF8 format"); | |
427c49bc | 645 | return errSecParam; |
b1ab9ed8 A |
646 | } |
647 | *passout = cfStr; | |
427c49bc | 648 | return errSecSuccess; |
b1ab9ed8 A |
649 | } |
650 | else { | |
651 | SecImpExpDbg("Passphrase not CFData or CFString"); | |
427c49bc | 652 | return errSecParam; |
b1ab9ed8 A |
653 | } |
654 | } | |
655 | ||
656 | /* | |
657 | * Given a CFTypeRef passphrase which may be a CFDataRef or a CFStringRef, | |
427c49bc | 658 | * return a refcounted CFDataRef whose bytes are suitable for use with |
b1ab9ed8 A |
659 | * PKCS5 (v1.5 and v2.0) key derivation. |
660 | */ | |
661 | OSStatus impExpPassphraseToCFData( | |
662 | CFTypeRef passin, | |
663 | CFDataRef *passout) // may be the same as passin, but refcounted | |
664 | { | |
665 | if(CFGetTypeID(passin) == CFDataGetTypeID()) { | |
666 | CFDataRef passInData = (CFDataRef)passin; | |
667 | CFRetain(passInData); | |
668 | *passout = passInData; | |
427c49bc | 669 | return errSecSuccess; |
b1ab9ed8 A |
670 | } |
671 | else if(CFGetTypeID(passin) == CFStringGetTypeID()) { | |
672 | CFStringRef passInStr = (CFStringRef)passin; | |
673 | CFDataRef outData; | |
674 | outData = CFStringCreateExternalRepresentation(NULL, | |
427c49bc | 675 | passInStr, |
b1ab9ed8 | 676 | kCFStringEncodingUTF8, |
427c49bc A |
677 | 0); // lossByte 0 ==> no loss allowed |
678 | if(outData == NULL) { | |
b1ab9ed8 A |
679 | /* Well try with lossy conversion */ |
680 | SecImpExpDbg("Trying lossy conversion of CFString passphrase to UTF8"); | |
681 | outData = CFStringCreateExternalRepresentation(NULL, | |
427c49bc | 682 | passInStr, |
b1ab9ed8 | 683 | kCFStringEncodingUTF8, |
427c49bc | 684 | 1); |
b1ab9ed8 A |
685 | if(outData == NULL) { |
686 | SecImpExpDbg("Failure on conversion of CFString passphrase to UTF8"); | |
687 | /* what do we do now, Batman? */ | |
427c49bc | 688 | return errSecParam; |
b1ab9ed8 A |
689 | } |
690 | } | |
691 | *passout = outData; | |
427c49bc | 692 | return errSecSuccess; |
b1ab9ed8 A |
693 | } |
694 | else { | |
695 | SecImpExpDbg("Passphrase not CFData or CFString"); | |
427c49bc | 696 | return errSecParam; |
b1ab9ed8 A |
697 | } |
698 | } | |
699 | ||
427c49bc A |
700 | /* |
701 | * Add a CFString to a crypto context handle. | |
b1ab9ed8 A |
702 | */ |
703 | static CSSM_RETURN impExpAddStringAttr( | |
427c49bc | 704 | CSSM_CC_HANDLE ccHand, |
b1ab9ed8 A |
705 | CFStringRef str, |
706 | CSSM_ATTRIBUTE_TYPE attrType) | |
707 | { | |
708 | /* CFStrings are passed as external rep in UTF8 encoding by convention */ | |
709 | CFDataRef outData; | |
710 | outData = CFStringCreateExternalRepresentation(NULL, | |
427c49bc | 711 | str, kCFStringEncodingUTF8, 0); // lossByte 0 ==> no loss allowed |
b1ab9ed8 A |
712 | if(outData == NULL) { |
713 | SecImpExpDbg("impExpAddStringAttr: bad string format"); | |
427c49bc | 714 | return errSecParam; |
b1ab9ed8 | 715 | } |
427c49bc | 716 | |
b1ab9ed8 A |
717 | CSSM_DATA attrData; |
718 | attrData.Data = (uint8 *)CFDataGetBytePtr(outData); | |
719 | attrData.Length = CFDataGetLength(outData); | |
720 | CSSM_RETURN crtn = impExpAddContextAttribute(ccHand, attrType, sizeof(CSSM_DATA), | |
721 | &attrData); | |
722 | CFRelease(outData); | |
723 | if(crtn) { | |
724 | SecImpExpDbg("impExpAddStringAttr: CSSM_UpdateContextAttributes error"); | |
725 | } | |
726 | return crtn; | |
727 | } | |
728 | ||
729 | /* | |
427c49bc | 730 | * Generate a secure passphrase key. Caller must eventually CSSM_FreeKey the result. |
b1ab9ed8 A |
731 | */ |
732 | static CSSM_RETURN impExpCreatePassKey( | |
733 | const SecKeyImportExportParameters *keyParams, // required | |
734 | CSSM_CSP_HANDLE cspHand, // MUST be CSPDL | |
735 | impExpVerifyPhrase verifyPhrase, // for secure passphrase | |
736 | CSSM_KEY_PTR *passKey) // mallocd and RETURNED | |
737 | { | |
738 | CSSM_RETURN crtn; | |
739 | CSSM_CC_HANDLE ccHand; | |
740 | uint32 verifyAttr; | |
741 | CSSM_DATA dummyLabel; | |
742 | CSSM_KEY_PTR ourKey = NULL; | |
427c49bc | 743 | |
b1ab9ed8 A |
744 | SecImpExpDbg("Generating secure passphrase key"); |
745 | ourKey = (CSSM_KEY_PTR)malloc(sizeof(CSSM_KEY)); | |
746 | if(ourKey == NULL) { | |
427c49bc | 747 | return errSecAllocate; |
b1ab9ed8 A |
748 | } |
749 | memset(ourKey, 0, sizeof(CSSM_KEY)); | |
427c49bc | 750 | |
b1ab9ed8 A |
751 | crtn = CSSM_CSP_CreateKeyGenContext(cspHand, |
752 | CSSM_ALGID_SECURE_PASSPHRASE, | |
753 | 4, // keySizeInBits must be non zero | |
754 | NULL, // Seed | |
755 | NULL, // Salt | |
756 | NULL, // StartDate | |
757 | NULL, // EndDate | |
758 | NULL, // Params | |
759 | &ccHand); | |
760 | if(crtn) { | |
761 | SecImpExpDbg("impExpCreatePassKey: CSSM_CSP_CreateKeyGenContext error"); | |
762 | return crtn; | |
763 | } | |
764 | /* subsequent errors to errOut: */ | |
427c49bc | 765 | |
b1ab9ed8 A |
766 | /* additional context attributes specific to this type of key gen */ |
767 | assert(keyParams != NULL); // or we wouldn't be here | |
768 | if(keyParams->alertTitle != NULL) { | |
427c49bc | 769 | crtn = impExpAddStringAttr(ccHand, keyParams->alertTitle, |
b1ab9ed8 A |
770 | CSSM_ATTRIBUTE_ALERT_TITLE); |
771 | if(crtn) { | |
772 | goto errOut; | |
773 | } | |
774 | } | |
775 | if(keyParams->alertPrompt != NULL) { | |
427c49bc | 776 | crtn = impExpAddStringAttr(ccHand, keyParams->alertPrompt, |
b1ab9ed8 A |
777 | CSSM_ATTRIBUTE_PROMPT); |
778 | if(crtn) { | |
779 | goto errOut; | |
780 | } | |
781 | } | |
782 | verifyAttr = (verifyPhrase == VP_Export) ? 1 : 0; | |
783 | crtn = impExpAddContextAttribute(ccHand, CSSM_ATTRIBUTE_VERIFY_PASSPHRASE, | |
427c49bc | 784 | sizeof(uint32), (const void *)((size_t) verifyAttr)); |
b1ab9ed8 A |
785 | if(crtn) { |
786 | SecImpExpDbg("impExpCreatePassKey: CSSM_UpdateContextAttributes error"); | |
787 | goto errOut; | |
788 | } | |
789 | ||
790 | dummyLabel.Data = (uint8 *)"Secure Passphrase"; | |
791 | dummyLabel.Length = strlen((char *)dummyLabel.Data); | |
792 | ||
793 | crtn = CSSM_GenerateKey(ccHand, | |
794 | CSSM_KEYUSE_ANY, | |
795 | CSSM_KEYATTR_RETURN_REF | CSSM_KEYATTR_SENSITIVE, | |
796 | &dummyLabel, | |
797 | NULL, // ACL | |
798 | ourKey); | |
799 | if(crtn) { | |
800 | SecImpExpDbg("impExpCreatePassKey: CSSM_GenerateKey error"); | |
801 | } | |
802 | errOut: | |
803 | CSSM_DeleteContext(ccHand); | |
804 | if(crtn == CSSM_OK) { | |
805 | *passKey = ourKey; | |
806 | } | |
807 | else if(ourKey != NULL) { | |
808 | free(ourKey); | |
809 | } | |
810 | return crtn; | |
811 | } | |
427c49bc | 812 | |
b1ab9ed8 | 813 | /* |
427c49bc | 814 | * Obtain passphrase, given a SecKeyImportExportParameters. |
b1ab9ed8 | 815 | * |
427c49bc | 816 | * Passphrase comes from one of two places: app-specified, in |
b1ab9ed8 A |
817 | * SecKeyImportExportParameters.passphrase (either as CFStringRef |
818 | * or CFDataRef); or via the secure passphrase mechanism. | |
819 | * | |
820 | * Passphrase is returned in AT MOST one of two forms: | |
821 | * | |
427c49bc A |
822 | * -- Secure passphrase is returned as a CSSM_KEY_PTR, which the |
823 | * caller must CSSM_FreeKey later as well as free()ing the actual | |
824 | * CSSM_KEY_PTR. | |
825 | * -- CFTypeRef for app-supplied passphrases. This can be one of | |
b1ab9ed8 A |
826 | * two types: |
827 | * | |
828 | * -- CFStringRef, for use with P12 | |
427c49bc A |
829 | * -- CFDataRef, for more general use (e.g. for PKCS5). |
830 | * | |
831 | * In either case the caller must CFRelease the result. | |
b1ab9ed8 A |
832 | */ |
833 | OSStatus impExpPassphraseCommon( | |
834 | const SecKeyImportExportParameters *keyParams, | |
835 | CSSM_CSP_HANDLE cspHand, // MUST be CSPDL, for passKey generation | |
836 | impExpPassphraseForm phraseForm, | |
837 | impExpVerifyPhrase verifyPhrase, // for secure passphrase | |
838 | CFTypeRef *phrase, // RETURNED, or | |
839 | CSSM_KEY_PTR *passKey) // mallocd and RETURNED | |
840 | { | |
841 | assert(keyParams != NULL); | |
427c49bc | 842 | |
b1ab9ed8 A |
843 | /* Give precedence to secure passphrase */ |
844 | if(keyParams->flags & kSecKeySecurePassphrase) { | |
845 | assert(passKey != NULL); | |
846 | return impExpCreatePassKey(keyParams, cspHand, verifyPhrase, passKey); | |
847 | } | |
848 | else if(keyParams->passphrase != NULL) { | |
849 | CFTypeRef phraseOut; | |
850 | OSStatus ortn; | |
851 | assert(phrase != NULL); | |
852 | switch(phraseForm) { | |
853 | case SPF_String: | |
427c49bc | 854 | ortn = impExpPassphraseToCFString(keyParams->passphrase, |
b1ab9ed8 A |
855 | (CFStringRef *)&phraseOut); |
856 | break; | |
857 | case SPF_Data: | |
427c49bc | 858 | ortn = impExpPassphraseToCFData(keyParams->passphrase, |
b1ab9ed8 A |
859 | (CFDataRef *)&phraseOut); |
860 | break; | |
861 | default: | |
862 | /* only called internally */ | |
863 | assert(0); | |
427c49bc | 864 | ortn = errSecParam; |
b1ab9ed8 | 865 | } |
427c49bc | 866 | if(ortn == errSecSuccess) { |
b1ab9ed8 A |
867 | *phrase = phraseOut; |
868 | } | |
869 | return ortn; | |
870 | } | |
871 | else { | |
872 | return errSecPassphraseRequired; | |
873 | } | |
874 | } | |
875 | ||
427c49bc A |
876 | static void ToggleKeyAttribute( |
877 | CFArrayRef keyAttrs, | |
878 | CFTypeRef attr, | |
879 | CSSM_KEYATTR_FLAGS mask, | |
880 | CSSM_KEYATTR_FLAGS &result) | |
881 | { | |
882 | // If the keyAttrs array contains the given attribute, | |
883 | // set the corresponding keyattr flags, otherwise clear them. | |
884 | // (Note: caller verifies that keyAttrs is not NULL.) | |
885 | CFIndex numItems = CFArrayGetCount(keyAttrs); | |
886 | result &= ~(mask); | |
887 | if (numItems > 0) { | |
888 | CFRange range = CFRangeMake(0, numItems); | |
889 | if (CFArrayContainsValue(keyAttrs, range, attr)) | |
890 | result |= mask; | |
891 | } | |
892 | } | |
893 | ||
894 | CSSM_KEYATTR_FLAGS ConvertArrayToKeyAttributes(SecKeyRef aKey, CFArrayRef keyAttrs) | |
b1ab9ed8 A |
895 | { |
896 | CSSM_KEYATTR_FLAGS result = CSSM_KEYATTR_RETURN_DEFAULT; | |
427c49bc A |
897 | |
898 | if (aKey) { | |
899 | const CSSM_KEY* cssmKey = NULL; | |
900 | if (errSecSuccess == SecKeyGetCSSMKey(aKey, &cssmKey)) | |
901 | result = cssmKey->KeyHeader.KeyAttr; | |
b1ab9ed8 | 902 | } |
427c49bc A |
903 | |
904 | if (!keyAttrs) | |
b1ab9ed8 | 905 | return result; |
427c49bc A |
906 | |
907 | CFMutableArrayRef attrs = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); | |
908 | CFIndex idx, count = CFArrayGetCount(keyAttrs); | |
909 | for (idx=0; idx<count; idx++) { | |
910 | CFTypeRef attr = (CFTypeRef) CFArrayGetValueAtIndex(keyAttrs, idx); | |
911 | if (attr && (CFNumberGetTypeID() == CFGetTypeID(attr))) { | |
912 | // Convert numeric CSSM keyattr values to equivalent attribute constants | |
913 | uint32 value; | |
914 | if (CFNumberGetValue((CFNumberRef)attr, kCFNumberSInt32Type, &value)) { | |
915 | switch (value) { | |
916 | case CSSM_KEYATTR_SENSITIVE: | |
917 | attr = kSecAttrIsSensitive; | |
918 | break; | |
919 | case CSSM_KEYATTR_EXTRACTABLE: | |
920 | attr = kSecAttrIsExtractable; | |
921 | break; | |
922 | case CSSM_KEYATTR_PERMANENT: | |
923 | attr = kSecAttrIsPermanent; | |
924 | break; | |
925 | default: | |
926 | attr = NULL; | |
927 | break; | |
928 | } | |
b1ab9ed8 | 929 | } |
b1ab9ed8 | 930 | } |
427c49bc A |
931 | if (attr) |
932 | CFArrayAppendValue(attrs, attr); | |
b1ab9ed8 | 933 | } |
427c49bc A |
934 | |
935 | // Set key attribute flag in result if present in the array, otherwise clear | |
936 | ToggleKeyAttribute(attrs, kSecAttrIsSensitive, CSSM_KEYATTR_SENSITIVE, result); | |
937 | ToggleKeyAttribute(attrs, kSecAttrIsExtractable, CSSM_KEYATTR_EXTRACTABLE, result); | |
938 | ToggleKeyAttribute(attrs, kSecAttrIsPermanent, CSSM_KEYATTR_PERMANENT, result); | |
939 | ||
940 | // if caller specified an attributes array which omitted kSecAttrIsExtractable, | |
941 | // this implies the sensitive attribute; force it on so that at least one bit | |
942 | // is set. If our result is 0, this is indistinguishable from the case where | |
943 | // no key attributes were specified, causing a default bitmask to be used | |
944 | // in subsequent import code. | |
945 | ||
946 | if (0==(result & CSSM_KEYATTR_EXTRACTABLE)) | |
947 | result |= CSSM_KEYATTR_SENSITIVE; | |
948 | ||
949 | CFRelease(attrs); | |
950 | return result; | |
b1ab9ed8 A |
951 | } |
952 | ||
953 | Boolean ConvertSecKeyImportExportParametersToSecImportExportKeyParameters(SecKeyRef aKey, | |
954 | const SecItemImportExportKeyParameters* newPtr, SecKeyImportExportParameters* oldPtr) | |
955 | { | |
956 | Boolean result = false; | |
427c49bc | 957 | |
b1ab9ed8 A |
958 | if (NULL != oldPtr && NULL != newPtr) |
959 | { | |
960 | oldPtr->version = newPtr->version; | |
961 | oldPtr->flags = newPtr->flags; | |
962 | oldPtr->passphrase = newPtr->passphrase; | |
963 | oldPtr->alertTitle = newPtr->alertTitle; | |
964 | oldPtr->alertPrompt = newPtr->alertPrompt; | |
965 | oldPtr->accessRef = newPtr->accessRef; | |
966 | oldPtr->keyUsage = ConvertArrayToKeyUsage(newPtr->keyUsage); | |
967 | oldPtr->keyAttributes = ConvertArrayToKeyAttributes(aKey, newPtr->keyAttributes); | |
968 | result = true; | |
969 | } | |
970 | return result; | |
971 | } | |
972 |