/ keychain / securityd / SecDbKeychainItemV7.m
SecDbKeychainItemV7.m
  1  /*
  2   * Copyright (c) 2017 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 "SecDbKeychainItemV7.h"
 25  #import "SecKeybagSupport.h"
 26  #import "SecItemServer.h"
 27  #import "SecAccessControl.h"
 28  #import "SecDbKeychainSerializedItemV7.h"
 29  #import "SecDbKeychainSerializedAKSWrappedKey.h"
 30  #import "SecDbKeychainSerializedMetadata.h"
 31  #import "SecDbKeychainSerializedSecretData.h"
 32  #import <dispatch/dispatch.h>
 33  #import <utilities/SecAKSWrappers.h>
 34  #import "SecAKSObjCWrappers.h"
 35  #import <utilities/der_plist.h>
 36  #import <SecurityFoundation/SFEncryptionOperation.h>
 37  #import <SecurityFoundation/SFKey_Private.h>
 38  #import <SecurityFoundation/SFCryptoServicesErrors.h>
 39  
 40  #import <Foundation/NSKeyedArchiver_Private.h>
 41  
 42  #if USE_KEYSTORE && __has_include(<Kernel/IOKit/crypto/AppleKeyStoreDefs.h>)
 43  #import <Kernel/IOKit/crypto/AppleKeyStoreDefs.h>
 44  #endif
 45  
 46  #import "SecDbKeychainMetadataKeyStore.h"
 47  #import "SecDbBackupManager.h"
 48  
 49  #define KEYCHAIN_ITEM_PADDING_MODULUS 20
 50  
 51  // See corresponding "reasonable size" client-side limit(s) in SecItem.
 52  
 53  // Generally the secret data dictionary contains a single key
 54  // with the client's password/key NSData therein, so 4k feels extremely luxurious
 55  #define REASONABLE_SECRET_DATA_SIZE 4096
 56  
 57  // This feels similarly generous, but let's find out
 58  #define REASONABLE_METADATA_SIZE 2048
 59  
 60  NSString* const SecDbKeychainErrorDomain = @"SecDbKeychainErrorDomain";
 61  const NSInteger SecDbKeychainErrorDeserializationFailed = 1;
 62  
 63  static NSString* const SecDBTamperCheck = @"TamperCheck";
 64  
 65  static NSDictionary* dictionaryFromDERData(NSData* data)
 66  {
 67      NSDictionary* dict = (__bridge_transfer NSDictionary*)CFPropertyListCreateWithDERData(NULL, (__bridge CFDataRef)data, 0, NULL, NULL);
 68      return [dict isKindOfClass:[NSDictionary class]] ? dict : nil;
 69  }
 70  
 71  typedef NS_ENUM(uint32_t, SecDbKeychainAKSWrappedKeyType) {
 72      SecDbKeychainAKSWrappedKeyTypeRegular,
 73      SecDbKeychainAKSWrappedKeyTypeRefKey
 74  };
 75  
 76  @interface SecDbKeychainAKSWrappedKey : NSObject
 77  
 78  @property (readonly) NSData* wrappedKey;
 79  @property (readonly) NSData* refKeyBlob;
 80  @property (readonly) SecDbKeychainAKSWrappedKeyType type;
 81  
 82  @property (readonly) NSData* serializedRepresentation;
 83  
 84  - (instancetype)initWithData:(NSData*)data;
 85  - (instancetype)initRegularWrappedKeyWithData:(NSData*)wrappedKey;
 86  - (instancetype)initRefKeyWrappedKeyWithData:(NSData*)wrappedKey refKeyBlob:(NSData*)refKeyBlob;
 87  
 88  @end
 89  
 90  @interface SecDbKeychainMetadata : NSObject
 91  
 92  @property (readonly) SFAuthenticatedCiphertext* ciphertext;
 93  @property (readonly) SFAuthenticatedCiphertext* wrappedKey;
 94  @property (readonly) NSString* tamperCheck;
 95  
 96  @property (readonly) NSData* serializedRepresentation;
 97  
 98  - (instancetype)initWithData:(NSData*)data;
 99  - (instancetype)initWithCiphertext:(SFAuthenticatedCiphertext*)ciphertext wrappedKey:(SFAuthenticatedCiphertext*)wrappedKey tamperCheck:(NSString*)tamperCheck error:(NSError**)error;
100  
101  @end
102  
103  @interface SecDbKeychainSecretData : NSObject
104  
105  @property (readonly) SFAuthenticatedCiphertext* ciphertext;
106  @property (readonly) SecDbKeychainAKSWrappedKey* wrappedKey;
107  @property (readonly) NSString* tamperCheck;
108  
109  @property (readonly) NSData* serializedRepresentation;
110  
111  - (instancetype)initWithData:(NSData*)data;
112  - (instancetype)initWithCiphertext:(SFAuthenticatedCiphertext*)ciphertext wrappedKey:(SecDbKeychainAKSWrappedKey*)wrappedKey tamperCheck:(NSString*)tamperCheck backupWrappedKey:(SecDbBackupWrappedKey*)backupWrappedKey error:(NSError**)error;
113  
114  @end
115  
116  @implementation SecDbKeychainAKSWrappedKey {
117      SecDbKeychainSerializedAKSWrappedKey* _serializedHolder;
118  }
119  
120  - (instancetype)initRegularWrappedKeyWithData:(NSData*)wrappedKey
121  {
122      if (self = [super init]) {
123          _serializedHolder = [[SecDbKeychainSerializedAKSWrappedKey alloc] init];
124          _serializedHolder.wrappedKey = wrappedKey;
125          _serializedHolder.type = SecDbKeychainAKSWrappedKeyTypeRegular;
126      }
127  
128      return self;
129  }
130  
131  - (instancetype)initRefKeyWrappedKeyWithData:(NSData*)wrappedKey refKeyBlob:(NSData*)refKeyBlob
132  {
133      if (self = [super init]) {
134          _serializedHolder = [[SecDbKeychainSerializedAKSWrappedKey alloc] init];
135          _serializedHolder.wrappedKey = wrappedKey;
136          _serializedHolder.refKeyBlob = refKeyBlob;
137          _serializedHolder.type = SecDbKeychainAKSWrappedKeyTypeRefKey;
138      }
139  
140      return self;
141  }
142  
143  - (instancetype)initWithData:(NSData*)data
144  {
145      if (self = [super init]) {
146          _serializedHolder = [[SecDbKeychainSerializedAKSWrappedKey alloc] initWithData:data];
147          if (!_serializedHolder.wrappedKey || (_serializedHolder.type == SecDbKeychainAKSWrappedKeyTypeRefKey && !_serializedHolder.refKeyBlob)) {
148              self = nil;
149          }
150      }
151  
152      return self;
153  }
154  
155  - (NSData*)serializedRepresentation
156  {
157      return _serializedHolder.data;
158  }
159  
160  - (NSData*)wrappedKey
161  {
162      return _serializedHolder.wrappedKey;
163  }
164  
165  - (NSData*)refKeyBlob
166  {
167      return _serializedHolder.refKeyBlob;
168  }
169  
170  - (SecDbKeychainAKSWrappedKeyType)type
171  {
172      return _serializedHolder.type;
173  }
174  
175  @end
176  
177  @implementation SecDbKeychainMetadata {
178      SecDbKeychainSerializedMetadata* _serializedHolder;
179  }
180  
181  - (instancetype)initWithCiphertext:(SFAuthenticatedCiphertext*)ciphertext
182                          wrappedKey:(SFAuthenticatedCiphertext*)wrappedKey
183                         tamperCheck:(NSString*)tamperCheck
184                               error:(NSError**)error
185  {
186      if (self = [super init]) {
187          _serializedHolder = [[SecDbKeychainSerializedMetadata alloc] init];
188          _serializedHolder.ciphertext = [NSKeyedArchiver archivedDataWithRootObject:ciphertext requiringSecureCoding:YES error:error];
189          _serializedHolder.wrappedKey = [NSKeyedArchiver archivedDataWithRootObject:wrappedKey requiringSecureCoding:YES error:error];
190          _serializedHolder.tamperCheck = tamperCheck;
191          if (!_serializedHolder.ciphertext || !_serializedHolder.wrappedKey || !_serializedHolder.tamperCheck) {
192              self = nil;
193          }
194      }
195  
196      return self;
197  }
198  
199  - (instancetype)initWithData:(NSData*)data
200  {
201      if (self = [super init]) {
202          _serializedHolder = [[SecDbKeychainSerializedMetadata alloc] initWithData:data];
203          if (!_serializedHolder.ciphertext || !_serializedHolder.wrappedKey || !_serializedHolder.tamperCheck) {
204              self = nil;
205          }
206      }
207  
208      return self;
209  }
210  
211  - (NSData*)serializedRepresentation
212  {
213      return _serializedHolder.data;
214  }
215  
216  - (SFAuthenticatedCiphertext*)ciphertext
217  {
218      NSError* error = nil;
219      SFAuthenticatedCiphertext* ciphertext =  [NSKeyedUnarchiver unarchivedObjectOfClass:[SFAuthenticatedCiphertext class] fromData:_serializedHolder.ciphertext error:&error];
220      if (!ciphertext) {
221          secerror("SecDbKeychainItemV7: error deserializing ciphertext from metadata: %@", error);
222      }
223  
224      return ciphertext;
225  }
226  
227  - (SFAuthenticatedCiphertext*)wrappedKey
228  {
229      NSError* error = nil;
230      SFAuthenticatedCiphertext* wrappedKey =  [NSKeyedUnarchiver unarchivedObjectOfClass:[SFAuthenticatedCiphertext class] fromData:_serializedHolder.wrappedKey error:&error];
231      if (!wrappedKey) {
232          secerror("SecDbKeychainItemV7: error deserializing wrappedKey from metadata: %@", error);
233      }
234  
235      return wrappedKey;
236  }
237  
238  - (NSString*)tamperCheck
239  {
240      return _serializedHolder.tamperCheck;
241  }
242  
243  @end
244  
245  @implementation SecDbKeychainSecretData {
246      SecDbKeychainSerializedSecretData* _serializedHolder;
247  }
248  
249  - (instancetype)initWithCiphertext:(SFAuthenticatedCiphertext*)ciphertext
250                          wrappedKey:(SecDbKeychainAKSWrappedKey*)wrappedKey
251                         tamperCheck:(NSString*)tamperCheck
252                    backupWrappedKey:(SecDbBackupWrappedKey*)backupWrappedKey
253                               error:(NSError**)error
254  {
255      if (self = [super init]) {
256          _serializedHolder = [[SecDbKeychainSerializedSecretData alloc] init];
257          _serializedHolder.ciphertext = [NSKeyedArchiver archivedDataWithRootObject:ciphertext requiringSecureCoding:YES error:error];
258          _serializedHolder.wrappedKey = wrappedKey.serializedRepresentation;
259          _serializedHolder.tamperCheck = tamperCheck;
260          _serializedHolder.secDbBackupWrappedItemKey = backupWrappedKey ? [NSKeyedArchiver archivedDataWithRootObject:backupWrappedKey requiringSecureCoding:YES error:error] : nil;
261          if (!_serializedHolder.ciphertext || !_serializedHolder.wrappedKey || !_serializedHolder.tamperCheck) {
262              self = nil;
263          }
264      }
265  
266      return self;
267  }
268  
269  - (instancetype)initWithData:(NSData*)data
270  {
271      if (self = [super init]) {
272          _serializedHolder = [[SecDbKeychainSerializedSecretData alloc] initWithData:data];
273          if (!_serializedHolder.ciphertext || !_serializedHolder.wrappedKey || !_serializedHolder.tamperCheck) {
274              self = nil;
275          }
276      }
277  
278      return self;
279  }
280  
281  - (NSData*)serializedRepresentation
282  {
283      return _serializedHolder.data;
284  }
285  
286  - (SFAuthenticatedCiphertext*)ciphertext
287  {
288      NSError* error = nil;
289      SFAuthenticatedCiphertext* ciphertext =  [NSKeyedUnarchiver unarchivedObjectOfClass:[SFAuthenticatedCiphertext class] fromData:_serializedHolder.ciphertext error:&error];
290      if (!ciphertext) {
291          secerror("SecDbKeychainItemV7: error deserializing ciphertext from secret data: %@", error);
292      }
293  
294      return ciphertext;
295  }
296  
297  - (SecDbKeychainAKSWrappedKey*)wrappedKey
298  {
299      return [[SecDbKeychainAKSWrappedKey alloc] initWithData:_serializedHolder.wrappedKey];
300  }
301  
302  - (NSString*)tamperCheck
303  {
304      return _serializedHolder.tamperCheck;
305  }
306  
307  @end
308  
309  @interface SecDbKeychainItemV7 ()
310  @property (nonatomic) NSData* backupUUID;
311  @end;
312  
313  @implementation SecDbKeychainItemV7 {
314      SecDbKeychainSecretData* _encryptedSecretData;
315      SecDbKeychainMetadata* _encryptedMetadata;
316      NSDictionary* _secretAttributes;
317      NSDictionary* _metadataAttributes;
318      NSString* _tamperCheck;
319      keyclass_t _keyclass;
320      keybag_handle_t _keybag;
321  }
322  
323  @synthesize keyclass = _keyclass;
324  
325  // bring back with <rdar://problem/37523001>
326  #if 0
327  + (bool)isKeychainUnlocked
328  {
329      return kc_is_unlocked();
330  }
331  #endif
332  
333  - (instancetype)initWithData:(NSData*)data decryptionKeybag:(keybag_handle_t)decryptionKeybag error:(NSError**)error
334  {
335      if (self = [super init]) {
336          SecDbKeychainSerializedItemV7* serializedItem = [[SecDbKeychainSerializedItemV7 alloc] initWithData:data];
337          if (serializedItem) {
338  
339              // Add 10% for serializing overhead. We're trying to catch blatant overstuffing, not enforce hard limits
340              if (data.length > ((REASONABLE_SECRET_DATA_SIZE + REASONABLE_METADATA_SIZE) * 1.1)) {
341                  secwarning("SecDbKeychainItemV7: serialized item exceeds reasonable size (%lu bytes)", (unsigned long)data.length);
342              }
343  
344              _keybag = decryptionKeybag;
345              _encryptedSecretData = [[SecDbKeychainSecretData alloc] initWithData:serializedItem.encryptedSecretData];
346              _encryptedMetadata = [[SecDbKeychainMetadata alloc] initWithData:serializedItem.encryptedMetadata];
347              _keyclass = serializedItem.keyclass;
348              if (![_encryptedSecretData.tamperCheck isEqualToString:_encryptedMetadata.tamperCheck]) {
349                  self = nil;
350              }
351          }
352          else {
353              self = nil;
354          }
355      }
356  
357      if (!self && error) {
358          *error = [NSError errorWithDomain:(id)kCFErrorDomainOSStatus code:errSecDecode userInfo:@{NSLocalizedDescriptionKey : @"failed to deserialize keychain item blob"}];
359      }
360  
361      return self;
362  }
363  
364  - (instancetype)initWithSecretAttributes:(NSDictionary*)secretAttributes metadataAttributes:(NSDictionary*)metadataAttributes tamperCheck:(NSString*)tamperCheck keyclass:(keyclass_t)keyclass
365  {
366      NSParameterAssert(tamperCheck);
367  
368      if (self = [super init]) {
369          _secretAttributes = secretAttributes ? secretAttributes.copy : [NSDictionary dictionary];
370          _metadataAttributes = metadataAttributes ? metadataAttributes.copy : [NSDictionary dictionary];
371          _tamperCheck = tamperCheck.copy;
372          _keyclass = keyclass;
373      }
374  
375      return self;
376  }
377  
378  + (SFAESKeySpecifier*)keySpecifier
379  {
380      static SFAESKeySpecifier* keySpecifier = nil;
381      static dispatch_once_t onceToken;
382      dispatch_once(&onceToken, ^{
383          keySpecifier = [[SFAESKeySpecifier alloc] initWithBitSize:SFAESKeyBitSize256];
384      });
385  
386      return keySpecifier;
387  }
388  
389  + (SFAuthenticatedEncryptionOperation*)encryptionOperation
390  {
391      static SFAuthenticatedEncryptionOperation* encryptionOperation = nil;
392      static dispatch_once_t onceToken;
393      dispatch_once(&onceToken, ^{
394          encryptionOperation = [[SFAuthenticatedEncryptionOperation alloc] initWithKeySpecifier:[self keySpecifier]];
395      });
396  
397      return encryptionOperation;
398  }
399  
400  + (SFAuthenticatedEncryptionOperation*)decryptionOperation
401  {
402      static SFAuthenticatedEncryptionOperation* decryptionOperation = nil;
403      static dispatch_once_t onceToken;
404      dispatch_once(&onceToken, ^{
405          decryptionOperation = [[SFAuthenticatedEncryptionOperation alloc] initWithKeySpecifier:[self keySpecifier]];
406      });
407  
408      return decryptionOperation;
409  }
410  
411  - (NSDictionary*)metadataAttributesWithError:(NSError**)error
412  {
413      if (!_metadataAttributes) {
414          SFAESKey* metadataClassKey = [self metadataClassKeyWithKeybag:_keybag
415                                                            allowWrites:false
416                                                                  error:error];
417          if (metadataClassKey) {
418              NSError* localError = nil;
419              NSData* keyData = [[self.class decryptionOperation] decrypt:_encryptedMetadata.wrappedKey withKey:metadataClassKey error:&localError];
420              if (!keyData) {
421                  secerror("SecDbKeychainItemV7: error unwrapping item metadata key (class %d, bag %d): %@", (int)self.keyclass, _keybag, localError);
422                  // TODO: track this in LocalKeychainAnalytics
423                  if (error) {
424                      CFErrorRef secError = (CFErrorRef)CFBridgingRetain(localError); // this makes localError become the underlying error
425                      SecError(errSecDecode, &secError, CFSTR("failed to unwrap item metadata key"));
426                      *error = CFBridgingRelease(secError);
427                  }
428                  return nil;
429              }
430              SFAESKey* key = [[SFAESKey alloc] initWithData:keyData specifier:[self.class keySpecifier] error:error];
431              if (!key) {
432                  return nil;
433              }
434  
435              NSData* metadata = [[self.class decryptionOperation] decrypt:_encryptedMetadata.ciphertext withKey:key error:&localError];
436              if (!metadata) {
437                  secerror("SecDbKeychainItemV7: error decrypting metadata content: %@", localError);
438                  if (error) {
439                      CFErrorRef secError = (CFErrorRef)CFBridgingRetain(localError); // this makes localError become the underlying error
440                      SecError(errSecDecode, &secError, CFSTR("failed to decrypt item metadata contents"));
441                      *error = CFBridgingRelease(secError);
442                  }
443                  return nil;
444              }
445              NSMutableDictionary* decryptedAttributes = dictionaryFromDERData(metadata).mutableCopy;
446              NSString* tamperCheck = decryptedAttributes[SecDBTamperCheck];
447              if ([tamperCheck isEqualToString:_encryptedMetadata.tamperCheck]) {
448                  [decryptedAttributes removeObjectForKey:SecDBTamperCheck];
449                  _metadataAttributes = decryptedAttributes;
450              }
451              else {
452                  secerror("SecDbKeychainItemV7: tamper check failed for metadata decryption, expected %@ found %@", tamperCheck, _encryptedMetadata.tamperCheck);
453                  if (error) {
454                      CFErrorRef secError = NULL;
455                      SecError(errSecDecode, &secError, CFSTR("tamper check failed for metadata decryption"));
456                      *error = CFBridgingRelease(secError);
457                  }
458              }
459          }
460      }
461  
462      return _metadataAttributes;
463  }
464  
465  - (NSDictionary*)secretAttributesWithAcmContext:(NSData*)acmContext accessControl:(SecAccessControlRef)accessControl callerAccessGroups:(NSArray*)callerAccessGroups error:(NSError**)error
466  {
467      if (!_secretAttributes) {
468          SFAESKey* key = [self unwrapFromAKS:_encryptedSecretData.wrappedKey accessControl:accessControl acmContext:acmContext callerAccessGroups:callerAccessGroups delete:NO error:error];
469          if (key) {
470              NSError* localError = nil;
471              NSData* secretDataWithPadding = [[self.class decryptionOperation] decrypt:_encryptedSecretData.ciphertext withKey:key error:&localError];
472              if (!secretDataWithPadding) {
473                  secerror("SecDbKeychainItemV7: error decrypting item secret data contents: %@", localError);
474                  if (error) {
475                      CFErrorRef secError = (CFErrorRef)CFBridgingRetain(localError); // this makes localError become the underlying error
476                      SecError(errSecDecode, &secError, CFSTR("error decrypting item secret data contents"));
477                      *error = CFBridgingRelease(secError);
478                  }
479                  return nil;
480              }
481              int8_t paddingLength = *((int8_t*)secretDataWithPadding.bytes + secretDataWithPadding.length - 1);
482              NSData* secretDataWithoutPadding = [secretDataWithPadding subdataWithRange:NSMakeRange(0, secretDataWithPadding.length - paddingLength)];
483  
484              NSMutableDictionary* decryptedAttributes = dictionaryFromDERData(secretDataWithoutPadding).mutableCopy;
485              NSString* tamperCheck = decryptedAttributes[SecDBTamperCheck];
486              if ([tamperCheck isEqualToString:_encryptedSecretData.tamperCheck]) {
487                  [decryptedAttributes removeObjectForKey:SecDBTamperCheck];
488                  _secretAttributes = decryptedAttributes;
489              }
490              else {
491                  secerror("SecDbKeychainItemV7: tamper check failed for secret data decryption, expected %@ found %@", tamperCheck, _encryptedMetadata.tamperCheck);
492              }
493          }
494      }
495  
496      return _secretAttributes;
497  }
498  
499  - (BOOL)deleteWithAcmContext:(NSData*)acmContext accessControl:(SecAccessControlRef)accessControl callerAccessGroups:(NSArray*)callerAccessGroups error:(NSError**)error
500  {
501      NSError* localError = nil;
502      (void)[self unwrapFromAKS:_encryptedSecretData.wrappedKey accessControl:accessControl acmContext:acmContext callerAccessGroups:callerAccessGroups delete:YES error:&localError];
503      if (localError) {
504          secerror("SecDbKeychainItemV7: failed to delete item secret key from aks");
505          if (error) {
506              *error = localError;
507          }
508  
509          return NO;
510      }
511  
512      return YES;
513  }
514  
515  - (NSData*)encryptedBlobWithKeybag:(keybag_handle_t)keybag accessControl:(SecAccessControlRef)accessControl acmContext:(NSData*)acmContext error:(NSError**)error
516  {
517      NSError* localError = nil;
518      BOOL success = [self encryptMetadataWithKeybag:keybag error:&localError];
519      if (!success || !_encryptedMetadata || localError) {
520          if (error) {
521              *error = localError;
522          }
523          return nil;
524      }
525  
526      success = [self encryptSecretDataWithKeybag:keybag accessControl:accessControl acmContext:acmContext error:&localError];
527      if (!success || !_encryptedSecretData || localError) {
528          if (error) {
529              *error = localError;
530          }
531          return nil;
532      }
533  
534      SecDbKeychainSerializedItemV7* serializedItem = [[SecDbKeychainSerializedItemV7 alloc] init];
535      serializedItem.encryptedMetadata = self.encryptedMetadataBlob;
536      serializedItem.encryptedSecretData = self.encryptedSecretDataBlob;
537      serializedItem.keyclass = _keyclass;
538      return serializedItem.data;
539  }
540  
541  - (NSData*)encryptedMetadataBlob
542  {
543      return _encryptedMetadata.serializedRepresentation;
544  }
545  
546  - (NSData*)encryptedSecretDataBlob
547  {
548      return _encryptedSecretData.serializedRepresentation;
549  }
550  
551  - (BOOL)encryptMetadataWithKeybag:(keybag_handle_t)keybag error:(NSError**)error
552  {
553      SFAESKey* key = [[SFAESKey alloc] initRandomKeyWithSpecifier:[self.class keySpecifier] error:error];
554      if (!key) {
555          return NO;
556      }
557      SFAuthenticatedEncryptionOperation* encryptionOperation = [self.class encryptionOperation];
558  
559      NSMutableDictionary* attributesToEncrypt = _metadataAttributes.mutableCopy;
560      attributesToEncrypt[SecDBTamperCheck] = _tamperCheck;
561      NSData* metadata = (__bridge_transfer NSData*)CFPropertyListCreateDERData(NULL, (__bridge CFDictionaryRef)attributesToEncrypt, NULL);
562  
563      if (metadata.length > REASONABLE_METADATA_SIZE) {
564          NSString *agrp = _metadataAttributes[(__bridge NSString *)kSecAttrAccessGroup];
565          secwarning("SecDbKeychainItemV7: item's metadata exceeds reasonable size (%lu bytes) (%@)", (unsigned long)metadata.length, agrp);
566      }
567  
568      SFAuthenticatedCiphertext* ciphertext = [encryptionOperation encrypt:metadata withKey:key error:error];
569  
570      SFAESKey* metadataClassKey = [self metadataClassKeyWithKeybag:keybag
571                                                        allowWrites:true
572                                                              error:error];
573      if (metadataClassKey) {
574          SFAuthenticatedCiphertext* wrappedKey = [encryptionOperation encrypt:key.keyData withKey:metadataClassKey error:error];
575          _encryptedMetadata = [[SecDbKeychainMetadata alloc] initWithCiphertext:ciphertext wrappedKey:wrappedKey tamperCheck:_tamperCheck error:error];
576      }
577  
578      return _encryptedMetadata != nil;
579  }
580  
581  - (BOOL)encryptSecretDataWithKeybag:(keybag_handle_t)keybag accessControl:(SecAccessControlRef)accessControl acmContext:(NSData*)acmContext error:(NSError**)error
582  {
583      SFAESKey* key = [[SFAESKey alloc] initRandomKeyWithSpecifier:[self.class keySpecifier] error:error];
584      if (!key) {
585          return NO;
586      }
587      SFAuthenticatedEncryptionOperation* encryptionOperation = [self.class encryptionOperation];
588  
589      NSMutableDictionary* attributesToEncrypt = _secretAttributes.mutableCopy;
590      attributesToEncrypt[SecDBTamperCheck] = _tamperCheck;
591      NSMutableData* secretData = [(__bridge_transfer NSData*)CFPropertyListCreateDERData(NULL, (__bridge CFDictionaryRef)attributesToEncrypt, NULL) mutableCopy];
592  
593      if (secretData.length > REASONABLE_SECRET_DATA_SIZE) {
594          NSString *agrp = _metadataAttributes[(__bridge NSString *)kSecAttrAccessGroup];
595          secwarning("SecDbKeychainItemV7: item's secret data exceeds reasonable size (%lu bytes) (%@)", (unsigned long)secretData.length, agrp);
596      }
597  
598      int8_t paddingLength = KEYCHAIN_ITEM_PADDING_MODULUS - (secretData.length % KEYCHAIN_ITEM_PADDING_MODULUS);
599      int8_t paddingBytes[KEYCHAIN_ITEM_PADDING_MODULUS];
600      for (int i = 0; i < KEYCHAIN_ITEM_PADDING_MODULUS; i++) {
601          paddingBytes[i] = paddingLength;
602      }
603      [secretData appendBytes:paddingBytes length:paddingLength];
604  
605      SFAuthenticatedCiphertext* ciphertext = [encryptionOperation encrypt:secretData withKey:key error:error];
606      SecDbKeychainAKSWrappedKey* wrappedKey = [self wrapToAKS:key withKeybag:keybag accessControl:accessControl acmContext:acmContext error:error];
607  
608      SecDbBackupWrappedKey* backupWrappedKey;
609      if (checkV12DevEnabled()) {
610          backupWrappedKey = [[SecDbBackupManager manager] wrapItemKey:key forKeyclass:_keyclass error:error];
611          if (backupWrappedKey) {
612              _backupUUID = backupWrappedKey.baguuid;
613          } else {
614              secwarning("SecDbKeychainItemV7: backup manager didn't return wrapped key: %@", error ? *error : nil);
615              if (error) {
616                  *error = nil;
617              }
618          }
619      }
620  
621      _encryptedSecretData = [[SecDbKeychainSecretData alloc] initWithCiphertext:ciphertext
622                                                                      wrappedKey:wrappedKey
623                                                                     tamperCheck:_tamperCheck
624                                                                backupWrappedKey:backupWrappedKey
625                                                                           error:error];
626      return _encryptedSecretData != nil;
627  }
628  
629  - (SFAESKey*)metadataClassKeyWithKeybag:(keybag_handle_t)keybag
630                              allowWrites:(bool)allowWrites
631                                    error:(NSError**)error
632  {
633      return [[SecDbKeychainMetadataKeyStore sharedStore] keyForKeyclass:_keyclass
634                                                                  keybag:keybag
635                                                            keySpecifier:[self.class keySpecifier]
636                                                             allowWrites:allowWrites
637                                                                   error:error];
638  }
639  
640  - (SecDbKeychainAKSWrappedKey*)wrapToAKS:(SFAESKey*)key withKeybag:(keybag_handle_t)keybag accessControl:(SecAccessControlRef)accessControl acmContext:(NSData*)acmContext error:(NSError**)error
641  {
642      NSData* keyData = key.keyData;
643  
644  #if USE_KEYSTORE
645      NSDictionary* constraints = (__bridge NSDictionary*)SecAccessControlGetConstraints(accessControl);
646      if (constraints) {
647          aks_ref_key_t refKey = NULL;
648          CFErrorRef cfError = NULL;
649          NSData* authData = (__bridge_transfer NSData*)CFPropertyListCreateDERData(NULL, (__bridge CFDictionaryRef)@{(id)kAKSKeyAcl : constraints}, &cfError);
650  
651          if (!acmContext || !SecAccessControlIsBound(accessControl)) {
652              secerror("SecDbKeychainItemV7: access control error");
653              if (error) {
654                  CFDataRef accessControlData = SecAccessControlCopyData(accessControl);
655                  ks_access_control_needed_error(&cfError, accessControlData, SecAccessControlIsBound(accessControl) ? kAKSKeyOpEncrypt : CFSTR(""));
656                  CFReleaseNull(accessControlData);
657              }
658  
659              BridgeCFErrorToNSErrorOut(error, cfError);
660              return nil;
661          }
662  
663          void* aksParams = NULL;
664          size_t aksParamsLength = 0;
665          aks_operation_optional_params(0, 0, authData.bytes, authData.length, acmContext.bytes, (int)acmContext.length, &aksParams, &aksParamsLength);
666  
667          int aksResult = aks_ref_key_create(keybag, _keyclass, key_type_sym, aksParams, aksParamsLength, &refKey);
668          if (aksResult != 0) {
669              CFDataRef accessControlData = SecAccessControlCopyData(accessControl);
670              create_cferror_from_aks(aksResult, kAKSKeyOpEncrypt, keybag, _keyclass, accessControlData, (__bridge CFDataRef)acmContext, &cfError);
671              CFReleaseNull(accessControlData);
672              free(aksParams);
673              BridgeCFErrorToNSErrorOut(error, cfError);
674              return nil;
675          }
676  
677          size_t wrappedKeySize = 0;
678          void* wrappedKeyBytes = NULL;
679          aksResult = aks_ref_key_encrypt(refKey, aksParams, aksParamsLength, keyData.bytes, keyData.length, &wrappedKeyBytes, &wrappedKeySize);
680          if (aksResult != 0) {
681              CFDataRef accessControlData = SecAccessControlCopyData(accessControl);
682              create_cferror_from_aks(aksResult, kAKSKeyOpEncrypt, keybag, _keyclass, accessControlData, (__bridge CFDataRef)acmContext, &cfError);
683              CFReleaseNull(accessControlData);
684              free(aksParams);
685              aks_ref_key_free(&refKey);
686              BridgeCFErrorToNSErrorOut(error, cfError);
687              return nil;
688          }
689          free(aksParams);
690  
691          BridgeCFErrorToNSErrorOut(error, cfError);
692  
693          NSData* wrappedKey = [[NSData alloc] initWithBytesNoCopy:wrappedKeyBytes length:wrappedKeySize];
694  
695          size_t refKeyBlobLength = 0;
696          const void* refKeyBlobBytes = aks_ref_key_get_blob(refKey, &refKeyBlobLength);
697          NSData* refKeyBlob = [[NSData alloc] initWithBytes:(void*)refKeyBlobBytes length:refKeyBlobLength];
698          aks_ref_key_free(&refKey);
699          return [[SecDbKeychainAKSWrappedKey alloc] initRefKeyWrappedKeyWithData:wrappedKey refKeyBlob:refKeyBlob];
700      }
701      else {
702          NSMutableData* wrappedKey = [[NSMutableData alloc] initWithLength:APPLE_KEYSTORE_MAX_SYM_WRAPPED_KEY_LEN];
703          bool success = [SecAKSObjCWrappers aksEncryptWithKeybag:keybag keyclass:_keyclass plaintext:keyData outKeyclass:&_keyclass ciphertext:wrappedKey error:error];
704          return success ? [[SecDbKeychainAKSWrappedKey alloc] initRegularWrappedKeyWithData:wrappedKey] : nil;
705      }
706  #else
707      NSMutableData* wrappedKey = [[NSMutableData alloc] initWithLength:APPLE_KEYSTORE_MAX_SYM_WRAPPED_KEY_LEN];
708      bool success = [SecAKSObjCWrappers aksEncryptWithKeybag:keybag keyclass:_keyclass plaintext:keyData outKeyclass:&_keyclass ciphertext:wrappedKey error:error];
709      return success ? [[SecDbKeychainAKSWrappedKey alloc] initRegularWrappedKeyWithData:wrappedKey] : nil;
710  #endif
711  }
712  
713  - (SFAESKey*)unwrapFromAKS:(SecDbKeychainAKSWrappedKey*)wrappedKey accessControl:(SecAccessControlRef)accessControl acmContext:(NSData*)acmContext callerAccessGroups:(NSArray*)callerAccessGroups delete:(BOOL)delete error:(NSError**)error
714  {
715      NSData* wrappedKeyData = wrappedKey.wrappedKey;
716  
717      if (wrappedKey.type == SecDbKeychainAKSWrappedKeyTypeRegular) {
718          NSMutableData* unwrappedKey = [NSMutableData dataWithLength:APPLE_KEYSTORE_MAX_KEY_LEN];
719          bool result = [SecAKSObjCWrappers aksDecryptWithKeybag:_keybag keyclass:_keyclass ciphertext:wrappedKeyData outKeyclass:&_keyclass plaintext:unwrappedKey error:error];
720          if (result) {
721              return [[SFAESKey alloc] initWithData:unwrappedKey specifier:[self.class keySpecifier] error:error];
722          }
723          else {
724              return nil;
725          }
726      }
727  #if USE_KEYSTORE
728      else if (wrappedKey.type == SecDbKeychainAKSWrappedKeyTypeRefKey) {
729          aks_ref_key_t refKey = NULL;
730          aks_ref_key_create_with_blob(_keybag, wrappedKey.refKeyBlob.bytes, wrappedKey.refKeyBlob.length, &refKey);
731  
732          CFErrorRef cfError = NULL;
733          size_t refKeyExternalDataLength = 0;
734          const uint8_t* refKeyExternalDataBytes = aks_ref_key_get_external_data(refKey, &refKeyExternalDataLength);
735          if (!refKeyExternalDataBytes) {
736              aks_ref_key_free(&refKey);
737              return nil;
738          }
739          NSDictionary* aclDict = nil;
740          der_decode_plist(NULL, (CFPropertyListRef*)(void*)&aclDict, &cfError, refKeyExternalDataBytes, refKeyExternalDataBytes + refKeyExternalDataLength);
741          if (!aclDict) {
742              SecError(errSecDecode, &cfError, CFSTR("SecDbKeychainItemV7: failed to decode acl dict"));
743          }
744          SecAccessControlSetConstraints(accessControl, (__bridge CFDictionaryRef)aclDict);
745          if (!SecAccessControlGetConstraint(accessControl, kAKSKeyOpEncrypt)) {
746              SecAccessControlAddConstraintForOperation(accessControl, kAKSKeyOpEncrypt, kCFBooleanTrue, &cfError);
747          }
748  
749          size_t derPlistLength = der_sizeof_plist((__bridge CFPropertyListRef)callerAccessGroups, &cfError);
750          NSMutableData* accessGroupDERData = [[NSMutableData alloc] initWithLength:derPlistLength];
751          der_encode_plist((__bridge CFPropertyListRef)callerAccessGroups, &cfError, accessGroupDERData.mutableBytes, accessGroupDERData.mutableBytes + derPlistLength);
752          void* aksParams = NULL;
753          size_t aksParamsLength = 0;
754          aks_operation_optional_params(accessGroupDERData.bytes, derPlistLength, NULL, 0, acmContext.bytes, (int)acmContext.length, &aksParams, &aksParamsLength);
755  
756          void* unwrappedKeyDERData = NULL;
757          size_t unwrappedKeyDERLength = 0;
758          int aksResult = aks_ref_key_decrypt(refKey, aksParams, aksParamsLength, wrappedKeyData.bytes, wrappedKeyData.length, &unwrappedKeyDERData, &unwrappedKeyDERLength);
759          if (aksResult != 0) {
760              CFDataRef accessControlData = SecAccessControlCopyData(accessControl);
761              create_cferror_from_aks(aksResult, kAKSKeyOpDecrypt, 0, 0, accessControlData, (__bridge CFDataRef)acmContext, &cfError);
762              CFReleaseNull(accessControlData);
763              aks_ref_key_free(&refKey);
764              free(aksParams);
765              BridgeCFErrorToNSErrorOut(error, cfError);
766              return nil;
767          }
768          if (!unwrappedKeyDERData) {
769              SecError(errSecDecode, &cfError, CFSTR("SecDbKeychainItemV7: failed to decrypt item, Item can't be decrypted due to failed decode der, so drop the item."));
770              aks_ref_key_free(&refKey);
771              free(aksParams);
772              BridgeCFErrorToNSErrorOut(error, cfError);
773              return nil;
774          }
775  
776          CFPropertyListRef unwrappedKeyData = NULL;
777          der_decode_plist(NULL, &unwrappedKeyData, &cfError, unwrappedKeyDERData, unwrappedKeyDERData + unwrappedKeyDERLength);
778          SFAESKey* result = nil;
779          if ([(__bridge NSData*)unwrappedKeyData isKindOfClass:[NSData class]]) {
780              result = [[SFAESKey alloc] initWithData:(__bridge NSData*)unwrappedKeyData specifier:[self.class keySpecifier] error:error];
781              CFReleaseNull(unwrappedKeyData);
782          }
783          else {
784              SecError(errSecDecode, &cfError, CFSTR("SecDbKeychainItemV7: failed to decrypt item, Item can't be decrypted due to failed decode der, so drop the item."));
785              CFReleaseNull(unwrappedKeyData);
786              aks_ref_key_free(&refKey);
787              free(aksParams);
788              free(unwrappedKeyDERData);
789              BridgeCFErrorToNSErrorOut(error, cfError);
790              return nil;
791          }
792  
793          if (delete) {
794              aksResult = aks_ref_key_delete(refKey, aksParams, aksParamsLength);
795              if (aksResult != 0) {
796                  CFDataRef accessControlData = SecAccessControlCopyData(accessControl);
797                  create_cferror_from_aks(aksResult, kAKSKeyOpDelete, 0, 0, accessControlData, (__bridge CFDataRef)acmContext, &cfError);
798                  CFReleaseNull(accessControlData);
799                  aks_ref_key_free(&refKey);
800                  free(aksParams);
801                  free(unwrappedKeyDERData);
802                  BridgeCFErrorToNSErrorOut(error, cfError);
803                  return nil;
804              }
805          }
806  
807          BridgeCFErrorToNSErrorOut(error, cfError);
808          aks_ref_key_free(&refKey);
809          free(aksParams);
810          free(unwrappedKeyDERData);
811          return result;
812      }
813  #endif
814      else {
815          return nil;
816      }
817  }
818  
819  @end