]> git.saurik.com Git - apple/security.git/blob - keychain/ckks/tests/CKKSFetchTests.m
Security-59306.11.20.tar.gz
[apple/security.git] / keychain / ckks / tests / CKKSFetchTests.m
1
2 #if OCTAGON
3
4 #import <CloudKit/CloudKit.h>
5 #import <XCTest/XCTest.h>
6 #import <OCMock/OCMock.h>
7 #import <notify.h>
8
9 #import "keychain/ckks/tests/CloudKitMockXCTest.h"
10 #import "keychain/ckks/tests/CloudKitKeychainSyncingMockXCTest.h"
11 #import "keychain/ckks/tests/CloudKitKeychainSyncingTestsBase.h"
12 #import "keychain/ckks/CKKS.h"
13 #import "keychain/ckks/CKKSKeychainView.h"
14
15 #import "keychain/ckks/tests/MockCloudKit.h"
16
17 @interface CloudKitKeychainFetchTests : CloudKitKeychainSyncingTestsBase
18 @end
19
20 @implementation CloudKitKeychainFetchTests
21
22 - (void)testMoreComing {
23 [self putFakeKeyHierarchyInCloudKit: self.keychainZoneID];
24 [self saveTLKMaterialToKeychain:self.keychainZoneID];
25
26 [self expectCKModifyKeyRecords:0 currentKeyPointerRecords:0 tlkShareRecords:1 zoneID:self.keychainZoneID];
27 [self startCKKSSubsystem];
28 XCTAssertEqual(0, [self.keychainView.keyHierarchyConditions[SecCKKSZoneKeyStateReady] wait:20*NSEC_PER_SEC], @"key state should enter 'ready'");
29 OCMVerifyAllWithDelay(self.mockDatabase, 20);
30 [self waitForCKModifications];
31
32 FakeCKZone* ckzone = self.zones[self.keychainZoneID];
33
34 [self.keychainZone addToZone: [self createFakeRecord: self.keychainZoneID recordName:@"7B598D31-0000-0000-0000-5A507ACB2D00" withAccount:@"account0"]];
35 [self.keychainZone addToZone: [self createFakeRecord: self.keychainZoneID recordName:@"7B598D31-0000-0000-0000-5A507ACB2D01" withAccount:@"account1"]];
36 CKServerChangeToken* ck1 = ckzone.currentChangeToken;
37 [self.keychainZone addToZone: [self createFakeRecord: self.keychainZoneID recordName:@"7B598D31-0000-0000-0000-5A507ACB2D02" withAccount:@"account2"]];
38 [self.keychainZone addToZone: [self createFakeRecord: self.keychainZoneID recordName:@"7B598D31-0000-0000-0000-5A507ACB2D03" withAccount:@"account3"]];
39
40 ckzone.limitFetchTo = ck1;
41
42 self.silentFetchesAllowed = false;
43 [self expectCKFetch];
44 [self expectCKFetchWithFilter:^BOOL(FakeCKFetchRecordZoneChangesOperation * _Nonnull frzco) {
45 // Assert that the fetch is happening with the change token we paused at before
46 CKServerChangeToken* changeToken = frzco.configurationsByRecordZoneID[self.keychainZoneID].previousServerChangeToken;
47 if(changeToken && [changeToken isEqual:ck1]) {
48 return YES;
49 } else {
50 return NO;
51 }
52 } runBeforeFinished:^{}];
53
54 // Trigger a notification (with hilariously fake data)
55 [self.keychainView notifyZoneChange:nil];
56
57 [self.keychainView waitForFetchAndIncomingQueueProcessing];
58
59 OCMVerifyAllWithDelay(self.mockDatabase, 20);
60
61 NSTimeInterval delta = [ckzone.fetchRecordZoneChangesTimestamps[2] timeIntervalSinceDate:ckzone.fetchRecordZoneChangesTimestamps[1]];
62 XCTAssertLessThan(delta, 2, "operation 1 and 2 should be back-to-back");
63
64 [self findGenericPassword: @"account0" expecting:errSecSuccess];
65 [self findGenericPassword: @"account1" expecting:errSecSuccess];
66 [self findGenericPassword: @"account2" expecting:errSecSuccess];
67 [self findGenericPassword: @"account3" expecting:errSecSuccess];
68 }
69
70 - (void)testMoreComingRepeated {
71 [self putFakeKeyHierarchyInCloudKit: self.keychainZoneID];
72 [self saveTLKMaterialToKeychain:self.keychainZoneID];
73
74 [self expectCKModifyKeyRecords:0 currentKeyPointerRecords:0 tlkShareRecords:1 zoneID:self.keychainZoneID];
75 [self startCKKSSubsystem];
76 XCTAssertEqual(0, [self.keychainView.keyHierarchyConditions[SecCKKSZoneKeyStateReady] wait:20*NSEC_PER_SEC], @"key state should enter 'ready'");
77 OCMVerifyAllWithDelay(self.mockDatabase, 20);
78 [self waitForCKModifications];
79
80 FakeCKZone* ckzone = self.zones[self.keychainZoneID];
81
82 [self.keychainZone addToZone: [self createFakeRecord: self.keychainZoneID recordName:@"7B598D31-0000-0000-0000-5A507ACB2D00" withAccount:@"account0"]];
83 [self.keychainZone addToZone: [self createFakeRecord: self.keychainZoneID recordName:@"7B598D31-0000-0000-0000-5A507ACB2D01" withAccount:@"account1"]];
84 CKServerChangeToken* ck1 = ckzone.currentChangeToken;
85 [self.keychainZone addToZone: [self createFakeRecord: self.keychainZoneID recordName:@"7B598D31-0000-0000-0000-5A507ACB2D02" withAccount:@"account2"]];
86 [self.keychainZone addToZone: [self createFakeRecord: self.keychainZoneID recordName:@"7B598D31-0000-0000-0000-5A507ACB2D03" withAccount:@"account3"]];
87 CKServerChangeToken* ck2 = ckzone.currentChangeToken;
88 [self.keychainZone addToZone: [self createFakeRecord: self.keychainZoneID recordName:@"7B598D31-0000-0000-0000-5A507ACB2D04" withAccount:@"account4"]];
89 [self.keychainZone addToZone: [self createFakeRecord: self.keychainZoneID recordName:@"7B598D31-0000-0000-0000-5A507ACB2D05" withAccount:@"account5"]];
90
91 ckzone.limitFetchTo = ck1;
92
93 self.silentFetchesAllowed = false;
94 // This fetch will return up to ck1.
95 [self expectCKFetchWithFilter:^BOOL(FakeCKFetchRecordZoneChangesOperation * _Nonnull frzco) {
96 return YES;
97 } runBeforeFinished:^{
98 ckzone.limitFetchTo = ck2;
99 }];
100
101 // This fetch will return up to ck2.
102 [self expectCKFetchWithFilter:^BOOL(FakeCKFetchRecordZoneChangesOperation * _Nonnull frzco) {
103 // Assert that the fetch is happening with the change token we paused at before
104 CKServerChangeToken* changeToken = frzco.configurationsByRecordZoneID[self.keychainZoneID].previousServerChangeToken;
105 if(changeToken && [changeToken isEqual:ck1]) {
106 return YES;
107 } else {
108 return NO;
109 }
110 } runBeforeFinished:^{}];
111
112 // This fetch will return the final two items.
113 [self expectCKFetchWithFilter:^BOOL(FakeCKFetchRecordZoneChangesOperation * _Nonnull frzco) {
114 // Assert that the fetch is happening with the change token we paused at before
115 CKServerChangeToken* changeToken = frzco.configurationsByRecordZoneID[self.keychainZoneID].previousServerChangeToken;
116 if(changeToken && [changeToken isEqual:ck2]) {
117 return YES;
118 } else {
119 return NO;
120 }
121 } runBeforeFinished:^{}];
122
123 // Trigger a notification (with hilariously fake data)
124 [self.keychainView notifyZoneChange:nil];
125
126 [self.keychainView waitForFetchAndIncomingQueueProcessing];
127
128 OCMVerifyAllWithDelay(self.mockDatabase, 20);
129
130 NSTimeInterval delta = [ckzone.fetchRecordZoneChangesTimestamps[2] timeIntervalSinceDate:ckzone.fetchRecordZoneChangesTimestamps[1]];
131 XCTAssertLessThan(delta, 2, "operation 1 and 2 should be back-to-back");
132
133 delta = [ckzone.fetchRecordZoneChangesTimestamps[3] timeIntervalSinceDate:ckzone.fetchRecordZoneChangesTimestamps[2]];
134 XCTAssertLessThan(delta, 2, "operation 2 and 3 should be back-to-back");
135
136 [self findGenericPassword: @"account0" expecting:errSecSuccess];
137 [self findGenericPassword: @"account1" expecting:errSecSuccess];
138 [self findGenericPassword: @"account2" expecting:errSecSuccess];
139 [self findGenericPassword: @"account3" expecting:errSecSuccess];
140 [self findGenericPassword: @"account4" expecting:errSecSuccess];
141 [self findGenericPassword: @"account5" expecting:errSecSuccess];
142 }
143
144 - (void)testMoreComingDespitePartialTimeout {
145 [self putFakeKeyHierarchyInCloudKit: self.keychainZoneID];
146 [self saveTLKMaterialToKeychain:self.keychainZoneID];
147
148 [self expectCKModifyKeyRecords:0 currentKeyPointerRecords:0 tlkShareRecords:1 zoneID:self.keychainZoneID];
149 [self startCKKSSubsystem];
150 XCTAssertEqual(0, [self.keychainView.keyHierarchyConditions[SecCKKSZoneKeyStateReady] wait:20*NSEC_PER_SEC], @"key state should enter 'ready'");
151 OCMVerifyAllWithDelay(self.mockDatabase, 20);
152 [self waitForCKModifications];
153
154 FakeCKZone* ckzone = self.zones[self.keychainZoneID];
155
156 [self.keychainZone addToZone: [self createFakeRecord: self.keychainZoneID recordName:@"7B598D31-0000-0000-0000-5A507ACB2D00" withAccount:@"account0"]];
157 [self.keychainZone addToZone: [self createFakeRecord: self.keychainZoneID recordName:@"7B598D31-0000-0000-0000-5A507ACB2D01" withAccount:@"account1"]];
158 CKServerChangeToken* ck1 = ckzone.currentChangeToken;
159 [self.keychainZone addToZone: [self createFakeRecord: self.keychainZoneID recordName:@"7B598D31-0000-0000-0000-5A507ACB2D02" withAccount:@"account2"]];
160 [self.keychainZone addToZone: [self createFakeRecord: self.keychainZoneID recordName:@"7B598D31-0000-0000-0000-5A507ACB2D03" withAccount:@"account3"]];
161
162 // The fetch fails with partial results
163 ckzone.limitFetchTo = ck1;
164 ckzone.limitFetchError = [[CKPrettyError alloc] initWithDomain:CKErrorDomain code:CKErrorNetworkFailure userInfo:@{CKErrorRetryAfterKey : [NSNumber numberWithInt:4]}];
165
166 self.silentFetchesAllowed = false;
167 [self expectCKFetch];
168 [self expectCKFetchWithFilter:^BOOL(FakeCKFetchRecordZoneChangesOperation * _Nonnull frzco) {
169 // Assert that the fetch is happening with the change token we paused at before
170 CKServerChangeToken* changeToken = frzco.configurationsByRecordZoneID[self.keychainZoneID].previousServerChangeToken;
171 if(changeToken && [changeToken isEqual:ck1]) {
172 return YES;
173 } else {
174 return NO;
175 }
176 } runBeforeFinished:^{}];
177
178 // Trigger a notification (with hilariously fake data)
179 [self.keychainView notifyZoneChange:nil];
180
181 [self.keychainView waitForFetchAndIncomingQueueProcessing];
182
183 OCMVerifyAllWithDelay(self.mockDatabase, 20);
184
185 [self findGenericPassword: @"account0" expecting:errSecSuccess];
186 [self findGenericPassword: @"account1" expecting:errSecSuccess];
187 [self findGenericPassword: @"account2" expecting:errSecSuccess];
188 [self findGenericPassword: @"account3" expecting:errSecSuccess];
189 }
190
191 @end
192
193 #endif // OCTAGON
194