]> git.saurik.com Git - apple/security.git/blob - OSX/sec/Security/SecCTKKey.m
Security-59306.11.20.tar.gz
[apple/security.git] / OSX / sec / Security / SecCTKKey.m
1 /*
2 * Copyright (c) 2015 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 #import <Foundation/Foundation.h>
25
26 #include <AssertMacros.h>
27 #include <Security/SecFramework.h>
28 #include <Security/SecKeyPriv.h>
29 #include <Security/SecItem.h>
30 #include <Security/SecItemPriv.h>
31 #include <Security/SecItemInternal.h>
32 #include <Security/SecBasePriv.h>
33 #include <Security/SecAccessControlPriv.h>
34 #include <utilities/SecCFError.h>
35 #include <utilities/SecCFWrappers.h>
36 #include <utilities/array_size.h>
37 #include <ctkclient/ctkclient.h>
38 #include <libaks_acl_cf_keys.h>
39 #include "OSX/sec/Security/SecItemShim.h"
40
41 #include "SecECKey.h"
42 #include "SecRSAKey.h"
43 #include "SecCTKKeyPriv.h"
44
45 const CFStringRef kSecUseToken = CFSTR("u_Token");
46
47 typedef struct {
48 TKTokenRef token;
49 CFStringRef token_id;
50 CFDataRef object_id;
51 SecCFDictionaryCOW auth_params;
52 SecCFDictionaryCOW attributes;
53 CFMutableDictionaryRef params;
54 } SecCTKKeyData;
55
56 static void SecCTKKeyDestroy(SecKeyRef key) {
57 SecCTKKeyData *kd = key->key;
58 CFReleaseNull(kd->token);
59 CFReleaseNull(kd->token_id);
60 CFReleaseNull(kd->object_id);
61 CFReleaseNull(kd->auth_params.mutable_dictionary);
62 CFReleaseNull(kd->attributes.mutable_dictionary);
63 CFReleaseNull(kd->params);
64 }
65
66 static CFIndex SecCTKGetAlgorithmID(SecKeyRef key) {
67 SecCTKKeyData *kd = key->key;
68 if (CFEqualSafe(CFDictionaryGetValue(kd->attributes.dictionary, kSecAttrKeyType), kSecAttrKeyTypeECSECPrimeRandom) ||
69 CFEqualSafe(CFDictionaryGetValue(kd->attributes.dictionary, kSecAttrKeyType), kSecAttrKeyTypeECSECPrimeRandomPKA) ||
70 CFEqualSafe(CFDictionaryGetValue(kd->attributes.dictionary, kSecAttrKeyType), kSecAttrKeyTypeSecureEnclaveAttestation)) {
71 return kSecECDSAAlgorithmID;
72 }
73 return kSecRSAAlgorithmID;
74 }
75
76 static SecItemAuthResult SecCTKProcessError(CFStringRef operation, TKTokenRef token, CFDataRef object_id, CFArrayRef *ac_pairs, CFErrorRef *error) {
77 if (CFEqualSafe(CFErrorGetDomain(*error), CFSTR(kTKErrorDomain)) &&
78 CFErrorGetCode(*error) == kTKErrorCodeAuthenticationNeeded &&
79 operation != NULL) {
80 CFDataRef access_control = TKTokenCopyObjectAccessControl(token, object_id, error);
81 if (access_control != NULL) {
82 CFArrayRef ac_pair = CFArrayCreateForCFTypes(NULL, access_control, operation, NULL);
83 CFAssignRetained(*ac_pairs, CFArrayCreateForCFTypes(NULL, ac_pair, NULL));
84
85 CFReleaseNull(*error);
86 CFRelease(ac_pair);
87 CFRelease(access_control);
88 return kSecItemAuthResultNeedAuth;
89 }
90 }
91 return kSecItemAuthResultError;
92 }
93
94 static TKTokenRef SecCTKKeyCreateToken(SecKeyRef key, CFDictionaryRef auth_params, CFDictionaryRef *last_params, CFErrorRef *error) {
95 TKTokenRef token = NULL;
96 SecCTKKeyData *kd = key->key;
97 SecCFDictionaryCOW attributes = { auth_params };
98 if (kd->params && CFDictionaryGetCount(kd->params) > 0) {
99 CFDictionarySetValue(SecCFDictionaryCOWGetMutable(&attributes), CFSTR(kTKTokenCreateAttributeAuxParams), kd->params);
100 }
101 require_quiet(token = SecTokenCreate(kd->token_id, &attributes, error), out);
102 if (last_params != NULL) {
103 CFAssignRetained(*last_params, auth_params ? CFDictionaryCreateCopy(NULL, auth_params) : NULL);
104 }
105
106 out:
107 CFReleaseNull(attributes.mutable_dictionary);
108 return token;
109 }
110
111 static TKTokenRef SecCTKKeyCopyToken(SecKeyRef key, CFErrorRef *error) {
112 SecCTKKeyData *kd = key->key;
113 TKTokenRef token = CFRetainSafe(kd->token);
114 if (token == NULL) {
115 token = SecCTKKeyCreateToken(key, kd->auth_params.dictionary, NULL, error);
116 }
117 return token;
118 }
119
120 static CFTypeRef SecCTKKeyCopyOperationResult(SecKeyRef key, SecKeyOperationType operation, SecKeyAlgorithm algorithm,
121 CFArrayRef algorithms, SecKeyOperationMode mode,
122 CFTypeRef in1, CFTypeRef in2, CFErrorRef *error) {
123 SecCTKKeyData *kd = key->key;
124 __block SecCFDictionaryCOW auth_params = { kd->auth_params.dictionary };
125 __block CFDictionaryRef last_params = kd->auth_params.dictionary ? CFDictionaryCreateCopy(NULL, kd->auth_params.dictionary) : NULL;
126 __block TKTokenRef token = CFRetainSafe(kd->token);
127 __block CFTypeRef result = kCFNull;
128
129 CFErrorRef localError = NULL;
130 SecItemAuthDo(&auth_params, &localError, ^SecItemAuthResult(CFArrayRef *ac_pairs, CFErrorRef *error) {
131 if (!CFEqualSafe(last_params, auth_params.dictionary) || token == NULL) {
132 // token was not connected yet or auth_params were modified, so reconnect the token in order to update the attributes.
133 CFAssignRetained(token, SecCTKKeyCreateToken(key, auth_params.dictionary, &last_params, error));
134 if (token == NULL) {
135 return kSecItemAuthResultError;
136 }
137 }
138
139 result = kCFBooleanTrue;
140 if (mode == kSecKeyOperationModePerform) {
141 // Check, whether we are not trying to perform the operation with large data. If yes, explicitly do the check whether
142 // the operation is supported first, in order to avoid jetsam of target extension with operation type which is typically
143 // not supported by the extension at all.
144 // <rdar://problem/31762984> unable to decrypt large data with kSecKeyAlgorithmECIESEncryptionCofactorX963SHA256AESGCM
145 CFIndex inputSize = 0;
146 if (in1 != NULL && CFGetTypeID(in1) == CFDataGetTypeID()) {
147 inputSize += CFDataGetLength(in1);
148 }
149 if (in2 != NULL && CFGetTypeID(in2) == CFDataGetTypeID()) {
150 inputSize += CFDataGetLength(in2);
151 }
152 if (inputSize > 32 * 1024) {
153 result = TKTokenCopyOperationResult(token, kd->object_id, operation, algorithms, kSecKeyOperationModeCheckIfSupported,
154 NULL, NULL, error);
155 }
156 }
157
158 if (CFEqualSafe(result, kCFBooleanTrue)) {
159 result = TKTokenCopyOperationResult(token, kd->object_id, operation, algorithms, mode, in1, in2, error);
160 }
161
162 if (result != NULL) {
163 return kSecItemAuthResultOK;
164 }
165
166 CFStringRef AKSOperation = NULL;
167 switch (operation) {
168 case kSecKeyOperationTypeSign:
169 AKSOperation = kAKSKeyOpSign;
170 break;
171 case kSecKeyOperationTypeDecrypt: {
172 AKSOperation = kAKSKeyOpDecrypt;
173 if (in2 != NULL && CFGetTypeID(in2) == CFDictionaryGetTypeID() && CFDictionaryGetValue(in2, kSecKeyEncryptionParameterRecryptCertificate) != NULL) {
174 // This is actually recrypt operation, which is special separate AKS operation.
175 AKSOperation = kAKSKeyOpECIESTranscode;
176 }
177 break;
178 }
179 case kSecKeyOperationTypeKeyExchange:
180 AKSOperation = kAKSKeyOpComputeKey;
181 break;
182 default:
183 break;;
184 }
185 return SecCTKProcessError(AKSOperation, token, kd->object_id, ac_pairs, error);
186 }, ^{
187 CFAssignRetained(token, SecCTKKeyCreateToken(key, auth_params.dictionary, &last_params, NULL));
188 });
189
190 CFErrorPropagate(localError, error);
191 CFReleaseNull(auth_params.mutable_dictionary);
192 CFReleaseNull(token);
193 CFReleaseNull(last_params);
194 return result;
195 }
196
197 static size_t SecCTKKeyBlockSize(SecKeyRef key) {
198 SecCTKKeyData *kd = key->key;
199 CFTypeRef keySize = CFDictionaryGetValue(kd->attributes.dictionary, kSecAttrKeySizeInBits);
200 if (CFGetTypeID(keySize) == CFNumberGetTypeID()) {
201 CFIndex bitSize;
202 if (CFNumberGetValue(keySize, kCFNumberCFIndexType, &bitSize))
203 return (bitSize + 7) / 8;
204 }
205
206 return 0;
207 }
208
209 static OSStatus SecCTKKeyCopyPublicOctets(SecKeyRef key, CFDataRef *data) {
210 OSStatus status = errSecSuccess;
211 CFErrorRef error = NULL;
212 CFDataRef publicData = NULL;
213 TKTokenRef token = NULL;
214
215 SecCTKKeyData *kd = key->key;
216 require_action_quiet(token = SecCTKKeyCopyToken(key, &error), out, status = SecErrorGetOSStatus(error));
217 require_action_quiet(publicData = TKTokenCopyPublicKeyData(token, kd->object_id, &error), out,
218 status = SecErrorGetOSStatus(error));
219 *data = publicData;
220
221 out:
222 CFReleaseSafe(error);
223 CFReleaseSafe(token);
224 return status;
225 }
226
227 static CFStringRef SecCTKKeyCopyKeyDescription(SecKeyRef key) {
228 SecCTKKeyData *kd = key->key;
229 return CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("<SecKeyRef:('%@') %p>"),
230 CFDictionaryGetValue(kd->attributes.dictionary, kSecAttrTokenID), key);
231 }
232
233 // Attributes allowed to be exported from all internal key attributes.
234 static const CFStringRef *kSecExportableCTKKeyAttributes[] = {
235 &kSecClass,
236 &kSecAttrTokenID,
237 &kSecAttrKeyClass,
238 &kSecAttrAccessControl,
239 &kSecAttrIsPrivate,
240 &kSecAttrIsModifiable,
241 &kSecAttrKeyType,
242 &kSecAttrKeySizeInBits,
243 &kSecAttrEffectiveKeySize,
244 &kSecAttrIsSensitive,
245 &kSecAttrWasAlwaysSensitive,
246 &kSecAttrIsExtractable,
247 &kSecAttrWasNeverExtractable,
248 &kSecAttrCanEncrypt,
249 &kSecAttrCanDecrypt,
250 &kSecAttrCanDerive,
251 &kSecAttrCanSign,
252 &kSecAttrCanVerify,
253 &kSecAttrCanSignRecover,
254 &kSecAttrCanVerifyRecover,
255 &kSecAttrCanWrap,
256 &kSecAttrCanUnwrap,
257 NULL
258 };
259
260 static CFDictionaryRef SecCTKKeyCopyAttributeDictionary(SecKeyRef key) {
261 CFMutableDictionaryRef attrs = NULL;
262 CFErrorRef error = NULL;
263 CFDataRef publicData = NULL, digest = NULL;
264 TKTokenRef token = NULL;
265 SecCTKKeyData *kd = key->key;
266
267 // Encode ApplicationLabel as SHA1 digest of public key bytes.
268 require_quiet(token = SecCTKKeyCopyToken(key, &error), out);
269 require_quiet(publicData = TKTokenCopyPublicKeyData(token, kd->object_id, &error), out);
270
271 // Calculate the digest of the public key.
272 require_quiet(digest = SecSHA1DigestCreate(NULL, CFDataGetBytePtr(publicData), CFDataGetLength(publicData)), out);
273 attrs = CFDictionaryCreateMutableForCFTypes(CFGetAllocator(key));
274 CFDictionarySetValue(attrs, kSecAttrApplicationLabel, digest);
275
276 for (const CFStringRef **attrKey = &kSecExportableCTKKeyAttributes[0]; *attrKey != NULL; attrKey++) {
277 CFTypeRef value = CFDictionaryGetValue(kd->attributes.dictionary, **attrKey);
278 if (value != NULL) {
279 CFDictionarySetValue(attrs, **attrKey, value);
280 }
281 }
282
283 // Consistently with existing RSA and EC software keys implementation, mark all keys as permanent ones.
284 CFDictionarySetValue(attrs, kSecAttrIsPermanent, kCFBooleanTrue);
285
286 // Always export token_id and object_id.
287 CFDictionarySetValue(attrs, kSecAttrTokenID, kd->token_id);
288 CFDictionarySetValue(attrs, kSecAttrTokenOID, kd->object_id);
289
290 out:
291 CFReleaseSafe(error);
292 CFReleaseSafe(publicData);
293 CFReleaseSafe(digest);
294 CFReleaseSafe(token);
295 return attrs;
296 }
297
298 static SecKeyRef SecCTKKeyCreateDuplicate(SecKeyRef key);
299
300 static Boolean SecCTKKeySetParameter(SecKeyRef key, CFStringRef name, CFPropertyListRef value, CFErrorRef *error) {
301 SecCTKKeyData *kd = key->key;
302 CFTypeRef acm_reference = NULL;
303
304 static const CFStringRef *const knownUseFlags[] = {
305 &kSecUseOperationPrompt,
306 &kSecUseAuthenticationContext,
307 &kSecUseAuthenticationUI,
308 &kSecUseCallerName,
309 &kSecUseCredentialReference,
310 };
311
312 // Check, whether name is part of known use flags.
313 bool isUseFlag = false;
314 for (size_t i = 0; i < array_size(knownUseFlags); i++) {
315 if (CFEqual(*knownUseFlags[i], name)) {
316 isUseFlag = true;
317 break;
318 }
319 }
320
321 if (CFEqual(name, kSecUseAuthenticationContext)) {
322 // Preprocess LAContext to ACMRef value.
323 if (value != NULL) {
324 require_quiet(acm_reference = SecItemAttributesCopyPreparedAuthContext(value, error), out);
325 value = acm_reference;
326 }
327 name = kSecUseCredentialReference;
328 }
329
330 // Release existing token connection to enforce creation of new connection with new params.
331 CFReleaseNull(kd->token);
332
333 if (isUseFlag) {
334 if (value != NULL) {
335 CFDictionarySetValue(SecCFDictionaryCOWGetMutable(&kd->auth_params), name, value);
336 } else {
337 CFDictionaryRemoveValue(SecCFDictionaryCOWGetMutable(&kd->auth_params), name);
338 }
339 } else {
340 if (kd->params == NULL) {
341 kd->params = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
342 }
343 if (value != NULL) {
344 CFDictionarySetValue(kd->params, name, value);
345 } else {
346 CFDictionaryRemoveValue(kd->params, name);
347 }
348 }
349
350 out:
351 CFReleaseSafe(acm_reference);
352 return TRUE;
353 }
354
355 static SecKeyDescriptor kSecCTKKeyDescriptor = {
356 .version = kSecKeyDescriptorVersion,
357 .name = "CTKKey",
358 .extraBytes = sizeof(SecCTKKeyData),
359
360 .destroy = SecCTKKeyDestroy,
361 .blockSize = SecCTKKeyBlockSize,
362 .copyDictionary = SecCTKKeyCopyAttributeDictionary,
363 .describe = SecCTKKeyCopyKeyDescription,
364 .getAlgorithmID = SecCTKGetAlgorithmID,
365 .copyPublic = SecCTKKeyCopyPublicOctets,
366 .copyOperationResult = SecCTKKeyCopyOperationResult,
367 .createDuplicate = SecCTKKeyCreateDuplicate,
368 .setParameter = SecCTKKeySetParameter,
369 };
370
371 static SecKeyRef SecCTKKeyCreateDuplicate(SecKeyRef key) {
372 SecKeyRef result = SecKeyCreate(CFGetAllocator(key), &kSecCTKKeyDescriptor, 0, 0, 0);
373 SecCTKKeyData *kd = key->key, *rd = result->key;
374 rd->token = CFRetainSafe(kd->token);
375 rd->object_id = CFRetainSafe(kd->object_id);
376 rd->token_id = CFRetainSafe(kd->token_id);
377 if (kd->attributes.dictionary != NULL) {
378 rd->attributes.dictionary = kd->attributes.dictionary;
379 SecCFDictionaryCOWGetMutable(&rd->attributes);
380 }
381 if (kd->auth_params.dictionary != NULL) {
382 rd->auth_params.dictionary = kd->auth_params.dictionary;
383 SecCFDictionaryCOWGetMutable(&rd->auth_params);
384 }
385 return result;
386 }
387
388 SecKeyRef SecKeyCreateCTKKey(CFAllocatorRef allocator, CFDictionaryRef refAttributes, CFErrorRef *error) {
389 SecKeyRef result = NULL;
390 SecKeyRef key = SecKeyCreate(allocator, &kSecCTKKeyDescriptor, 0, 0, 0);
391 SecCTKKeyData *kd = key->key;
392 kd->token = CFRetainSafe(CFDictionaryGetValue(refAttributes, kSecUseToken));
393 kd->object_id = CFRetainSafe(CFDictionaryGetValue(refAttributes, kSecAttrTokenOID));
394 kd->token_id = CFRetainSafe(CFDictionaryGetValue(refAttributes, kSecAttrTokenID));
395 kd->attributes.dictionary = refAttributes;
396 CFDictionaryRemoveValue(SecCFDictionaryCOWGetMutable(&kd->attributes), kSecUseToken);
397 CFDictionaryRemoveValue(SecCFDictionaryCOWGetMutable(&kd->attributes), kSecAttrTokenOID);
398 SecItemAuthCopyParams(&kd->auth_params, &kd->attributes);
399 if (CFDictionaryGetValue(kd->attributes.dictionary, kSecAttrIsPrivate) == NULL) {
400 CFDictionarySetValue(SecCFDictionaryCOWGetMutable(&kd->attributes), kSecAttrIsPrivate, kCFBooleanTrue);
401 }
402
403 // Convert some attributes which are stored as numbers in iOS keychain but a lot of code counts that the values
404 // are actually strings as specified by kSecAttrXxx constants.
405 static const CFStringRef *numericAttributes[] = {
406 &kSecAttrKeyType,
407 &kSecAttrKeyClass,
408 NULL,
409 };
410
411 CFMutableDictionaryRef attrs = NULL;
412 if (kd->token == NULL) {
413 require_quiet(kd->token = SecCTKKeyCopyToken(key, error), out);
414 if (kd->token != NULL) {
415 attrs = CFDictionaryCreateMutableCopy(kCFAllocatorDefault, 0, kd->attributes.dictionary);
416 CFAssignRetained(kd->object_id, TKTokenCreateOrUpdateObject(kd->token, kd->object_id, attrs, error));
417 require_quiet(kd->object_id, out);
418 CFDictionaryForEach(attrs, ^(const void *key, const void *value) {
419 CFDictionaryAddValue(SecCFDictionaryCOWGetMutable(&kd->attributes), key, value);
420 });
421
422 CFTypeRef accc = CFDictionaryGetValue(kd->attributes.dictionary, kSecAttrAccessControl);
423 if (accc && CFDataGetTypeID() == CFGetTypeID(accc)) {
424 SecAccessControlRef ac = SecAccessControlCreateFromData(kCFAllocatorDefault, accc, error);
425 require_quiet(ac, out);
426 CFDictionarySetValue(SecCFDictionaryCOWGetMutable(&kd->attributes), kSecAttrAccessControl, ac);
427 CFRelease(ac);
428 }
429 CFDictionaryRemoveValue(SecCFDictionaryCOWGetMutable(&kd->attributes), kSecAttrTokenOID);
430 }
431 require_quiet(kd->token != NULL && kd->object_id != NULL, out);
432 }
433
434 for (const CFStringRef **attrName = &numericAttributes[0]; *attrName != NULL; attrName++) {
435 CFTypeRef value = CFDictionaryGetValue(kd->attributes.dictionary, **attrName);
436 if (value != NULL && CFGetTypeID(value) == CFNumberGetTypeID()) {
437 CFIndex number;
438 if (CFNumberGetValue(value, kCFNumberCFIndexType, &number)) {
439 CFStringRef newValue = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%ld"), (long)number);
440 if (newValue != NULL) {
441 CFDictionarySetValue(SecCFDictionaryCOWGetMutable(&kd->attributes), **attrName, newValue);
442 CFRelease(newValue);
443 }
444 }
445 }
446 }
447 result = (SecKeyRef)CFRetain(key);
448
449 out:
450 CFReleaseSafe(attrs);
451 CFReleaseSafe(key);
452 return result;
453 }
454
455 OSStatus SecCTKKeyGeneratePair(CFDictionaryRef parameters, SecKeyRef *publicKey, SecKeyRef *privateKey) {
456 OSStatus status;
457 __block SecCFDictionaryCOW attrs = { NULL };
458 CFDataRef publicData = NULL;
459
460 require_action_quiet(publicKey != NULL, out, status = errSecParam);
461 require_action_quiet(privateKey != NULL, out, status = errSecParam);
462
463 // Simply adding key on the token without value will cause the token to generate the key.
464 // Prepare dictionary specifying item to add.
465 attrs.dictionary = CFDictionaryGetValue(parameters, kSecPrivateKeyAttrs);
466
467 CFDictionaryForEach(parameters, ^(const void *key, const void *value) {
468 if (!CFEqual(key, kSecPrivateKeyAttrs) && !CFEqual(key, kSecPublicKeyAttrs)) {
469 CFDictionarySetValue(SecCFDictionaryCOWGetMutable(&attrs), key, value);
470 }
471 });
472 CFDictionaryRemoveValue(SecCFDictionaryCOWGetMutable(&attrs), kSecValueData);
473 CFDictionarySetValue(SecCFDictionaryCOWGetMutable(&attrs), kSecClass, kSecClassKey);
474 CFDictionarySetValue(SecCFDictionaryCOWGetMutable(&attrs), kSecAttrKeyClass, kSecAttrKeyClassPrivate);
475 CFDictionarySetValue(SecCFDictionaryCOWGetMutable(&attrs), kSecReturnRef, kCFBooleanTrue);
476
477 // Do not automatically store tke key into the keychain, caller will do it on its own if it is really requested.
478 CFDictionarySetValue(SecCFDictionaryCOWGetMutable(&attrs), kSecAttrIsPermanent, kCFBooleanFalse);
479
480 // Add key from given attributes to the token (having no data will cause the token to actually generate the key).
481 require_noerr_quiet(status = SecItemAdd(attrs.dictionary, (CFTypeRef *)privateKey), out);
482
483 // Create non-token public key.
484 require_noerr_quiet(status = SecCTKKeyCopyPublicOctets(*privateKey, &publicData), out);
485 if (CFEqualSafe(CFDictionaryGetValue(parameters, kSecAttrKeyType), kSecAttrKeyTypeEC) ||
486 CFEqualSafe(CFDictionaryGetValue(parameters, kSecAttrKeyType), kSecAttrKeyTypeECSECPrimeRandomPKA) ||
487 CFEqualSafe(CFDictionaryGetValue(parameters, kSecAttrKeyType), kSecAttrKeyTypeSecureEnclaveAttestation)) {
488 *publicKey = SecKeyCreateECPublicKey(NULL, CFDataGetBytePtr(publicData), CFDataGetLength(publicData),
489 kSecKeyEncodingBytes);
490 } else if (CFEqualSafe(CFDictionaryGetValue(parameters, kSecAttrKeyType), kSecAttrKeyTypeRSA)) {
491 *publicKey = SecKeyCreateRSAPublicKey(NULL, CFDataGetBytePtr(publicData), CFDataGetLength(publicData),
492 kSecKeyEncodingBytes);
493 }
494
495 if (*publicKey != NULL) {
496 status = errSecSuccess;
497 } else {
498 status = errSecInvalidKey;
499 CFReleaseNull(*privateKey);
500 }
501
502 out:
503 CFReleaseSafe(attrs.mutable_dictionary);
504 CFReleaseSafe(publicData);
505 return status;
506 }
507
508 const CFStringRef kSecKeyParameterSETokenAttestationNonce = CFSTR("com.apple.security.seckey.setoken.attestation-nonce");
509
510 SecKeyRef SecKeyCopyAttestationKey(SecKeyAttestationKeyType keyType, CFErrorRef *error) {
511 CFDictionaryRef attributes = NULL;
512 CFDataRef object_id = NULL;
513 SecKeyRef key = NULL;
514
515 // [[TKTLVBERRecord alloc] initWithPropertyList:[@"com.apple.setoken.sik" dataUsingEncoding:NSUTF8StringEncoding]].data
516 static const uint8_t sikObjectIDBytes[] = { 0x04, 21, 'c', 'o', 'm', '.', 'a', 'p', 'p', 'l', 'e', '.', 's', 'e', 't', 'o', 'k', 'e', 'n', '.', 's', 'i', 'k' };
517 // [[TKTLVBERRecord alloc] initWithPropertyList:[@"com.apple.setoken.gid" dataUsingEncoding:NSUTF8StringEncoding]].data
518 static const uint8_t gidObjectIDBytes[] = { 0x04, 21, 'c', 'o', 'm', '.', 'a', 'p', 'p', 'l', 'e', '.', 's', 'e', 't', 'o', 'k', 'e', 'n', '.', 'g', 'i', 'd' };
519 // [[TKTLVBERRecord alloc] initWithPropertyList:[@"com.apple.setoken.uikc" dataUsingEncoding:NSUTF8StringEncoding]].data
520 static const uint8_t uikCommittedObjectIDBytes[] = { 0x04, 22, 'c', 'o', 'm', '.', 'a', 'p', 'p', 'l', 'e', '.', 's', 'e', 't', 'o', 'k', 'e', 'n', '.', 'u', 'i', 'k', 'c' };
521 // [[TKTLVBERRecord alloc] initWithPropertyList:[@"com.apple.setoken.uikp" dataUsingEncoding:NSUTF8StringEncoding]].data
522 static const uint8_t uikProposedObjectIDBytes[] = { 0x04, 22, 'c', 'o', 'm', '.', 'a', 'p', 'p', 'l', 'e', '.', 's', 'e', 't', 'o', 'k', 'e', 'n', '.', 'u', 'i', 'k', 'p' };
523
524 static const uint8_t casdObjectIDBytes[] = { 0x04, 27, 'c', 'o', 'm', '.', 'a', 'p', 'p', 'l', 'e', '.', 's', 'e', 'c', 'e', 'l', 'e', 'm', 't', 'o', 'k', 'e', 'n', '.', 'c', 'a', 's', 'd' };
525
526 CFStringRef token = kSecAttrTokenIDAppleKeyStore;
527
528 switch (keyType) {
529 case kSecKeyAttestationKeyTypeSIK:
530 object_id = CFDataCreate(kCFAllocatorDefault, sikObjectIDBytes, sizeof(sikObjectIDBytes));
531 break;
532 case kSecKeyAttestationKeyTypeGID:
533 object_id = CFDataCreate(kCFAllocatorDefault, gidObjectIDBytes, sizeof(gidObjectIDBytes));
534 break;
535 case kSecKeyAttestationKeyTypeUIKCommitted:
536 object_id = CFDataCreate(kCFAllocatorDefault, uikCommittedObjectIDBytes, sizeof(uikCommittedObjectIDBytes));
537 break;
538 case kSecKeyAttestationKeyTypeUIKProposed:
539 object_id = CFDataCreate(kCFAllocatorDefault, uikProposedObjectIDBytes, sizeof(uikProposedObjectIDBytes));
540 break;
541 case kSecKeyAttestationKeyTypeSecureElement:
542 object_id = CFDataCreate(kCFAllocatorDefault, casdObjectIDBytes, sizeof(casdObjectIDBytes));
543 token = kSecAttrTokenIDSecureElement;
544 break;
545 default:
546 SecError(errSecParam, error, CFSTR("unexpected attestation key type %d"), (int)keyType);
547 goto out;
548 }
549
550 attributes = CFDictionaryCreateForCFTypes(kCFAllocatorDefault,
551 kSecAttrTokenOID, object_id,
552 kSecAttrTokenID, token,
553 NULL);
554 key = SecKeyCreateCTKKey(kCFAllocatorDefault, attributes, error);
555
556 out:
557 CFReleaseSafe(attributes);
558 CFReleaseSafe(object_id);
559 return key;
560 }
561
562 CFDataRef SecKeyCreateAttestation(SecKeyRef key, SecKeyRef keyToAttest, CFErrorRef *error) {
563 __block CFDictionaryRef attributes = NULL, outputAttributes = NULL;
564 CFDataRef attestationData = NULL;
565 CFErrorRef localError = NULL;
566 SecCTKKeyData *attestingKeyData = key->key;
567 SecCTKKeyData *keyToAttestData = keyToAttest->key;
568 __block TKTokenRef token = NULL;
569
570 if (error == NULL) {
571 error = &localError;
572 }
573
574 __block SecCFDictionaryCOW auth_params = { keyToAttestData->auth_params.dictionary };
575
576 require_action_quiet(key->key_class == &kSecCTKKeyDescriptor, out,
577 SecError(errSecUnsupportedOperation, error, CFSTR("attestation not supported by key %@"), key));
578 require_action_quiet(keyToAttest->key_class == &kSecCTKKeyDescriptor, out,
579 SecError(errSecUnsupportedOperation, error, CFSTR("attestation not supported for key %@"), keyToAttest));
580
581 attributes = CFDictionaryCreateForCFTypes(kCFAllocatorDefault,
582 CFSTR(kTKTokenControlAttribAttestingKey), attestingKeyData->object_id,
583 CFSTR(kTKTokenControlAttribKeyToAttest), keyToAttestData->object_id,
584 NULL);
585
586 bool ok = SecItemAuthDo(&auth_params, error, ^SecItemAuthResult(CFArrayRef *ac_pairs, CFErrorRef *error) {
587 if (auth_params.mutable_dictionary != NULL || token == NULL) {
588 CFAssignRetained(token, SecCTKKeyCopyToken(key, error));
589 if (token == NULL) {
590 return kSecItemAuthResultError;
591 }
592 }
593
594 outputAttributes = TKTokenControl(token, attributes, error);
595 return outputAttributes ? kSecItemAuthResultOK : SecCTKProcessError(kAKSKeyOpAttest, keyToAttestData->token, keyToAttestData->object_id, ac_pairs, error);
596 }, NULL);
597 require_quiet(ok, out);
598 require_action_quiet(attestationData = CFRetainSafe(CFDictionaryGetValue(outputAttributes, CFSTR(kTKTokenControlAttribAttestationData))),
599 out, SecError(errSecInternal, error, CFSTR("could not get attestation data")));
600
601 out:
602 CFReleaseSafe(attributes);
603 CFReleaseSafe(outputAttributes);
604 CFReleaseSafe(localError);
605 CFReleaseSafe(auth_params.mutable_dictionary);
606 CFReleaseSafe(token);
607 return attestationData;
608 }
609
610 Boolean SecKeyControlLifetime(SecKeyRef key, SecKeyControlLifetimeType type, CFErrorRef *error) {
611 NSError *localError;
612 __block id token;
613 if (error == NULL) {
614 error = (void *)&localError;
615 }
616
617 SecCTKKeyData *keyData = key->key;
618 NSDictionary *attributes = @{
619 @kTKTokenControlAttribLifetimeControlKey: (__bridge NSData *)keyData->object_id,
620 @kTKTokenControlAttribLifetimeType: @(type),
621 };
622
623 if (key->key_class != &kSecCTKKeyDescriptor) {
624 return SecError(errSecUnsupportedOperation, error, CFSTR("lifetimecontrol not supported for key %@"), key);
625 }
626
627 __block SecCFDictionaryCOW auth_params = { keyData->auth_params.dictionary };
628 return SecItemAuthDo(&auth_params, error, ^SecItemAuthResult(CFArrayRef *ac_pairs, CFErrorRef *error) {
629 if (auth_params.mutable_dictionary != NULL || token == NULL) {
630 token = CFBridgingRelease(SecCTKKeyCopyToken(key, error));
631 if (token == nil) {
632 return kSecItemAuthResultError;
633 }
634 }
635
636 NSDictionary *outputAttributes = CFBridgingRelease(TKTokenControl((__bridge TKTokenRef)token, (__bridge CFDictionaryRef)attributes, error));
637 return outputAttributes ? kSecItemAuthResultOK : kSecItemAuthResultError;
638 }, NULL);
639 }
640
641 #if TKTOKEN_CLIENT_INTERFACE_VERSION < 5
642 #define kTKTokenCreateAttributeTestMode "testmode"
643 #endif
644
645 void SecCTKKeySetTestMode(CFStringRef tokenID, CFTypeRef enable) {
646 CFErrorRef error = NULL;
647 CFDictionaryRef options = CFDictionaryCreateForCFTypes(kCFAllocatorDefault, kSecAttrTokenID, tokenID, @kTKTokenCreateAttributeTestMode, enable, nil);
648 TKTokenRef token = TKTokenCreate(options, &error);
649 if (token == NULL) {
650 secerror("Failed to set token attributes %@: error %@", options, error);
651 }
652 CFReleaseNull(options);
653 CFReleaseNull(error);
654 CFReleaseNull(token);
655 }