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