]>
Commit | Line | Data |
---|---|---|
b1ab9ed8 A |
1 | /* |
2 | * Copyright (c) 2006-2011 Apple Inc. All Rights Reserved. | |
427c49bc | 3 | * |
b1ab9ed8 | 4 | * @APPLE_LICENSE_HEADER_START@ |
427c49bc | 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. | |
427c49bc | 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. | |
427c49bc | 20 | * |
b1ab9ed8 A |
21 | * @APPLE_LICENSE_HEADER_END@ |
22 | */ | |
23 | ||
427c49bc | 24 | /* |
b1ab9ed8 A |
25 | * SecKey.c - CoreFoundation based key object |
26 | */ | |
27 | ||
28 | ||
29 | #include <Security/SecKeyInternal.h> | |
30 | #include <Security/SecItem.h> | |
31 | #include <Security/SecItemPriv.h> | |
32 | #include <Security/SecFramework.h> | |
33 | ||
427c49bc A |
34 | #include <utilities/SecIOFormat.h> |
35 | ||
36 | #include <utilities/SecCFWrappers.h> | |
37 | ||
b1ab9ed8 A |
38 | #include "SecRSAKeyPriv.h" |
39 | #include "SecECKey.h" | |
40 | #include "SecBasePriv.h" | |
41 | ||
42 | #include <CoreFoundation/CFNumber.h> | |
43 | #include <CoreFoundation/CFString.h> | |
427c49bc | 44 | #include <Security/SecBase.h> |
b1ab9ed8 A |
45 | #include <pthread.h> |
46 | #include <string.h> | |
47 | #include <AssertMacros.h> | |
427c49bc A |
48 | #include <utilities/debugging.h> |
49 | #include <utilities/SecCFError.h> | |
b1ab9ed8 A |
50 | #include <CommonCrypto/CommonDigest.h> |
51 | #include <Security/SecAsn1Coder.h> | |
52 | #include <Security/oidsalg.h> | |
53 | #include <Security/SecInternal.h> | |
54 | #include <Security/SecRandom.h> | |
55 | #include <corecrypto/ccrng_system.h> | |
56 | #include <asl.h> | |
427c49bc | 57 | #include <stdlib.h> |
b1ab9ed8 A |
58 | |
59 | static pthread_once_t kSecKeyRegisterClass = PTHREAD_ONCE_INIT; | |
60 | static CFTypeID kSecKeyTypeID = _kCFRuntimeNotATypeID; | |
61 | ||
62 | /* Forward declartions of static functions. */ | |
427c49bc | 63 | static CFStringRef SecKeyCopyDescription(CFTypeRef cf); |
b1ab9ed8 A |
64 | static void SecKeyDestroy(CFTypeRef cf); |
65 | ||
66 | /* Static functions. */ | |
67 | #define MAX_DIGEST_LEN (CC_SHA512_DIGEST_LENGTH) | |
68 | ||
69 | /* Currently length of SHA512 oid + 1 */ | |
70 | #define MAX_OID_LEN (10) | |
71 | ||
72 | #define DER_MAX_DIGEST_INFO_LEN (10 + MAX_DIGEST_LEN + MAX_OID_LEN) | |
73 | ||
74 | /* Encode the digestInfo header into digestInfo and return the offset from | |
427c49bc A |
75 | digestInfo at which to put the actual digest. Returns 0 if digestInfo |
76 | won't fit within digestInfoLength bytes. | |
77 | ||
78 | 0x30, topLen, | |
79 | 0x30, algIdLen, | |
80 | 0x06, oid.Len, oid.Data, | |
81 | 0x05, 0x00 | |
82 | 0x04, digestLen | |
83 | digestData | |
84 | */ | |
85 | ||
b1ab9ed8 | 86 | static size_t DEREncodeDigestInfoPrefix(const SecAsn1Oid *oid, |
427c49bc | 87 | size_t digestLength, uint8_t *digestInfo, size_t digestInfoLength) { |
b1ab9ed8 A |
88 | size_t algIdLen = oid->Length + 4; |
89 | size_t topLen = algIdLen + digestLength + 4; | |
90 | size_t totalLen = topLen + 2; | |
427c49bc | 91 | |
b1ab9ed8 A |
92 | if (totalLen > digestInfoLength) { |
93 | return 0; | |
94 | } | |
427c49bc | 95 | |
b1ab9ed8 A |
96 | size_t ix = 0; |
97 | digestInfo[ix++] = (SEC_ASN1_SEQUENCE | SEC_ASN1_CONSTRUCTED); | |
98 | digestInfo[ix++] = topLen; | |
99 | digestInfo[ix++] = (SEC_ASN1_SEQUENCE | SEC_ASN1_CONSTRUCTED); | |
100 | digestInfo[ix++] = algIdLen; | |
101 | digestInfo[ix++] = SEC_ASN1_OBJECT_ID; | |
102 | digestInfo[ix++] = oid->Length; | |
103 | memcpy(&digestInfo[ix], oid->Data, oid->Length); | |
104 | ix += oid->Length; | |
105 | digestInfo[ix++] = SEC_ASN1_NULL; | |
106 | digestInfo[ix++] = 0; | |
107 | digestInfo[ix++] = SEC_ASN1_OCTET_STRING; | |
108 | digestInfo[ix++] = digestLength; | |
427c49bc | 109 | |
b1ab9ed8 A |
110 | return ix; |
111 | } | |
112 | ||
113 | static struct ccrng_system_state ccrng_system_state_seckey; | |
114 | ||
115 | static void register_algs(void) { | |
116 | ccrng_seckey = (struct ccrng_state *)&ccrng_system_state_seckey; | |
117 | ccrng_system_init(&ccrng_system_state_seckey); | |
118 | } | |
119 | ||
120 | ||
427c49bc A |
121 | static CFDataRef SecKeyCopyPublicKeyHash(SecKeyRef key) |
122 | { | |
123 | CFDataRef pubKeyDigest = NULL, pubKeyBlob = NULL; | |
124 | ||
125 | /* encode the public key. */ | |
126 | require_noerr(SecKeyCopyPublicBytes(key, &pubKeyBlob), errOut); | |
127 | require(pubKeyBlob, errOut); | |
128 | ||
129 | /* Calculate the digest of the public key. */ | |
130 | require(pubKeyDigest = SecSHA1DigestCreate(CFGetAllocator(key), | |
131 | CFDataGetBytePtr(pubKeyBlob), CFDataGetLength(pubKeyBlob)), | |
132 | errOut); | |
133 | errOut: | |
134 | CFReleaseNull(pubKeyBlob); | |
135 | return pubKeyDigest; | |
136 | } | |
137 | ||
138 | ||
b1ab9ed8 A |
139 | /* |
140 | */ | |
427c49bc A |
141 | static CF_RETURNS_RETAINED CFDictionaryRef SecKeyGenerateAttributeDictionaryFor(SecKeyRef key, |
142 | CFTypeRef keyType, | |
143 | CFDataRef privateBlob) | |
b1ab9ed8 A |
144 | { |
145 | CFAllocatorRef allocator = CFGetAllocator(key); | |
146 | DICT_DECLARE(25); | |
147 | CFDataRef pubKeyDigest = NULL, pubKeyBlob = NULL; | |
148 | CFDictionaryRef dict = NULL; | |
427c49bc | 149 | |
b1ab9ed8 A |
150 | size_t sizeValue = SecKeyGetSize(key, kSecKeyKeySizeInBits); |
151 | CFNumberRef sizeInBits = CFNumberCreate(allocator, kCFNumberLongType, &sizeValue); | |
427c49bc | 152 | |
b1ab9ed8 A |
153 | /* encode the public key. */ |
154 | require_noerr(SecKeyCopyPublicBytes(key, &pubKeyBlob), errOut); | |
155 | require(pubKeyBlob, errOut); | |
427c49bc | 156 | |
b1ab9ed8 A |
157 | /* Calculate the digest of the public key. */ |
158 | require(pubKeyDigest = SecSHA1DigestCreate(allocator, | |
159 | CFDataGetBytePtr(pubKeyBlob), CFDataGetLength(pubKeyBlob)), | |
160 | errOut); | |
427c49bc | 161 | |
b1ab9ed8 A |
162 | DICT_ADDPAIR(kSecClass, kSecClassKey); |
163 | DICT_ADDPAIR(kSecAttrKeyClass, privateBlob ? kSecAttrKeyClassPrivate : kSecAttrKeyClassPublic); | |
164 | DICT_ADDPAIR(kSecAttrApplicationLabel, pubKeyDigest); | |
165 | DICT_ADDPAIR(kSecAttrIsPermanent, kCFBooleanTrue); | |
166 | DICT_ADDPAIR(kSecAttrIsPrivate, kCFBooleanTrue); | |
167 | DICT_ADDPAIR(kSecAttrIsModifiable, kCFBooleanTrue); | |
168 | DICT_ADDPAIR(kSecAttrKeyType, keyType); | |
169 | DICT_ADDPAIR(kSecAttrKeySizeInBits, sizeInBits); | |
170 | DICT_ADDPAIR(kSecAttrEffectiveKeySize, sizeInBits); | |
171 | DICT_ADDPAIR(kSecAttrIsSensitive, kCFBooleanFalse); | |
172 | DICT_ADDPAIR(kSecAttrWasAlwaysSensitive, kCFBooleanFalse); | |
173 | DICT_ADDPAIR(kSecAttrIsExtractable, kCFBooleanTrue); | |
174 | DICT_ADDPAIR(kSecAttrWasNeverExtractable, kCFBooleanFalse); | |
175 | DICT_ADDPAIR(kSecAttrCanEncrypt, kCFBooleanFalse); | |
176 | DICT_ADDPAIR(kSecAttrCanDecrypt, kCFBooleanTrue); | |
177 | DICT_ADDPAIR(kSecAttrCanDerive, kCFBooleanTrue); | |
178 | DICT_ADDPAIR(kSecAttrCanSign, kCFBooleanTrue); | |
179 | DICT_ADDPAIR(kSecAttrCanVerify, kCFBooleanFalse); | |
180 | DICT_ADDPAIR(kSecAttrCanSignRecover, kCFBooleanFalse); | |
181 | DICT_ADDPAIR(kSecAttrCanVerifyRecover, kCFBooleanFalse); | |
182 | DICT_ADDPAIR(kSecAttrCanWrap, kCFBooleanFalse); | |
183 | DICT_ADDPAIR(kSecAttrCanUnwrap, kCFBooleanTrue); | |
184 | DICT_ADDPAIR(kSecValueData, privateBlob ? privateBlob : pubKeyBlob); | |
185 | dict = DICT_CREATE(allocator); | |
427c49bc | 186 | |
b1ab9ed8 A |
187 | errOut: |
188 | // @@@ Zero out key material. | |
189 | CFReleaseSafe(pubKeyDigest); | |
190 | CFReleaseSafe(pubKeyBlob); | |
191 | CFReleaseSafe(sizeInBits); | |
427c49bc | 192 | |
b1ab9ed8 A |
193 | return dict; |
194 | } | |
195 | ||
196 | CFDictionaryRef SecKeyGeneratePrivateAttributeDictionary(SecKeyRef key, | |
197 | CFTypeRef keyType, | |
198 | CFDataRef privateBlob) | |
199 | { | |
200 | return SecKeyGenerateAttributeDictionaryFor(key, keyType, privateBlob); | |
201 | } | |
202 | ||
203 | CFDictionaryRef SecKeyGeneratePublicAttributeDictionary(SecKeyRef key, CFTypeRef keyType) | |
204 | { | |
205 | return SecKeyGenerateAttributeDictionaryFor(key, keyType, NULL); | |
206 | } | |
207 | ||
208 | /* | |
209 | */ | |
210 | ||
427c49bc | 211 | static CFStringRef SecKeyCopyDescription(CFTypeRef cf) { |
b1ab9ed8 | 212 | SecKeyRef key = (SecKeyRef)cf; |
427c49bc A |
213 | |
214 | if(key->key_class->describe) | |
215 | return key->key_class->describe(key); | |
216 | else | |
217 | return CFStringCreateWithFormat(kCFAllocatorDefault,NULL,CFSTR("<SecKeyRef: %p>"), key); | |
b1ab9ed8 A |
218 | } |
219 | ||
220 | static void SecKeyDestroy(CFTypeRef cf) { | |
221 | SecKeyRef key = (SecKeyRef)cf; | |
222 | if (key->key_class->destroy) | |
223 | key->key_class->destroy(key); | |
224 | } | |
225 | ||
427c49bc A |
226 | static Boolean SecKeyEqual(CFTypeRef cf1, CFTypeRef cf2) |
227 | { | |
b1ab9ed8 A |
228 | SecKeyRef key1 = (SecKeyRef)cf1; |
229 | SecKeyRef key2 = (SecKeyRef)cf2; | |
230 | if (key1 == key2) | |
231 | return true; | |
232 | if (!key2 || key1->key_class != key2->key_class) | |
233 | return false; | |
234 | if (key1->key_class->extraBytes) | |
235 | return !memcmp(key1->key, key2->key, key1->key_class->extraBytes); | |
427c49bc | 236 | |
b1ab9ed8 A |
237 | /* TODO: Won't work when we get reference keys. */ |
238 | CFDictionaryRef d1, d2; | |
239 | d1 = SecKeyCopyAttributeDictionary(key1); | |
240 | d2 = SecKeyCopyAttributeDictionary(key2); | |
241 | Boolean result = CFEqual(d1, d2); | |
242 | CFReleaseSafe(d1); | |
243 | CFReleaseSafe(d2); | |
244 | return result; | |
245 | } | |
246 | ||
247 | static void SecKeyRegisterClass(void) { | |
248 | static const CFRuntimeClass kSecKeyClass = { | |
249 | 0, /* version */ | |
250 | "SecKey", /* class name */ | |
251 | NULL, /* init */ | |
252 | NULL, /* copy */ | |
253 | SecKeyDestroy, /* dealloc */ | |
254 | SecKeyEqual, /* equal */ | |
255 | NULL, /* hash */ | |
256 | NULL, /* copyFormattingDesc */ | |
427c49bc | 257 | SecKeyCopyDescription /* copyDebugDesc */ |
b1ab9ed8 | 258 | }; |
427c49bc | 259 | |
b1ab9ed8 A |
260 | kSecKeyTypeID = _CFRuntimeRegisterClass(&kSecKeyClass); |
261 | register_algs(); | |
262 | } | |
263 | ||
264 | /* Public API functions. */ | |
265 | CFTypeID SecKeyGetTypeID(void) { | |
266 | pthread_once(&kSecKeyRegisterClass, SecKeyRegisterClass); | |
267 | return kSecKeyTypeID; | |
268 | } | |
269 | ||
270 | static bool getBoolForKey(CFDictionaryRef dict, CFStringRef key, bool default_value) { | |
271 | CFTypeRef value = CFDictionaryGetValue(dict, key); | |
272 | if (value) { | |
273 | if (CFGetTypeID(value) == CFBooleanGetTypeID()) { | |
274 | return CFBooleanGetValue(value); | |
275 | } else { | |
276 | secwarning("Value %@ for key %@ is not bool", value, key); | |
277 | } | |
278 | } | |
427c49bc | 279 | |
b1ab9ed8 A |
280 | return default_value; |
281 | } | |
282 | ||
283 | static OSStatus add_ref(CFTypeRef item, CFMutableDictionaryRef dict) { | |
284 | CFDictionarySetValue(dict, kSecValueRef, item); | |
285 | return SecItemAdd(dict, NULL); | |
286 | } | |
287 | ||
288 | static void merge_params_applier(const void *key, const void *value, | |
289 | void *context) { | |
290 | CFMutableDictionaryRef result = (CFMutableDictionaryRef)context; | |
291 | CFDictionaryAddValue(result, key, value); | |
292 | } | |
293 | ||
294 | /* Create a mutable dictionary that is based on the subdictionary for key | |
295 | with any attributes from the top level dict merged in. */ | |
296 | static CFMutableDictionaryRef merge_params(CFDictionaryRef dict, | |
297 | CFStringRef key) { | |
298 | CFDictionaryRef subdict = CFDictionaryGetValue(dict, key); | |
299 | CFMutableDictionaryRef result; | |
427c49bc | 300 | |
b1ab9ed8 A |
301 | if (subdict) { |
302 | result = CFDictionaryCreateMutableCopy(NULL, 0, subdict); | |
303 | /* Add everything in dict not already in result to result. */ | |
304 | CFDictionaryApplyFunction(dict, merge_params_applier, result); | |
305 | } else { | |
306 | result = CFDictionaryCreateMutableCopy(NULL, 0, dict); | |
307 | } | |
427c49bc | 308 | |
b1ab9ed8 A |
309 | /* Remove values that only belong in the top level dict. */ |
310 | CFDictionaryRemoveValue(result, kSecPublicKeyAttrs); | |
311 | CFDictionaryRemoveValue(result, kSecPrivateKeyAttrs); | |
312 | CFDictionaryRemoveValue(result, kSecAttrKeyType); | |
313 | CFDictionaryRemoveValue(result, kSecAttrKeySizeInBits); | |
427c49bc | 314 | |
b1ab9ed8 A |
315 | return result; |
316 | } | |
317 | ||
318 | /* Generate a private/public keypair. */ | |
319 | OSStatus SecKeyGeneratePair(CFDictionaryRef parameters, | |
320 | SecKeyRef *publicKey, SecKeyRef *privateKey) { | |
321 | OSStatus result = errSecUnsupportedAlgorithm; | |
322 | SecKeyRef privKey = NULL; | |
323 | SecKeyRef pubKey = NULL; | |
324 | CFMutableDictionaryRef pubParams = merge_params(parameters, kSecPublicKeyAttrs), | |
427c49bc | 325 | privParams = merge_params(parameters, kSecPrivateKeyAttrs); |
b1ab9ed8 | 326 | CFStringRef ktype = CFDictionaryGetValue(parameters, kSecAttrKeyType); |
427c49bc | 327 | |
b1ab9ed8 | 328 | require(ktype, errOut); |
427c49bc | 329 | |
b1ab9ed8 A |
330 | if (CFEqual(ktype, kSecAttrKeyTypeEC)) { |
331 | result = SecECKeyGeneratePair(parameters, &pubKey, &privKey); | |
332 | } else if (CFEqual(ktype, kSecAttrKeyTypeRSA)) { | |
333 | result = SecRSAKeyGeneratePair(parameters, &pubKey, &privKey); | |
334 | } | |
427c49bc | 335 | |
b1ab9ed8 | 336 | require_noerr(result, errOut); |
427c49bc | 337 | |
b1ab9ed8 A |
338 | /* Store the keys in the keychain if they are marked as permanent. */ |
339 | if (getBoolForKey(pubParams, kSecAttrIsPermanent, false)) { | |
427c49bc | 340 | require_noerr_quiet(result = add_ref(pubKey, pubParams), errOut); |
b1ab9ed8 A |
341 | } |
342 | if (getBoolForKey(privParams, kSecAttrIsPermanent, false)) { | |
427c49bc | 343 | require_noerr_quiet(result = add_ref(privKey, privParams), errOut); |
b1ab9ed8 | 344 | } |
427c49bc | 345 | |
b1ab9ed8 A |
346 | if (publicKey) { |
347 | *publicKey = pubKey; | |
348 | pubKey = NULL; | |
349 | } | |
350 | if (privateKey) { | |
351 | *privateKey = privKey; | |
352 | privKey = NULL; | |
353 | } | |
427c49bc | 354 | |
b1ab9ed8 A |
355 | errOut: |
356 | CFReleaseSafe(pubParams); | |
357 | CFReleaseSafe(privParams); | |
358 | CFReleaseSafe(pubKey); | |
359 | CFReleaseSafe(privKey); | |
427c49bc A |
360 | |
361 | return result; | |
362 | } | |
b1ab9ed8 | 363 | |
427c49bc A |
364 | SecKeyRef SecKeyCreatePublicFromPrivate(SecKeyRef privateKey) { |
365 | CFDataRef serializedPublic = NULL; | |
366 | SecKeyRef result = NULL; | |
367 | ||
368 | require_noerr_quiet(SecKeyCopyPublicBytes(privateKey, &serializedPublic), fail); | |
369 | require_quiet(serializedPublic, fail); | |
370 | ||
371 | result = SecKeyCreateFromPublicData(kCFAllocatorDefault, SecKeyGetAlgorithmID(privateKey), serializedPublic); | |
372 | ||
373 | fail: | |
374 | CFReleaseSafe(serializedPublic); | |
375 | ||
b1ab9ed8 A |
376 | return result; |
377 | } | |
378 | ||
427c49bc A |
379 | static CFDictionaryRef CreatePrivateKeyMatchingQuery(SecKeyRef publicKey, bool returnPersistentRef) |
380 | { | |
381 | CFDataRef public_key_hash = SecKeyCopyPublicKeyHash(publicKey); | |
382 | ||
383 | CFDictionaryRef query = CFDictionaryCreateForCFTypes(kCFAllocatorDefault, | |
384 | kSecClass, kSecClassKey, | |
385 | kSecAttrKeyClass, kSecAttrKeyClassPrivate, | |
386 | kSecAttrSynchronizable, kSecAttrSynchronizableAny, | |
387 | kSecAttrApplicationLabel, public_key_hash, | |
388 | kSecReturnPersistentRef, kCFBooleanTrue, | |
389 | NULL); | |
390 | CFReleaseNull(public_key_hash); | |
391 | ||
392 | return query; | |
393 | } | |
394 | ||
395 | CFDataRef SecKeyCreatePersistentRefToMatchingPrivateKey(SecKeyRef publicKey, CFErrorRef *error) { | |
396 | CFTypeRef persistentRef = NULL; | |
397 | CFDictionaryRef query = CreatePrivateKeyMatchingQuery(publicKey, true); | |
398 | ||
399 | require_quiet(SecError(SecItemCopyMatching(query, &persistentRef),error , | |
400 | CFSTR("Error finding persistent ref to key from public: %@"), publicKey), fail); | |
401 | fail: | |
402 | CFReleaseNull(query); | |
403 | return (CFDataRef)persistentRef; | |
404 | } | |
405 | ||
406 | SecKeyRef SecKeyCopyMatchingPrivateKey(SecKeyRef publicKey, CFErrorRef *error) { | |
407 | CFTypeRef private_key = NULL; | |
408 | ||
409 | CFDictionaryRef query = CreatePrivateKeyMatchingQuery(publicKey, false); | |
410 | ||
411 | require_quiet(SecError(SecItemCopyMatching(query, &private_key), error, | |
412 | CFSTR("Error finding private key from public: %@"), publicKey), fail); | |
413 | fail: | |
414 | CFReleaseNull(query); | |
415 | return (SecKeyRef)private_key; | |
416 | } | |
417 | ||
b1ab9ed8 | 418 | SecKeyRef SecKeyCreatePublicFromDER(CFAllocatorRef allocator, |
427c49bc A |
419 | const SecAsn1Oid *oid, const SecAsn1Item *params, |
420 | const SecAsn1Item *keyData) { | |
b1ab9ed8 A |
421 | SecKeyRef publicKey = NULL; |
422 | if (SecAsn1OidCompare(oid, &CSSMOID_RSA)) { | |
423 | /* pkcs1 1 */ | |
424 | publicKey = SecKeyCreateRSAPublicKey(kCFAllocatorDefault, | |
427c49bc | 425 | keyData->Data, keyData->Length, kSecKeyEncodingPkcs1); |
b1ab9ed8 A |
426 | } else if (SecAsn1OidCompare(oid, &CSSMOID_ecPublicKey)) { |
427 | SecDERKey derKey = { | |
428 | .oid = oid->Data, | |
429 | .oidLength = oid->Length, | |
430 | .key = keyData->Data, | |
431 | .keyLength = keyData->Length, | |
432 | }; | |
433 | if (params) { | |
434 | derKey.parameters = params->Data; | |
435 | derKey.parametersLength = params->Length; | |
436 | } | |
437 | publicKey = SecKeyCreateECPublicKey(kCFAllocatorDefault, | |
427c49bc | 438 | (const uint8_t *)&derKey, sizeof(derKey), kSecDERKeyEncoding); |
b1ab9ed8 A |
439 | } else { |
440 | secwarning("Unsupported algorithm oid"); | |
441 | } | |
427c49bc | 442 | |
b1ab9ed8 A |
443 | return publicKey; |
444 | } | |
445 | ||
446 | SecKeyRef SecKeyCreate(CFAllocatorRef allocator, | |
427c49bc A |
447 | const SecKeyDescriptor *key_class, const uint8_t *keyData, |
448 | CFIndex keyDataLength, SecKeyEncoding encoding) { | |
b1ab9ed8 | 449 | check(key_class); |
427c49bc | 450 | |
b1ab9ed8 A |
451 | size_t size = sizeof(struct __SecKey) + key_class->extraBytes; |
452 | SecKeyRef result = (SecKeyRef)_CFRuntimeCreateInstance(allocator, | |
427c49bc | 453 | SecKeyGetTypeID(), size - sizeof(CFRuntimeBase), NULL); |
b1ab9ed8 A |
454 | if (result) { |
455 | memset((char*)result + sizeof(result->_base), 0, size - sizeof(result->_base)); | |
456 | result->key_class = key_class; | |
457 | if (key_class->extraBytes) { | |
458 | /* Make result->key point to the extraBytes we allocated. */ | |
459 | result->key = ((char*)result) + sizeof(*result); | |
460 | } | |
461 | if (key_class->init) { | |
462 | OSStatus status; | |
463 | status = key_class->init(result, keyData, keyDataLength, encoding); | |
464 | if (status) { | |
427c49bc | 465 | secwarning("init %s key: %" PRIdOSStatus, key_class->name, status); |
b1ab9ed8 A |
466 | CFRelease(result); |
467 | result = NULL; | |
468 | } | |
469 | } | |
470 | } | |
471 | return result; | |
472 | } | |
473 | ||
474 | enum { | |
475 | kSecKeyDigestInfoSign, | |
476 | kSecKeyDigestInfoVerify | |
477 | }; | |
478 | ||
479 | static OSStatus SecKeyDigestInfoSignVerify( | |
427c49bc A |
480 | SecKeyRef key, /* Private key */ |
481 | SecPadding padding, /* kSecPaddingPKCS1@@@ */ | |
482 | const uint8_t *dataToSign, /* signature over this data */ | |
483 | size_t dataToSignLen, /* length of dataToSign */ | |
484 | uint8_t *sig, /* signature, RETURNED */ | |
485 | size_t *sigLen, /* IN/OUT */ | |
486 | int mode) { | |
b1ab9ed8 A |
487 | size_t digestInfoLength = DER_MAX_DIGEST_INFO_LEN; |
488 | uint8_t digestInfo[digestInfoLength]; | |
489 | const SecAsn1Oid *digestOid; | |
490 | size_t digestLen; | |
427c49bc | 491 | |
b1ab9ed8 A |
492 | switch (padding) { |
493 | #if 0 | |
427c49bc A |
494 | case kSecPaddingPKCS1MD2: |
495 | digestLen = CC_MD2_DIGEST_LENGTH; | |
496 | digestOid = &CSSMOID_MD2; | |
497 | break; | |
498 | case kSecPaddingPKCS1MD4: | |
499 | digestLen = CC_MD4_DIGEST_LENGTH; | |
500 | digestOid = &CSSMOID_MD4; | |
501 | break; | |
502 | case kSecPaddingPKCS1MD5: | |
503 | digestLen = CC_MD5_DIGEST_LENGTH; | |
504 | digestOid = &CSSMOID_MD5; | |
505 | break; | |
b1ab9ed8 | 506 | #endif |
427c49bc A |
507 | case kSecPaddingPKCS1SHA1: |
508 | digestLen = CC_SHA1_DIGEST_LENGTH; | |
509 | digestOid = &CSSMOID_SHA1; | |
510 | break; | |
511 | case kSecPaddingPKCS1SHA224: | |
512 | digestLen = CC_SHA224_DIGEST_LENGTH; | |
513 | digestOid = &CSSMOID_SHA224; | |
514 | break; | |
515 | case kSecPaddingPKCS1SHA256: | |
516 | digestLen = CC_SHA256_DIGEST_LENGTH; | |
517 | digestOid = &CSSMOID_SHA256; | |
518 | break; | |
519 | case kSecPaddingPKCS1SHA384: | |
520 | digestLen = CC_SHA384_DIGEST_LENGTH; | |
521 | digestOid = &CSSMOID_SHA384; | |
522 | break; | |
523 | case kSecPaddingPKCS1SHA512: | |
524 | digestLen = CC_SHA512_DIGEST_LENGTH; | |
525 | digestOid = &CSSMOID_SHA512; | |
526 | break; | |
527 | default: | |
528 | return errSecUnsupportedPadding; | |
b1ab9ed8 | 529 | } |
427c49bc | 530 | |
b1ab9ed8 A |
531 | if (dataToSignLen != digestLen) |
532 | return errSecParam; | |
427c49bc | 533 | |
b1ab9ed8 | 534 | size_t offset = DEREncodeDigestInfoPrefix(digestOid, digestLen, |
427c49bc | 535 | digestInfo, digestInfoLength); |
b1ab9ed8 A |
536 | if (!offset) |
537 | return errSecBufferTooSmall; | |
427c49bc | 538 | |
b1ab9ed8 A |
539 | /* Append the digest to the digestInfo prefix and adjust the length. */ |
540 | memcpy(&digestInfo[offset], dataToSign, digestLen); | |
541 | digestInfoLength = offset + digestLen; | |
427c49bc | 542 | |
b1ab9ed8 A |
543 | if (mode == kSecKeyDigestInfoSign) { |
544 | return key->key_class->rawSign(key, kSecPaddingPKCS1, | |
427c49bc | 545 | digestInfo, digestInfoLength, sig, sigLen); |
b1ab9ed8 A |
546 | } else { |
547 | return key->key_class->rawVerify(key, kSecPaddingPKCS1, | |
427c49bc | 548 | digestInfo, digestInfoLength, sig, *sigLen); |
b1ab9ed8 | 549 | } |
427c49bc A |
550 | |
551 | return errSecSuccess; | |
b1ab9ed8 A |
552 | } |
553 | ||
554 | OSStatus SecKeyRawSign( | |
427c49bc A |
555 | SecKeyRef key, /* Private key */ |
556 | SecPadding padding, /* kSecPaddingNone or kSecPaddingPKCS1 */ | |
557 | const uint8_t *dataToSign, /* signature over this data */ | |
558 | size_t dataToSignLen, /* length of dataToSign */ | |
559 | uint8_t *sig, /* signature, RETURNED */ | |
560 | size_t *sigLen) { /* IN/OUT */ | |
b1ab9ed8 A |
561 | if (!key->key_class->rawSign) |
562 | return errSecUnsupportedOperation; | |
427c49bc | 563 | |
b1ab9ed8 A |
564 | if (padding < kSecPaddingPKCS1MD2) { |
565 | return key->key_class->rawSign(key, padding, dataToSign, dataToSignLen, | |
427c49bc | 566 | sig, sigLen); |
b1ab9ed8 A |
567 | } else { |
568 | return SecKeyDigestInfoSignVerify(key, padding, dataToSign, dataToSignLen, | |
427c49bc | 569 | sig, sigLen, kSecKeyDigestInfoSign); |
b1ab9ed8 A |
570 | } |
571 | } | |
572 | ||
573 | OSStatus SecKeyRawVerify( | |
427c49bc A |
574 | SecKeyRef key, /* Public key */ |
575 | SecPadding padding, /* kSecPaddingNone or kSecPaddingPKCS1 */ | |
576 | const uint8_t *signedData, /* signature over this data */ | |
577 | size_t signedDataLen, /* length of dataToSign */ | |
578 | const uint8_t *sig, /* signature */ | |
579 | size_t sigLen) { /* length of signature */ | |
b1ab9ed8 A |
580 | if (!key->key_class->rawVerify) |
581 | return errSecUnsupportedOperation; | |
427c49bc | 582 | |
b1ab9ed8 A |
583 | if (padding < kSecPaddingPKCS1MD2) { |
584 | return key->key_class->rawVerify(key, padding, signedData, signedDataLen, | |
427c49bc | 585 | sig, sigLen); |
b1ab9ed8 A |
586 | } else { |
587 | /* Casting away the constness of sig is safe since | |
427c49bc A |
588 | SecKeyDigestInfoSignVerify only modifies sig if |
589 | mode == kSecKeyDigestInfoSign. */ | |
b1ab9ed8 | 590 | return SecKeyDigestInfoSignVerify(key, padding, |
427c49bc A |
591 | signedData, signedDataLen, (uint8_t *)sig, &sigLen, |
592 | kSecKeyDigestInfoVerify); | |
b1ab9ed8 A |
593 | } |
594 | } | |
595 | ||
596 | OSStatus SecKeyEncrypt( | |
427c49bc A |
597 | SecKeyRef key, /* Public key */ |
598 | SecPadding padding, /* kSecPaddingNone, kSecPaddingPKCS1, kSecPaddingOAEP */ | |
599 | const uint8_t *plainText, | |
600 | size_t plainTextLen, /* length of plainText */ | |
601 | uint8_t *cipherText, | |
602 | size_t *cipherTextLen) { /* IN/OUT */ | |
b1ab9ed8 A |
603 | if (key->key_class->encrypt) |
604 | return key->key_class->encrypt(key, padding, plainText, plainTextLen, | |
427c49bc | 605 | cipherText, cipherTextLen); |
b1ab9ed8 A |
606 | return errSecUnsupportedOperation; |
607 | } | |
608 | ||
609 | OSStatus SecKeyDecrypt( | |
427c49bc A |
610 | SecKeyRef key, /* Private key */ |
611 | SecPadding padding, /* kSecPaddingNone, kSecPaddingPKCS1, kSecPaddingOAEP */ | |
612 | const uint8_t *cipherText, | |
613 | size_t cipherTextLen, /* length of cipherText */ | |
614 | uint8_t *plainText, | |
615 | size_t *plainTextLen) { /* IN/OUT */ | |
b1ab9ed8 A |
616 | if (key->key_class->decrypt) |
617 | return key->key_class->decrypt(key, padding, cipherText, cipherTextLen, | |
427c49bc | 618 | plainText, plainTextLen); |
b1ab9ed8 A |
619 | return errSecUnsupportedOperation; |
620 | } | |
621 | ||
622 | size_t SecKeyGetBlockSize(SecKeyRef key) { | |
623 | if (key->key_class->blockSize) | |
624 | return key->key_class->blockSize(key); | |
625 | return 0; | |
626 | } | |
627 | ||
628 | /* Private API functions. */ | |
629 | ||
630 | CFDictionaryRef SecKeyCopyAttributeDictionary(SecKeyRef key) { | |
631 | if (key->key_class->copyDictionary) | |
632 | return key->key_class->copyDictionary(key); | |
633 | return NULL; | |
634 | } | |
635 | ||
636 | SecKeyRef SecKeyCreateFromAttributeDictionary(CFDictionaryRef refAttributes) { | |
637 | /* TODO: Support having an allocator in refAttributes. */ | |
638 | CFAllocatorRef allocator = NULL; | |
639 | CFDataRef data = CFDictionaryGetValue(refAttributes, kSecValueData); | |
640 | CFTypeRef ktype = CFDictionaryGetValue(refAttributes, kSecAttrKeyType); | |
641 | SInt32 algorithm; | |
642 | SecKeyRef ref; | |
427c49bc | 643 | |
b1ab9ed8 A |
644 | /* First figure out the key type (algorithm). */ |
645 | if (CFGetTypeID(ktype) == CFNumberGetTypeID()) { | |
646 | CFNumberGetValue(ktype, kCFNumberSInt32Type, &algorithm); | |
427c49bc A |
647 | } else if (isString(ktype)) { |
648 | algorithm = CFStringGetIntValue(ktype); | |
649 | CFStringRef t = CFStringCreateWithFormat(0, 0, CFSTR("%ld"), (long) algorithm); | |
650 | if (!CFEqual(t, ktype)) { | |
651 | secwarning("Unsupported key class: %@", ktype); | |
652 | CFReleaseSafe(t); | |
653 | return NULL; | |
654 | } | |
655 | CFReleaseSafe(t); | |
656 | } else { | |
b1ab9ed8 A |
657 | secwarning("Unsupported key type: %@", ktype); |
658 | return NULL; | |
659 | } | |
427c49bc | 660 | |
b1ab9ed8 | 661 | /* TODO: The code below won't scale well, consider moving to something |
427c49bc | 662 | table driven. */ |
b1ab9ed8 A |
663 | SInt32 class; |
664 | CFTypeRef kclass = CFDictionaryGetValue(refAttributes, kSecAttrKeyClass); | |
665 | if (CFGetTypeID(kclass) == CFNumberGetTypeID()) { | |
666 | CFNumberGetValue(kclass, kCFNumberSInt32Type, &class); | |
427c49bc A |
667 | } else if (isString(kclass)) { |
668 | class = CFStringGetIntValue(kclass); | |
669 | CFStringRef t = CFStringCreateWithFormat(0, 0, CFSTR("%ld"), (long) class); | |
670 | if (!CFEqual(t, kclass)) { | |
671 | CFReleaseSafe(t); | |
672 | secwarning("Unsupported key class: %@", kclass); | |
673 | return NULL; | |
674 | } | |
675 | CFReleaseSafe(t); | |
676 | } else { | |
b1ab9ed8 A |
677 | secwarning("Unsupported key class: %@", kclass); |
678 | return NULL; | |
679 | } | |
427c49bc | 680 | |
b1ab9ed8 A |
681 | switch (class) { |
682 | case 0: // kSecAttrKeyClassPublic | |
683 | switch (algorithm) { | |
684 | case 42: // kSecAlgorithmRSA | |
685 | ref = SecKeyCreateRSAPublicKey(allocator, | |
686 | CFDataGetBytePtr(data), CFDataGetLength(data), | |
687 | kSecKeyEncodingBytes); | |
688 | break; | |
689 | case 43: // kSecAlgorithmECDSA | |
427c49bc | 690 | case 73: // kSecAlgorithmEC |
b1ab9ed8 A |
691 | ref = SecKeyCreateECPublicKey(allocator, |
692 | CFDataGetBytePtr(data), CFDataGetLength(data), | |
693 | kSecKeyEncodingBytes); | |
694 | break; | |
695 | default: | |
696 | secwarning("Unsupported public key type: %@", ktype); | |
697 | ref = NULL; | |
698 | break; | |
699 | }; | |
700 | break; | |
701 | case 1: // kSecAttrKeyClassPrivate | |
702 | switch (algorithm) { | |
703 | case 42: // kSecAlgorithmRSA | |
704 | ref = SecKeyCreateRSAPrivateKey(allocator, | |
705 | CFDataGetBytePtr(data), CFDataGetLength(data), | |
706 | kSecKeyEncodingBytes); | |
707 | break; | |
708 | case 43: // kSecAlgorithmECDSA | |
427c49bc | 709 | case 73: // kSecAlgorithmEC |
b1ab9ed8 A |
710 | ref = SecKeyCreateECPrivateKey(allocator, |
711 | CFDataGetBytePtr(data), CFDataGetLength(data), | |
712 | kSecKeyEncodingBytes); | |
713 | break; | |
714 | default: | |
715 | secwarning("Unsupported private key type: %@", ktype); | |
716 | ref = NULL; | |
717 | break; | |
718 | }; | |
719 | break; | |
720 | case 2: // kSecAttrKeyClassSymmetric | |
721 | secwarning("Unsupported symmetric key type: %@", ktype); | |
722 | ref = NULL; | |
723 | break; | |
724 | default: | |
725 | secwarning("Unsupported key class: %@", kclass); | |
726 | ref = NULL; | |
727 | } | |
427c49bc | 728 | |
b1ab9ed8 A |
729 | return ref; |
730 | } | |
731 | ||
732 | /* TODO: This function should ensure that this keys algorithm matches the | |
427c49bc | 733 | signature algorithm. */ |
b1ab9ed8 | 734 | static OSStatus SecKeyGetDigestInfo(SecKeyRef this, const SecAsn1AlgId *algId, |
427c49bc A |
735 | const uint8_t *data, size_t dataLen, bool digestData, |
736 | uint8_t *digestInfo, size_t *digestInfoLen /* IN/OUT */) { | |
b1ab9ed8 A |
737 | unsigned char *(*digestFcn)(const void *, CC_LONG, unsigned char *); |
738 | CFIndex keyAlgID = kSecNullAlgorithmID; | |
739 | const SecAsn1Oid *digestOid; | |
740 | size_t digestLen; | |
741 | size_t offset = 0; | |
427c49bc | 742 | |
b1ab9ed8 A |
743 | /* Since these oids all have the same prefix, use switch. */ |
744 | if ((algId->algorithm.Length == CSSMOID_RSA.Length) && | |
745 | !memcmp(algId->algorithm.Data, CSSMOID_RSA.Data, | |
427c49bc A |
746 | algId->algorithm.Length - 1)) { |
747 | keyAlgID = kSecRSAAlgorithmID; | |
748 | switch (algId->algorithm.Data[algId->algorithm.Length - 1]) { | |
b1ab9ed8 | 749 | #if 0 |
427c49bc A |
750 | case 2: /* oidMD2WithRSA */ |
751 | digestFcn = CC_MD2; | |
752 | digestLen = CC_MD2_DIGEST_LENGTH; | |
753 | digestOid = &CSSMOID_MD2; | |
754 | break; | |
755 | case 3: /* oidMD4WithRSA */ | |
756 | digestFcn = CC_MD4; | |
757 | digestLen = CC_MD4_DIGEST_LENGTH; | |
758 | digestOid = &CSSMOID_MD4; | |
759 | break; | |
760 | case 4: /* oidMD5WithRSA */ | |
761 | digestFcn = CC_MD5; | |
762 | digestLen = CC_MD5_DIGEST_LENGTH; | |
763 | digestOid = &CSSMOID_MD5; | |
764 | break; | |
b1ab9ed8 | 765 | #endif /* 0 */ |
427c49bc A |
766 | case 5: /* oidSHA1WithRSA */ |
767 | digestFcn = CC_SHA1; | |
768 | digestLen = CC_SHA1_DIGEST_LENGTH; | |
769 | digestOid = &CSSMOID_SHA1; | |
770 | break; | |
771 | case 11: /* oidSHA256WithRSA */ | |
772 | digestFcn = CC_SHA256; | |
773 | digestLen = CC_SHA256_DIGEST_LENGTH; | |
774 | digestOid = &CSSMOID_SHA256; | |
775 | break; | |
776 | case 12: /* oidSHA384WithRSA */ | |
777 | /* pkcs1 12 */ | |
778 | digestFcn = CC_SHA384; | |
779 | digestLen = CC_SHA384_DIGEST_LENGTH; | |
780 | digestOid = &CSSMOID_SHA384; | |
781 | break; | |
782 | case 13: /* oidSHA512WithRSA */ | |
783 | digestFcn = CC_SHA512; | |
784 | digestLen = CC_SHA512_DIGEST_LENGTH; | |
785 | digestOid = &CSSMOID_SHA512; | |
786 | break; | |
787 | case 14: /* oidSHA224WithRSA */ | |
788 | digestFcn = CC_SHA224; | |
789 | digestLen = CC_SHA224_DIGEST_LENGTH; | |
790 | digestOid = &CSSMOID_SHA224; | |
791 | break; | |
792 | default: | |
793 | secdebug("key", "unsupported rsa signature algorithm"); | |
794 | return errSecUnsupportedAlgorithm; | |
795 | } | |
796 | } else if ((algId->algorithm.Length == CSSMOID_ECDSA_WithSHA224.Length) && | |
797 | !memcmp(algId->algorithm.Data, CSSMOID_ECDSA_WithSHA224.Data, | |
798 | algId->algorithm.Length - 1)) { | |
799 | keyAlgID = kSecECDSAAlgorithmID; | |
800 | switch (algId->algorithm.Data[algId->algorithm.Length - 1]) { | |
801 | case 1: /* oidSHA224WithECDSA */ | |
802 | digestFcn = CC_SHA224; | |
803 | digestLen = CC_SHA224_DIGEST_LENGTH; | |
804 | break; | |
805 | case 2: /* oidSHA256WithECDSA */ | |
806 | digestFcn = CC_SHA256; | |
807 | digestLen = CC_SHA256_DIGEST_LENGTH; | |
808 | break; | |
809 | case 3: /* oidSHA384WithECDSA */ | |
810 | /* pkcs1 12 */ | |
811 | digestFcn = CC_SHA384; | |
812 | digestLen = CC_SHA384_DIGEST_LENGTH; | |
813 | break; | |
814 | case 4: /* oidSHA512WithECDSA */ | |
815 | digestFcn = CC_SHA512; | |
816 | digestLen = CC_SHA512_DIGEST_LENGTH; | |
817 | break; | |
818 | default: | |
819 | secdebug("key", "unsupported ecdsa signature algorithm"); | |
820 | return errSecUnsupportedAlgorithm; | |
821 | } | |
822 | } else if (SecAsn1OidCompare(&algId->algorithm, &CSSMOID_ECDSA_WithSHA1)) { | |
823 | keyAlgID = kSecECDSAAlgorithmID; | |
824 | digestFcn = CC_SHA1; | |
825 | digestLen = CC_SHA1_DIGEST_LENGTH; | |
826 | } else if (SecAsn1OidCompare(&algId->algorithm, &CSSMOID_SHA1)) { | |
827 | digestFcn = CC_SHA1; | |
828 | digestLen = CC_SHA1_DIGEST_LENGTH; | |
829 | digestOid = &CSSMOID_SHA1; | |
830 | } else if ((algId->algorithm.Length == CSSMOID_SHA224.Length) && | |
831 | !memcmp(algId->algorithm.Data, CSSMOID_SHA224.Data, algId->algorithm.Length - 1)) | |
832 | { | |
833 | switch (algId->algorithm.Data[algId->algorithm.Length - 1]) { | |
834 | case 4: /* OID_SHA224 */ | |
835 | digestFcn = CC_SHA224; | |
836 | digestLen = CC_SHA224_DIGEST_LENGTH; | |
837 | digestOid = &CSSMOID_SHA224; | |
838 | break; | |
839 | case 1: /* OID_SHA256 */ | |
840 | digestFcn = CC_SHA256; | |
841 | digestLen = CC_SHA256_DIGEST_LENGTH; | |
842 | digestOid = &CSSMOID_SHA256; | |
843 | break; | |
844 | case 2: /* OID_SHA384 */ | |
845 | /* pkcs1 12 */ | |
846 | digestFcn = CC_SHA384; | |
847 | digestLen = CC_SHA384_DIGEST_LENGTH; | |
848 | digestOid = &CSSMOID_SHA384; | |
849 | break; | |
850 | case 3: /* OID_SHA512 */ | |
851 | digestFcn = CC_SHA512; | |
852 | digestLen = CC_SHA512_DIGEST_LENGTH; | |
853 | digestOid = &CSSMOID_SHA512; | |
854 | break; | |
855 | default: | |
856 | secdebug("key", "unsupported sha-2 signature algorithm"); | |
857 | return errSecUnsupportedAlgorithm; | |
858 | } | |
859 | } else if (SecAsn1OidCompare(&algId->algorithm, &CSSMOID_MD5)) { | |
860 | digestFcn = CC_MD5; | |
861 | digestLen = CC_MD5_DIGEST_LENGTH; | |
862 | digestOid = &CSSMOID_MD5; | |
863 | } else { | |
864 | secdebug("key", "unsupported digesting algorithm"); | |
865 | return errSecUnsupportedAlgorithm; | |
866 | } | |
867 | ||
b1ab9ed8 A |
868 | /* check key is appropriate for signature (superfluous for digest only oid) */ |
869 | if (keyAlgID == kSecNullAlgorithmID) | |
870 | keyAlgID = SecKeyGetAlgorithmID(this); | |
871 | else if (keyAlgID != SecKeyGetAlgorithmID(this)) | |
872 | return errSecUnsupportedAlgorithm; | |
427c49bc | 873 | |
b1ab9ed8 A |
874 | switch(keyAlgID) { |
875 | case kSecRSAAlgorithmID: | |
876 | offset = DEREncodeDigestInfoPrefix(digestOid, digestLen, | |
877 | digestInfo, *digestInfoLen); | |
878 | if (!offset) | |
879 | return errSecBufferTooSmall; | |
880 | break; | |
881 | case kSecDSAAlgorithmID: | |
882 | if (digestOid != &CSSMOID_SHA1) | |
883 | return errSecUnsupportedAlgorithm; | |
884 | break; | |
885 | case kSecECDSAAlgorithmID: | |
886 | break; | |
887 | default: | |
888 | secdebug("key", "unsupported signature algorithm"); | |
889 | return errSecUnsupportedAlgorithm; | |
890 | } | |
427c49bc | 891 | |
b1ab9ed8 A |
892 | if (digestData) { |
893 | if(dataLen>UINT32_MAX) /* Check for overflow with CC_LONG cast */ | |
427c49bc | 894 | return errSecParam; |
b1ab9ed8 A |
895 | digestFcn(data, (CC_LONG)dataLen, &digestInfo[offset]); |
896 | *digestInfoLen = offset + digestLen; | |
897 | } else { | |
898 | if (dataLen != digestLen) | |
427c49bc | 899 | return errSecParam; |
b1ab9ed8 A |
900 | memcpy(&digestInfo[offset], data, dataLen); |
901 | *digestInfoLen = offset + dataLen; | |
902 | } | |
427c49bc A |
903 | |
904 | return errSecSuccess; | |
b1ab9ed8 A |
905 | } |
906 | ||
907 | OSStatus SecKeyDigestAndVerify( | |
427c49bc A |
908 | SecKeyRef this, /* Private key */ |
909 | const SecAsn1AlgId *algId, /* algorithm oid/params */ | |
910 | const uint8_t *dataToDigest, /* signature over this data */ | |
911 | size_t dataToDigestLen,/* length of dataToDigest */ | |
912 | const uint8_t *sig, /* signature to verify */ | |
913 | size_t sigLen) { /* length of sig */ | |
b1ab9ed8 A |
914 | size_t digestInfoLength = DER_MAX_DIGEST_INFO_LEN; |
915 | uint8_t digestInfo[digestInfoLength]; | |
916 | OSStatus status; | |
427c49bc A |
917 | |
918 | if (this == NULL) | |
919 | return errSecParam; | |
920 | ||
b1ab9ed8 | 921 | status = SecKeyGetDigestInfo(this, algId, dataToDigest, dataToDigestLen, true, |
427c49bc | 922 | digestInfo, &digestInfoLength); |
b1ab9ed8 A |
923 | if (status) |
924 | return status; | |
925 | return SecKeyRawVerify(this, kSecPaddingPKCS1, | |
427c49bc | 926 | digestInfo, digestInfoLength, sig, sigLen); |
b1ab9ed8 A |
927 | } |
928 | ||
929 | OSStatus SecKeyDigestAndSign( | |
427c49bc A |
930 | SecKeyRef this, /* Private key */ |
931 | const SecAsn1AlgId *algId, /* algorithm oid/params */ | |
932 | const uint8_t *dataToDigest, /* signature over this data */ | |
933 | size_t dataToDigestLen,/* length of dataToDigest */ | |
934 | uint8_t *sig, /* signature, RETURNED */ | |
935 | size_t *sigLen) { /* IN/OUT */ | |
b1ab9ed8 A |
936 | size_t digestInfoLength = DER_MAX_DIGEST_INFO_LEN; |
937 | uint8_t digestInfo[digestInfoLength]; | |
938 | OSStatus status; | |
427c49bc | 939 | |
b1ab9ed8 | 940 | status = SecKeyGetDigestInfo(this, algId, dataToDigest, dataToDigestLen, true /* digest data */, |
427c49bc | 941 | digestInfo, &digestInfoLength); |
b1ab9ed8 A |
942 | if (status) |
943 | return status; | |
944 | return SecKeyRawSign(this, kSecPaddingPKCS1, | |
427c49bc | 945 | digestInfo, digestInfoLength, sig, sigLen); |
b1ab9ed8 A |
946 | } |
947 | ||
948 | OSStatus SecKeyVerifyDigest( | |
427c49bc A |
949 | SecKeyRef this, /* Private key */ |
950 | const SecAsn1AlgId *algId, /* algorithm oid/params */ | |
951 | const uint8_t *digestData, /* signature over this digest */ | |
952 | size_t digestDataLen,/* length of dataToDigest */ | |
953 | const uint8_t *sig, /* signature to verify */ | |
954 | size_t sigLen) { /* length of sig */ | |
b1ab9ed8 A |
955 | size_t digestInfoLength = DER_MAX_DIGEST_INFO_LEN; |
956 | uint8_t digestInfo[digestInfoLength]; | |
957 | OSStatus status; | |
427c49bc | 958 | |
b1ab9ed8 A |
959 | status = SecKeyGetDigestInfo(this, algId, digestData, digestDataLen, false /* data is digest */, |
960 | digestInfo, &digestInfoLength); | |
961 | if (status) | |
962 | return status; | |
963 | return SecKeyRawVerify(this, kSecPaddingPKCS1, | |
964 | digestInfo, digestInfoLength, sig, sigLen); | |
965 | } | |
966 | ||
967 | OSStatus SecKeySignDigest( | |
427c49bc A |
968 | SecKeyRef this, /* Private key */ |
969 | const SecAsn1AlgId *algId, /* algorithm oid/params */ | |
970 | const uint8_t *digestData, /* signature over this digest */ | |
971 | size_t digestDataLen,/* length of digestData */ | |
972 | uint8_t *sig, /* signature, RETURNED */ | |
973 | size_t *sigLen) { /* IN/OUT */ | |
b1ab9ed8 A |
974 | size_t digestInfoLength = DER_MAX_DIGEST_INFO_LEN; |
975 | uint8_t digestInfo[digestInfoLength]; | |
976 | OSStatus status; | |
427c49bc | 977 | |
b1ab9ed8 A |
978 | status = SecKeyGetDigestInfo(this, algId, digestData, digestDataLen, false, |
979 | digestInfo, &digestInfoLength); | |
980 | if (status) | |
981 | return status; | |
982 | return SecKeyRawSign(this, kSecPaddingPKCS1, | |
983 | digestInfo, digestInfoLength, sig, sigLen); | |
984 | } | |
985 | ||
986 | CFIndex SecKeyGetAlgorithmID(SecKeyRef key) { | |
987 | /* This method was added to version 1 keys. */ | |
988 | if (key->key_class->version > 0 && key->key_class->getAlgorithmID) | |
989 | return key->key_class->getAlgorithmID(key); | |
990 | /* All version 0 key were RSA. */ | |
991 | return kSecRSAAlgorithmID; | |
992 | } | |
993 | ||
994 | ||
995 | OSStatus SecKeyCopyPublicBytes(SecKeyRef key, CFDataRef* serializedPublic) { | |
996 | if (key->key_class->version > 1 && key->key_class->copyPublic) | |
997 | return key->key_class->copyPublic(key, serializedPublic); | |
998 | return errSecUnimplemented; | |
999 | } | |
1000 | ||
1001 | SecKeyRef SecKeyCreateFromPublicBytes(CFAllocatorRef allocator, CFIndex algorithmID, const uint8_t *keyData, CFIndex keyDataLength) | |
1002 | { | |
1003 | switch (algorithmID) | |
1004 | { | |
1005 | case kSecRSAAlgorithmID: | |
1006 | return SecKeyCreateRSAPublicKey(allocator, | |
1007 | keyData, keyDataLength, | |
1008 | kSecKeyEncodingBytes); | |
1009 | case kSecECDSAAlgorithmID: | |
1010 | return SecKeyCreateECPublicKey(allocator, | |
1011 | keyData, keyDataLength, | |
1012 | kSecKeyEncodingBytes); | |
1013 | default: | |
1014 | return NULL; | |
1015 | } | |
1016 | } | |
1017 | ||
1018 | SecKeyRef SecKeyCreateFromPublicData(CFAllocatorRef allocator, CFIndex algorithmID, CFDataRef serialized) | |
1019 | { | |
1020 | return SecKeyCreateFromPublicBytes(allocator, algorithmID, CFDataGetBytePtr(serialized), CFDataGetLength(serialized)); | |
1021 | } | |
1022 | ||
1023 | // This is a bit icky hack to avoid changing the vtable for | |
1024 | // SecKey. | |
1025 | size_t SecKeyGetSize(SecKeyRef key, SecKeySize whichSize) | |
1026 | { | |
1027 | size_t result = SecKeyGetBlockSize(key); | |
427c49bc | 1028 | |
b1ab9ed8 A |
1029 | if (kSecECDSAAlgorithmID == SecKeyGetAlgorithmID(key)) { |
1030 | switch (whichSize) { | |
1031 | case kSecKeyEncryptedDataSize: | |
1032 | result = 0; | |
1033 | break; | |
1034 | case kSecKeySignatureSize: | |
427c49bc | 1035 | result = (result >= 66 ? 9 : 8) + 2 * result; |
b1ab9ed8 A |
1036 | break; |
1037 | case kSecKeyKeySizeInBits: | |
1038 | if (result >= 66) | |
1039 | return 521; | |
1040 | } | |
1041 | } | |
427c49bc | 1042 | |
b1ab9ed8 | 1043 | if (whichSize == kSecKeyKeySizeInBits) |
427c49bc A |
1044 | result *= 8; |
1045 | ||
b1ab9ed8 | 1046 | return result; |
427c49bc A |
1047 | |
1048 | } | |
b1ab9ed8 | 1049 | |
427c49bc A |
1050 | OSStatus SecKeyFindWithPersistentRef(CFDataRef persistentRef, SecKeyRef* lookedUpData) |
1051 | { | |
1052 | CFDictionaryRef query = CFDictionaryCreateForCFTypes(kCFAllocatorDefault, | |
1053 | kSecReturnRef, kCFBooleanTrue, | |
1054 | kSecClass, kSecClassKey, | |
1055 | kSecValuePersistentRef, persistentRef, | |
1056 | NULL); | |
1057 | CFTypeRef foundRef = NULL; | |
1058 | OSStatus status = SecItemCopyMatching(query, &foundRef); | |
1059 | ||
1060 | if (status == errSecSuccess) { | |
1061 | if (CFGetTypeID(foundRef) == SecKeyGetTypeID()) { | |
1062 | *lookedUpData = (SecKeyRef) foundRef; | |
1063 | foundRef = NULL; | |
1064 | status = errSecSuccess; | |
1065 | } else { | |
1066 | status = errSecItemNotFound; | |
1067 | } | |
1068 | } | |
1069 | ||
1070 | CFReleaseSafe(foundRef); | |
1071 | CFReleaseSafe(query); | |
1072 | ||
1073 | return status; | |
b1ab9ed8 A |
1074 | } |
1075 | ||
427c49bc A |
1076 | OSStatus SecKeyCopyPersistentRef(SecKeyRef key, CFDataRef* persistentRef) |
1077 | { | |
1078 | CFDictionaryRef query = CFDictionaryCreateForCFTypes(kCFAllocatorDefault, | |
1079 | kSecReturnPersistentRef, kCFBooleanTrue, | |
1080 | kSecValueRef, key, | |
1081 | kSecAttrSynchronizable, kSecAttrSynchronizableAny, | |
1082 | NULL); | |
1083 | CFTypeRef foundRef = NULL; | |
1084 | OSStatus status = SecItemCopyMatching(query, &foundRef); | |
1085 | ||
1086 | if (status == errSecSuccess) { | |
1087 | if (CFGetTypeID(foundRef) == CFDataGetTypeID()) { | |
1088 | *persistentRef = foundRef; | |
1089 | foundRef = NULL; | |
1090 | } else { | |
1091 | status = errSecItemNotFound; | |
1092 | } | |
1093 | } | |
1094 | ||
1095 | CFReleaseSafe(foundRef); | |
1096 | CFReleaseSafe(query); | |
1097 | ||
1098 | return status; | |
1099 | } |