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