2 * Copyright (c) 2015 Apple Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
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>
38 #include "SecRSAKey.h"
39 #include "SecCTKKeyPriv.h"
41 const CFStringRef kSecUseToken
= CFSTR("u_Token");
47 SecCFDictionaryCOW auth_params
;
48 SecCFDictionaryCOW attributes
;
49 CFMutableDictionaryRef params
;
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
);
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
;
69 return kSecRSAAlgorithmID
;
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
));
80 CFReleaseNull(*error
);
82 CFRelease(access_control
);
83 return kSecItemAuthResultNeedAuth
;
86 return kSecItemAuthResultError
;
89 static const CFTypeRef
*aclOperations
[] = {
90 [kSecKeyOperationTypeSign
] = &kAKSKeyOpSign
,
91 [kSecKeyOperationTypeDecrypt
] = &kAKSKeyOpDecrypt
,
92 [kSecKeyOperationTypeKeyExchange
] = &kAKSKeyOpComputeKey
,
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
);
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
);
108 CFReleaseNull(attributes
.mutable_dictionary
);
112 static TKTokenRef
SecCTKKeyCopyToken(SecKeyRef key
, CFErrorRef
*error
) {
113 SecCTKKeyData
*kd
= key
->key
;
114 TKTokenRef token
= CFRetainSafe(kd
->token
);
116 token
= SecCTKKeyCreateToken(key
, kd
->auth_params
.dictionary
, NULL
, error
);
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
;
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
));
136 return kSecItemAuthResultError
;
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
);
150 if (in2
!= NULL
&& CFGetTypeID(in2
) == CFDataGetTypeID()) {
151 inputSize
+= CFDataGetLength(in2
);
153 if (inputSize
> 32 * 1024) {
154 result
= TKTokenCopyOperationResult(token
, kd
->object_id
, operation
, algorithms
, kSecKeyOperationModeCheckIfSupported
,
159 if (CFEqualSafe(result
, kCFBooleanTrue
)) {
160 result
= TKTokenCopyOperationResult(token
, kd
->object_id
, operation
, algorithms
, mode
, in1
, in2
, error
);
162 return (result
!= NULL
) ? kSecItemAuthResultOK
: SecCTKProcessError(*aclOperations
[operation
], token
,
163 kd
->object_id
, ac_pairs
, error
);
165 CFAssignRetained(token
, SecCTKKeyCreateToken(key
, auth_params
.dictionary
, &last_params
, NULL
));
168 CFErrorPropagate(localError
, error
);
169 CFReleaseNull(auth_params
.mutable_dictionary
);
170 CFReleaseNull(token
);
171 CFReleaseNull(last_params
);
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()) {
180 if (CFNumberGetValue(keySize
, kCFNumberCFIndexType
, &bitSize
))
181 return (bitSize
+ 7) / 8;
187 static OSStatus
SecCTKKeyCopyPublicOctets(SecKeyRef key
, CFDataRef
*data
) {
188 OSStatus status
= errSecSuccess
;
189 CFErrorRef error
= NULL
;
190 CFDataRef publicData
= NULL
;
191 TKTokenRef token
= NULL
;
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
));
200 CFReleaseSafe(error
);
201 CFReleaseSafe(token
);
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
);
211 // Attributes allowed to be exported from all internal key attributes.
212 static const CFStringRef
*kSecExportableCTKKeyAttributes
[] = {
216 &kSecAttrAccessControl
,
218 &kSecAttrIsModifiable
,
220 &kSecAttrKeySizeInBits
,
221 &kSecAttrEffectiveKeySize
,
222 &kSecAttrIsSensitive
,
223 &kSecAttrWasAlwaysSensitive
,
224 &kSecAttrIsExtractable
,
225 &kSecAttrWasNeverExtractable
,
231 &kSecAttrCanSignRecover
,
232 &kSecAttrCanVerifyRecover
,
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
;
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
);
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
);
254 for (const CFStringRef
**attrKey
= &kSecExportableCTKKeyAttributes
[0]; *attrKey
!= NULL
; attrKey
++) {
255 CFTypeRef value
= CFDictionaryGetValue(kd
->attributes
.dictionary
, **attrKey
);
257 CFDictionarySetValue(attrs
, **attrKey
, value
);
261 // Consistently with existing RSA and EC software keys implementation, mark all keys as permanent ones.
262 CFDictionarySetValue(attrs
, kSecAttrIsPermanent
, kCFBooleanTrue
);
264 // Always export token_id and object_id.
265 CFDictionarySetValue(attrs
, kSecAttrTokenID
, kd
->token_id
);
266 CFDictionarySetValue(attrs
, kSecAttrTokenOID
, kd
->object_id
);
269 CFReleaseSafe(error
);
270 CFReleaseSafe(publicData
);
271 CFReleaseSafe(digest
);
272 CFReleaseSafe(token
);
276 static SecKeyRef
SecCTKKeyCreateDuplicate(SecKeyRef key
);
278 static Boolean
SecCTKKeySetParameter(SecKeyRef key
, CFStringRef name
, CFPropertyListRef value
, CFErrorRef
*error
) {
279 SecCTKKeyData
*kd
= key
->key
;
280 CFTypeRef acm_reference
= NULL
;
282 static const CFStringRef
*const knownUseFlags
[] = {
283 &kSecUseOperationPrompt
,
284 &kSecUseAuthenticationContext
,
285 &kSecUseAuthenticationUI
,
287 &kSecUseCredentialReference
,
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
)) {
299 if (CFEqual(name
, kSecUseAuthenticationContext
)) {
300 // Preprocess LAContext to ACMRef value.
302 require_quiet(acm_reference
= SecItemAttributesCopyPreparedAuthContext(value
, error
), out
);
303 value
= acm_reference
;
305 name
= kSecUseCredentialReference
;
308 // Release existing token connection to enforce creation of new connection with new params.
309 CFReleaseNull(kd
->token
);
313 CFDictionarySetValue(SecCFDictionaryCOWGetMutable(&kd
->auth_params
), name
, value
);
315 CFDictionaryRemoveValue(SecCFDictionaryCOWGetMutable(&kd
->auth_params
), name
);
318 if (kd
->params
== NULL
) {
319 kd
->params
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
322 CFDictionarySetValue(kd
->params
, name
, value
);
324 CFDictionaryRemoveValue(kd
->params
, name
);
329 CFReleaseSafe(acm_reference
);
333 static SecKeyDescriptor kSecCTKKeyDescriptor
= {
334 .version
= kSecKeyDescriptorVersion
,
336 .extraBytes
= sizeof(SecCTKKeyData
),
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
,
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
);
359 if (kd
->auth_params
.dictionary
!= NULL
) {
360 rd
->auth_params
.dictionary
= kd
->auth_params
.dictionary
;
361 SecCFDictionaryCOWGetMutable(&rd
->auth_params
);
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
);
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
[] = {
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
);
396 CFDictionaryRemoveValue(SecCFDictionaryCOWGetMutable(&kd
->attributes
), kSecAttrTokenOID
);
397 CFReleaseSafe(attrs
);
400 if (kd
->token
== NULL
|| kd
->object_id
== 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()) {
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
);
424 OSStatus
SecCTKKeyGeneratePair(CFDictionaryRef parameters
, SecKeyRef
*publicKey
, SecKeyRef
*privateKey
) {
426 __block SecCFDictionaryCOW attrs
= { NULL
};
427 CFDataRef publicData
= NULL
;
429 require_action_quiet(publicKey
!= NULL
, out
, status
= errSecParam
);
430 require_action_quiet(privateKey
!= NULL
, out
, status
= errSecParam
);
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
);
436 CFDictionaryForEach(parameters
, ^(const void *key
, const void *value
) {
437 if (!CFEqual(key
, kSecPrivateKeyAttrs
) && !CFEqual(key
, kSecPublicKeyAttrs
)) {
438 CFDictionarySetValue(SecCFDictionaryCOWGetMutable(&attrs
), key
, value
);
441 CFDictionaryRemoveValue(SecCFDictionaryCOWGetMutable(&attrs
), kSecValueData
);
442 CFDictionarySetValue(SecCFDictionaryCOWGetMutable(&attrs
), kSecClass
, kSecClassKey
);
443 CFDictionarySetValue(SecCFDictionaryCOWGetMutable(&attrs
), kSecAttrKeyClass
, kSecAttrKeyClassPrivate
);
444 CFDictionarySetValue(SecCFDictionaryCOWGetMutable(&attrs
), kSecReturnRef
, kCFBooleanTrue
);
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
);
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
);
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
);
464 if (*publicKey
!= NULL
) {
465 status
= errSecSuccess
;
467 status
= errSecInvalidKey
;
468 CFReleaseNull(*privateKey
);
472 CFReleaseSafe(attrs
.mutable_dictionary
);
473 CFReleaseSafe(publicData
);
477 const CFStringRef kSecKeyParameterSETokenAttestationNonce
= CFSTR("com.apple.security.seckey.setoken.attestation-nonce");
479 SecKeyRef
SecKeyCopyAttestationKey(SecKeyAttestationKeyType keyType
, CFErrorRef
*error
) {
480 CFDictionaryRef attributes
= NULL
;
481 CFDataRef object_id
= NULL
;
482 SecKeyRef key
= NULL
;
484 require_action_quiet(keyType
== kSecKeyAttestationKeyTypeSIK
|| keyType
== kSecKeyAttestationKeyTypeGID
, out
,
485 SecError(errSecParam
, error
, CFSTR("unexpected attestation key type %u"), (unsigned)keyType
));
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' };
492 object_id
= (keyType
== kSecKeyAttestationKeyTypeSIK
?
493 CFDataCreate(kCFAllocatorDefault
, sikObjectIDBytes
, sizeof(sikObjectIDBytes
)) :
494 CFDataCreate(kCFAllocatorDefault
, gidObjectIDBytes
, sizeof(gidObjectIDBytes
)));
496 attributes
= CFDictionaryCreateForCFTypes(kCFAllocatorDefault
,
497 kSecAttrTokenOID
, object_id
,
498 kSecAttrTokenID
, kSecAttrTokenIDAppleKeyStore
,
500 key
= SecKeyCreateCTKKey(kCFAllocatorDefault
, attributes
, error
);
503 CFReleaseSafe(attributes
);
504 CFReleaseSafe(object_id
);
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
;
520 __block SecCFDictionaryCOW auth_params
= { keyToAttestData
->auth_params
.dictionary
};
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
));
527 attributes
= CFDictionaryCreateForCFTypes(kCFAllocatorDefault
,
528 CFSTR(kTKTokenControlAttribAttestingKey
), attestingKeyData
->object_id
,
529 CFSTR(kTKTokenControlAttribKeyToAttest
), keyToAttestData
->object_id
,
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
));
536 return kSecItemAuthResultError
;
540 outputAttributes
= TKTokenControl(token
, attributes
, error
);
541 return outputAttributes
? kSecItemAuthResultOK
: SecCTKProcessError(kAKSKeyOpAttest
, keyToAttestData
->token
, keyToAttestData
->object_id
, ac_pairs
, error
);
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")));
548 CFReleaseSafe(attributes
);
549 CFReleaseSafe(outputAttributes
);
550 CFReleaseSafe(localError
);
551 CFReleaseSafe(auth_params
.mutable_dictionary
);
552 CFReleaseSafe(token
);
553 return attestationData
;