/ secdxctests / KeychainBackupTests.m
KeychainBackupTests.m
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> 6 7 @interface KeychainBackupTests : KeychainXCTest 8 @end 9 10 11 @implementation KeychainBackupTests { 12 NSString* _applicationIdentifier; 13 } 14 15 - (void)setUp { 16 // Put setup code here. This method is called before the invocation of each test method in the class. 17 [super setUp]; 18 _applicationIdentifier = @"com.apple.security.backuptests"; 19 SecSecurityClientSetApplicationIdentifier((__bridge CFStringRef)_applicationIdentifier); 20 } 21 22 - (void)tearDown { 23 // Put teardown code here. This method is called after the invocation of each test method in the class. 24 } 25 26 # pragma mark - Test OTA Backups 27 28 // Code lovingly adapted from si-33-keychain-backup 29 #if USE_KEYSTORE 30 - (NSData*)createKeybagWithType:(keybag_handle_t)bag_type password:(NSData*)password 31 { 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"); 35 36 void *bag = NULL; 37 int bagLen = 0; 38 kern_return_t bag_saved = aks_save_bag(handle, &bag, &bagLen); 39 XCTAssertEqual(bag_saved, kAKSReturnSuccess, @"Unable to save keybag"); 40 41 NSData* bagData = [NSData dataWithBytes:bag length:bagLen]; 42 XCTAssertNotNil(bagData, @"Unable to create NSData from bag bytes"); 43 44 return bagData; 45 } 46 #endif 47 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 { 51 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], 57 } mutableCopy]; 58 59 XCTAssertEqual(SecItemAdd((__bridge CFDictionaryRef)query, NULL), errSecSuccess); 60 XCTAssertEqual(SecItemCopyMatching((__bridge CFDictionaryRef)query, NULL), errSecSuccess); 61 62 query[(id)kSecClass] = (id)kSecClassInternetPassword; 63 XCTAssertEqual(SecItemAdd((__bridge CFDictionaryRef)query, NULL), errSecSuccess); 64 XCTAssertEqual(SecItemCopyMatching((__bridge CFDictionaryRef)query, NULL), errSecSuccess); 65 66 query[(id)kSecClass] = (id)kSecClassCertificate; 67 XCTAssertEqual(SecItemAdd((__bridge CFDictionaryRef)query, NULL), errSecSuccess); 68 XCTAssertEqual(SecItemCopyMatching((__bridge CFDictionaryRef)query, NULL), errSecSuccess); 69 70 query[(id)kSecClass] = (id)kSecClassKey; 71 XCTAssertEqual(SecItemAdd((__bridge CFDictionaryRef)query, NULL), errSecSuccess); 72 XCTAssertEqual(SecItemCopyMatching((__bridge CFDictionaryRef)query, NULL), errSecSuccess); 73 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]; 77 78 query[(id)kSecClass] = (id)kSecClassGenericPassword; 79 XCTAssertEqual(SecItemAdd((__bridge CFDictionaryRef)query, NULL), errSecSuccess); 80 XCTAssertEqual(SecItemCopyMatching((__bridge CFDictionaryRef)query, NULL), errSecSuccess); 81 82 query[(id)kSecClass] = (id)kSecClassInternetPassword; 83 XCTAssertEqual(SecItemAdd((__bridge CFDictionaryRef)query, NULL), errSecSuccess); 84 XCTAssertEqual(SecItemCopyMatching((__bridge CFDictionaryRef)query, NULL), errSecSuccess); 85 86 query[(id)kSecClass] = (id)kSecClassCertificate; 87 XCTAssertEqual(SecItemAdd((__bridge CFDictionaryRef)query, NULL), errSecSuccess); 88 XCTAssertEqual(SecItemCopyMatching((__bridge CFDictionaryRef)query, NULL), errSecSuccess); 89 90 query[(id)kSecClass] = (id)kSecClassKey; 91 XCTAssertEqual(SecItemAdd((__bridge CFDictionaryRef)query, NULL), errSecSuccess); 92 XCTAssertEqual(SecItemCopyMatching((__bridge CFDictionaryRef)query, NULL), errSecSuccess); 93 94 SecSecurityClientAppClipToRegular(); 95 SecAddLocalSecuritydXPCFakeEntitlement(kSecEntitlementRestoreKeychain, kCFBooleanTrue); 96 97 // Code lovingly adapted from si-33-keychain-backup 98 NSData* keybag; 99 #if USE_KEYSTORE 100 keybag = [self createKeybagWithType:kAppleKeyStoreBackupBag password:nil]; 101 #else 102 keybag = [NSData new]; 103 #endif 104 105 NSData* data = CFBridgingRelease(_SecKeychainCopyBackup((__bridge CFDataRef)keybag, nil)); 106 107 XCTAssert(data); 108 XCTAssertGreaterThan([data length], 42, @"Got empty dictionary"); 109 NSDictionary* keychain = [NSPropertyListSerialization propertyListWithData:data options:NSPropertyListImmutable format:nil error:nil]; 110 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); 116 } 117 118 @end