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