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");
42 const CFStringRef kSecUseTokenObjectID
= CFSTR("u_TokenOID");
48 SecCFDictionaryCOW auth_params
;
49 SecCFDictionaryCOW attributes
;
50 CFMutableDictionaryRef params
;
53 static void SecCTKKeyDestroy(SecKeyRef key
) {
54 SecCTKKeyData
*kd
= key
->key
;
55 CFReleaseSafe(kd
->token
);
56 CFReleaseSafe(kd
->token_id
);
57 CFReleaseSafe(kd
->objectID
);
58 CFReleaseSafe(kd
->auth_params
.mutable_dictionary
);
59 CFReleaseSafe(kd
->attributes
.mutable_dictionary
);
60 CFReleaseSafe(kd
->params
);
63 static CFIndex
SecCTKGetAlgorithmID(SecKeyRef key
) {
64 SecCTKKeyData
*kd
= key
->key
;
65 if (CFEqualSafe(CFDictionaryGetValue(kd
->attributes
.dictionary
, kSecAttrKeyType
), kSecAttrKeyTypeECSECPrimeRandom
)) {
66 return kSecECDSAAlgorithmID
;
68 return kSecRSAAlgorithmID
;
71 static SecItemAuthResult
SecCTKProcessError(CFStringRef operation
, TKTokenRef token
, CFDataRef object_id
, CFArrayRef
*ac_pairs
, CFErrorRef
*error
) {
72 if (CFEqualSafe(CFErrorGetDomain(*error
), CFSTR(kTKErrorDomain
)) &&
73 CFErrorGetCode(*error
) == kTKErrorCodeAuthenticationFailed
) {
74 CFDataRef access_control
= TKTokenCopyObjectAccessControl(token
, object_id
, error
);
75 if (access_control
!= NULL
) {
76 CFArrayRef ac_pair
= CFArrayCreateForCFTypes(NULL
, access_control
, operation
, NULL
);
77 CFAssignRetained(*ac_pairs
, CFArrayCreateForCFTypes(NULL
, ac_pair
, NULL
));
79 CFReleaseNull(*error
);
81 CFRelease(access_control
);
82 return kSecItemAuthResultNeedAuth
;
85 return kSecItemAuthResultError
;
88 static const CFTypeRef
*aclOperations
[] = {
89 [kSecKeyOperationTypeSign
] = &kAKSKeyOpSign
,
90 [kSecKeyOperationTypeDecrypt
] = &kAKSKeyOpDecrypt
,
91 [kSecKeyOperationTypeKeyExchange
] = &kAKSKeyOpComputeKey
,
94 static CFTypeRef
SecCTKKeyCopyOperationResult(SecKeyRef key
, SecKeyOperationType operation
, SecKeyAlgorithm algorithm
,
95 CFArrayRef algorithms
, SecKeyOperationMode mode
,
96 CFTypeRef in1
, CFTypeRef in2
, CFErrorRef
*error
) {
97 SecCTKKeyData
*kd
= key
->key
;
98 __block SecCFDictionaryCOW auth_params
= { kd
->auth_params
.dictionary
};
99 __block TKTokenRef token
= CFRetainSafe(kd
->token
);
100 __block CFTypeRef result
= kCFNull
;
102 CFErrorRef localError
= NULL
;
103 SecItemAuthDo(&auth_params
, &localError
, ^SecItemAuthResult(CFDictionaryRef ap
, CFArrayRef
*ac_pairs
, CFErrorRef
*error
) {
104 if (auth_params
.mutable_dictionary
!= NULL
|| token
== NULL
|| kd
->params
!= NULL
) {
105 // token was not connected yet or auth_params were modified, so reconnect the token in order to update the attributes.
106 SecCFDictionaryCOW attributes
= { ap
};
107 if (kd
->params
&& CFDictionaryGetCount(kd
->params
) > 0) {
108 CFDictionarySetValue(SecCFDictionaryCOWGetMutable(&attributes
), CFSTR(kTKTokenCreateAttributeAuxParams
), kd
->params
);
110 CFAssignRetained(token
, SecTokenCreate(kd
->token_id
, attributes
.dictionary
, error
));
111 CFReleaseSafe(attributes
.mutable_dictionary
);
113 return kSecItemAuthResultError
;
117 result
= TKTokenCopyOperationResult(token
, kd
->objectID
, operation
, algorithms
, mode
, in1
, in2
, error
);
118 return (result
!= NULL
) ? kSecItemAuthResultOK
: SecCTKProcessError(*aclOperations
[operation
], token
,
119 kd
->objectID
, ac_pairs
, error
);
122 CFErrorPropagate(localError
, error
);
123 CFReleaseSafe(auth_params
.mutable_dictionary
);
124 CFReleaseSafe(token
);
128 static size_t SecCTKKeyBlockSize(SecKeyRef key
) {
129 SecCTKKeyData
*kd
= key
->key
;
130 CFTypeRef keySize
= CFDictionaryGetValue(kd
->attributes
.dictionary
, kSecAttrKeySizeInBits
);
131 if (CFGetTypeID(keySize
) == CFNumberGetTypeID()) {
133 if (CFNumberGetValue(keySize
, kCFNumberCFIndexType
, &bitSize
))
134 return (bitSize
+ 7) / 8;
140 static OSStatus
SecCTKKeyCopyPublicOctets(SecKeyRef key
, CFDataRef
*data
) {
141 OSStatus status
= errSecSuccess
;
142 CFErrorRef error
= NULL
;
143 CFDataRef publicData
= NULL
;
145 SecCTKKeyData
*kd
= key
->key
;
146 require_action_quiet(publicData
= TKTokenCopyPublicKeyData(kd
->token
, kd
->objectID
, &error
), out
,
147 status
= SecErrorGetOSStatus(error
));
151 CFReleaseSafe(error
);
155 static CFStringRef
SecCTKKeyCopyKeyDescription(SecKeyRef key
) {
156 SecCTKKeyData
*kd
= key
->key
;
157 return CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
, CFSTR("<SecKeyRef:('%@') %p>"),
158 CFDictionaryGetValue(kd
->attributes
.dictionary
, kSecAttrTokenID
), key
);
161 // Attributes allowed to be exported from all internal key attributes.
162 static const CFStringRef
*kSecExportableCTKKeyAttributes
[] = {
166 &kSecAttrIsPermanent
,
168 &kSecAttrIsModifiable
,
170 &kSecAttrKeySizeInBits
,
171 &kSecAttrEffectiveKeySize
,
172 &kSecAttrIsSensitive
,
173 &kSecAttrWasAlwaysSensitive
,
174 &kSecAttrIsExtractable
,
175 &kSecAttrWasNeverExtractable
,
181 &kSecAttrCanSignRecover
,
182 &kSecAttrCanVerifyRecover
,
188 static CFDictionaryRef
SecCTKKeyCopyAttributeDictionary(SecKeyRef key
) {
189 CFMutableDictionaryRef attrs
= NULL
;
190 CFErrorRef error
= NULL
;
191 CFDataRef publicData
= NULL
, digest
= NULL
;
192 SecCTKKeyData
*kd
= key
->key
;
194 // Encode ApplicationLabel as SHA1 digest of public key bytes.
195 require_quiet(publicData
= TKTokenCopyPublicKeyData(kd
->token
, kd
->objectID
, &error
), out
);
197 /* Calculate the digest of the public key. */
198 require(digest
= SecSHA1DigestCreate(NULL
, CFDataGetBytePtr(publicData
), CFDataGetLength(publicData
)), out
);
199 attrs
= CFDictionaryCreateMutableForCFTypes(CFGetAllocator(key
));
200 CFDictionarySetValue(attrs
, kSecAttrApplicationLabel
, digest
);
202 for (const CFStringRef
**attrKey
= &kSecExportableCTKKeyAttributes
[0]; *attrKey
!= NULL
; attrKey
++) {
203 CFTypeRef value
= CFDictionaryGetValue(kd
->attributes
.dictionary
, **attrKey
);
205 CFDictionarySetValue(attrs
, **attrKey
, value
);
210 CFReleaseSafe(error
);
211 CFReleaseSafe(publicData
);
212 CFReleaseSafe(digest
);
216 static SecKeyRef
SecCTKKeyCreateDuplicate(SecKeyRef key
);
218 static Boolean
SecCTKKeySetParameter(SecKeyRef key
, CFStringRef name
, CFPropertyListRef value
, CFErrorRef
*error
) {
219 SecCTKKeyData
*kd
= key
->key
;
220 CFTypeRef acm_reference
= NULL
;
222 static const CFStringRef
*const knownUseFlags
[] = {
223 &kSecUseOperationPrompt
,
224 &kSecUseAuthenticationContext
,
225 &kSecUseAuthenticationUI
,
227 &kSecUseCredentialReference
,
230 // Check, whether name is part of known use flags.
231 bool isUseFlag
= false;
232 for (size_t i
= 0; i
< array_size(knownUseFlags
); i
++) {
233 if (CFEqual(*knownUseFlags
[i
], name
)) {
239 if (CFEqual(name
, kSecUseAuthenticationContext
)) {
240 // Preprocess LAContext to ACMRef value.
242 require_quiet(acm_reference
= SecItemAttributesCopyPreparedAuthContext(value
, error
), out
);
243 value
= acm_reference
;
245 name
= kSecUseCredentialReference
;
249 // Release existing token connection to enforce creation of new connection with new auth params.
250 CFReleaseNull(kd
->token
);
252 CFDictionarySetValue(SecCFDictionaryCOWGetMutable(&kd
->auth_params
), name
, value
);
254 CFDictionaryRemoveValue(SecCFDictionaryCOWGetMutable(&kd
->auth_params
), name
);
257 if (kd
->params
== NULL
) {
258 kd
->params
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
261 CFDictionarySetValue(kd
->params
, name
, value
);
263 CFDictionaryRemoveValue(kd
->params
, name
);
268 CFReleaseSafe(acm_reference
);
272 static SecKeyDescriptor kSecCTKKeyDescriptor
= {
273 .version
= kSecKeyDescriptorVersion
,
275 .extraBytes
= sizeof(SecCTKKeyData
),
277 .destroy
= SecCTKKeyDestroy
,
278 .blockSize
= SecCTKKeyBlockSize
,
279 .copyDictionary
= SecCTKKeyCopyAttributeDictionary
,
280 .describe
= SecCTKKeyCopyKeyDescription
,
281 .getAlgorithmID
= SecCTKGetAlgorithmID
,
282 .copyPublic
= SecCTKKeyCopyPublicOctets
,
283 .copyOperationResult
= SecCTKKeyCopyOperationResult
,
284 .createDuplicate
= SecCTKKeyCreateDuplicate
,
285 .setParameter
= SecCTKKeySetParameter
,
288 static SecKeyRef
SecCTKKeyCreateDuplicate(SecKeyRef key
) {
289 SecKeyRef result
= SecKeyCreate(CFGetAllocator(key
), &kSecCTKKeyDescriptor
, 0, 0, 0);
290 SecCTKKeyData
*kd
= key
->key
, *rd
= result
->key
;
291 rd
->token
= CFRetainSafe(kd
->token
);
292 rd
->objectID
= CFRetainSafe(kd
->objectID
);
293 rd
->token_id
= CFRetainSafe(kd
->token_id
);
294 if (kd
->attributes
.dictionary
!= NULL
) {
295 rd
->attributes
.dictionary
= kd
->attributes
.dictionary
;
296 SecCFDictionaryCOWGetMutable(&rd
->attributes
);
298 if (kd
->auth_params
.dictionary
!= NULL
) {
299 rd
->auth_params
.dictionary
= kd
->auth_params
.dictionary
;
300 SecCFDictionaryCOWGetMutable(&rd
->auth_params
);
305 SecKeyRef
SecKeyCreateCTKKey(CFAllocatorRef allocator
, CFDictionaryRef refAttributes
, CFErrorRef
*error
) {
306 SecKeyRef key
= SecKeyCreate(allocator
, &kSecCTKKeyDescriptor
, 0, 0, 0);
307 SecCTKKeyData
*kd
= key
->key
;
308 kd
->token
= CFRetainSafe(CFDictionaryGetValue(refAttributes
, kSecUseToken
));
309 kd
->objectID
= CFRetainSafe(CFDictionaryGetValue(refAttributes
, kSecUseTokenObjectID
));
310 kd
->token_id
= CFRetainSafe(CFDictionaryGetValue(refAttributes
, kSecAttrTokenID
));
311 kd
->attributes
.dictionary
= refAttributes
;
312 CFDictionaryRemoveValue(SecCFDictionaryCOWGetMutable(&kd
->attributes
), kSecUseToken
);
313 CFDictionaryRemoveValue(SecCFDictionaryCOWGetMutable(&kd
->attributes
), kSecUseTokenObjectID
);
314 SecItemAuthCopyParams(&kd
->auth_params
, &kd
->attributes
);
315 if (CFDictionaryGetValue(kd
->attributes
.dictionary
, kSecAttrIsPrivate
) == NULL
) {
316 CFDictionarySetValue(SecCFDictionaryCOWGetMutable(&kd
->attributes
), kSecAttrIsPrivate
, kCFBooleanTrue
);
319 // Convert some attributes which are stored as numbers in iOS keychain but a lot of code counts that the values
320 // are actually strings as specified by kSecAttrXxx constants.
321 static const CFStringRef
*numericAttributes
[] = {
327 for (const CFStringRef
**attrName
= &numericAttributes
[0]; *attrName
!= NULL
; attrName
++) {
328 CFTypeRef value
= CFDictionaryGetValue(kd
->attributes
.dictionary
, **attrName
);
329 if (value
!= NULL
&& CFGetTypeID(value
) == CFNumberGetTypeID()) {
331 if (CFNumberGetValue(value
, kCFNumberCFIndexType
, &number
)) {
332 CFStringRef newValue
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
, CFSTR("%ld"), (long)number
);
333 if (newValue
!= NULL
) {
334 CFDictionarySetValue(SecCFDictionaryCOWGetMutable(&kd
->attributes
), **attrName
, newValue
);
344 OSStatus
SecCTKKeyGeneratePair(CFDictionaryRef parameters
, SecKeyRef
*publicKey
, SecKeyRef
*privateKey
) {
346 CFMutableDictionaryRef attrs
= NULL
;
347 CFDictionaryRef keyAttrs
= NULL
;
348 CFDataRef publicData
= NULL
;
350 require_action_quiet(publicKey
!= NULL
, out
, status
= errSecParam
);
351 require_action_quiet(privateKey
!= NULL
, out
, status
= errSecParam
);
353 // Simply adding key on the token without value will cause the token to generate the key and automatically
354 // add it to the keychain. Prepare dictionary specifying item to add.
355 keyAttrs
= CFDictionaryGetValue(parameters
, kSecPrivateKeyAttrs
);
356 attrs
= (keyAttrs
== NULL
) ? CFDictionaryCreateMutableForCFTypes(NULL
) : CFDictionaryCreateMutableCopy(NULL
, 0, keyAttrs
);
358 CFDictionaryForEach(parameters
, ^(const void *key
, const void *value
) {
359 if (!CFEqual(key
, kSecPrivateKeyAttrs
) && !CFEqual(key
, kSecPublicKeyAttrs
)) {
360 CFDictionarySetValue(attrs
, key
, value
);
363 CFDictionaryRemoveValue(attrs
, kSecValueData
);
364 CFDictionarySetValue(attrs
, kSecClass
, kSecClassKey
);
365 CFDictionarySetValue(attrs
, kSecAttrKeyClass
, kSecAttrKeyClassPrivate
);
366 CFDictionarySetValue(attrs
, kSecReturnRef
, kCFBooleanTrue
);
368 // Add key from given attributes to the token (having no data will cause the token to actually generate the key).
369 require_noerr_quiet(status
= SecItemAdd(attrs
, (CFTypeRef
*)privateKey
), out
);
371 // Create non-token public key.
372 require_noerr_quiet(status
= SecCTKKeyCopyPublicOctets(*privateKey
, &publicData
), out
);
373 if (CFEqualSafe(CFDictionaryGetValue(parameters
, kSecAttrKeyType
), kSecAttrKeyTypeEC
)) {
374 *publicKey
= SecKeyCreateECPublicKey(NULL
, CFDataGetBytePtr(publicData
), CFDataGetLength(publicData
),
375 kSecKeyEncodingBytes
);
376 } else if (CFEqualSafe(CFDictionaryGetValue(parameters
, kSecAttrKeyType
), kSecAttrKeyTypeRSA
)) {
377 *publicKey
= SecKeyCreateRSAPublicKey(NULL
, CFDataGetBytePtr(publicData
), CFDataGetLength(publicData
),
378 kSecKeyEncodingBytes
);
381 if (*publicKey
!= NULL
) {
382 status
= errSecSuccess
;
384 status
= errSecInvalidKey
;
385 CFReleaseNull(*privateKey
);
389 CFReleaseSafe(attrs
);
390 CFReleaseSafe(publicData
);
394 SecKeyRef
SecKeyCopyAttestationKey(SecKeyAttestationKeyType keyType
, CFErrorRef
*error
) {
395 if (keyType
!= kSecKeyAttestationKeyTypeSIK
&& keyType
!= kSecKeyAttestationKeyTypeGID
) {
396 SecError(errSecParam
, error
, CFSTR("unexpected attestation key type %u"), (unsigned)keyType
);
400 // [NSKeyedArchiver archivedDataWithRootObject:[@"com.apple.setoken.sik" dataUsingEncoding:NSUTF8StringEncoding]];
401 static const uint8_t sikObjectIDBytes
[] = {
402 0x62, 0x70, 0x6c, 0x69, 0x73, 0x74, 0x30, 0x30, 0xd4, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x14,
403 0x15, 0x58, 0x24, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x58, 0x24, 0x6f, 0x62, 0x6a, 0x65,
404 0x63, 0x74, 0x73, 0x59, 0x24, 0x61, 0x72, 0x63, 0x68, 0x69, 0x76, 0x65, 0x72, 0x54, 0x24, 0x74,
405 0x6f, 0x70, 0x12, 0x00, 0x01, 0x86, 0xa0, 0xa3, 0x07, 0x08, 0x0d, 0x55, 0x24, 0x6e, 0x75, 0x6c,
406 0x6c, 0xd2, 0x09, 0x0a, 0x0b, 0x0c, 0x57, 0x4e, 0x53, 0x2e, 0x64, 0x61, 0x74, 0x61, 0x56, 0x24,
407 0x63, 0x6c, 0x61, 0x73, 0x73, 0x4f, 0x10, 0x15, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c,
408 0x65, 0x2e, 0x73, 0x65, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x2e, 0x73, 0x69, 0x6b, 0x80, 0x02, 0xd2,
409 0x0e, 0x0f, 0x10, 0x11, 0x5a, 0x24, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x6e, 0x61, 0x6d, 0x65, 0x58,
410 0x24, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x65, 0x73, 0x5d, 0x4e, 0x53, 0x4d, 0x75, 0x74, 0x61, 0x62,
411 0x6c, 0x65, 0x44, 0x61, 0x74, 0x61, 0xa3, 0x10, 0x12, 0x13, 0x56, 0x4e, 0x53, 0x44, 0x61, 0x74,
412 0x61, 0x58, 0x4e, 0x53, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x10, 0x0f, 0x4e, 0x53, 0x4b,
413 0x65, 0x79, 0x65, 0x64, 0x41, 0x72, 0x63, 0x68, 0x69, 0x76, 0x65, 0x72, 0xd1, 0x16, 0x17, 0x54,
414 0x72, 0x6f, 0x6f, 0x74, 0x80, 0x01, 0x08, 0x11, 0x1a, 0x23, 0x2d, 0x32, 0x37, 0x3b, 0x41, 0x46,
415 0x4e, 0x55, 0x6d, 0x6f, 0x74, 0x7f, 0x88, 0x96, 0x9a, 0xa1, 0xaa, 0xbc, 0xbf, 0xc4, 0x00, 0x00,
416 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00,
417 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6
420 // [NSKeyedArchiver archivedDataWithRootObject:[@"com.apple.setoken.gid" dataUsingEncoding:NSUTF8StringEncoding]];
421 static const uint8_t gidObjectIDBytes
[] = {
422 0x62, 0x70, 0x6c, 0x69, 0x73, 0x74, 0x30, 0x30, 0xd4, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x14,
423 0x15, 0x58, 0x24, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x58, 0x24, 0x6f, 0x62, 0x6a, 0x65,
424 0x63, 0x74, 0x73, 0x59, 0x24, 0x61, 0x72, 0x63, 0x68, 0x69, 0x76, 0x65, 0x72, 0x54, 0x24, 0x74,
425 0x6f, 0x70, 0x12, 0x00, 0x01, 0x86, 0xa0, 0xa3, 0x07, 0x08, 0x0d, 0x55, 0x24, 0x6e, 0x75, 0x6c,
426 0x6c, 0xd2, 0x09, 0x0a, 0x0b, 0x0c, 0x57, 0x4e, 0x53, 0x2e, 0x64, 0x61, 0x74, 0x61, 0x56, 0x24,
427 0x63, 0x6c, 0x61, 0x73, 0x73, 0x4f, 0x10, 0x15, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c,
428 0x65, 0x2e, 0x73, 0x65, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x2e, 0x67, 0x69, 0x64, 0x80, 0x02, 0xd2,
429 0x0e, 0x0f, 0x10, 0x11, 0x5a, 0x24, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x6e, 0x61, 0x6d, 0x65, 0x58,
430 0x24, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x65, 0x73, 0x5d, 0x4e, 0x53, 0x4d, 0x75, 0x74, 0x61, 0x62,
431 0x6c, 0x65, 0x44, 0x61, 0x74, 0x61, 0xa3, 0x10, 0x12, 0x13, 0x56, 0x4e, 0x53, 0x44, 0x61, 0x74,
432 0x61, 0x58, 0x4e, 0x53, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x10, 0x0f, 0x4e, 0x53, 0x4b,
433 0x65, 0x79, 0x65, 0x64, 0x41, 0x72, 0x63, 0x68, 0x69, 0x76, 0x65, 0x72, 0xd1, 0x16, 0x17, 0x54,
434 0x72, 0x6f, 0x6f, 0x74, 0x80, 0x01, 0x08, 0x11, 0x1a, 0x23, 0x2d, 0x32, 0x37, 0x3b, 0x41, 0x46,
435 0x4e, 0x55, 0x6d, 0x6f, 0x74, 0x7f, 0x88, 0x96, 0x9a, 0xa1, 0xaa, 0xbc, 0xbf, 0xc4, 0x00, 0x00,
436 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00,
437 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6
440 CFDataRef objectID
= (keyType
== kSecKeyAttestationKeyTypeSIK
) ?
441 CFDataCreate(kCFAllocatorDefault
, sikObjectIDBytes
, sizeof(sikObjectIDBytes
)) :
442 CFDataCreate(kCFAllocatorDefault
, gidObjectIDBytes
, sizeof(gidObjectIDBytes
)) ;
444 const void *keys
[] = { kSecUseToken
, kSecUseTokenObjectID
, kSecAttrTokenID
};
445 const void *values
[] = { kCFNull
, objectID
, CFSTR("com.apple.setoken.attest") };
447 CFDictionaryRef attributes
= CFDictionaryCreate(kCFAllocatorDefault
,
448 keys
, values
, sizeof(keys
) / sizeof(*keys
),
449 &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
451 return SecKeyCreateCTKKey(kCFAllocatorDefault
, attributes
, error
);
454 CFDataRef
SecKeyCreateAttestation(SecKeyRef key
, SecKeyRef keyToAttest
, CFErrorRef
*error
) {
455 if (!key
|| !keyToAttest
) {
456 SecError(errSecParam
, error
, CFSTR("attestation key(s) is NULL"));
460 SecCTKKeyData
*attestingKeyData
= key
->key
;
461 SecCTKKeyData
*keyToAttestData
= keyToAttest
->key
;
463 if (key
->key_class
!= &kSecCTKKeyDescriptor
) {
464 SecError(errSecUnsupportedOperation
, error
, CFSTR("attestation not supported by key %@"), key
);
467 if (keyToAttest
->key_class
!= &kSecCTKKeyDescriptor
|| CFEqual(keyToAttestData
->token
, kCFNull
)) {
468 SecError(errSecUnsupportedOperation
, error
, CFSTR("attestation not supported for key %@"), keyToAttest
);
472 const void *keys
[] = {
473 CFSTR(kTKTokenControlAttribAttestingKey
),
474 CFSTR(kTKTokenControlAttribKeyToAttest
),
476 const void *values
[] = {
477 attestingKeyData
->objectID
,
478 keyToAttestData
->objectID
481 CFDictionaryRef attributes
= NULL
;
482 __block CFDictionaryRef outputAttributes
= NULL
;
483 CFDataRef attestationData
= NULL
;
484 __block SecCFDictionaryCOW sign_auth_params
= { keyToAttestData
->auth_params
.dictionary
};
486 attributes
= CFDictionaryCreate(kCFAllocatorDefault
, keys
, values
, sizeof(keys
) / sizeof(*keys
),
487 &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
489 SecItemAuthDo(&sign_auth_params
, error
, ^SecItemAuthResult(CFDictionaryRef auth_params
, CFArrayRef
*ac_pairs
, CFErrorRef
*error
) {
490 outputAttributes
= TKTokenControl(keyToAttestData
->token
, attributes
, error
);
491 return outputAttributes
? kSecItemAuthResultOK
: SecCTKProcessError(kAKSKeyOpAttest
, keyToAttestData
->token
, keyToAttestData
->objectID
, ac_pairs
, error
);
493 require(outputAttributes
, out
);
495 attestationData
= CFDictionaryGetValue(outputAttributes
, CFSTR(kTKTokenControlAttribAttestationData
));
496 require_action(attestationData
, out
, SecError(errSecInternal
, error
, CFSTR("could not get attestation data")));
498 if (CFGetTypeID(attestationData
) != CFDataGetTypeID()) {
499 SecError(errSecInternal
, error
, CFSTR("unexpected attestation object type"));
500 attestationData
= NULL
;
503 CFRetainSafe(attestationData
);
506 CFReleaseSafe(attributes
);
507 CFReleaseSafe(outputAttributes
);
508 return attestationData
;