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