#include <AssertMacros.h>
#include <Security/SecFramework.h>
#include <Security/SecKeyPriv.h>
#include <Security/SecItem.h>
#include <Security/SecItemPriv.h>
#include <Security/SecItemInternal.h>
#include <Security/SecBasePriv.h>
#include <utilities/SecCFError.h>
#include <utilities/SecCFWrappers.h>
#include <utilities/array_size.h>
#include <ctkclient.h>
#include <libaks_acl_cf_keys.h>
#include "SecECKey.h"
#include "SecRSAKey.h"
#include "SecCTKKeyPriv.h"
const CFStringRef kSecUseToken = CFSTR("u_Token");
const CFStringRef kSecUseTokenObjectID = CFSTR("u_TokenOID");
typedef struct {
TKTokenRef token;
CFStringRef token_id;
CFDataRef objectID;
SecCFDictionaryCOW auth_params;
SecCFDictionaryCOW attributes;
CFMutableDictionaryRef params;
} SecCTKKeyData;
static void SecCTKKeyDestroy(SecKeyRef key) {
SecCTKKeyData *kd = key->key;
CFReleaseSafe(kd->token);
CFReleaseSafe(kd->token_id);
CFReleaseSafe(kd->objectID);
CFReleaseSafe(kd->auth_params.mutable_dictionary);
CFReleaseSafe(kd->attributes.mutable_dictionary);
CFReleaseSafe(kd->params);
}
static CFIndex SecCTKGetAlgorithmID(SecKeyRef key) {
SecCTKKeyData *kd = key->key;
if (CFEqualSafe(CFDictionaryGetValue(kd->attributes.dictionary, kSecAttrKeyType), kSecAttrKeyTypeECSECPrimeRandom)) {
return kSecECDSAAlgorithmID;
}
return kSecRSAAlgorithmID;
}
static SecItemAuthResult SecCTKProcessError(CFStringRef operation, TKTokenRef token, CFDataRef object_id, CFArrayRef *ac_pairs, CFErrorRef *error) {
if (CFEqualSafe(CFErrorGetDomain(*error), CFSTR(kTKErrorDomain)) &&
CFErrorGetCode(*error) == kTKErrorCodeAuthenticationFailed) {
CFDataRef access_control = TKTokenCopyObjectAccessControl(token, object_id, error);
if (access_control != NULL) {
CFArrayRef ac_pair = CFArrayCreateForCFTypes(NULL, access_control, operation, NULL);
CFAssignRetained(*ac_pairs, CFArrayCreateForCFTypes(NULL, ac_pair, NULL));
CFReleaseNull(*error);
CFRelease(ac_pair);
CFRelease(access_control);
return kSecItemAuthResultNeedAuth;
}
}
return kSecItemAuthResultError;
}
static const CFTypeRef *aclOperations[] = {
[kSecKeyOperationTypeSign] = &kAKSKeyOpSign,
[kSecKeyOperationTypeDecrypt] = &kAKSKeyOpDecrypt,
[kSecKeyOperationTypeKeyExchange] = &kAKSKeyOpComputeKey,
};
static CFTypeRef SecCTKKeyCopyOperationResult(SecKeyRef key, SecKeyOperationType operation, SecKeyAlgorithm algorithm,
CFArrayRef algorithms, SecKeyOperationMode mode,
CFTypeRef in1, CFTypeRef in2, CFErrorRef *error) {
SecCTKKeyData *kd = key->key;
__block SecCFDictionaryCOW auth_params = { kd->auth_params.dictionary };
__block TKTokenRef token = CFRetainSafe(kd->token);
__block CFTypeRef result = kCFNull;
CFErrorRef localError = NULL;
SecItemAuthDo(&auth_params, &localError, ^SecItemAuthResult(CFDictionaryRef ap, CFArrayRef *ac_pairs, CFErrorRef *error) {
if (auth_params.mutable_dictionary != NULL || token == NULL || kd->params != NULL) {
SecCFDictionaryCOW attributes = { ap };
if (kd->params && CFDictionaryGetCount(kd->params) > 0) {
CFDictionarySetValue(SecCFDictionaryCOWGetMutable(&attributes), CFSTR(kTKTokenCreateAttributeAuxParams), kd->params);
}
CFAssignRetained(token, SecTokenCreate(kd->token_id, attributes.dictionary, error));
CFReleaseSafe(attributes.mutable_dictionary);
if (token == NULL) {
return kSecItemAuthResultError;
}
}
result = TKTokenCopyOperationResult(token, kd->objectID, operation, algorithms, mode, in1, in2, error);
return (result != NULL) ? kSecItemAuthResultOK : SecCTKProcessError(*aclOperations[operation], token,
kd->objectID, ac_pairs, error);
});
CFErrorPropagate(localError, error);
CFReleaseSafe(auth_params.mutable_dictionary);
CFReleaseSafe(token);
return result;
}
static size_t SecCTKKeyBlockSize(SecKeyRef key) {
SecCTKKeyData *kd = key->key;
CFTypeRef keySize = CFDictionaryGetValue(kd->attributes.dictionary, kSecAttrKeySizeInBits);
if (CFGetTypeID(keySize) == CFNumberGetTypeID()) {
CFIndex bitSize;
if (CFNumberGetValue(keySize, kCFNumberCFIndexType, &bitSize))
return (bitSize + 7) / 8;
}
return 0;
}
static OSStatus SecCTKKeyCopyPublicOctets(SecKeyRef key, CFDataRef *data) {
OSStatus status = errSecSuccess;
CFErrorRef error = NULL;
CFDataRef publicData = NULL;
SecCTKKeyData *kd = key->key;
require_action_quiet(publicData = TKTokenCopyPublicKeyData(kd->token, kd->objectID, &error), out,
status = SecErrorGetOSStatus(error));
*data = publicData;
out:
CFReleaseSafe(error);
return status;
}
static CFStringRef SecCTKKeyCopyKeyDescription(SecKeyRef key) {
SecCTKKeyData *kd = key->key;
return CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("<SecKeyRef:('%@') %p>"),
CFDictionaryGetValue(kd->attributes.dictionary, kSecAttrTokenID), key);
}
static const CFStringRef *kSecExportableCTKKeyAttributes[] = {
&kSecClass,
&kSecAttrTokenID,
&kSecAttrKeyClass,
&kSecAttrIsPermanent,
&kSecAttrIsPrivate,
&kSecAttrIsModifiable,
&kSecAttrKeyType,
&kSecAttrKeySizeInBits,
&kSecAttrEffectiveKeySize,
&kSecAttrIsSensitive,
&kSecAttrWasAlwaysSensitive,
&kSecAttrIsExtractable,
&kSecAttrWasNeverExtractable,
&kSecAttrCanEncrypt,
&kSecAttrCanDecrypt,
&kSecAttrCanDerive,
&kSecAttrCanSign,
&kSecAttrCanVerify,
&kSecAttrCanSignRecover,
&kSecAttrCanVerifyRecover,
&kSecAttrCanWrap,
&kSecAttrCanUnwrap,
NULL
};
static CFDictionaryRef SecCTKKeyCopyAttributeDictionary(SecKeyRef key) {
CFMutableDictionaryRef attrs = NULL;
CFErrorRef error = NULL;
CFDataRef publicData = NULL, digest = NULL;
SecCTKKeyData *kd = key->key;
require_quiet(publicData = TKTokenCopyPublicKeyData(kd->token, kd->objectID, &error), out);
require(digest = SecSHA1DigestCreate(NULL, CFDataGetBytePtr(publicData), CFDataGetLength(publicData)), out);
attrs = CFDictionaryCreateMutableForCFTypes(CFGetAllocator(key));
CFDictionarySetValue(attrs, kSecAttrApplicationLabel, digest);
for (const CFStringRef **attrKey = &kSecExportableCTKKeyAttributes[0]; *attrKey != NULL; attrKey++) {
CFTypeRef value = CFDictionaryGetValue(kd->attributes.dictionary, **attrKey);
if (value != NULL) {
CFDictionarySetValue(attrs, **attrKey, value);
}
}
out:
CFReleaseSafe(error);
CFReleaseSafe(publicData);
CFReleaseSafe(digest);
return attrs;
}
static SecKeyRef SecCTKKeyCreateDuplicate(SecKeyRef key);
static Boolean SecCTKKeySetParameter(SecKeyRef key, CFStringRef name, CFPropertyListRef value, CFErrorRef *error) {
SecCTKKeyData *kd = key->key;
CFTypeRef acm_reference = NULL;
static const CFStringRef *const knownUseFlags[] = {
&kSecUseOperationPrompt,
&kSecUseAuthenticationContext,
&kSecUseAuthenticationUI,
&kSecUseCallerName,
&kSecUseCredentialReference,
};
bool isUseFlag = false;
for (size_t i = 0; i < array_size(knownUseFlags); i++) {
if (CFEqual(*knownUseFlags[i], name)) {
isUseFlag = true;
break;
}
}
if (CFEqual(name, kSecUseAuthenticationContext)) {
if (value != NULL) {
require_quiet(acm_reference = SecItemAttributesCopyPreparedAuthContext(value, error), out);
value = acm_reference;
}
name = kSecUseCredentialReference;
}
if (isUseFlag) {
CFReleaseNull(kd->token);
if (value != NULL) {
CFDictionarySetValue(SecCFDictionaryCOWGetMutable(&kd->auth_params), name, value);
} else {
CFDictionaryRemoveValue(SecCFDictionaryCOWGetMutable(&kd->auth_params), name);
}
} else {
if (kd->params == NULL) {
kd->params = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
}
if (value != NULL) {
CFDictionarySetValue(kd->params, name, value);
} else {
CFDictionaryRemoveValue(kd->params, name);
}
}
out:
CFReleaseSafe(acm_reference);
return TRUE;
}
static SecKeyDescriptor kSecCTKKeyDescriptor = {
.version = kSecKeyDescriptorVersion,
.name = "CTKKey",
.extraBytes = sizeof(SecCTKKeyData),
.destroy = SecCTKKeyDestroy,
.blockSize = SecCTKKeyBlockSize,
.copyDictionary = SecCTKKeyCopyAttributeDictionary,
.describe = SecCTKKeyCopyKeyDescription,
.getAlgorithmID = SecCTKGetAlgorithmID,
.copyPublic = SecCTKKeyCopyPublicOctets,
.copyOperationResult = SecCTKKeyCopyOperationResult,
.createDuplicate = SecCTKKeyCreateDuplicate,
.setParameter = SecCTKKeySetParameter,
};
static SecKeyRef SecCTKKeyCreateDuplicate(SecKeyRef key) {
SecKeyRef result = SecKeyCreate(CFGetAllocator(key), &kSecCTKKeyDescriptor, 0, 0, 0);
SecCTKKeyData *kd = key->key, *rd = result->key;
rd->token = CFRetainSafe(kd->token);
rd->objectID = CFRetainSafe(kd->objectID);
rd->token_id = CFRetainSafe(kd->token_id);
if (kd->attributes.dictionary != NULL) {
rd->attributes.dictionary = kd->attributes.dictionary;
SecCFDictionaryCOWGetMutable(&rd->attributes);
}
if (kd->auth_params.dictionary != NULL) {
rd->auth_params.dictionary = kd->auth_params.dictionary;
SecCFDictionaryCOWGetMutable(&rd->auth_params);
}
return result;
}
SecKeyRef SecKeyCreateCTKKey(CFAllocatorRef allocator, CFDictionaryRef refAttributes, CFErrorRef *error) {
SecKeyRef key = SecKeyCreate(allocator, &kSecCTKKeyDescriptor, 0, 0, 0);
SecCTKKeyData *kd = key->key;
kd->token = CFRetainSafe(CFDictionaryGetValue(refAttributes, kSecUseToken));
kd->objectID = CFRetainSafe(CFDictionaryGetValue(refAttributes, kSecUseTokenObjectID));
kd->token_id = CFRetainSafe(CFDictionaryGetValue(refAttributes, kSecAttrTokenID));
kd->attributes.dictionary = refAttributes;
CFDictionaryRemoveValue(SecCFDictionaryCOWGetMutable(&kd->attributes), kSecUseToken);
CFDictionaryRemoveValue(SecCFDictionaryCOWGetMutable(&kd->attributes), kSecUseTokenObjectID);
SecItemAuthCopyParams(&kd->auth_params, &kd->attributes);
if (CFDictionaryGetValue(kd->attributes.dictionary, kSecAttrIsPrivate) == NULL) {
CFDictionarySetValue(SecCFDictionaryCOWGetMutable(&kd->attributes), kSecAttrIsPrivate, kCFBooleanTrue);
}
static const CFStringRef *numericAttributes[] = {
&kSecAttrKeyType,
&kSecAttrKeyClass,
NULL,
};
for (const CFStringRef **attrName = &numericAttributes[0]; *attrName != NULL; attrName++) {
CFTypeRef value = CFDictionaryGetValue(kd->attributes.dictionary, **attrName);
if (value != NULL && CFGetTypeID(value) == CFNumberGetTypeID()) {
CFIndex number;
if (CFNumberGetValue(value, kCFNumberCFIndexType, &number)) {
CFStringRef newValue = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%ld"), (long)number);
if (newValue != NULL) {
CFDictionarySetValue(SecCFDictionaryCOWGetMutable(&kd->attributes), **attrName, newValue);
CFRelease(newValue);
}
}
}
}
return key;
}
OSStatus SecCTKKeyGeneratePair(CFDictionaryRef parameters, SecKeyRef *publicKey, SecKeyRef *privateKey) {
OSStatus status;
CFMutableDictionaryRef attrs = NULL;
CFDictionaryRef keyAttrs = NULL;
CFDataRef publicData = NULL;
require_action_quiet(publicKey != NULL, out, status = errSecParam);
require_action_quiet(privateKey != NULL, out, status = errSecParam);
keyAttrs = CFDictionaryGetValue(parameters, kSecPrivateKeyAttrs);
attrs = (keyAttrs == NULL) ? CFDictionaryCreateMutableForCFTypes(NULL) : CFDictionaryCreateMutableCopy(NULL, 0, keyAttrs);
CFDictionaryForEach(parameters, ^(const void *key, const void *value) {
if (!CFEqual(key, kSecPrivateKeyAttrs) && !CFEqual(key, kSecPublicKeyAttrs)) {
CFDictionarySetValue(attrs, key, value);
}
});
CFDictionaryRemoveValue(attrs, kSecValueData);
CFDictionarySetValue(attrs, kSecClass, kSecClassKey);
CFDictionarySetValue(attrs, kSecAttrKeyClass, kSecAttrKeyClassPrivate);
CFDictionarySetValue(attrs, kSecReturnRef, kCFBooleanTrue);
require_noerr_quiet(status = SecItemAdd(attrs, (CFTypeRef *)privateKey), out);
require_noerr_quiet(status = SecCTKKeyCopyPublicOctets(*privateKey, &publicData), out);
if (CFEqualSafe(CFDictionaryGetValue(parameters, kSecAttrKeyType), kSecAttrKeyTypeEC)) {
*publicKey = SecKeyCreateECPublicKey(NULL, CFDataGetBytePtr(publicData), CFDataGetLength(publicData),
kSecKeyEncodingBytes);
} else if (CFEqualSafe(CFDictionaryGetValue(parameters, kSecAttrKeyType), kSecAttrKeyTypeRSA)) {
*publicKey = SecKeyCreateRSAPublicKey(NULL, CFDataGetBytePtr(publicData), CFDataGetLength(publicData),
kSecKeyEncodingBytes);
}
if (*publicKey != NULL) {
status = errSecSuccess;
} else {
status = errSecInvalidKey;
CFReleaseNull(*privateKey);
}
out:
CFReleaseSafe(attrs);
CFReleaseSafe(publicData);
return status;
}
SecKeyRef SecKeyCopyAttestationKey(SecKeyAttestationKeyType keyType, CFErrorRef *error) {
if (keyType != kSecKeyAttestationKeyTypeSIK && keyType != kSecKeyAttestationKeyTypeGID) {
SecError(errSecParam, error, CFSTR("unexpected attestation key type %u"), (unsigned)keyType);
return NULL;
}
static const uint8_t sikObjectIDBytes[] = {
0x62, 0x70, 0x6c, 0x69, 0x73, 0x74, 0x30, 0x30, 0xd4, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x14,
0x15, 0x58, 0x24, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x58, 0x24, 0x6f, 0x62, 0x6a, 0x65,
0x63, 0x74, 0x73, 0x59, 0x24, 0x61, 0x72, 0x63, 0x68, 0x69, 0x76, 0x65, 0x72, 0x54, 0x24, 0x74,
0x6f, 0x70, 0x12, 0x00, 0x01, 0x86, 0xa0, 0xa3, 0x07, 0x08, 0x0d, 0x55, 0x24, 0x6e, 0x75, 0x6c,
0x6c, 0xd2, 0x09, 0x0a, 0x0b, 0x0c, 0x57, 0x4e, 0x53, 0x2e, 0x64, 0x61, 0x74, 0x61, 0x56, 0x24,
0x63, 0x6c, 0x61, 0x73, 0x73, 0x4f, 0x10, 0x15, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c,
0x65, 0x2e, 0x73, 0x65, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x2e, 0x73, 0x69, 0x6b, 0x80, 0x02, 0xd2,
0x0e, 0x0f, 0x10, 0x11, 0x5a, 0x24, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x6e, 0x61, 0x6d, 0x65, 0x58,
0x24, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x65, 0x73, 0x5d, 0x4e, 0x53, 0x4d, 0x75, 0x74, 0x61, 0x62,
0x6c, 0x65, 0x44, 0x61, 0x74, 0x61, 0xa3, 0x10, 0x12, 0x13, 0x56, 0x4e, 0x53, 0x44, 0x61, 0x74,
0x61, 0x58, 0x4e, 0x53, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x10, 0x0f, 0x4e, 0x53, 0x4b,
0x65, 0x79, 0x65, 0x64, 0x41, 0x72, 0x63, 0x68, 0x69, 0x76, 0x65, 0x72, 0xd1, 0x16, 0x17, 0x54,
0x72, 0x6f, 0x6f, 0x74, 0x80, 0x01, 0x08, 0x11, 0x1a, 0x23, 0x2d, 0x32, 0x37, 0x3b, 0x41, 0x46,
0x4e, 0x55, 0x6d, 0x6f, 0x74, 0x7f, 0x88, 0x96, 0x9a, 0xa1, 0xaa, 0xbc, 0xbf, 0xc4, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6
};
static const uint8_t gidObjectIDBytes[] = {
0x62, 0x70, 0x6c, 0x69, 0x73, 0x74, 0x30, 0x30, 0xd4, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x14,
0x15, 0x58, 0x24, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x58, 0x24, 0x6f, 0x62, 0x6a, 0x65,
0x63, 0x74, 0x73, 0x59, 0x24, 0x61, 0x72, 0x63, 0x68, 0x69, 0x76, 0x65, 0x72, 0x54, 0x24, 0x74,
0x6f, 0x70, 0x12, 0x00, 0x01, 0x86, 0xa0, 0xa3, 0x07, 0x08, 0x0d, 0x55, 0x24, 0x6e, 0x75, 0x6c,
0x6c, 0xd2, 0x09, 0x0a, 0x0b, 0x0c, 0x57, 0x4e, 0x53, 0x2e, 0x64, 0x61, 0x74, 0x61, 0x56, 0x24,
0x63, 0x6c, 0x61, 0x73, 0x73, 0x4f, 0x10, 0x15, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c,
0x65, 0x2e, 0x73, 0x65, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x2e, 0x67, 0x69, 0x64, 0x80, 0x02, 0xd2,
0x0e, 0x0f, 0x10, 0x11, 0x5a, 0x24, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x6e, 0x61, 0x6d, 0x65, 0x58,
0x24, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x65, 0x73, 0x5d, 0x4e, 0x53, 0x4d, 0x75, 0x74, 0x61, 0x62,
0x6c, 0x65, 0x44, 0x61, 0x74, 0x61, 0xa3, 0x10, 0x12, 0x13, 0x56, 0x4e, 0x53, 0x44, 0x61, 0x74,
0x61, 0x58, 0x4e, 0x53, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x10, 0x0f, 0x4e, 0x53, 0x4b,
0x65, 0x79, 0x65, 0x64, 0x41, 0x72, 0x63, 0x68, 0x69, 0x76, 0x65, 0x72, 0xd1, 0x16, 0x17, 0x54,
0x72, 0x6f, 0x6f, 0x74, 0x80, 0x01, 0x08, 0x11, 0x1a, 0x23, 0x2d, 0x32, 0x37, 0x3b, 0x41, 0x46,
0x4e, 0x55, 0x6d, 0x6f, 0x74, 0x7f, 0x88, 0x96, 0x9a, 0xa1, 0xaa, 0xbc, 0xbf, 0xc4, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6
};
CFDataRef objectID = (keyType == kSecKeyAttestationKeyTypeSIK) ?
CFDataCreate(kCFAllocatorDefault, sikObjectIDBytes, sizeof(sikObjectIDBytes)) :
CFDataCreate(kCFAllocatorDefault, gidObjectIDBytes, sizeof(gidObjectIDBytes)) ;
const void *keys[] = { kSecUseToken, kSecUseTokenObjectID, kSecAttrTokenID };
const void *values[] = { kCFNull, objectID, CFSTR("com.apple.setoken.attest") };
CFDictionaryRef attributes = CFDictionaryCreate(kCFAllocatorDefault,
keys, values, sizeof(keys) / sizeof(*keys),
&kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
return SecKeyCreateCTKKey(kCFAllocatorDefault, attributes, error);
}
CFDataRef SecKeyCreateAttestation(SecKeyRef key, SecKeyRef keyToAttest, CFErrorRef *error) {
if (!key || !keyToAttest) {
SecError(errSecParam, error, CFSTR("attestation key(s) is NULL"));
return NULL;
}
SecCTKKeyData *attestingKeyData = key->key;
SecCTKKeyData *keyToAttestData = keyToAttest->key;
if (key->key_class != &kSecCTKKeyDescriptor) {
SecError(errSecUnsupportedOperation, error, CFSTR("attestation not supported by key %@"), key);
return NULL;
}
if (keyToAttest->key_class != &kSecCTKKeyDescriptor || CFEqual(keyToAttestData->token, kCFNull)) {
SecError(errSecUnsupportedOperation, error, CFSTR("attestation not supported for key %@"), keyToAttest);
return NULL;
}
const void *keys[] = {
CFSTR(kTKTokenControlAttribAttestingKey),
CFSTR(kTKTokenControlAttribKeyToAttest),
};
const void *values[] = {
attestingKeyData->objectID,
keyToAttestData->objectID
};
CFDictionaryRef attributes = NULL;
__block CFDictionaryRef outputAttributes = NULL;
CFDataRef attestationData = NULL;
__block SecCFDictionaryCOW sign_auth_params = { keyToAttestData->auth_params.dictionary };
attributes = CFDictionaryCreate(kCFAllocatorDefault, keys, values, sizeof(keys) / sizeof(*keys),
&kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
SecItemAuthDo(&sign_auth_params, error, ^SecItemAuthResult(CFDictionaryRef auth_params, CFArrayRef *ac_pairs, CFErrorRef *error) {
outputAttributes = TKTokenControl(keyToAttestData->token, attributes, error);
return outputAttributes ? kSecItemAuthResultOK : SecCTKProcessError(kAKSKeyOpAttest, keyToAttestData->token, keyToAttestData->objectID, ac_pairs, error);
});
require(outputAttributes, out);
attestationData = CFDictionaryGetValue(outputAttributes, CFSTR(kTKTokenControlAttribAttestationData));
require_action(attestationData, out, SecError(errSecInternal, error, CFSTR("could not get attestation data")));
if (CFGetTypeID(attestationData) != CFDataGetTypeID()) {
SecError(errSecInternal, error, CFSTR("unexpected attestation object type"));
attestationData = NULL;
}
CFRetainSafe(attestationData);
out:
CFReleaseSafe(attributes);
CFReleaseSafe(outputAttributes);
return attestationData;
}