/ OSX / sec / Security / SecCTKKey.m
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  }