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