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