]>
Commit | Line | Data |
---|---|---|
b1ab9ed8 A |
1 | /* |
2 | * Copyright (c) 2002-2012 Apple Inc. All Rights Reserved. | |
3 | * | |
4 | * @APPLE_LICENSE_HEADER_START@ | |
5 | * | |
6 | * This file contains Original Code and/or Modifications of Original Code | |
7 | * as defined in and that are subject to the Apple Public Source License | |
8 | * Version 2.0 (the 'License'). You may not use this file except in | |
9 | * compliance with the License. Please obtain a copy of the License at | |
10 | * http://www.opensource.apple.com/apsl/ and read it before using this | |
11 | * file. | |
12 | * | |
13 | * The Original Code and all software distributed under the License are | |
14 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER | |
15 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, | |
16 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
17 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. | |
18 | * Please see the License for the specific language governing rights and | |
19 | * limitations under the License. | |
20 | * | |
21 | * @APPLE_LICENSE_HEADER_END@ | |
22 | */ | |
23 | ||
24 | #include "SecKey.h" | |
25 | #include "SecKeyPriv.h" | |
26 | #include "SecItem.h" | |
27 | #include "SecItemPriv.h" | |
28 | #include <libDER/asn1Types.h> | |
29 | #include <libDER/DER_Encode.h> | |
30 | #include <libDER/DER_Decode.h> | |
31 | #include <libDER/DER_Keys.h> | |
32 | #include <Security/SecAsn1Types.h> | |
33 | #include <Security/SecAsn1Coder.h> | |
34 | #include <security_keychain/KeyItem.h> | |
35 | #include <CommonCrypto/CommonKeyDerivation.h> | |
36 | ||
37 | #include "SecBridge.h" | |
38 | ||
39 | #include <security_keychain/Access.h> | |
40 | #include <security_keychain/Keychains.h> | |
41 | #include <security_keychain/KeyItem.h> | |
42 | #include <string.h> | |
43 | #include <syslog.h> | |
44 | ||
45 | #include <security_cdsa_utils/cuCdsaUtils.h> | |
46 | #include <security_cdsa_client/wrapkey.h> | |
47 | ||
48 | #include "SecImportExportCrypto.h" | |
49 | ||
50 | CFTypeID | |
51 | SecKeyGetTypeID(void) | |
52 | { | |
53 | BEGIN_SECAPI | |
54 | ||
55 | return gTypes().KeyItem.typeID; | |
56 | ||
57 | END_SECAPI1(_kCFRuntimeNotATypeID) | |
58 | } | |
59 | ||
427c49bc | 60 | static OSStatus SecKeyCreatePairInternal( |
b1ab9ed8 A |
61 | SecKeychainRef keychainRef, |
62 | CSSM_ALGORITHMS algorithm, | |
63 | uint32 keySizeInBits, | |
64 | CSSM_CC_HANDLE contextHandle, | |
65 | CSSM_KEYUSE publicKeyUsage, | |
66 | uint32 publicKeyAttr, | |
67 | CSSM_KEYUSE privateKeyUsage, | |
68 | uint32 privateKeyAttr, | |
69 | SecAccessRef initialAccess, | |
70 | SecKeyRef* publicKeyRef, | |
71 | SecKeyRef* privateKeyRef) | |
72 | { | |
73 | BEGIN_SECAPI | |
74 | ||
75 | Keychain keychain = Keychain::optional(keychainRef); | |
76 | SecPointer<Access> theAccess(initialAccess ? Access::required(initialAccess) : new Access("<key>")); | |
77 | SecPointer<KeyItem> pubItem, privItem; | |
78 | ||
427c49bc A |
79 | Mutex *keychainMutex = keychain->getKeychainMutex(); |
80 | StLock<Mutex> _(*keychainMutex); | |
81 | ||
b1ab9ed8 A |
82 | KeyItem::createPair(keychain, |
83 | algorithm, | |
84 | keySizeInBits, | |
85 | contextHandle, | |
86 | publicKeyUsage, | |
87 | publicKeyAttr, | |
88 | privateKeyUsage, | |
89 | privateKeyAttr, | |
90 | theAccess, | |
91 | pubItem, | |
92 | privItem); | |
93 | ||
94 | // Return the generated keys. | |
95 | if (publicKeyRef) | |
96 | *publicKeyRef = pubItem->handle(); | |
97 | if (privateKeyRef) | |
98 | *privateKeyRef = privItem->handle(); | |
99 | ||
100 | END_SECAPI | |
101 | } | |
102 | ||
427c49bc A |
103 | OSStatus |
104 | SecKeyCreatePair( | |
105 | SecKeychainRef keychainRef, | |
106 | CSSM_ALGORITHMS algorithm, | |
107 | uint32 keySizeInBits, | |
108 | CSSM_CC_HANDLE contextHandle, | |
109 | CSSM_KEYUSE publicKeyUsage, | |
110 | uint32 publicKeyAttr, | |
111 | CSSM_KEYUSE privateKeyUsage, | |
112 | uint32 privateKeyAttr, | |
113 | SecAccessRef initialAccess, | |
114 | SecKeyRef* publicKeyRef, | |
115 | SecKeyRef* privateKeyRef) | |
116 | { | |
117 | OSStatus result = SecKeyCreatePairInternal(keychainRef, algorithm, keySizeInBits, contextHandle, publicKeyUsage, | |
118 | publicKeyAttr, privateKeyUsage, privateKeyAttr, initialAccess, publicKeyRef, privateKeyRef); | |
119 | ||
120 | return result; | |
121 | } | |
122 | ||
123 | ||
124 | ||
b1ab9ed8 A |
125 | OSStatus |
126 | SecKeyGetCSSMKey(SecKeyRef key, const CSSM_KEY **cssmKey) | |
127 | { | |
128 | BEGIN_SECAPI | |
129 | ||
130 | Required(cssmKey) = KeyItem::required(key)->key(); | |
131 | ||
132 | END_SECAPI | |
133 | } | |
134 | ||
135 | ||
136 | // | |
137 | // Private APIs | |
138 | // | |
139 | ||
140 | OSStatus | |
141 | SecKeyGetCSPHandle(SecKeyRef keyRef, CSSM_CSP_HANDLE *cspHandle) | |
142 | { | |
143 | BEGIN_SECAPI | |
144 | ||
145 | SecPointer<KeyItem> keyItem(KeyItem::required(keyRef)); | |
146 | Required(cspHandle) = keyItem->csp()->handle(); | |
147 | ||
148 | END_SECAPI | |
149 | } | |
150 | ||
151 | /* deprecated as of 10.8 */ | |
152 | OSStatus | |
153 | SecKeyGetAlgorithmID(SecKeyRef keyRef, const CSSM_X509_ALGORITHM_IDENTIFIER **algid) | |
154 | { | |
155 | BEGIN_SECAPI | |
156 | ||
157 | SecPointer<KeyItem> keyItem(KeyItem::required(keyRef)); | |
158 | Required(algid) = &keyItem->algorithmIdentifier(); | |
159 | ||
160 | END_SECAPI | |
161 | } | |
162 | ||
163 | /* new for 10.8 */ | |
164 | CFIndex | |
165 | SecKeyGetAlgorithmId(SecKeyRef key) | |
166 | { | |
167 | const CSSM_KEY *cssmKey; | |
168 | ||
427c49bc | 169 | if (SecKeyGetCSSMKey(key, &cssmKey) != errSecSuccess) |
b1ab9ed8 A |
170 | return kSecNullAlgorithmID; |
171 | ||
172 | switch (cssmKey->KeyHeader.AlgorithmId) { | |
173 | case CSSM_ALGID_RSA: | |
174 | return kSecRSAAlgorithmID; | |
175 | case CSSM_ALGID_DSA: | |
176 | return kSecDSAAlgorithmID; | |
177 | case CSSM_ALGID_ECDSA: | |
178 | return kSecECDSAAlgorithmID; | |
179 | default: | |
180 | assert(0); /* other algorithms TBA */ | |
181 | return kSecNullAlgorithmID; | |
182 | } | |
183 | } | |
184 | ||
185 | OSStatus | |
186 | SecKeyGetStrengthInBits(SecKeyRef keyRef, const CSSM_X509_ALGORITHM_IDENTIFIER *algid, unsigned int *strength) | |
187 | { | |
188 | BEGIN_SECAPI | |
189 | ||
190 | SecPointer<KeyItem> keyItem(KeyItem::required(keyRef)); | |
191 | Required(strength) = keyItem->strengthInBits(algid); | |
192 | ||
193 | END_SECAPI | |
194 | } | |
195 | ||
196 | OSStatus | |
197 | SecKeyGetCredentials( | |
198 | SecKeyRef keyRef, | |
199 | CSSM_ACL_AUTHORIZATION_TAG operation, | |
200 | SecCredentialType credentialType, | |
201 | const CSSM_ACCESS_CREDENTIALS **outCredentials) | |
202 | { | |
203 | BEGIN_SECAPI | |
204 | ||
205 | SecPointer<KeyItem> keyItem(KeyItem::required(keyRef)); | |
206 | Required(outCredentials) = keyItem->getCredentials(operation, credentialType); | |
207 | ||
208 | END_SECAPI | |
209 | } | |
210 | ||
211 | OSStatus | |
212 | SecKeyImportPair( | |
213 | SecKeychainRef keychainRef, | |
214 | const CSSM_KEY *publicCssmKey, | |
215 | const CSSM_KEY *privateCssmKey, | |
216 | SecAccessRef initialAccess, | |
217 | SecKeyRef* publicKey, | |
218 | SecKeyRef* privateKey) | |
219 | { | |
220 | BEGIN_SECAPI | |
221 | ||
222 | Keychain keychain = Keychain::optional(keychainRef); | |
223 | SecPointer<Access> theAccess(initialAccess ? Access::required(initialAccess) : new Access("<key>")); | |
224 | SecPointer<KeyItem> pubItem, privItem; | |
225 | ||
226 | KeyItem::importPair(keychain, | |
227 | Required(publicCssmKey), | |
228 | Required(privateCssmKey), | |
229 | theAccess, | |
230 | pubItem, | |
231 | privItem); | |
232 | ||
233 | // Return the generated keys. | |
234 | if (publicKey) | |
235 | *publicKey = pubItem->handle(); | |
236 | if (privateKey) | |
237 | *privateKey = privItem->handle(); | |
238 | ||
239 | END_SECAPI | |
240 | } | |
241 | ||
427c49bc | 242 | static OSStatus |
b1ab9ed8 A |
243 | SecKeyGenerateWithAttributes( |
244 | SecKeychainAttributeList* attrList, | |
245 | SecKeychainRef keychainRef, | |
246 | CSSM_ALGORITHMS algorithm, | |
247 | uint32 keySizeInBits, | |
248 | CSSM_CC_HANDLE contextHandle, | |
249 | CSSM_KEYUSE keyUsage, | |
250 | uint32 keyAttr, | |
251 | SecAccessRef initialAccess, | |
252 | SecKeyRef* keyRef) | |
253 | { | |
254 | BEGIN_SECAPI | |
255 | ||
256 | Keychain keychain; | |
257 | SecPointer<Access> theAccess; | |
258 | ||
259 | if (keychainRef) | |
260 | keychain = KeychainImpl::required(keychainRef); | |
261 | if (initialAccess) | |
262 | theAccess = Access::required(initialAccess); | |
263 | ||
264 | SecPointer<KeyItem> item = KeyItem::generateWithAttributes(attrList, | |
265 | keychain, | |
266 | algorithm, | |
267 | keySizeInBits, | |
268 | contextHandle, | |
269 | keyUsage, | |
270 | keyAttr, | |
271 | theAccess); | |
272 | ||
273 | // Return the generated key. | |
274 | if (keyRef) | |
275 | *keyRef = item->handle(); | |
276 | ||
277 | END_SECAPI | |
278 | } | |
279 | ||
280 | OSStatus | |
281 | SecKeyGenerate( | |
282 | SecKeychainRef keychainRef, | |
283 | CSSM_ALGORITHMS algorithm, | |
284 | uint32 keySizeInBits, | |
285 | CSSM_CC_HANDLE contextHandle, | |
286 | CSSM_KEYUSE keyUsage, | |
287 | uint32 keyAttr, | |
288 | SecAccessRef initialAccess, | |
289 | SecKeyRef* keyRef) | |
290 | { | |
291 | return SecKeyGenerateWithAttributes(NULL, | |
292 | keychainRef, algorithm, keySizeInBits, | |
293 | contextHandle, keyUsage, keyAttr, | |
294 | initialAccess, keyRef); | |
295 | } | |
296 | ||
297 | ||
298 | /* new in 10.6 */ | |
299 | /* Create a key from supplied data and parameters */ | |
300 | SecKeyRef | |
301 | SecKeyCreate(CFAllocatorRef allocator, | |
302 | const SecKeyDescriptor *keyClass, | |
303 | const uint8_t *keyData, | |
304 | CFIndex keyDataLength, | |
305 | SecKeyEncoding encoding) | |
306 | { | |
307 | SecKeyRef keyRef = NULL; | |
308 | OSStatus __secapiresult; | |
309 | try { | |
310 | //FIXME: needs implementation | |
311 | ||
427c49bc | 312 | __secapiresult=errSecSuccess; |
b1ab9ed8 A |
313 | } |
314 | catch (const MacOSError &err) { __secapiresult=err.osStatus(); } | |
315 | catch (const CommonError &err) { __secapiresult=SecKeychainErrFromOSStatus(err.osStatus()); } | |
427c49bc A |
316 | catch (const std::bad_alloc &) { __secapiresult=errSecAllocate; } |
317 | catch (...) { __secapiresult=errSecInternalComponent; } | |
b1ab9ed8 A |
318 | return keyRef; |
319 | } | |
320 | ||
321 | /* new in 10.6 */ | |
322 | /* Generate a floating key reference from a CSSM_KEY */ | |
323 | OSStatus | |
324 | SecKeyCreateWithCSSMKey(const CSSM_KEY *cssmKey, | |
325 | SecKeyRef *keyRef) | |
326 | { | |
327 | BEGIN_SECAPI | |
328 | ||
329 | Required(cssmKey); | |
330 | CssmClient::CSP csp(cssmKey->KeyHeader.CspId); | |
331 | CssmClient::Key key(csp, *cssmKey); | |
332 | KeyItem *item = new KeyItem(key); | |
333 | ||
334 | // Return the generated key. | |
335 | if (keyRef) | |
336 | *keyRef = item->handle(); | |
337 | ||
338 | END_SECAPI | |
339 | } | |
340 | ||
341 | ||
342 | ||
343 | static u_int32_t ConvertCFStringToInteger(CFStringRef ref) | |
344 | { | |
345 | if (ref == NULL) | |
346 | { | |
347 | return 0; | |
348 | } | |
349 | ||
350 | // figure out the size of the string | |
427c49bc | 351 | CFIndex numChars = CFStringGetMaximumSizeForEncoding(CFStringGetLength(ref), kCFStringEncodingUTF8); |
b1ab9ed8 A |
352 | char buffer[numChars]; |
353 | if (!CFStringGetCString(ref, buffer, numChars, kCFStringEncodingUTF8)) | |
354 | { | |
427c49bc | 355 | MacOSError::throwMe(errSecParam); |
b1ab9ed8 A |
356 | } |
357 | ||
358 | return atoi(buffer); | |
359 | } | |
360 | ||
361 | ||
362 | ||
363 | static OSStatus CheckAlgorithmType(CFDictionaryRef parameters, CSSM_ALGORITHMS &algorithms) | |
364 | { | |
365 | // figure out the algorithm to use | |
366 | CFStringRef ktype = (CFStringRef) CFDictionaryGetValue(parameters, kSecAttrKeyType); | |
367 | if (ktype == NULL) | |
368 | { | |
369 | return errSecParam; | |
370 | } | |
371 | ||
372 | if (CFEqual(ktype, kSecAttrKeyTypeRSA)) { | |
373 | algorithms = CSSM_ALGID_RSA; | |
427c49bc A |
374 | return errSecSuccess; |
375 | } else if(CFEqual(ktype, kSecAttrKeyTypeECDSA) || | |
376 | CFEqual(ktype, kSecAttrKeyTypeEC)) { | |
b1ab9ed8 | 377 | algorithms = CSSM_ALGID_ECDSA; |
427c49bc | 378 | return errSecSuccess; |
b1ab9ed8 A |
379 | } else if(CFEqual(ktype, kSecAttrKeyTypeAES)) { |
380 | algorithms = CSSM_ALGID_AES; | |
427c49bc | 381 | return errSecSuccess; |
b1ab9ed8 A |
382 | } else if(CFEqual(ktype, kSecAttrKeyType3DES)) { |
383 | algorithms = CSSM_ALGID_3DES; | |
427c49bc | 384 | return errSecSuccess; |
b1ab9ed8 A |
385 | } else { |
386 | return errSecUnsupportedAlgorithm; | |
387 | } | |
388 | } | |
389 | ||
390 | ||
391 | ||
392 | static OSStatus GetKeySize(CFDictionaryRef parameters, CSSM_ALGORITHMS algorithms, uint32 &keySizeInBits) | |
393 | { | |
394 | ||
395 | // get the key size and check it for validity | |
396 | CFTypeRef ref = CFDictionaryGetValue(parameters, kSecAttrKeySizeInBits); | |
397 | ||
398 | keySizeInBits = kSecDefaultKeySize; | |
399 | ||
400 | CFTypeID bitSizeType = CFGetTypeID(ref); | |
401 | if (bitSizeType == CFStringGetTypeID()) | |
402 | keySizeInBits = ConvertCFStringToInteger((CFStringRef) ref); | |
403 | else if (bitSizeType == CFNumberGetTypeID()) | |
404 | CFNumberGetValue((CFNumberRef) ref, kCFNumberSInt32Type, &keySizeInBits); | |
405 | else return errSecParam; | |
406 | ||
407 | ||
408 | switch (algorithms) { | |
409 | case CSSM_ALGID_ECDSA: | |
410 | if(keySizeInBits == kSecDefaultKeySize) keySizeInBits = kSecp256r1; | |
427c49bc | 411 | if(keySizeInBits == kSecp192r1 || keySizeInBits == kSecp256r1 || keySizeInBits == kSecp384r1 || keySizeInBits == kSecp521r1 ) return errSecSuccess; |
b1ab9ed8 A |
412 | break; |
413 | case CSSM_ALGID_RSA: | |
414 | if(keySizeInBits % 8) return errSecParam; | |
415 | if(keySizeInBits == kSecDefaultKeySize) keySizeInBits = 2048; | |
427c49bc | 416 | if(keySizeInBits >= kSecRSAMin && keySizeInBits <= kSecRSAMax) return errSecSuccess; |
b1ab9ed8 A |
417 | break; |
418 | case CSSM_ALGID_AES: | |
419 | if(keySizeInBits == kSecDefaultKeySize) keySizeInBits = kSecAES128; | |
427c49bc | 420 | if(keySizeInBits == kSecAES128 || keySizeInBits == kSecAES192 || keySizeInBits == kSecAES256) return errSecSuccess; |
b1ab9ed8 A |
421 | break; |
422 | case CSSM_ALGID_3DES: | |
423 | if(keySizeInBits == kSecDefaultKeySize) keySizeInBits = kSec3DES192; | |
427c49bc | 424 | if(keySizeInBits == kSec3DES192) return errSecSuccess; |
b1ab9ed8 A |
425 | break; |
426 | default: | |
427 | break; | |
428 | } | |
429 | return errSecParam; | |
430 | } | |
431 | ||
432 | ||
433 | ||
434 | enum AttributeType | |
435 | { | |
436 | kStringType, | |
437 | kBooleanType, | |
438 | kIntegerType | |
439 | }; | |
440 | ||
441 | ||
442 | ||
443 | struct ParameterAttribute | |
444 | { | |
445 | const CFTypeRef *name; | |
446 | AttributeType type; | |
447 | }; | |
448 | ||
449 | ||
450 | ||
451 | static ParameterAttribute gAttributes[] = | |
452 | { | |
453 | { | |
454 | &kSecAttrLabel, | |
455 | kStringType | |
456 | }, | |
457 | { | |
458 | &kSecAttrIsPermanent, | |
459 | kBooleanType | |
460 | }, | |
461 | { | |
462 | &kSecAttrApplicationTag, | |
463 | kStringType | |
464 | }, | |
465 | { | |
466 | &kSecAttrEffectiveKeySize, | |
467 | kBooleanType | |
468 | }, | |
469 | { | |
470 | &kSecAttrCanEncrypt, | |
471 | kBooleanType | |
472 | }, | |
473 | { | |
474 | &kSecAttrCanDecrypt, | |
475 | kBooleanType | |
476 | }, | |
477 | { | |
478 | &kSecAttrCanDerive, | |
479 | kBooleanType | |
480 | }, | |
481 | { | |
482 | &kSecAttrCanSign, | |
483 | kBooleanType | |
484 | }, | |
485 | { | |
486 | &kSecAttrCanVerify, | |
487 | kBooleanType | |
488 | }, | |
489 | { | |
490 | &kSecAttrCanUnwrap, | |
491 | kBooleanType | |
492 | } | |
493 | }; | |
494 | ||
495 | const int kNumberOfAttributes = sizeof(gAttributes) / sizeof(ParameterAttribute); | |
496 | ||
497 | static OSStatus ScanDictionaryForParameters(CFDictionaryRef parameters, void* attributePointers[]) | |
498 | { | |
499 | int i; | |
500 | for (i = 0; i < kNumberOfAttributes; ++i) | |
501 | { | |
502 | // see if the corresponding tag exists in the dictionary | |
503 | CFTypeRef value = CFDictionaryGetValue(parameters, *(gAttributes[i].name)); | |
504 | if (value != NULL) | |
505 | { | |
506 | switch (gAttributes[i].type) | |
507 | { | |
508 | case kStringType: | |
509 | // just return the value | |
510 | *(CFTypeRef*) attributePointers[i] = value; | |
511 | break; | |
512 | ||
513 | case kBooleanType: | |
514 | { | |
515 | CFBooleanRef bRef = (CFBooleanRef) value; | |
516 | *(bool*) attributePointers[i] = CFBooleanGetValue(bRef); | |
517 | } | |
518 | break; | |
519 | ||
520 | case kIntegerType: | |
521 | { | |
522 | CFNumberRef nRef = (CFNumberRef) value; | |
523 | CFNumberGetValue(nRef, kCFNumberSInt32Type, attributePointers[i]); | |
524 | } | |
525 | break; | |
526 | } | |
527 | } | |
528 | } | |
529 | ||
427c49bc | 530 | return errSecSuccess; |
b1ab9ed8 A |
531 | } |
532 | ||
533 | ||
534 | ||
535 | static OSStatus GetKeyParameters(CFDictionaryRef parameters, int keySize, bool isPublic, CSSM_KEYUSE &keyUse, uint32 &attrs, CFTypeRef &labelRef, CFDataRef &applicationTagRef) | |
536 | { | |
537 | // establish default values | |
538 | labelRef = NULL; | |
539 | bool isPermanent = false; | |
540 | applicationTagRef = NULL; | |
541 | CFTypeRef effectiveKeySize = NULL; | |
542 | bool canDecrypt = isPublic ? false : true; | |
543 | bool canEncrypt = !canDecrypt; | |
544 | bool canDerive = true; | |
545 | bool canSign = isPublic ? false : true; | |
546 | bool canVerify = !canSign; | |
547 | bool canUnwrap = isPublic ? false : true; | |
548 | attrs = CSSM_KEYATTR_EXTRACTABLE; | |
549 | keyUse = 0; | |
550 | ||
551 | void* attributePointers[] = {&labelRef, &isPermanent, &applicationTagRef, &effectiveKeySize, &canEncrypt, &canDecrypt, | |
552 | &canDerive, &canSign, &canVerify, &canUnwrap}; | |
553 | ||
554 | // look for modifiers in the general dictionary | |
555 | OSStatus result = ScanDictionaryForParameters(parameters, attributePointers); | |
427c49bc | 556 | if (result != errSecSuccess) |
b1ab9ed8 A |
557 | { |
558 | return result; | |
559 | } | |
560 | ||
561 | // see if we have anything which modifies the defaults | |
562 | CFTypeRef key; | |
563 | if (isPublic) | |
564 | { | |
565 | key = kSecPublicKeyAttrs; | |
566 | } | |
567 | else | |
568 | { | |
569 | key = kSecPrivateKeyAttrs; | |
570 | } | |
571 | ||
572 | CFTypeRef dType = CFDictionaryGetValue(parameters, key); | |
573 | if (dType != NULL) | |
574 | { | |
575 | // this had better be a dictionary | |
576 | if (CFGetTypeID(dType) != CFDictionaryGetTypeID()) | |
577 | { | |
578 | return errSecParam; | |
579 | } | |
580 | ||
581 | // pull any additional parameters out of this dictionary | |
427c49bc A |
582 | result = ScanDictionaryForParameters((CFDictionaryRef)dType, attributePointers); |
583 | if (result != errSecSuccess) | |
b1ab9ed8 A |
584 | { |
585 | return result; | |
586 | } | |
587 | } | |
588 | ||
589 | // figure out the key usage | |
590 | keyUse = 0; | |
591 | if (canDecrypt) | |
592 | { | |
593 | keyUse |= CSSM_KEYUSE_DECRYPT; | |
594 | } | |
595 | ||
596 | if (canEncrypt) | |
597 | { | |
598 | keyUse |= CSSM_KEYUSE_ENCRYPT; | |
599 | } | |
600 | ||
601 | if (canDerive) | |
602 | { | |
603 | keyUse |= CSSM_KEYUSE_DERIVE; | |
604 | } | |
605 | ||
606 | if (canSign) | |
607 | { | |
608 | keyUse |= CSSM_KEYUSE_SIGN; | |
609 | } | |
610 | ||
611 | if (canVerify) | |
612 | { | |
613 | keyUse |= CSSM_KEYUSE_VERIFY; | |
614 | } | |
615 | ||
616 | if (canUnwrap) | |
617 | { | |
618 | keyUse |= CSSM_KEYUSE_UNWRAP; | |
619 | } | |
620 | ||
621 | // public key is always extractable; | |
622 | // private key is extractable by default unless explicitly set to false | |
623 | CFTypeRef value = NULL; | |
624 | if (!isPublic && CFDictionaryGetValueIfPresent(parameters, kSecAttrIsExtractable, (const void **)&value) && value) | |
625 | { | |
626 | Boolean keyIsExtractable = CFEqual(kCFBooleanTrue, value); | |
627 | if (!keyIsExtractable) | |
628 | attrs = 0; | |
629 | } | |
630 | ||
631 | attrs |= CSSM_KEYATTR_PERMANENT; | |
632 | ||
427c49bc | 633 | return errSecSuccess; |
b1ab9ed8 A |
634 | } |
635 | ||
636 | ||
637 | ||
638 | static OSStatus MakeKeyGenParametersFromDictionary(CFDictionaryRef parameters, | |
639 | CSSM_ALGORITHMS &algorithms, | |
640 | uint32 &keySizeInBits, | |
641 | CSSM_KEYUSE &publicKeyUse, | |
642 | uint32 &publicKeyAttr, | |
643 | CFTypeRef &publicKeyLabelRef, | |
644 | CFDataRef &publicKeyAttributeTagRef, | |
645 | CSSM_KEYUSE &privateKeyUse, | |
646 | uint32 &privateKeyAttr, | |
647 | CFTypeRef &privateKeyLabelRef, | |
648 | CFDataRef &privateKeyAttributeTagRef, | |
649 | SecAccessRef &initialAccess) | |
650 | { | |
651 | OSStatus result; | |
652 | ||
653 | result = CheckAlgorithmType(parameters, algorithms); | |
427c49bc | 654 | if (result != errSecSuccess) |
b1ab9ed8 A |
655 | { |
656 | return result; | |
657 | } | |
658 | ||
659 | result = GetKeySize(parameters, algorithms, keySizeInBits); | |
427c49bc | 660 | if (result != errSecSuccess) |
b1ab9ed8 A |
661 | { |
662 | return result; | |
663 | } | |
664 | ||
427c49bc A |
665 | result = GetKeyParameters(parameters, keySizeInBits, false, privateKeyUse, privateKeyAttr, privateKeyLabelRef, privateKeyAttributeTagRef); |
666 | if (result != errSecSuccess) | |
b1ab9ed8 A |
667 | { |
668 | return result; | |
669 | } | |
670 | ||
427c49bc A |
671 | result = GetKeyParameters(parameters, keySizeInBits, true, publicKeyUse, publicKeyAttr, publicKeyLabelRef, publicKeyAttributeTagRef); |
672 | if (result != errSecSuccess) | |
b1ab9ed8 A |
673 | { |
674 | return result; | |
675 | } | |
676 | ||
677 | if (!CFDictionaryGetValueIfPresent(parameters, kSecAttrAccess, (const void **)&initialAccess)) | |
678 | { | |
679 | initialAccess = NULL; | |
680 | } | |
681 | else if (SecAccessGetTypeID() != CFGetTypeID(initialAccess)) | |
682 | { | |
427c49bc | 683 | return errSecParam; |
b1ab9ed8 A |
684 | } |
685 | ||
427c49bc | 686 | return errSecSuccess; |
b1ab9ed8 A |
687 | } |
688 | ||
689 | ||
690 | ||
691 | static OSStatus SetKeyLabelAndTag(SecKeyRef keyRef, CFTypeRef label, CFDataRef tag) | |
692 | { | |
693 | int numToModify = 0; | |
694 | if (label != NULL) | |
695 | { | |
696 | numToModify += 1; | |
697 | } | |
698 | ||
699 | if (tag != NULL) | |
700 | { | |
701 | numToModify += 1; | |
702 | } | |
703 | ||
704 | if (numToModify == 0) | |
705 | { | |
427c49bc | 706 | return errSecSuccess; |
b1ab9ed8 A |
707 | } |
708 | ||
709 | SecKeychainAttributeList attrList; | |
710 | SecKeychainAttribute attributes[numToModify]; | |
711 | ||
712 | int i = 0; | |
713 | ||
714 | if (label != NULL) | |
715 | { | |
716 | if (CFStringGetTypeID() == CFGetTypeID(label)) { | |
717 | CFStringRef label_string = static_cast<CFStringRef>(label); | |
718 | attributes[i].tag = kSecKeyPrintName; | |
719 | attributes[i].data = (void*) CFStringGetCStringPtr(label_string, kCFStringEncodingUTF8); | |
720 | if (NULL == attributes[i].data) { | |
721 | CFIndex buffer_length = CFStringGetMaximumSizeForEncoding(CFStringGetLength(label_string), kCFStringEncodingUTF8); | |
722 | attributes[i].data = alloca((size_t)buffer_length); | |
723 | if (NULL == attributes[i].data) { | |
724 | UnixError::throwMe(ENOMEM); | |
725 | } | |
726 | if (!CFStringGetCString(label_string, static_cast<char *>(attributes[i].data), buffer_length, kCFStringEncodingUTF8)) { | |
427c49bc | 727 | MacOSError::throwMe(errSecParam); |
b1ab9ed8 A |
728 | } |
729 | } | |
427c49bc | 730 | attributes[i].length = (UInt32)strlen(static_cast<char *>(attributes[i].data)); |
b1ab9ed8 A |
731 | } else if (CFDataGetTypeID() == CFGetTypeID(label)) { |
732 | // 10.6 bug compatibility | |
733 | CFDataRef label_data = static_cast<CFDataRef>(label); | |
734 | attributes[i].tag = kSecKeyLabel; | |
735 | attributes[i].data = (void*) CFDataGetBytePtr(label_data); | |
427c49bc | 736 | attributes[i].length = (UInt32)CFDataGetLength(label_data); |
b1ab9ed8 | 737 | } else { |
427c49bc | 738 | MacOSError::throwMe(errSecParam); |
b1ab9ed8 A |
739 | } |
740 | i++; | |
741 | } | |
742 | ||
743 | if (tag != NULL) | |
744 | { | |
745 | attributes[i].tag = kSecKeyApplicationTag; | |
746 | attributes[i].data = (void*) CFDataGetBytePtr(tag); | |
427c49bc | 747 | attributes[i].length = (UInt32)CFDataGetLength(tag); |
b1ab9ed8 A |
748 | i++; |
749 | } | |
750 | ||
751 | attrList.count = numToModify; | |
752 | attrList.attr = attributes; | |
753 | ||
754 | return SecKeychainItemModifyAttributesAndData((SecKeychainItemRef) keyRef, &attrList, 0, NULL); | |
755 | } | |
756 | ||
757 | ||
758 | ||
759 | /* new in 10.6 */ | |
760 | /* Generate a private/public keypair. */ | |
761 | OSStatus | |
762 | SecKeyGeneratePair( | |
763 | CFDictionaryRef parameters, | |
764 | SecKeyRef *publicKey, | |
765 | SecKeyRef *privateKey) | |
766 | { | |
767 | BEGIN_SECAPI | |
768 | ||
769 | Required(parameters); | |
770 | Required(publicKey); | |
771 | Required(privateKey); | |
772 | ||
773 | CSSM_ALGORITHMS algorithms; | |
774 | uint32 keySizeInBits; | |
775 | CSSM_KEYUSE publicKeyUse; | |
776 | uint32 publicKeyAttr; | |
777 | CFTypeRef publicKeyLabelRef; | |
778 | CFDataRef publicKeyAttributeTagRef; | |
779 | CSSM_KEYUSE privateKeyUse; | |
780 | uint32 privateKeyAttr; | |
781 | CFTypeRef privateKeyLabelRef; | |
782 | CFDataRef privateKeyAttributeTagRef; | |
783 | SecAccessRef initialAccess; | |
784 | SecKeychainRef keychain; | |
785 | ||
786 | OSStatus result = MakeKeyGenParametersFromDictionary(parameters, algorithms, keySizeInBits, publicKeyUse, publicKeyAttr, publicKeyLabelRef, | |
787 | publicKeyAttributeTagRef, privateKeyUse, privateKeyAttr, privateKeyLabelRef, privateKeyAttributeTagRef, | |
788 | initialAccess); | |
789 | ||
427c49bc | 790 | if (result != errSecSuccess) |
b1ab9ed8 A |
791 | { |
792 | return result; | |
793 | } | |
794 | ||
795 | // verify keychain parameter | |
796 | keychain = NULL; | |
797 | if (!CFDictionaryGetValueIfPresent(parameters, kSecUseKeychain, (const void **)&keychain)) | |
798 | keychain = NULL; | |
799 | else if (SecKeychainGetTypeID() != CFGetTypeID(keychain)) | |
800 | keychain = NULL; | |
801 | ||
802 | // do the key generation | |
803 | result = SecKeyCreatePair(keychain, algorithms, keySizeInBits, 0, publicKeyUse, publicKeyAttr, privateKeyUse, privateKeyAttr, initialAccess, publicKey, privateKey); | |
427c49bc | 804 | if (result != errSecSuccess) |
b1ab9ed8 A |
805 | { |
806 | return result; | |
807 | } | |
808 | ||
809 | // set the label and print attributes on the keys | |
810 | SetKeyLabelAndTag(*publicKey, publicKeyLabelRef, publicKeyAttributeTagRef); | |
811 | SetKeyLabelAndTag(*privateKey, privateKeyLabelRef, privateKeyAttributeTagRef); | |
812 | return result; | |
813 | ||
814 | END_SECAPI | |
815 | } | |
816 | ||
817 | /* new in 10.6 */ | |
818 | OSStatus | |
819 | SecKeyRawSign( | |
820 | SecKeyRef key, | |
821 | SecPadding padding, | |
822 | const uint8_t *dataToSign, | |
823 | size_t dataToSignLen, | |
824 | uint8_t *sig, | |
825 | size_t *sigLen) | |
826 | { | |
827 | BEGIN_SECAPI | |
828 | ||
829 | Required(key); | |
830 | SecPointer<KeyItem> keyItem(KeyItem::required(key)); | |
831 | CSSM_DATA dataInput; | |
832 | ||
833 | dataInput.Data = (uint8_t*) dataToSign; | |
834 | dataInput.Length = dataToSignLen; | |
835 | ||
836 | CSSM_DATA output; | |
837 | output.Data = sig; | |
838 | output.Length = *sigLen; | |
839 | ||
840 | const AccessCredentials* credentials = keyItem->getCredentials(CSSM_ACL_AUTHORIZATION_SIGN, kSecCredentialTypeDefault); | |
841 | ||
842 | keyItem->RawSign(padding, dataInput, credentials, output); | |
843 | *sigLen = output.Length; | |
844 | ||
845 | END_SECAPI | |
846 | } | |
847 | ||
427c49bc A |
848 | OSStatus SecKeyRawVerifyOSX( |
849 | SecKeyRef key, /* Public key */ | |
850 | SecPadding padding, /* kSecPaddingNone or kSecPaddingPKCS1 */ | |
851 | const uint8_t *signedData, /* signature over this data */ | |
852 | size_t signedDataLen, /* length of dataToSign */ | |
853 | const uint8_t *sig, /* signature */ | |
854 | size_t sigLen) | |
855 | { | |
856 | return SecKeyRawVerify(key,padding,signedData,signedDataLen,sig,sigLen); | |
857 | } | |
858 | ||
b1ab9ed8 A |
859 | /* new in 10.6 */ |
860 | OSStatus | |
861 | SecKeyRawVerify( | |
862 | SecKeyRef key, | |
863 | SecPadding padding, | |
864 | const uint8_t *signedData, | |
865 | size_t signedDataLen, | |
866 | const uint8_t *sig, | |
867 | size_t sigLen) | |
868 | { | |
869 | BEGIN_SECAPI | |
870 | ||
871 | Required(key); | |
872 | ||
873 | SecPointer<KeyItem> keyItem(KeyItem::required(key)); | |
874 | CSSM_DATA dataInput; | |
875 | ||
876 | dataInput.Data = (uint8_t*) signedData; | |
877 | dataInput.Length = signedDataLen; | |
878 | ||
879 | CSSM_DATA signature; | |
880 | signature.Data = (uint8_t*) sig; | |
881 | signature.Length = sigLen; | |
882 | ||
883 | const AccessCredentials* credentials = keyItem->getCredentials(CSSM_ACL_AUTHORIZATION_ANY, kSecCredentialTypeDefault); | |
884 | ||
885 | keyItem->RawVerify(padding, dataInput, credentials, signature); | |
886 | ||
887 | END_SECAPI | |
888 | } | |
889 | ||
890 | /* new in 10.6 */ | |
891 | OSStatus | |
892 | SecKeyEncrypt( | |
893 | SecKeyRef key, | |
894 | SecPadding padding, | |
895 | const uint8_t *plainText, | |
896 | size_t plainTextLen, | |
897 | uint8_t *cipherText, | |
898 | size_t *cipherTextLen) | |
899 | { | |
900 | BEGIN_SECAPI | |
901 | ||
902 | SecPointer<KeyItem> keyItem(KeyItem::required(key)); | |
903 | CSSM_DATA inData, outData; | |
904 | inData.Data = (uint8*) plainText; | |
905 | inData.Length = plainTextLen; | |
906 | outData.Data = cipherText; | |
907 | outData.Length = *cipherTextLen; | |
908 | ||
909 | const AccessCredentials* credentials = keyItem->getCredentials(CSSM_ACL_AUTHORIZATION_ENCRYPT, kSecCredentialTypeDefault); | |
910 | ||
911 | keyItem->Encrypt(padding, inData, credentials, outData); | |
912 | *cipherTextLen = outData.Length; | |
913 | ||
914 | END_SECAPI | |
915 | } | |
916 | ||
917 | /* new in 10.6 */ | |
918 | OSStatus | |
919 | SecKeyDecrypt( | |
920 | SecKeyRef key, /* Private key */ | |
921 | SecPadding padding, /* kSecPaddingNone, kSecPaddingPKCS1, kSecPaddingOAEP */ | |
922 | const uint8_t *cipherText, | |
923 | size_t cipherTextLen, /* length of cipherText */ | |
924 | uint8_t *plainText, | |
925 | size_t *plainTextLen) /* IN/OUT */ | |
926 | { | |
927 | BEGIN_SECAPI | |
928 | ||
929 | SecPointer<KeyItem> keyItem(KeyItem::required(key)); | |
930 | CSSM_DATA inData, outData; | |
931 | inData.Data = (uint8*) cipherText; | |
932 | inData.Length = cipherTextLen; | |
933 | outData.Data = plainText; | |
934 | outData.Length = *plainTextLen; | |
935 | ||
936 | const AccessCredentials* credentials = keyItem->getCredentials(CSSM_ACL_AUTHORIZATION_DECRYPT, kSecCredentialTypeDefault); | |
937 | ||
938 | keyItem->Decrypt(padding, inData, credentials, outData); | |
939 | *plainTextLen = outData.Length; | |
940 | ||
941 | END_SECAPI | |
942 | } | |
943 | ||
944 | /* new in 10.6 */ | |
945 | size_t | |
946 | SecKeyGetBlockSize(SecKeyRef key) | |
947 | { | |
948 | size_t blockSize = 0; | |
949 | OSStatus __secapiresult; | |
950 | try { | |
951 | CSSM_KEY cssmKey = KeyItem::required(key)->key(); | |
952 | switch(cssmKey.KeyHeader.AlgorithmId) | |
953 | { | |
954 | case CSSM_ALGID_RSA: | |
955 | case CSSM_ALGID_DSA: | |
956 | blockSize = cssmKey.KeyHeader.LogicalKeySizeInBits / 8; | |
957 | break; | |
958 | case CSSM_ALGID_ECDSA: | |
959 | { | |
960 | /* Block size is up to 9 bytes of DER encoding for sequence of 2 integers, | |
961 | * plus both coordinates for the point used */ | |
962 | #define ECDSA_KEY_SIZE_IN_BYTES(bits) (((bits) + 7) / 8) | |
963 | #define ECDSA_MAX_COORD_SIZE_IN_BYTES(n) (ECDSA_KEY_SIZE_IN_BYTES(n) + 1) | |
964 | size_t coordSize = ECDSA_MAX_COORD_SIZE_IN_BYTES(cssmKey.KeyHeader.LogicalKeySizeInBits); | |
965 | assert(coordSize < 256); /* size must fit in a byte for DER */ | |
966 | size_t coordDERLen = (coordSize > 127) ? 2 : 1; | |
967 | size_t coordLen = 1 + coordDERLen + coordSize; | |
968 | ||
969 | size_t pointSize = 2 * coordLen; | |
970 | assert(pointSize < 256); /* size must fit in a byte for DER */ | |
971 | size_t pointDERLen = (pointSize > 127) ? 2 : 1; | |
972 | size_t pointLen = 1 + pointDERLen + pointSize; | |
973 | ||
974 | blockSize = pointLen; | |
975 | } | |
976 | break; | |
977 | case CSSM_ALGID_AES: | |
978 | blockSize = 16; /* all AES keys use 128-bit blocks */ | |
979 | break; | |
980 | case CSSM_ALGID_DES: | |
981 | case CSSM_ALGID_3DES_3KEY: | |
982 | blockSize = 8; /* all DES keys use 64-bit blocks */ | |
983 | break; | |
984 | default: | |
985 | assert(0); /* some other key algorithm */ | |
986 | blockSize = 16; /* FIXME: revisit this */ | |
987 | break; | |
988 | } | |
427c49bc | 989 | __secapiresult=errSecSuccess; |
b1ab9ed8 A |
990 | } |
991 | catch (const MacOSError &err) { __secapiresult=err.osStatus(); } | |
992 | catch (const CommonError &err) { __secapiresult=SecKeychainErrFromOSStatus(err.osStatus()); } | |
427c49bc A |
993 | catch (const std::bad_alloc &) { __secapiresult=errSecAllocate; } |
994 | catch (...) { __secapiresult=errSecInternalComponent; } | |
b1ab9ed8 A |
995 | return blockSize; |
996 | } | |
997 | ||
998 | ||
999 | /* | |
1000 | M4 Additions | |
1001 | */ | |
1002 | ||
1003 | static CFTypeRef | |
1004 | utilGetStringFromCFDict(CFDictionaryRef parameters, CFTypeRef key, CFTypeRef defaultValue) | |
1005 | { | |
1006 | CFTypeRef value = CFDictionaryGetValue(parameters, key); | |
1007 | if (value != NULL) return value; | |
1008 | return defaultValue; | |
1009 | } | |
1010 | ||
1011 | static uint32_t | |
1012 | utilGetNumberFromCFDict(CFDictionaryRef parameters, CFTypeRef key, uint32_t defaultValue) | |
1013 | { | |
1014 | uint32_t integerValue; | |
1015 | CFTypeRef value = CFDictionaryGetValue(parameters, key); | |
1016 | if (value != NULL) { | |
1017 | CFNumberRef nRef = (CFNumberRef) value; | |
1018 | CFNumberGetValue(nRef, kCFNumberSInt32Type, &integerValue); | |
1019 | return integerValue; | |
1020 | } | |
1021 | return defaultValue; | |
1022 | } | |
1023 | ||
1024 | static uint32_t | |
1025 | utilGetMaskValFromCFDict(CFDictionaryRef parameters, CFTypeRef key, uint32_t maskValue) | |
1026 | { | |
1027 | CFTypeRef value = CFDictionaryGetValue(parameters, key); | |
1028 | if (value != NULL) { | |
1029 | CFBooleanRef bRef = (CFBooleanRef) value; | |
1030 | if(CFBooleanGetValue(bRef)) return maskValue; | |
1031 | } | |
1032 | return 0; | |
1033 | } | |
1034 | ||
1035 | static void | |
1036 | utilGetKeyParametersFromCFDict(CFDictionaryRef parameters, CSSM_ALGORITHMS *algorithm, uint32 *keySizeInBits, CSSM_KEYUSE *keyUsage, CSSM_KEYCLASS *keyClass) | |
1037 | { | |
1038 | CFTypeRef algorithmDictValue = utilGetStringFromCFDict(parameters, kSecAttrKeyType, kSecAttrKeyTypeAES); | |
1039 | CFTypeRef keyClassDictValue = utilGetStringFromCFDict(parameters, kSecAttrKeyClass, kSecAttrKeyClassSymmetric); | |
1040 | ||
1041 | if(CFEqual(algorithmDictValue, kSecAttrKeyTypeAES)) { | |
1042 | *algorithm = CSSM_ALGID_AES; | |
1043 | *keySizeInBits = 128; | |
1044 | *keyClass = CSSM_KEYCLASS_SESSION_KEY; | |
1045 | } else if(CFEqual(algorithmDictValue, kSecAttrKeyTypeDES)) { | |
1046 | *algorithm = CSSM_ALGID_DES; | |
1047 | *keySizeInBits = 128; | |
1048 | *keyClass = CSSM_KEYCLASS_SESSION_KEY; | |
1049 | } else if(CFEqual(algorithmDictValue, kSecAttrKeyType3DES)) { | |
1050 | *algorithm = CSSM_ALGID_3DES_3KEY_EDE; | |
1051 | *keySizeInBits = 128; | |
1052 | *keyClass = CSSM_KEYCLASS_SESSION_KEY; | |
1053 | } else if(CFEqual(algorithmDictValue, kSecAttrKeyTypeRC4)) { | |
1054 | *algorithm = CSSM_ALGID_RC4; | |
1055 | *keySizeInBits = 128; | |
1056 | *keyClass = CSSM_KEYCLASS_SESSION_KEY; | |
1057 | } else if(CFEqual(algorithmDictValue, kSecAttrKeyTypeRC2)) { | |
1058 | *algorithm = CSSM_ALGID_RC2; | |
1059 | *keySizeInBits = 128; | |
1060 | *keyClass = CSSM_KEYCLASS_SESSION_KEY; | |
1061 | } else if(CFEqual(algorithmDictValue, kSecAttrKeyTypeCAST)) { | |
1062 | *algorithm = CSSM_ALGID_CAST; | |
1063 | *keySizeInBits = 128; | |
1064 | *keyClass = CSSM_KEYCLASS_SESSION_KEY; | |
1065 | } else if(CFEqual(algorithmDictValue, kSecAttrKeyTypeRSA)) { | |
1066 | *algorithm = CSSM_ALGID_RSA; | |
1067 | *keySizeInBits = 128; | |
1068 | *keyClass = CSSM_KEYCLASS_PRIVATE_KEY; | |
1069 | } else if(CFEqual(algorithmDictValue, kSecAttrKeyTypeDSA)) { | |
1070 | *algorithm = CSSM_ALGID_DSA; | |
1071 | *keySizeInBits = 128; | |
1072 | *keyClass = CSSM_KEYCLASS_PRIVATE_KEY; | |
427c49bc A |
1073 | } else if(CFEqual(algorithmDictValue, kSecAttrKeyTypeECDSA) || |
1074 | CFEqual(algorithmDictValue, kSecAttrKeyTypeEC)) { | |
b1ab9ed8 A |
1075 | *algorithm = CSSM_ALGID_ECDSA; |
1076 | *keySizeInBits = 128; | |
1077 | *keyClass = CSSM_KEYCLASS_PRIVATE_KEY; | |
1078 | } else { | |
1079 | *algorithm = CSSM_ALGID_AES; | |
1080 | *keySizeInBits = 128; | |
1081 | *keyClass = CSSM_KEYCLASS_SESSION_KEY; | |
1082 | } | |
1083 | ||
1084 | if(CFEqual(keyClassDictValue, kSecAttrKeyClassPublic)) { | |
1085 | *keyClass = CSSM_KEYCLASS_PUBLIC_KEY; | |
1086 | } else if(CFEqual(keyClassDictValue, kSecAttrKeyClassPrivate)) { | |
1087 | *keyClass = CSSM_KEYCLASS_PRIVATE_KEY; | |
1088 | } else if(CFEqual(keyClassDictValue, kSecAttrKeyClassSymmetric)) { | |
1089 | *keyClass = CSSM_KEYCLASS_SESSION_KEY; | |
1090 | } | |
1091 | ||
1092 | *keySizeInBits = utilGetNumberFromCFDict(parameters, kSecAttrKeySizeInBits, *keySizeInBits); | |
1093 | *keyUsage = utilGetMaskValFromCFDict(parameters, kSecAttrCanEncrypt, CSSM_KEYUSE_ENCRYPT) | | |
1094 | utilGetMaskValFromCFDict(parameters, kSecAttrCanDecrypt, CSSM_KEYUSE_DECRYPT) | | |
1095 | utilGetMaskValFromCFDict(parameters, kSecAttrCanWrap, CSSM_KEYUSE_WRAP) | | |
1096 | utilGetMaskValFromCFDict(parameters, kSecAttrCanUnwrap, CSSM_KEYUSE_UNWRAP); | |
1097 | ||
1098 | ||
1099 | if(*keyClass == CSSM_KEYCLASS_PRIVATE_KEY || *keyClass == CSSM_KEYCLASS_PUBLIC_KEY) { | |
1100 | *keyUsage |= utilGetMaskValFromCFDict(parameters, kSecAttrCanSign, CSSM_KEYUSE_SIGN) | | |
1101 | utilGetMaskValFromCFDict(parameters, kSecAttrCanVerify, CSSM_KEYUSE_VERIFY); | |
1102 | } | |
1103 | ||
1104 | if(*keyUsage == 0) { | |
1105 | switch (*keyClass) { | |
1106 | case CSSM_KEYCLASS_PRIVATE_KEY: | |
1107 | *keyUsage = CSSM_KEYUSE_DECRYPT | CSSM_KEYUSE_UNWRAP | CSSM_KEYUSE_SIGN; | |
1108 | break; | |
1109 | case CSSM_KEYCLASS_PUBLIC_KEY: | |
1110 | *keyUsage = CSSM_KEYUSE_ENCRYPT | CSSM_KEYUSE_VERIFY | CSSM_KEYUSE_WRAP; | |
1111 | break; | |
1112 | default: | |
1113 | *keyUsage = CSSM_KEYUSE_ENCRYPT | CSSM_KEYUSE_DECRYPT | CSSM_KEYUSE_WRAP | CSSM_KEYUSE_UNWRAP | CSSM_KEYUSE_SIGN | CSSM_KEYUSE_VERIFY; | |
1114 | break; | |
1115 | } | |
1116 | } | |
1117 | } | |
1118 | ||
1119 | static CFStringRef | |
1120 | utilCopyDefaultKeyLabel(void) | |
1121 | { | |
1122 | // generate a default label from the current date | |
1123 | CFDateRef dateNow = CFDateCreate(kCFAllocatorDefault, CFAbsoluteTimeGetCurrent()); | |
1124 | CFStringRef defaultLabel = CFCopyDescription(dateNow); | |
1125 | CFRelease(dateNow); | |
1126 | ||
1127 | return defaultLabel; | |
1128 | } | |
1129 | ||
1130 | SecKeyRef | |
1131 | SecKeyGenerateSymmetric(CFDictionaryRef parameters, CFErrorRef *error) | |
1132 | { | |
427c49bc | 1133 | OSStatus result = errSecParam; // default result for an early exit |
b1ab9ed8 A |
1134 | SecKeyRef key = NULL; |
1135 | SecKeychainRef keychain = NULL; | |
1136 | SecAccessRef access; | |
1137 | CFStringRef label; | |
1138 | CFStringRef appLabel; | |
1139 | CFStringRef appTag; | |
1140 | CFStringRef dateLabel = NULL; | |
1141 | ||
1142 | CSSM_ALGORITHMS algorithm; | |
1143 | uint32 keySizeInBits; | |
1144 | CSSM_KEYUSE keyUsage; | |
1145 | uint32 keyAttr = CSSM_KEYATTR_RETURN_DEFAULT; | |
1146 | CSSM_KEYCLASS keyClass; | |
1147 | CFTypeRef value; | |
1148 | Boolean isPermanent; | |
1149 | Boolean isExtractable; | |
1150 | ||
1151 | // verify keychain parameter | |
1152 | if (!CFDictionaryGetValueIfPresent(parameters, kSecUseKeychain, (const void **)&keychain)) | |
1153 | keychain = NULL; | |
1154 | else if (SecKeychainGetTypeID() != CFGetTypeID(keychain)) { | |
1155 | keychain = NULL; | |
1156 | goto errorExit; | |
1157 | } | |
1158 | else | |
1159 | CFRetain(keychain); | |
1160 | ||
1161 | // verify permanent parameter | |
1162 | if (!CFDictionaryGetValueIfPresent(parameters, kSecAttrIsPermanent, (const void **)&value)) | |
1163 | isPermanent = false; | |
1164 | else if (!value || (CFBooleanGetTypeID() != CFGetTypeID(value))) | |
1165 | goto errorExit; | |
1166 | else | |
1167 | isPermanent = CFEqual(kCFBooleanTrue, value); | |
1168 | if (isPermanent) { | |
1169 | if (keychain == NULL) { | |
1170 | // no keychain was specified, so use the default keychain | |
1171 | result = SecKeychainCopyDefault(&keychain); | |
1172 | } | |
1173 | keyAttr |= CSSM_KEYATTR_PERMANENT; | |
1174 | } | |
1175 | ||
1176 | // verify extractable parameter | |
1177 | if (!CFDictionaryGetValueIfPresent(parameters, kSecAttrIsExtractable, (const void **)&value)) | |
1178 | isExtractable = true; // default to extractable if value not specified | |
1179 | else if (!value || (CFBooleanGetTypeID() != CFGetTypeID(value))) | |
1180 | goto errorExit; | |
1181 | else | |
1182 | isExtractable = CFEqual(kCFBooleanTrue, value); | |
1183 | if (isExtractable) | |
1184 | keyAttr |= CSSM_KEYATTR_EXTRACTABLE; | |
1185 | ||
1186 | // verify access parameter | |
1187 | if (!CFDictionaryGetValueIfPresent(parameters, kSecAttrAccess, (const void **)&access)) | |
1188 | access = NULL; | |
1189 | else if (SecAccessGetTypeID() != CFGetTypeID(access)) | |
1190 | goto errorExit; | |
1191 | ||
1192 | // verify label parameter | |
1193 | if (!CFDictionaryGetValueIfPresent(parameters, kSecAttrLabel, (const void **)&label)) | |
1194 | label = (dateLabel = utilCopyDefaultKeyLabel()); // no label provided, so use default | |
1195 | else if (CFStringGetTypeID() != CFGetTypeID(label)) | |
1196 | goto errorExit; | |
1197 | ||
1198 | // verify application label parameter | |
1199 | if (!CFDictionaryGetValueIfPresent(parameters, kSecAttrApplicationLabel, (const void **)&appLabel)) | |
1200 | appLabel = (dateLabel) ? dateLabel : (dateLabel = utilCopyDefaultKeyLabel()); | |
1201 | else if (CFStringGetTypeID() != CFGetTypeID(appLabel)) | |
1202 | goto errorExit; | |
1203 | ||
1204 | // verify application tag parameter | |
1205 | if (!CFDictionaryGetValueIfPresent(parameters, kSecAttrApplicationTag, (const void **)&appTag)) | |
1206 | appTag = NULL; | |
1207 | else if (CFStringGetTypeID() != CFGetTypeID(appTag)) | |
1208 | goto errorExit; | |
1209 | ||
1210 | utilGetKeyParametersFromCFDict(parameters, &algorithm, &keySizeInBits, &keyUsage, &keyClass); | |
1211 | ||
1212 | if (!keychain) { | |
1213 | // the generated key will not be stored in any keychain | |
1214 | result = SecKeyGenerate(keychain, algorithm, keySizeInBits, 0, keyUsage, keyAttr, access, &key); | |
1215 | } | |
1216 | else { | |
1217 | // we can set the label attributes on the generated key if it's a keychain item | |
1218 | size_t labelBufLen = (label) ? (size_t)CFStringGetMaximumSizeForEncoding(CFStringGetLength(label), kCFStringEncodingUTF8) + 1 : 0; | |
1219 | char *labelBuf = (char *)malloc(labelBufLen); | |
1220 | size_t appLabelBufLen = (appLabel) ? (size_t)CFStringGetMaximumSizeForEncoding(CFStringGetLength(appLabel), kCFStringEncodingUTF8) + 1 : 0; | |
1221 | char *appLabelBuf = (char *)malloc(appLabelBufLen); | |
1222 | size_t appTagBufLen = (appTag) ? (size_t)CFStringGetMaximumSizeForEncoding(CFStringGetLength(appTag), kCFStringEncodingUTF8) + 1 : 0; | |
1223 | char *appTagBuf = (char *)malloc(appTagBufLen); | |
1224 | ||
1225 | if (label && !CFStringGetCString(label, labelBuf, labelBufLen-1, kCFStringEncodingUTF8)) | |
1226 | labelBuf[0]=0; | |
1227 | if (appLabel && !CFStringGetCString(appLabel, appLabelBuf, appLabelBufLen-1, kCFStringEncodingUTF8)) | |
1228 | appLabelBuf[0]=0; | |
1229 | if (appTag && !CFStringGetCString(appTag, appTagBuf, appTagBufLen-1, kCFStringEncodingUTF8)) | |
1230 | appTagBuf[0]=0; | |
1231 | ||
1232 | SecKeychainAttribute attrs[] = { | |
427c49bc A |
1233 | { kSecKeyPrintName, (UInt32)strlen(labelBuf), (char *)labelBuf }, |
1234 | { kSecKeyLabel, (UInt32)strlen(appLabelBuf), (char *)appLabelBuf }, | |
1235 | { kSecKeyApplicationTag, (UInt32)strlen(appTagBuf), (char *)appTagBuf } }; | |
b1ab9ed8 A |
1236 | SecKeychainAttributeList attributes = { sizeof(attrs) / sizeof(attrs[0]), attrs }; |
1237 | if (!appTag) --attributes.count; | |
1238 | ||
1239 | result = SecKeyGenerateWithAttributes(&attributes, | |
1240 | keychain, algorithm, keySizeInBits, 0, | |
1241 | keyUsage, keyAttr, access, &key); | |
1242 | ||
1243 | free(labelBuf); | |
1244 | free(appLabelBuf); | |
1245 | free(appTagBuf); | |
1246 | } | |
1247 | ||
1248 | errorExit: | |
1249 | if (result && error) { | |
1250 | *error = CFErrorCreate(kCFAllocatorDefault, kCFErrorDomainOSStatus, result, NULL); | |
1251 | } | |
1252 | if (dateLabel) | |
1253 | CFRelease(dateLabel); | |
1254 | if (keychain) | |
1255 | CFRelease(keychain); | |
1256 | ||
1257 | return key; | |
1258 | } | |
1259 | ||
1260 | ||
1261 | ||
1262 | SecKeyRef | |
1263 | SecKeyCreateFromData(CFDictionaryRef parameters, CFDataRef keyData, CFErrorRef *error) | |
1264 | { | |
1265 | CSSM_ALGORITHMS algorithm; | |
1266 | uint32 keySizeInBits; | |
1267 | CSSM_KEYUSE keyUsage; | |
1268 | CSSM_KEYCLASS keyClass; | |
1269 | CSSM_RETURN crtn; | |
1270 | ||
1271 | utilGetKeyParametersFromCFDict(parameters, &algorithm, &keySizeInBits, &keyUsage, &keyClass); | |
1272 | ||
1273 | CSSM_CSP_HANDLE cspHandle = cuCspStartup(CSSM_FALSE); // TRUE => CSP, FALSE => CSPDL | |
1274 | ||
1275 | SecKeyImportExportParameters iparam; | |
1276 | memset(&iparam, 0, sizeof(iparam)); | |
1277 | iparam.keyUsage = keyUsage; | |
1278 | ||
1279 | SecExternalItemType itype; | |
1280 | switch (keyClass) { | |
1281 | case CSSM_KEYCLASS_PRIVATE_KEY: | |
1282 | itype = kSecItemTypePrivateKey; | |
1283 | break; | |
1284 | case CSSM_KEYCLASS_PUBLIC_KEY: | |
1285 | itype = kSecItemTypePublicKey; | |
1286 | break; | |
1287 | case CSSM_KEYCLASS_SESSION_KEY: | |
1288 | itype = kSecItemTypeSessionKey; | |
1289 | break; | |
1290 | default: | |
1291 | itype = kSecItemTypeUnknown; | |
1292 | break; | |
1293 | } | |
1294 | ||
1295 | CFMutableArrayRef ka = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); | |
1296 | // NOTE: if we had a way to specify values other then kSecFormatUnknown we might be more useful. | |
1297 | crtn = impExpImportRawKey(keyData, kSecFormatUnknown, itype, algorithm, NULL, cspHandle, 0, NULL, NULL, ka); | |
1298 | if (crtn == CSSM_OK && CFArrayGetCount((CFArrayRef)ka)) { | |
1299 | SecKeyRef sk = (SecKeyRef)CFArrayGetValueAtIndex((CFArrayRef)ka, 0); | |
1300 | CFRetain(sk); | |
1301 | CFRelease(ka); | |
1302 | return sk; | |
1303 | } else { | |
1304 | if (error) { | |
1305 | *error = CFErrorCreate(NULL, kCFErrorDomainOSStatus, crtn ? crtn : CSSM_ERRCODE_INTERNAL_ERROR, NULL); | |
1306 | } | |
1307 | return NULL; | |
1308 | } | |
1309 | } | |
1310 | ||
1311 | ||
1312 | void | |
1313 | SecKeyGeneratePairAsync(CFDictionaryRef parametersWhichMightBeMutiable, dispatch_queue_t deliveryQueue, | |
1314 | SecKeyGeneratePairBlock result) | |
1315 | { | |
1316 | CFDictionaryRef parameters = CFDictionaryCreateCopy(NULL, parametersWhichMightBeMutiable); | |
1317 | dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ | |
1318 | SecKeyRef publicKey = NULL; | |
1319 | SecKeyRef privateKey = NULL; | |
1320 | OSStatus status = SecKeyGeneratePair(parameters, &publicKey, &privateKey); | |
1321 | dispatch_async(deliveryQueue, ^{ | |
1322 | CFErrorRef error = NULL; | |
427c49bc | 1323 | if (errSecSuccess != status) { |
b1ab9ed8 A |
1324 | error = CFErrorCreate(NULL, kCFErrorDomainOSStatus, status, NULL); |
1325 | } | |
1326 | result(publicKey, privateKey, error); | |
1327 | if (error) { | |
1328 | CFRelease(error); | |
1329 | } | |
1330 | if (publicKey) { | |
1331 | CFRelease(publicKey); | |
1332 | } | |
1333 | if (privateKey) { | |
1334 | CFRelease(privateKey); | |
1335 | } | |
1336 | CFRelease(parameters); | |
1337 | }); | |
1338 | }); | |
1339 | } | |
1340 | ||
1341 | SecKeyRef | |
1342 | SecKeyDeriveFromPassword(CFStringRef password, CFDictionaryRef parameters, CFErrorRef *error) | |
1343 | { | |
1344 | char *thePassword = NULL; | |
1345 | CFIndex passwordLen; | |
1346 | uint8_t *salt = NULL; | |
1347 | size_t saltLen; | |
1348 | CCPBKDFAlgorithm algorithm; | |
1349 | uint rounds; | |
1350 | uint8_t *derivedKey = NULL; | |
1351 | size_t derivedKeyLen; | |
1352 | CFDataRef saltDictValue, algorithmDictValue; | |
1353 | ||
1354 | /* Pick Values from parameters */ | |
1355 | ||
1356 | if((saltDictValue = (CFDataRef) CFDictionaryGetValue(parameters, kSecAttrSalt)) == NULL) { | |
1357 | *error = CFErrorCreate(NULL, kCFErrorDomainOSStatus, errSecMissingAlgorithmParms, NULL); | |
1358 | return NULL; | |
1359 | } | |
1360 | ||
1361 | derivedKeyLen = utilGetNumberFromCFDict(parameters, kSecAttrKeySizeInBits, 128); | |
1362 | // This value come in bits but the rest of the code treats it as bytes | |
1363 | derivedKeyLen /= 8; | |
1364 | ||
1365 | algorithmDictValue = (CFDataRef) utilGetStringFromCFDict(parameters, kSecAttrPRF, kSecAttrPRFHmacAlgSHA256); | |
1366 | ||
1367 | rounds = utilGetNumberFromCFDict(parameters, kSecAttrRounds, 0); | |
1368 | ||
1369 | /* Convert any remaining parameters and get the password bytes */ | |
1370 | ||
1371 | saltLen = CFDataGetLength(saltDictValue); | |
1372 | if((salt = (uint8_t *) malloc(saltLen)) == NULL) { | |
1373 | *error = CFErrorCreate(NULL, kCFErrorDomainOSStatus, errSecAllocate, NULL); | |
1374 | return NULL; | |
1375 | } | |
1376 | ||
1377 | CFDataGetBytes(saltDictValue, CFRangeMake(0, saltLen), (UInt8 *) salt); | |
1378 | ||
1379 | passwordLen = CFStringGetMaximumSizeForEncoding(CFStringGetLength(password), kCFStringEncodingUTF8) + 1; | |
1380 | if((thePassword = (char *) malloc(passwordLen)) == NULL) { | |
1381 | free(salt); | |
1382 | *error = CFErrorCreate(NULL, kCFErrorDomainOSStatus, errSecAllocate, NULL); | |
1383 | return NULL; | |
1384 | } | |
1385 | CFStringGetBytes(password, CFRangeMake(0, CFStringGetLength(password)), kCFStringEncodingUTF8, '?', FALSE, (UInt8*)thePassword, passwordLen, &passwordLen); | |
1386 | ||
1387 | if((derivedKey = (uint8_t *) malloc(derivedKeyLen)) == NULL) { | |
1388 | free(salt); | |
1389 | bzero(thePassword, strlen(thePassword)); | |
1390 | free(thePassword); | |
1391 | *error = CFErrorCreate(NULL, kCFErrorDomainOSStatus, errSecAllocate, NULL); | |
1392 | return NULL; | |
1393 | } | |
1394 | ||
1395 | ||
1396 | if(algorithmDictValue == NULL) { | |
1397 | algorithm = kCCPRFHmacAlgSHA1; /* default */ | |
1398 | } else if(CFEqual(algorithmDictValue, kSecAttrPRFHmacAlgSHA1)) { | |
1399 | algorithm = kCCPRFHmacAlgSHA1; | |
1400 | } else if(CFEqual(algorithmDictValue, kSecAttrPRFHmacAlgSHA224)) { | |
1401 | algorithm = kCCPRFHmacAlgSHA224; | |
1402 | } else if(CFEqual(algorithmDictValue, kSecAttrPRFHmacAlgSHA256)) { | |
1403 | algorithm = kCCPRFHmacAlgSHA256; | |
1404 | } else if(CFEqual(algorithmDictValue, kSecAttrPRFHmacAlgSHA384)) { | |
1405 | algorithm = kCCPRFHmacAlgSHA384; | |
1406 | } else if(CFEqual(algorithmDictValue, kSecAttrPRFHmacAlgSHA512)) { | |
1407 | algorithm = kCCPRFHmacAlgSHA512; | |
427c49bc A |
1408 | } else { |
1409 | #warning "This else clause is here to prevent the use of unitialized variable, but really, this should return an error, without leaking." | |
1410 | algorithm = kCCPRFHmacAlgSHA1; | |
b1ab9ed8 A |
1411 | } |
1412 | ||
1413 | if(rounds == 0) { | |
1414 | rounds = 33333; // we need to pass back a consistent value since there's no way to record the round count. | |
1415 | } | |
1416 | ||
1417 | ||
1418 | if(CCKeyDerivationPBKDF(kCCPBKDF2, thePassword, passwordLen, salt, saltLen, algorithm, rounds, derivedKey, derivedKeyLen)) { | |
427c49bc | 1419 | #warning "Aren't we leaking salt and thePassword when this fail???" |
b1ab9ed8 A |
1420 | *error = CFErrorCreate(NULL, kCFErrorDomainOSStatus, errSecInternalError, NULL); |
1421 | return NULL; | |
1422 | } | |
1423 | ||
1424 | free(salt); | |
1425 | bzero(thePassword, strlen(thePassword)); | |
1426 | free(thePassword); | |
1427 | ||
1428 | CFDataRef keyData = CFDataCreate(NULL, derivedKey, derivedKeyLen); | |
1429 | bzero(derivedKey, derivedKeyLen); | |
1430 | free(derivedKey); | |
1431 | ||
1432 | SecKeyRef retval = SecKeyCreateFromData(parameters, keyData, error); | |
1433 | return retval; | |
1434 | ||
1435 | } | |
1436 | ||
1437 | CFDataRef | |
1438 | SecKeyWrapSymmetric(SecKeyRef keyToWrap, SecKeyRef wrappingKey, CFDictionaryRef parameters, CFErrorRef *error) | |
1439 | { | |
1440 | *error = CFErrorCreate(NULL, kCFErrorDomainOSStatus, errSecUnimplemented, NULL); | |
1441 | return NULL; | |
1442 | } | |
1443 | ||
1444 | SecKeyRef | |
1445 | SecKeyUnwrapSymmetric(CFDataRef *keyToUnwrap, SecKeyRef unwrappingKey, CFDictionaryRef parameters, CFErrorRef *error) | |
1446 | { | |
1447 | *error = CFErrorCreate(NULL, kCFErrorDomainOSStatus, errSecUnimplemented, NULL); | |
1448 | return NULL; | |
1449 | } | |
1450 | ||
1451 | ||
1452 | /* iOS SecKey shim functions */ | |
1453 | ||
1454 | #define MAX_DIGEST_LEN (CC_SHA512_DIGEST_LENGTH) | |
1455 | ||
1456 | /* Currently length of SHA512 oid + 1 */ | |
1457 | #define MAX_OID_LEN (10) | |
1458 | ||
1459 | #define DER_MAX_DIGEST_INFO_LEN (10 + MAX_DIGEST_LEN + MAX_OID_LEN) | |
1460 | ||
1461 | /* Encode the digestInfo header into digestInfo and return the offset from | |
1462 | digestInfo at which to put the actual digest. Returns 0 if digestInfo | |
1463 | won't fit within digestInfoLength bytes. | |
1464 | ||
1465 | 0x30, topLen, | |
1466 | 0x30, algIdLen, | |
1467 | 0x06, oid.Len, oid.Data, | |
1468 | 0x05, 0x00 | |
1469 | 0x04, digestLen | |
1470 | digestData | |
1471 | */ | |
1472 | static size_t DEREncodeDigestInfoPrefix(const SecAsn1Oid *oid, | |
1473 | size_t digestLength, | |
1474 | uint8_t *digestInfo, | |
1475 | size_t digestInfoLength) | |
1476 | { | |
1477 | size_t algIdLen = oid->Length + 4; | |
1478 | size_t topLen = algIdLen + digestLength + 4; | |
1479 | size_t totalLen = topLen + 2; | |
1480 | ||
1481 | if (totalLen > digestInfoLength) { | |
1482 | return 0; | |
1483 | } | |
1484 | ||
1485 | size_t ix = 0; | |
1486 | digestInfo[ix++] = (SEC_ASN1_SEQUENCE | SEC_ASN1_CONSTRUCTED); | |
1487 | digestInfo[ix++] = topLen; | |
1488 | digestInfo[ix++] = (SEC_ASN1_SEQUENCE | SEC_ASN1_CONSTRUCTED); | |
1489 | digestInfo[ix++] = algIdLen; | |
1490 | digestInfo[ix++] = SEC_ASN1_OBJECT_ID; | |
1491 | digestInfo[ix++] = oid->Length; | |
1492 | memcpy(&digestInfo[ix], oid->Data, oid->Length); | |
1493 | ix += oid->Length; | |
1494 | digestInfo[ix++] = SEC_ASN1_NULL; | |
1495 | digestInfo[ix++] = 0; | |
1496 | digestInfo[ix++] = SEC_ASN1_OCTET_STRING; | |
1497 | digestInfo[ix++] = digestLength; | |
1498 | ||
1499 | return ix; | |
1500 | } | |
1501 | ||
1502 | static OSStatus SecKeyGetDigestInfo(SecKeyRef key, const SecAsn1AlgId *algId, | |
1503 | const uint8_t *data, size_t dataLen, bool digestData, | |
1504 | uint8_t *digestInfo, size_t *digestInfoLen /* IN/OUT */) | |
1505 | { | |
1506 | unsigned char *(*digestFcn)(const void *, CC_LONG, unsigned char *); | |
1507 | CFIndex keyAlgID = kSecNullAlgorithmID; | |
1508 | const SecAsn1Oid *digestOid; | |
1509 | size_t digestLen; | |
1510 | size_t offset = 0; | |
1511 | ||
1512 | /* Since these oids all have the same prefix, use switch. */ | |
1513 | if ((algId->algorithm.Length == CSSMOID_RSA.Length) && | |
1514 | !memcmp(algId->algorithm.Data, CSSMOID_RSA.Data, | |
1515 | algId->algorithm.Length - 1)) { | |
1516 | keyAlgID = kSecRSAAlgorithmID; | |
1517 | switch (algId->algorithm.Data[algId->algorithm.Length - 1]) { | |
1518 | #if 0 | |
1519 | case 2: /* oidMD2WithRSA */ | |
1520 | digestFcn = CC_MD2; | |
1521 | digestLen = CC_MD2_DIGEST_LENGTH; | |
1522 | digestOid = &CSSMOID_MD2; | |
1523 | break; | |
1524 | case 3: /* oidMD4WithRSA */ | |
1525 | digestFcn = CC_MD4; | |
1526 | digestLen = CC_MD4_DIGEST_LENGTH; | |
1527 | digestOid = &CSSMOID_MD4; | |
1528 | break; | |
1529 | case 4: /* oidMD5WithRSA */ | |
1530 | digestFcn = CC_MD5; | |
1531 | digestLen = CC_MD5_DIGEST_LENGTH; | |
1532 | digestOid = &CSSMOID_MD5; | |
1533 | break; | |
1534 | #endif /* 0 */ | |
1535 | case 5: /* oidSHA1WithRSA */ | |
1536 | digestFcn = CC_SHA1; | |
1537 | digestLen = CC_SHA1_DIGEST_LENGTH; | |
1538 | digestOid = &CSSMOID_SHA1; | |
1539 | break; | |
1540 | case 11: /* oidSHA256WithRSA */ | |
1541 | digestFcn = CC_SHA256; | |
1542 | digestLen = CC_SHA256_DIGEST_LENGTH; | |
1543 | digestOid = &CSSMOID_SHA256; | |
1544 | break; | |
1545 | case 12: /* oidSHA384WithRSA */ | |
1546 | /* pkcs1 12 */ | |
1547 | digestFcn = CC_SHA384; | |
1548 | digestLen = CC_SHA384_DIGEST_LENGTH; | |
1549 | digestOid = &CSSMOID_SHA384; | |
1550 | break; | |
1551 | case 13: /* oidSHA512WithRSA */ | |
1552 | digestFcn = CC_SHA512; | |
1553 | digestLen = CC_SHA512_DIGEST_LENGTH; | |
1554 | digestOid = &CSSMOID_SHA512; | |
1555 | break; | |
1556 | case 14: /* oidSHA224WithRSA */ | |
1557 | digestFcn = CC_SHA224; | |
1558 | digestLen = CC_SHA224_DIGEST_LENGTH; | |
1559 | digestOid = &CSSMOID_SHA224; | |
1560 | break; | |
1561 | default: | |
1562 | secdebug("key", "unsupported rsa signature algorithm"); | |
1563 | return errSecUnsupportedAlgorithm; | |
1564 | } | |
1565 | } else if ((algId->algorithm.Length == CSSMOID_ECDSA_WithSHA224.Length) && | |
1566 | !memcmp(algId->algorithm.Data, CSSMOID_ECDSA_WithSHA224.Data, | |
1567 | algId->algorithm.Length - 1)) { | |
1568 | keyAlgID = kSecECDSAAlgorithmID; | |
1569 | switch (algId->algorithm.Data[algId->algorithm.Length - 1]) { | |
1570 | case 1: /* oidSHA224WithECDSA */ | |
1571 | digestFcn = CC_SHA224; | |
1572 | digestLen = CC_SHA224_DIGEST_LENGTH; | |
1573 | break; | |
1574 | case 2: /* oidSHA256WithECDSA */ | |
1575 | digestFcn = CC_SHA256; | |
1576 | digestLen = CC_SHA256_DIGEST_LENGTH; | |
1577 | break; | |
1578 | case 3: /* oidSHA384WithECDSA */ | |
1579 | /* pkcs1 12 */ | |
1580 | digestFcn = CC_SHA384; | |
1581 | digestLen = CC_SHA384_DIGEST_LENGTH; | |
1582 | break; | |
1583 | case 4: /* oidSHA512WithECDSA */ | |
1584 | digestFcn = CC_SHA512; | |
1585 | digestLen = CC_SHA512_DIGEST_LENGTH; | |
1586 | break; | |
1587 | default: | |
1588 | secdebug("key", "unsupported ecdsa signature algorithm"); | |
1589 | return errSecUnsupportedAlgorithm; | |
1590 | } | |
1591 | } else if (SecAsn1OidCompare(&algId->algorithm, &CSSMOID_ECDSA_WithSHA1)) { | |
1592 | keyAlgID = kSecECDSAAlgorithmID; | |
1593 | digestFcn = CC_SHA1; | |
1594 | digestLen = CC_SHA1_DIGEST_LENGTH; | |
1595 | } else if (SecAsn1OidCompare(&algId->algorithm, &CSSMOID_SHA1)) { | |
1596 | digestFcn = CC_SHA1; | |
1597 | digestLen = CC_SHA1_DIGEST_LENGTH; | |
1598 | digestOid = &CSSMOID_SHA1; | |
1599 | } else if ((algId->algorithm.Length == CSSMOID_SHA224.Length) && | |
1600 | !memcmp(algId->algorithm.Data, CSSMOID_SHA224.Data, algId->algorithm.Length - 1)) | |
1601 | { | |
1602 | switch (algId->algorithm.Data[algId->algorithm.Length - 1]) { | |
1603 | case 4: /* OID_SHA224 */ | |
1604 | digestFcn = CC_SHA224; | |
1605 | digestLen = CC_SHA224_DIGEST_LENGTH; | |
1606 | digestOid = &CSSMOID_SHA224; | |
1607 | break; | |
1608 | case 1: /* OID_SHA256 */ | |
1609 | digestFcn = CC_SHA256; | |
1610 | digestLen = CC_SHA256_DIGEST_LENGTH; | |
1611 | digestOid = &CSSMOID_SHA256; | |
1612 | break; | |
1613 | case 2: /* OID_SHA384 */ | |
1614 | /* pkcs1 12 */ | |
1615 | digestFcn = CC_SHA384; | |
1616 | digestLen = CC_SHA384_DIGEST_LENGTH; | |
1617 | digestOid = &CSSMOID_SHA384; | |
1618 | break; | |
1619 | case 3: /* OID_SHA512 */ | |
1620 | digestFcn = CC_SHA512; | |
1621 | digestLen = CC_SHA512_DIGEST_LENGTH; | |
1622 | digestOid = &CSSMOID_SHA512; | |
1623 | break; | |
1624 | default: | |
1625 | secdebug("key", "unsupported sha-2 signature algorithm"); | |
1626 | return errSecUnsupportedAlgorithm; | |
1627 | } | |
1628 | } else if (SecAsn1OidCompare(&algId->algorithm, &CSSMOID_MD5)) { | |
1629 | digestFcn = CC_MD5; | |
1630 | digestLen = CC_MD5_DIGEST_LENGTH; | |
1631 | digestOid = &CSSMOID_MD5; | |
1632 | } else { | |
1633 | secdebug("key", "unsupported digesting algorithm"); | |
1634 | return errSecUnsupportedAlgorithm; | |
1635 | } | |
1636 | ||
1637 | /* check key is appropriate for signature (superfluous for digest only oid) */ | |
1638 | { | |
1639 | CFIndex supportedKeyAlgID = kSecNullAlgorithmID; | |
1640 | #if TARGET_OS_EMBEDDED | |
1641 | supportedKeyAlgID = SecKeyGetAlgorithmID(key); | |
1642 | #else | |
1643 | const CSSM_KEY* temporaryKey; | |
1644 | SecKeyGetCSSMKey(key, &temporaryKey); | |
1645 | CSSM_ALGORITHMS tempAlgorithm = temporaryKey->KeyHeader.AlgorithmId; | |
1646 | if (CSSM_ALGID_RSA == tempAlgorithm) { | |
1647 | supportedKeyAlgID = kSecRSAAlgorithmID; | |
1648 | } else if (CSSM_ALGID_ECDSA == tempAlgorithm) { | |
1649 | supportedKeyAlgID = kSecECDSAAlgorithmID; | |
1650 | } | |
1651 | #endif | |
1652 | ||
1653 | if (keyAlgID == kSecNullAlgorithmID) { | |
1654 | keyAlgID = supportedKeyAlgID; | |
1655 | } | |
1656 | else if (keyAlgID != supportedKeyAlgID) { | |
1657 | return errSecUnsupportedAlgorithm; | |
1658 | } | |
1659 | } | |
1660 | ||
1661 | switch(keyAlgID) { | |
1662 | case kSecRSAAlgorithmID: | |
1663 | offset = DEREncodeDigestInfoPrefix(digestOid, digestLen, | |
1664 | digestInfo, *digestInfoLen); | |
1665 | if (!offset) | |
1666 | return errSecBufferTooSmall; | |
1667 | break; | |
1668 | case kSecDSAAlgorithmID: | |
1669 | if (digestOid != &CSSMOID_SHA1) | |
1670 | return errSecUnsupportedAlgorithm; | |
1671 | break; | |
1672 | case kSecECDSAAlgorithmID: | |
1673 | break; | |
1674 | default: | |
1675 | secdebug("key", "unsupported signature algorithm"); | |
1676 | return errSecUnsupportedAlgorithm; | |
1677 | } | |
1678 | ||
1679 | if (digestData) { | |
1680 | if(dataLen>UINT32_MAX) /* Check for overflow with CC_LONG cast */ | |
427c49bc | 1681 | return errSecParam; |
b1ab9ed8 A |
1682 | digestFcn(data, (CC_LONG)dataLen, &digestInfo[offset]); |
1683 | *digestInfoLen = offset + digestLen; | |
1684 | } else { | |
1685 | if (dataLen != digestLen) | |
427c49bc | 1686 | return errSecParam; |
b1ab9ed8 A |
1687 | memcpy(&digestInfo[offset], data, dataLen); |
1688 | *digestInfoLen = offset + dataLen; | |
1689 | } | |
1690 | ||
427c49bc | 1691 | return errSecSuccess; |
b1ab9ed8 A |
1692 | } |
1693 | ||
1694 | OSStatus SecKeyVerifyDigest( | |
1695 | SecKeyRef key, /* Private key */ | |
1696 | const SecAsn1AlgId *algId, /* algorithm oid/params */ | |
1697 | const uint8_t *digestData, /* signature over this digest */ | |
1698 | size_t digestDataLen, /* length of dataToDigest */ | |
1699 | const uint8_t *sig, /* signature to verify */ | |
1700 | size_t sigLen) /* length of sig */ | |
1701 | { | |
1702 | size_t digestInfoLength = DER_MAX_DIGEST_INFO_LEN; | |
1703 | uint8_t digestInfo[digestInfoLength]; | |
1704 | OSStatus status; | |
1705 | ||
1706 | status = SecKeyGetDigestInfo(key, algId, digestData, digestDataLen, false /* data is digest */, | |
1707 | digestInfo, &digestInfoLength); | |
1708 | if (status) | |
1709 | return status; | |
1710 | return SecKeyRawVerify(key, kSecPaddingPKCS1, | |
1711 | digestInfo, digestInfoLength, sig, sigLen); | |
1712 | } | |
1713 | ||
1714 | OSStatus SecKeySignDigest( | |
1715 | SecKeyRef key, /* Private key */ | |
1716 | const SecAsn1AlgId *algId, /* algorithm oid/params */ | |
1717 | const uint8_t *digestData, /* signature over this digest */ | |
1718 | size_t digestDataLen, /* length of digestData */ | |
1719 | uint8_t *sig, /* signature, RETURNED */ | |
1720 | size_t *sigLen) /* IN/OUT */ | |
1721 | { | |
1722 | size_t digestInfoLength = DER_MAX_DIGEST_INFO_LEN; | |
1723 | uint8_t digestInfo[digestInfoLength]; | |
1724 | OSStatus status; | |
1725 | ||
1726 | status = SecKeyGetDigestInfo(key, algId, digestData, digestDataLen, false, | |
1727 | digestInfo, &digestInfoLength); | |
1728 | if (status) | |
1729 | return status; | |
1730 | return SecKeyRawSign(key, kSecPaddingPKCS1, | |
1731 | digestInfo, digestInfoLength, sig, sigLen); | |
1732 | } | |
1733 | ||
1734 | /* It's debatable whether this belongs here or in the ssl code since the | |
1735 | curve values come from a tls related rfc4492. */ | |
1736 | SecECNamedCurve SecECKeyGetNamedCurve(SecKeyRef key) | |
1737 | { | |
1738 | try { | |
1739 | SecPointer<KeyItem> keyItem(KeyItem::required(key)); | |
1740 | switch (keyItem->key().header().LogicalKeySizeInBits) { | |
1741 | #if 0 | |
1742 | case 192: | |
1743 | return kSecECCurveSecp192r1; | |
1744 | case 224: | |
1745 | return kSecECCurveSecp224r1; | |
1746 | #endif | |
1747 | case 256: | |
1748 | return kSecECCurveSecp256r1; | |
1749 | case 384: | |
1750 | return kSecECCurveSecp384r1; | |
1751 | case 521: | |
1752 | return kSecECCurveSecp521r1; | |
1753 | } | |
1754 | } | |
1755 | catch (...) {} | |
1756 | return kSecECCurveNone; | |
1757 | } | |
1758 | ||
1759 | static inline CFDataRef _CFDataCreateReferenceFromRange(CFAllocatorRef allocator, CFDataRef sourceData, CFRange range) | |
1760 | { | |
1761 | return CFDataCreateWithBytesNoCopy(allocator, | |
1762 | CFDataGetBytePtr(sourceData) + range.location, range.length, | |
1763 | kCFAllocatorNull); | |
1764 | } | |
1765 | ||
1766 | static inline CFDataRef _CFDataCreateCopyFromRange(CFAllocatorRef allocator, CFDataRef sourceData, CFRange range) | |
1767 | { | |
1768 | return CFDataCreate(allocator, CFDataGetBytePtr(sourceData) + range.location, range.length); | |
1769 | } | |
1770 | ||
1771 | static inline bool _CFDataEquals(CFDataRef left, CFDataRef right) | |
1772 | { | |
1773 | return (left != NULL) && | |
1774 | (right != NULL) && | |
1775 | (CFDataGetLength(left) == CFDataGetLength(right)) && | |
1776 | (0 == memcmp(CFDataGetBytePtr(left), CFDataGetBytePtr(right), (size_t)CFDataGetLength(left))); | |
1777 | } | |
1778 | ||
1779 | #if ECDSA_DEBUG | |
1780 | void secdump(const unsigned char *data, unsigned long len) | |
1781 | { | |
1782 | unsigned long i; | |
1783 | char s[128]; | |
1784 | char t[32]; | |
1785 | s[0]=0; | |
1786 | for(i=0;i<len;i++) | |
1787 | { | |
1788 | if((i&0xf)==0) { | |
1789 | sprintf(t, "%04lx :", i); | |
1790 | strcat(s, t); | |
1791 | } | |
1792 | sprintf(t, " %02x", data[i]); | |
1793 | strcat(s, t); | |
1794 | if((i&0xf)==0xf) { | |
1795 | strcat(s, "\n"); | |
1796 | syslog(LOG_NOTICE, s); | |
1797 | s[0]=0; | |
1798 | } | |
1799 | } | |
1800 | strcat(s, "\n"); | |
1801 | syslog(LOG_NOTICE, s); | |
1802 | } | |
1803 | #endif | |
1804 | ||
427c49bc | 1805 | OSStatus SecKeyCopyPublicBytes(SecKeyRef key, CFDataRef* publicBytes) |
b1ab9ed8 A |
1806 | { |
1807 | CFIndex keyAlgId; | |
1808 | #if TARGET_OS_EMBEDDED | |
1809 | keyAlgId = SecKeyGetAlgorithmID(key); | |
1810 | #else | |
1811 | keyAlgId = SecKeyGetAlgorithmId(key); | |
1812 | #endif | |
427c49bc A |
1813 | |
1814 | OSStatus ecStatus = errSecParam; | |
1815 | CFDataRef tempPublicData = NULL; | |
1816 | CFDataRef headerlessPublicData = NULL; | |
1817 | CFIndex headerLength = 0; | |
1818 | const UInt8* pData_Ptr = NULL; | |
1819 | ||
1820 | if (kSecRSAAlgorithmID == keyAlgId) | |
1821 | { | |
b1ab9ed8 A |
1822 | return SecItemExport(key, kSecFormatBSAFE, 0, NULL, publicBytes); |
1823 | } | |
427c49bc A |
1824 | |
1825 | if (kSecECDSAAlgorithmID == keyAlgId) | |
1826 | { | |
1827 | // First export the key so there is access to the underlying key material | |
b1ab9ed8 | 1828 | ecStatus = SecItemExport(key, kSecFormatOpenSSL, 0, NULL, &tempPublicData); |
427c49bc A |
1829 | if(ecStatus != errSecSuccess) |
1830 | { | |
b1ab9ed8 A |
1831 | secdebug("key", "SecKeyCopyPublicBytes: SecItemExport error (%d) for ECDSA public key %p", |
1832 | ecStatus, (uintptr_t)key); | |
427c49bc A |
1833 | |
1834 | return ecStatus; | |
1835 | } | |
1836 | ||
1837 | ||
1838 | // Get a pointer to the first byte of the exported data | |
1839 | pData_Ptr = CFDataGetBytePtr(tempPublicData); | |
1840 | ||
1841 | // the first byte should be a sequence 0x30 | |
1842 | if (*pData_Ptr != 0x30) | |
1843 | { | |
1844 | secdebug("key", "SecKeyCopyPublicBytes: exported data is invalid"); | |
1845 | if (NULL != tempPublicData) | |
1846 | CFRelease(tempPublicData); | |
1847 | ||
1848 | ecStatus = errSecParam; | |
1849 | return ecStatus; | |
1850 | } | |
1851 | ||
1852 | // move past the sequence byte | |
1853 | pData_Ptr++; | |
1854 | ||
1855 | // Check to see if the high bit is set which | |
1856 | // indicates that the length will be at least | |
1857 | // two bytes. If the high bit is set then | |
1858 | // The lower seven bits specifies the number of | |
1859 | // bytes used for the length. The additonal 1 | |
1860 | // is for the current byte. Otherwise just move past the | |
1861 | // single length byte | |
1862 | pData_Ptr += (*pData_Ptr & 0x80) ? ((*pData_Ptr & 0x7F) + 1) : 1; | |
1863 | ||
1864 | // The current byte should be a sequence 0x30 | |
1865 | if (*pData_Ptr != 0x30) | |
1866 | { | |
1867 | secdebug("key", "SecKeyCopyPublicBytes: Could not find the key sequence"); | |
1868 | if (NULL != tempPublicData) | |
1869 | CFRelease(tempPublicData); | |
1870 | ||
1871 | ecStatus = errSecParam; | |
1872 | ||
1873 | return ecStatus; | |
1874 | } | |
1875 | ||
1876 | // The next bytes will always be the same | |
1877 | // 0x30 = SEQUENCE | |
1878 | // XX Length Byte | |
1879 | // 0x06 OBJECT ID | |
1880 | // 0x07 Length Byte | |
1881 | // ECDSA public KEY OID value 0x2a,0x86,0x48,0xce,0x3d,0x02,0x01 | |
1882 | // 0x06 OBJECT ID | |
1883 | // This is a total of 12 bytes | |
1884 | pData_Ptr += 12; | |
1885 | ||
1886 | // Next byte is the length of the ECDSA curve OID | |
1887 | // Move past the length byte and the curve OID | |
1888 | pData_Ptr += (((int)*pData_Ptr) + 1); | |
1889 | ||
1890 | // Should be at a BINARY String which is specifed by a 0x3 | |
1891 | if (*pData_Ptr != 0x03) | |
1892 | { | |
1893 | secdebug("key", "SecKeyCopyPublicBytes: Invalid key structure"); | |
1894 | if (NULL != tempPublicData) | |
1895 | CFRelease(tempPublicData); | |
1896 | ||
1897 | ecStatus = errSecParam; | |
1898 | ||
1899 | return ecStatus; | |
1900 | } | |
1901 | ||
1902 | // Move past the BINARY String specifier 0x03 | |
1903 | pData_Ptr++; | |
1904 | ||
1905 | ||
1906 | // Check to see if the high bit is set which | |
1907 | // indicates that the length will be at least | |
1908 | // two bytes. If the high bit is set then | |
1909 | // The lower seven bits specifies the number of | |
1910 | // bytes used for the length. The additonal 1 | |
1911 | // is for the current byte. Otherwise just move past the | |
1912 | // single length byte | |
1913 | pData_Ptr += (*pData_Ptr & 0x80) ? ((*pData_Ptr & 0x7F) + 1) : 1; | |
1914 | ||
1915 | // Move past the beginning marker for the BINARY String 0x00 | |
1916 | pData_Ptr++; | |
1917 | ||
1918 | // pData_Ptr now points to the first bytes of the key material | |
1919 | headerLength = (CFIndex)(((intptr_t)pData_Ptr) - ((intptr_t)CFDataGetBytePtr(tempPublicData))); | |
1920 | ||
1921 | headerlessPublicData = _CFDataCreateCopyFromRange(kCFAllocatorDefault, | |
1922 | tempPublicData, CFRangeMake(headerLength, CFDataGetLength(tempPublicData) - headerLength)); | |
1923 | ||
1924 | if (!headerlessPublicData) | |
1925 | { | |
1926 | printf("SecKeyCopyPublicBytes: headerlessPublicData is nil (1)\n"); | |
1927 | if (NULL != tempPublicData) | |
1928 | CFRelease(tempPublicData); | |
1929 | ||
1930 | ecStatus = errSecParam; | |
1931 | ||
1932 | return ecStatus; | |
b1ab9ed8 | 1933 | } |
427c49bc A |
1934 | |
1935 | if (publicBytes) | |
1936 | { | |
1937 | *publicBytes = headerlessPublicData; | |
1938 | } | |
b1ab9ed8 | 1939 | |
427c49bc A |
1940 | ecStatus = errSecSuccess; |
1941 | ||
1942 | if (NULL != tempPublicData) | |
1943 | CFRelease(tempPublicData); | |
1944 | ||
1945 | return ecStatus; | |
1946 | } | |
1947 | ||
1948 | return errSecParam; | |
b1ab9ed8 A |
1949 | } |
1950 | ||
427c49bc | 1951 | |
b1ab9ed8 A |
1952 | CFDataRef SecECKeyCopyPublicBits(SecKeyRef key) |
1953 | { | |
1954 | CFDataRef exportedKey; | |
427c49bc | 1955 | if(SecKeyCopyPublicBytes(key, &exportedKey) != errSecSuccess) { |
b1ab9ed8 A |
1956 | exportedKey = NULL; |
1957 | } | |
1958 | return exportedKey; | |
1959 | } | |
1960 | ||
427c49bc | 1961 | SecKeyRef SecKeyCreateFromPublicData(CFAllocatorRef allocator, CFIndex algorithmID, CFDataRef publicBytes) |
b1ab9ed8 A |
1962 | { |
1963 | SecExternalFormat externalFormat = kSecFormatOpenSSL; | |
1964 | SecExternalItemType externalItemType = kSecItemTypePublicKey; | |
1965 | CFDataRef workingData = NULL; | |
1966 | CFArrayRef outArray = NULL; | |
1967 | SecKeyRef retVal = NULL; | |
1968 | ||
1969 | if (kSecRSAAlgorithmID == algorithmID) { | |
1970 | /* | |
1971 | * kSecFormatBSAFE uses the original PKCS#1 definition: | |
1972 | * RSAPublicKey ::= SEQUENCE { | |
1973 | * modulus INTEGER, -- n | |
1974 | * publicExponent INTEGER -- e | |
1975 | * } | |
1976 | * kSecFormatOpenSSL uses different ASN.1 encoding. | |
1977 | */ | |
1978 | externalFormat = kSecFormatBSAFE; | |
1979 | workingData = _CFDataCreateReferenceFromRange(kCFAllocatorDefault, publicBytes, CFRangeMake(0, CFDataGetLength(publicBytes))); | |
1980 | } else if (kSecECDSAAlgorithmID == algorithmID) { | |
1981 | CFMutableDataRef tempData; | |
1982 | uint8 headerBytes[] = { 0x30,0x59,0x30,0x13,0x06,0x07,0x2a,0x86, | |
1983 | 0x48,0xce,0x3d,0x02,0x01,0x06,0x08,0x2a, | |
1984 | 0x86,0x48,0xce,0x3d,0x03,0x01,0x07,0x03, | |
1985 | 0x42,0x00 }; | |
1986 | ||
1987 | /* FIXME: this code only handles one specific curve type; need to expand this */ | |
1988 | tempData = CFDataCreateMutable(kCFAllocatorDefault, 0); | |
1989 | CFDataAppendBytes(tempData, headerBytes, sizeof(headerBytes)); | |
1990 | CFDataAppendBytes(tempData, CFDataGetBytePtr(publicBytes), CFDataGetLength(publicBytes)); | |
1991 | workingData = tempData; | |
1992 | } | |
1993 | if(SecItemImport(workingData, NULL, &externalFormat, &externalItemType, 0, NULL, NULL, &outArray) != errSecSuccess) { | |
1994 | goto cleanup; | |
1995 | } | |
1996 | if(!outArray || CFArrayGetCount(outArray) == 0) { | |
1997 | goto cleanup; | |
1998 | } | |
1999 | retVal = (SecKeyRef)CFArrayGetValueAtIndex(outArray, 0); | |
2000 | CFRetain(retVal); | |
2001 | ||
2002 | cleanup: | |
2003 | if(workingData) CFRelease(workingData); | |
2004 | if(outArray) CFRelease(outArray); | |
2005 | return retVal; | |
2006 | } | |
2007 | ||
2008 | SecKeyRef SecKeyCreateRSAPublicKey(CFAllocatorRef allocator, | |
2009 | const uint8_t *keyData, CFIndex keyDataLength, | |
2010 | SecKeyEncoding encoding) | |
2011 | { | |
2012 | CFDataRef pubKeyData = NULL; | |
2013 | if(kSecKeyEncodingPkcs1 == encoding) { | |
2014 | /* DER-encoded according to PKCS1. */ | |
2015 | pubKeyData = CFDataCreate(allocator, keyData, keyDataLength); | |
2016 | ||
2017 | } else if(kSecKeyEncodingApplePkcs1 == encoding) { | |
2018 | /* DER-encoded according to PKCS1 with Apple Extensions. */ | |
2019 | /* FIXME: need to actually handle extensions */ | |
2020 | return NULL; | |
2021 | ||
2022 | } else if(kSecKeyEncodingRSAPublicParams == encoding) { | |
2023 | /* SecRSAPublicKeyParams format; we must encode as PKCS1. */ | |
b1ab9ed8 A |
2024 | SecRSAPublicKeyParams *params = (SecRSAPublicKeyParams *)keyData; |
2025 | DERSize m_size = params->modulusLength; | |
2026 | DERSize e_size = params->exponentLength; | |
2027 | const DERSize seq_size = DERLengthOfItem(ASN1_INTEGER, m_size) + | |
2028 | DERLengthOfItem(ASN1_INTEGER, e_size); | |
2029 | const DERSize result_size = DERLengthOfItem(ASN1_SEQUENCE, seq_size); | |
2030 | DERSize r_size, remaining_size = result_size; | |
2031 | DERReturn drtn; | |
2032 | ||
2033 | CFMutableDataRef pkcs1 = CFDataCreateMutable(allocator, result_size); | |
2034 | if (pkcs1 == NULL) { | |
2035 | return NULL; | |
2036 | } | |
2037 | CFDataSetLength(pkcs1, result_size); | |
2038 | uint8_t *bytes = CFDataGetMutableBytePtr(pkcs1); | |
2039 | ||
2040 | *bytes++ = ASN1_CONSTR_SEQUENCE; | |
2041 | remaining_size--; | |
2042 | r_size = 4; | |
2043 | drtn = DEREncodeLength(seq_size, bytes, &r_size); | |
2044 | if (r_size <= remaining_size) { | |
2045 | bytes += r_size; | |
2046 | remaining_size -= r_size; | |
2047 | } | |
2048 | r_size = remaining_size; | |
2049 | drtn = DEREncodeItem(ASN1_INTEGER, m_size, (const DERByte *)params->modulus, (DERByte *)bytes, &r_size); | |
2050 | if (r_size <= remaining_size) { | |
2051 | bytes += r_size; | |
2052 | remaining_size -= r_size; | |
2053 | } | |
2054 | r_size = remaining_size; | |
2055 | drtn = DEREncodeItem(ASN1_INTEGER, e_size, (const DERByte *)params->exponent, (DERByte *)bytes, &r_size); | |
2056 | ||
2057 | pubKeyData = pkcs1; | |
2058 | ||
2059 | } else { | |
2060 | /* unsupported encoding */ | |
2061 | return NULL; | |
2062 | } | |
427c49bc | 2063 | SecKeyRef publicKey = SecKeyCreateFromPublicData(allocator, kSecRSAAlgorithmID, pubKeyData); |
b1ab9ed8 A |
2064 | CFRelease(pubKeyData); |
2065 | return publicKey; | |
2066 | } | |
2067 | ||
2068 | #if !TARGET_OS_EMBEDDED | |
2069 | // | |
2070 | // Given a CSSM public key, copy its modulus and/or exponent data. | |
2071 | // Caller is responsible for releasing the returned CFDataRefs. | |
2072 | // | |
427c49bc | 2073 | static |
b1ab9ed8 A |
2074 | OSStatus _SecKeyCopyRSAPublicModulusAndExponent(SecKeyRef key, CFDataRef *modulus, CFDataRef *exponent) |
2075 | { | |
2076 | const CSSM_KEY *pubKey; | |
2077 | const CSSM_KEYHEADER *hdr; | |
2078 | CSSM_DATA pubKeyBlob; | |
2079 | OSStatus result; | |
2080 | ||
2081 | result = SecKeyGetCSSMKey(key, &pubKey); | |
427c49bc | 2082 | if(result != errSecSuccess) { |
b1ab9ed8 A |
2083 | return result; |
2084 | } | |
2085 | hdr = &pubKey->KeyHeader; | |
2086 | if(hdr->KeyClass != CSSM_KEYCLASS_PUBLIC_KEY) { | |
2087 | return errSSLInternal; | |
2088 | } | |
2089 | if(hdr->AlgorithmId != CSSM_ALGID_RSA) { | |
2090 | return errSSLInternal; | |
2091 | } | |
2092 | switch(hdr->BlobType) { | |
2093 | case CSSM_KEYBLOB_RAW: | |
2094 | pubKeyBlob.Length = pubKey->KeyData.Length; | |
2095 | pubKeyBlob.Data = pubKey->KeyData.Data; | |
2096 | break; | |
2097 | case CSSM_KEYBLOB_REFERENCE: | |
2098 | // FIXME: currently SSL only uses raw public keys, obtained from the CL | |
2099 | default: | |
2100 | return errSSLInternal; | |
2101 | } | |
2102 | assert(hdr->BlobType == CSSM_KEYBLOB_RAW); | |
2103 | // at this point we should have a PKCS1-encoded blob | |
2104 | ||
2105 | DERItem keyItem = {(DERByte *)pubKeyBlob.Data, pubKeyBlob.Length}; | |
2106 | DERRSAPubKeyPKCS1 decodedKey; | |
2107 | if(DERParseSequence(&keyItem, DERNumRSAPubKeyPKCS1ItemSpecs, | |
2108 | DERRSAPubKeyPKCS1ItemSpecs, | |
2109 | &decodedKey, sizeof(decodedKey)) != DR_Success) { | |
2110 | return errSecDecode; | |
2111 | } | |
2112 | if(modulus) { | |
2113 | *modulus = CFDataCreate(kCFAllocatorDefault, decodedKey.modulus.data, decodedKey.modulus.length); | |
2114 | if(*modulus == NULL) { | |
2115 | return errSecDecode; | |
2116 | } | |
2117 | } | |
2118 | if(exponent) { | |
2119 | *exponent = CFDataCreate(kCFAllocatorDefault, decodedKey.pubExponent.data, decodedKey.pubExponent.length); | |
2120 | if(*exponent == NULL) { | |
2121 | return errSecDecode; | |
2122 | } | |
2123 | } | |
2124 | ||
2125 | return errSecSuccess; | |
2126 | } | |
2127 | #endif /* !TARGET_OS_EMBEDDED */ | |
2128 | ||
2129 | CFDataRef SecKeyCopyModulus(SecKeyRef key) | |
2130 | { | |
2131 | #if TARGET_OS_EMBEDDED | |
2132 | ccrsa_pub_ctx_t pubkey; | |
2133 | pubkey.pub = key->key; | |
2134 | ||
2135 | size_t m_size = ccn_write_uint_size(ccrsa_ctx_n(pubkey), ccrsa_ctx_m(pubkey)); | |
2136 | ||
2137 | CFAllocatorRef allocator = CFGetAllocator(key); | |
2138 | CFMutableDataRef modulusData = CFDataCreateMutable(allocator, m_size); | |
2139 | ||
2140 | if (modulusData == NULL) | |
2141 | return NULL; | |
2142 | ||
2143 | CFDataSetLength(modulusData, m_size); | |
2144 | ||
2145 | ccn_write_uint(ccrsa_ctx_n(pubkey), ccrsa_ctx_m(pubkey), m_size, CFDataGetMutableBytePtr(modulusData)); | |
2146 | #else | |
2147 | CFDataRef modulusData; | |
2148 | OSStatus status = _SecKeyCopyRSAPublicModulusAndExponent(key, &modulusData, NULL); | |
2149 | if(status != errSecSuccess) { | |
2150 | modulusData = NULL; | |
2151 | } | |
2152 | #endif | |
2153 | ||
2154 | return modulusData; | |
2155 | } | |
2156 | ||
2157 | CFDataRef SecKeyCopyExponent(SecKeyRef key) | |
2158 | { | |
2159 | #if TARGET_OS_EMBEDDED | |
2160 | ccrsa_pub_ctx_t pubkey; | |
2161 | pubkey.pub = key->key; | |
2162 | ||
2163 | size_t e_size = ccn_write_uint_size(ccrsa_ctx_n(pubkey), ccrsa_ctx_e(pubkey)); | |
2164 | ||
2165 | CFAllocatorRef allocator = CFGetAllocator(key); | |
2166 | CFMutableDataRef exponentData = CFDataCreateMutable(allocator, e_size); | |
2167 | ||
2168 | if (exponentData == NULL) | |
2169 | return NULL; | |
2170 | ||
2171 | CFDataSetLength(exponentData, e_size); | |
2172 | ||
2173 | ccn_write_uint(ccrsa_ctx_n(pubkey), ccrsa_ctx_m(pubkey), e_size, CFDataGetMutableBytePtr(exponentData)); | |
2174 | #else | |
2175 | CFDataRef exponentData; | |
2176 | OSStatus status = _SecKeyCopyRSAPublicModulusAndExponent(key, NULL, &exponentData); | |
2177 | if(status != errSecSuccess) { | |
2178 | exponentData = NULL; | |
2179 | } | |
2180 | #endif | |
2181 | ||
2182 | return exponentData; | |
2183 | } | |
2184 | ||
427c49bc A |
2185 | SecKeyRef SecKeyCreatePublicFromPrivate(SecKeyRef privateKey) { |
2186 | OSStatus status = errSecParam; | |
2187 | ||
2188 | CFDataRef serializedPublic = NULL; | |
2189 | ||
2190 | status = SecKeyCopyPublicBytes(privateKey, &serializedPublic); | |
2191 | if ((status == errSecSuccess) && (serializedPublic != NULL)) { | |
2192 | SecKeyRef publicKeyRef = SecKeyCreateFromPublicData(kCFAllocatorDefault, SecKeyGetAlgorithmId(privateKey), serializedPublic); | |
2193 | CFRelease(serializedPublic); | |
2194 | if (publicKeyRef != NULL) { | |
2195 | return publicKeyRef; | |
2196 | } | |
2197 | } | |
2198 | ||
2199 | const void *keys[] = { kSecClass, kSecValueRef, kSecReturnAttributes }; | |
2200 | const void *values[] = { kSecClassKey, privateKey, kCFBooleanTrue }; | |
2201 | CFDictionaryRef query= CFDictionaryCreate(NULL, keys, values, | |
2202 | (sizeof(values) / sizeof(*values)), | |
2203 | &kCFTypeDictionaryKeyCallBacks, | |
2204 | &kCFTypeDictionaryValueCallBacks); | |
2205 | CFTypeRef foundItem = NULL; | |
2206 | status = SecItemCopyMatching(query, &foundItem); | |
2207 | ||
2208 | if (status == errSecSuccess) { | |
2209 | if (CFGetTypeID(foundItem) == CFDictionaryGetTypeID()) { | |
2210 | CFMutableDictionaryRef query2 = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); | |
2211 | CFDictionaryAddValue(query2, kSecClass, kSecClassKey); | |
2212 | CFDictionaryAddValue(query2, kSecAttrKeyClass, kSecAttrKeyClassPublic); | |
2213 | CFDictionaryAddValue(query2, kSecAttrApplicationLabel, CFDictionaryGetValue((CFDictionaryRef)foundItem, kSecAttrApplicationLabel)); | |
2214 | CFDictionaryAddValue(query2, kSecReturnRef, kCFBooleanTrue); | |
2215 | ||
2216 | CFTypeRef foundKey = NULL; | |
2217 | status = SecItemCopyMatching(query2, &foundKey); | |
2218 | if (status == errSecSuccess) { | |
2219 | if (CFGetTypeID(foundKey) == SecKeyGetTypeID()) { | |
2220 | CFRelease(query); | |
2221 | CFRelease(query2); | |
2222 | CFRelease(foundItem); | |
2223 | return (SecKeyRef)foundKey; | |
2224 | } else { | |
2225 | status = errSecItemNotFound; | |
2226 | } | |
2227 | } | |
2228 | CFRelease(query2); | |
2229 | ||
2230 | } else { | |
2231 | status = errSecItemNotFound; | |
2232 | } | |
2233 | CFRelease(foundItem); | |
2234 | } | |
2235 | ||
2236 | CFRelease(query); | |
2237 | return NULL; | |
2238 | } | |
2239 |