1 #import "KeychainXCTest.h"
2 #import <Security/Security.h>
3 #import <Security/SecItemPriv.h>
4 #include <Security/SecEntitlements.h>
5 #include <ipc/server_security_helpers.h>
7 @interface KeychainBackupTests : KeychainXCTest
11 @implementation KeychainBackupTests {
12 NSString* _applicationIdentifier;
16 // Put setup code here. This method is called before the invocation of each test method in the class.
18 _applicationIdentifier = @"com.apple.security.backuptests";
19 SecSecurityClientSetApplicationIdentifier((__bridge CFStringRef)_applicationIdentifier);
23 // Put teardown code here. This method is called after the invocation of each test method in the class.
26 # pragma mark - Test OTA Backups
28 // Code lovingly adapted from si-33-keychain-backup
30 - (NSData*)createKeybagWithType:(keybag_handle_t)bag_type password:(NSData*)password
32 keybag_handle_t handle = bad_keybag_handle;
33 kern_return_t bag_created = aks_create_bag(password ? password.bytes : NULL, password ? (int)password.length : 0, bag_type, &handle);
34 XCTAssertEqual(bag_created, kAKSReturnSuccess, @"Unable to create keybag");
38 kern_return_t bag_saved = aks_save_bag(handle, &bag, &bagLen);
39 XCTAssertEqual(bag_saved, kAKSReturnSuccess, @"Unable to save keybag");
41 NSData* bagData = [NSData dataWithBytes:bag length:bagLen];
42 XCTAssertNotNil(bagData, @"Unable to create NSData from bag bytes");
48 // All backup paths ultimately lead to SecServerCopyKeychainPlist which does the actual exporting,
49 // so this test ought to suffice for all backup configurations
50 - (void)testAppClipDoesNotBackup {
52 // First add a "regular" item for each class, which we expect to be in the backup later
53 NSMutableDictionary* query = [@{
54 (id)kSecClass : (id)kSecClassGenericPassword,
55 (id)kSecUseDataProtectionKeychain : @YES,
56 (id)kSecValueData : [@"password" dataUsingEncoding:NSUTF8StringEncoding],
59 XCTAssertEqual(SecItemAdd((__bridge CFDictionaryRef)query, NULL), errSecSuccess);
60 XCTAssertEqual(SecItemCopyMatching((__bridge CFDictionaryRef)query, NULL), errSecSuccess);
62 query[(id)kSecClass] = (id)kSecClassInternetPassword;
63 XCTAssertEqual(SecItemAdd((__bridge CFDictionaryRef)query, NULL), errSecSuccess);
64 XCTAssertEqual(SecItemCopyMatching((__bridge CFDictionaryRef)query, NULL), errSecSuccess);
66 query[(id)kSecClass] = (id)kSecClassCertificate;
67 XCTAssertEqual(SecItemAdd((__bridge CFDictionaryRef)query, NULL), errSecSuccess);
68 XCTAssertEqual(SecItemCopyMatching((__bridge CFDictionaryRef)query, NULL), errSecSuccess);
70 query[(id)kSecClass] = (id)kSecClassKey;
71 XCTAssertEqual(SecItemAdd((__bridge CFDictionaryRef)query, NULL), errSecSuccess);
72 XCTAssertEqual(SecItemCopyMatching((__bridge CFDictionaryRef)query, NULL), errSecSuccess);
74 // Switch to being an app clip, add another item for each class, which we expect not to find in the backup
75 SecSecurityClientRegularToAppClip();
76 [self setEntitlements:@{@"com.apple.application-identifier" : _applicationIdentifier} validated:YES];
78 query[(id)kSecClass] = (id)kSecClassGenericPassword;
79 XCTAssertEqual(SecItemAdd((__bridge CFDictionaryRef)query, NULL), errSecSuccess);
80 XCTAssertEqual(SecItemCopyMatching((__bridge CFDictionaryRef)query, NULL), errSecSuccess);
82 query[(id)kSecClass] = (id)kSecClassInternetPassword;
83 XCTAssertEqual(SecItemAdd((__bridge CFDictionaryRef)query, NULL), errSecSuccess);
84 XCTAssertEqual(SecItemCopyMatching((__bridge CFDictionaryRef)query, NULL), errSecSuccess);
86 query[(id)kSecClass] = (id)kSecClassCertificate;
87 XCTAssertEqual(SecItemAdd((__bridge CFDictionaryRef)query, NULL), errSecSuccess);
88 XCTAssertEqual(SecItemCopyMatching((__bridge CFDictionaryRef)query, NULL), errSecSuccess);
90 query[(id)kSecClass] = (id)kSecClassKey;
91 XCTAssertEqual(SecItemAdd((__bridge CFDictionaryRef)query, NULL), errSecSuccess);
92 XCTAssertEqual(SecItemCopyMatching((__bridge CFDictionaryRef)query, NULL), errSecSuccess);
94 SecSecurityClientAppClipToRegular();
95 SecAddLocalSecuritydXPCFakeEntitlement(kSecEntitlementRestoreKeychain, @YES);
97 // Code lovingly adapted from si-33-keychain-backup
100 keybag = [self createKeybagWithType:kAppleKeyStoreBackupBag password:nil];
102 keybag = [NSData new];
105 NSData* data = CFBridgingRelease(_SecKeychainCopyBackup((__bridge CFDataRef)keybag, nil));
108 XCTAssertGreaterThan([data length], 42, @"Got empty dictionary");
109 NSDictionary* keychain = [NSPropertyListSerialization propertyListWithData:data options:NSPropertyListImmutable format:nil error:nil];
111 // Only one item should be here for each class, which is the regular one.
112 XCTAssertEqual([keychain[@"genp"] count], 1);
113 XCTAssertEqual([keychain[@"inet"] count], 1);
114 XCTAssertEqual([keychain[@"cert"] count], 1);
115 XCTAssertEqual([keychain[@"keys"] count], 1);