]> git.saurik.com Git - apple/security.git/blob - keychain/escrowrequest/tests/SecEscrowRequestTests.m
Security-59754.80.3.tar.gz
[apple/security.git] / keychain / escrowrequest / tests / SecEscrowRequestTests.m
1
2 #import <XCTest/XCTest.h>
3 #import <OCMock/OCMock.h>
4 #import <CoreCDP/CDPError.h>
5 #import <CoreCDP/CDPStateController.h>
6 #import <CloudServices/CloudServices.h>
7
8 #import "keychain/categories/NSError+UsefulConstructors.h"
9 #import "keychain/escrowrequest/Framework/SecEscrowRequest.h"
10 #import "keychain/escrowrequest/EscrowRequestServer.h"
11
12 #import "keychain/escrowrequest/EscrowRequestServerHelpers.h"
13 #import "keychain/escrowrequest/operations/EscrowRequestInformCloudServicesOperation.h"
14 #import "keychain/escrowrequest/operations/EscrowRequestPerformEscrowEnrollOperation.h"
15 #import "keychain/escrowrequest/EscrowRequestServerHelpers.h"
16
17 #import "keychain/escrowrequest/tests/MockSynchronousEscrowServer.h"
18
19 #include "keychain/ckks/CKKS.h"
20 #include "keychain/ckks/CKKSLockStateTracker.h"
21 #include "keychain/securityd/SecItemServer.h"
22 #include "keychain/securityd/spi.h"
23 #include "utilities/SecFileLocations.h"
24
25 #include "tests/secdmockaks/mockaks.h"
26
27 @interface SecEscrowRequestTests : XCTestCase
28 @property SecEscrowRequest* escrowRequest;
29 @property EscrowRequestServer* escrowServer;
30 @property CKKSLockStateTracker* lockStateTracker;
31
32 @property id escrowRequestServerClassMock;
33 @property id escrowRequestInformCloudServicesOperationMock;
34 @property id escrowRequestPerformEscrowEnrollOperationMock;
35 @end
36
37 @implementation SecEscrowRequestTests
38
39 + (void)setUp {
40 securityd_init_local_spi();
41 EscrowRequestServerSetEnabled(true);
42 }
43
44 - (void)setUp {
45 NSString* testName = [self.name componentsSeparatedByString:@" "][1];
46 testName = [testName stringByReplacingOccurrencesOfString:@"]" withString:@""];
47 secnotice("secescrowtest", "Beginning test %@", testName);
48
49 [SecMockAKS unlockAllClasses];
50
51 // Make a new fake keychain
52 NSString* tmp_dir = [NSString stringWithFormat: @"/tmp/%@.%X", testName, arc4random()];
53 [[NSFileManager defaultManager] createDirectoryAtPath:[NSString stringWithFormat: @"%@/Library/Keychains", tmp_dir] withIntermediateDirectories:YES attributes:nil error:NULL];
54
55 SecCKKSDisable();
56 SecCKKSTestDisableSOS();
57
58 // Mock out the SBD layer
59 self.escrowRequestServerClassMock = OCMClassMock([EscrowRequestServer class]);
60 self.escrowRequestInformCloudServicesOperationMock = OCMClassMock([EscrowRequestInformCloudServicesOperation class]);
61 self.escrowRequestPerformEscrowEnrollOperationMock = OCMClassMock([EscrowRequestPerformEscrowEnrollOperation class]);
62
63 self.lockStateTracker = [[CKKSLockStateTracker alloc] init];
64 self.escrowServer = [[EscrowRequestServer alloc] initWithLockStateTracker:self.lockStateTracker];
65
66 id mockConnection = OCMPartialMock([[NSXPCConnection alloc] init]);
67 OCMStub([mockConnection remoteObjectProxyWithErrorHandler:[OCMArg any]]).andCall(self, @selector(escrowServer));
68 OCMStub([mockConnection synchronousRemoteObjectProxyWithErrorHandler:[OCMArg any]]).andCall(self, @selector(synchronousEscrowServer));
69 self.escrowRequest = [[SecEscrowRequest alloc] initWithConnection:mockConnection];
70
71 SetCustomHomeURLString((__bridge CFStringRef) tmp_dir);
72 SecKeychainDbReset(NULL);
73
74 // Actually load the database.
75 kc_with_dbt(true, NULL, ^bool (SecDbConnectionRef dbt) { return false; });
76 }
77
78
79 - (NSData* _Nullable)mockTriggerCloudServicesPasscodeRequest:(NSString*)uuid error:(NSError**)error
80 {
81 return [@"certdata" dataUsingEncoding:NSUTF8StringEncoding];
82 }
83
84 - (NSData* _Nullable)mockTriggerCloudServicesPasscodeRequestAndLockClassA:(NSString*)uuid error:(NSError**)error
85 {
86 [SecMockAKS lockClassA];
87 [self.lockStateTracker recheck];
88 return [@"certdata" dataUsingEncoding:NSUTF8StringEncoding];
89 }
90
91 - (NSData* _Nullable)mockFailTriggerCloudServicesPasscodeRequest:(NSString*)uuid error:(NSError**)error
92 {
93 if(error) {
94 *error = [NSError errorWithDomain:@"NSURLErrorDomain"
95 code:-1009
96 description:@"The internet connection appears to be offline (mock)"];
97 }
98 return nil;
99 }
100
101 - (void)mockcdpUploadPrerecord:(SecEscrowPendingRecord*)recordToSend
102 secretType:(CDPDeviceSecretType)secretType
103 reply:(void (^)(bool didUpdate, NSError* _Nullable error))reply
104 {
105 dispatch_async(dispatch_get_global_queue(QOS_CLASS_UTILITY, 0), ^ {
106 reply(YES, nil);
107 });
108 }
109
110 - (void)mockFailcdpUploadPrerecord:(SecEscrowPendingRecord*)recordToSend
111 secretType:(CDPDeviceSecretType)secretType
112 reply:(void (^)(bool didUpdate, NSError* _Nullable error))reply
113 {
114 dispatch_async(dispatch_get_global_queue(QOS_CLASS_UTILITY, 0), ^ {
115 reply(NO, [NSError errorWithDomain:@"EscrowServiceErrorDomain" code:-6570 description:@"mock CLUBH error"]);
116 });
117 }
118
119 - (void)mockcdpFailBadPeerUploadPrerecord:(SecEscrowPendingRecord*)recordToSend
120 secretType:(CDPDeviceSecretType)secretType
121 reply:(void (^)(bool didUpdate, NSError* _Nullable error))reply
122 {
123 dispatch_async(dispatch_get_global_queue(QOS_CLASS_UTILITY, 0), ^ {
124 reply(NO, [NSError errorWithDomain:kSecureBackupErrorDomain code:kSecureBackupInternalError description:@"SOS peer ID mismatch"]);
125 });
126 }
127
128 - (void)mockcdpFailNoSOSPeerUploadPrerecord:(SecEscrowPendingRecord*)recordToSend
129 secretType:(CDPDeviceSecretType)secretType
130 reply:(void (^)(bool didUpdate, NSError* _Nullable error))reply
131 {
132 dispatch_async(dispatch_get_global_queue(QOS_CLASS_UTILITY, 0), ^ {
133 reply(NO, [NSError errorWithDomain:kSecureBackupErrorDomain code:kSecureBackupNotInSyncCircleError description:@"SOS peer not present"]);
134 });
135 }
136
137 - (void)mockcdpFailNoCDPPeerUploadPrerecord:(SecEscrowPendingRecord*)recordToSend
138 secretType:(CDPDeviceSecretType)secretType
139 reply:(void (^)(bool didUpdate, NSError* _Nullable error))reply
140 {
141 dispatch_async(dispatch_get_global_queue(QOS_CLASS_UTILITY, 0), ^ {
142 reply(NO, [NSError errorWithDomain:CDPStateErrorDomain code:CDPStateErrorNoPeerIdFound description:@"SOS peer not present"]);
143 });
144 }
145
146
147 - (id<EscrowRequestXPCProtocol>)synchronousEscrowServer
148 {
149 return [[MockSynchronousEscrowServer alloc] initWithServer:self.escrowServer];
150 }
151
152 - (void)tearDown {
153 [self.escrowServer.controller.stateMachine haltOperation];
154 }
155
156 - (void)allCloudServicesCallsSucceed {
157 OCMStub([self.escrowRequestInformCloudServicesOperationMock triggerCloudServicesPasscodeRequest:[OCMArg any] error:[OCMArg anyObjectRef]]).andCall(self, @selector(mockTriggerCloudServicesPasscodeRequest:error:));
158 }
159
160 - (void)testStateMachineEntersNothingToDo
161 {
162 [self.escrowServer.controller.stateMachine startOperation];
163 XCTAssertEqual(0, [self.escrowServer.controller.stateMachine.stateConditions[EscrowRequestStateNothingToDo] wait:10*NSEC_PER_SEC], "State machine enters NothingToDo");
164 }
165
166 - (void)testTriggerUpdate {
167 [self allCloudServicesCallsSucceed];
168
169 NSError* error = nil;
170
171 XCTAssertNil([self.escrowRequest fetchRequestWaitingOnPasscode:&error], @"Should be no pending updates");
172 XCTAssertNil(error, @"Should be no error fetching pending updates");
173
174 XCTAssertTrue([self.escrowRequest triggerEscrowUpdate:@"test" error:&error], @"Should be able to trigger an update");
175 XCTAssertNil(error, @"Should be no error triggering an escrow update");
176
177 XCTAssertEqual(0, [self.escrowServer.controller.stateMachine.paused wait:10*NSEC_PER_SEC], @"State machine should churn through");
178
179 NSString* pendingUUID = [self.escrowRequest fetchRequestWaitingOnPasscode:&error];
180 XCTAssertNil(error, @"Should be no error fetching a pending UUID");
181 XCTAssertNotNil(pendingUUID, "Should be a pending request UUID after a trigger");
182 }
183
184 - (void)testTriggerUpdateWhileLocked {
185 [self allCloudServicesCallsSucceed];
186
187 [SecMockAKS lockClassA];
188
189 NSError* error = nil;
190
191 XCTAssertNil([self.escrowRequest fetchRequestWaitingOnPasscode:&error], @"Should be no pending updates");
192 XCTAssertNil(error, @"Should be no error fetching pending updates");
193
194 XCTAssertFalse([self.escrowRequest triggerEscrowUpdate:@"test" error:&error], @"Should not be able to trigger an update when locked");
195 XCTAssertNotNil(error, @"Should be an error triggering an escrow update (while locked)");
196 error = nil;
197
198 XCTAssertEqual(0, [self.escrowServer.controller.stateMachine.paused wait:10*NSEC_PER_SEC], @"State machine should churn through");
199
200 NSString* pendingUUID = [self.escrowRequest fetchRequestWaitingOnPasscode:&error];
201 XCTAssertNil(error, @"Should be no error fetching a pending UUID");
202 XCTAssertNil(pendingUUID, "Should be no pending request UUID after a failed trigger");
203 }
204
205 - (void)testMultipleTriggers {
206 [self allCloudServicesCallsSucceed];
207
208 NSError* error = nil;
209
210 XCTAssertTrue([self.escrowRequest triggerEscrowUpdate:@"test" error:&error], @"Should be able to trigger an update");
211 XCTAssertNil(error, @"Should be no error triggering an escrow update");
212
213 XCTAssertTrue([self.escrowRequest triggerEscrowUpdate:@"test" error:&error], @"Should be able to trigger an update");
214 XCTAssertNil(error, @"Should be no error triggering an escrow update");
215
216 XCTAssertTrue([self.escrowRequest triggerEscrowUpdate:@"test" error:&error], @"Should be able to trigger an update");
217 XCTAssertNil(error, @"Should be no error triggering an escrow update");
218
219 NSDictionary<NSString*, NSString*>* statuses = [self.escrowRequest fetchStatuses:&error];
220 XCTAssertNotNil(statuses, @"Should be able to fetch statuses");
221 XCTAssertNil(error, @"Should be no error fetching statuses");
222
223 XCTAssertEqual(statuses.count, 1, @"Should be one in-flight status");
224 }
225
226 - (void)testFetchUpdateWhileLocked {
227 [self allCloudServicesCallsSucceed];
228
229 NSError* error = nil;
230
231 XCTAssertNil([self.escrowRequest fetchRequestWaitingOnPasscode:&error], @"Should be no pending updates");
232 XCTAssertNil(error, @"Should be no error fetching pending updates");
233
234 XCTAssertTrue([self.escrowRequest triggerEscrowUpdate:@"test" error:&error], @"Should be able to trigger an update");
235 XCTAssertNil(error, @"Should be no error triggering an escrow update");
236
237 XCTAssertEqual(0, [self.escrowServer.controller.stateMachine.paused wait:10*NSEC_PER_SEC], @"State machine should churn through");
238 error = nil;
239
240 [SecMockAKS lockClassA];
241
242 NSString* pendingUUID = [self.escrowRequest fetchRequestWaitingOnPasscode:&error];
243 XCTAssertNotNil(error, @"Should be an error fetching a pending UUID while the device is locked");
244 XCTAssertNil(pendingUUID, "Should be no pending request UUID while device is locked");
245 }
246
247 - (void)testReaskCloudServicesWhenLocked {
248 // In this test, CloudServices is able to succeed (and cache a certificate), but we can't write down that the operation succeeded.
249 NSError* error = nil;
250
251 // No rate limiting for us!
252 self.escrowServer.controller.forceIgnoreCloudServicesRateLimiting = true;
253
254 // Pause the state machine for a bit...
255 [self.escrowServer.controller.stateMachine startOperation];
256 XCTAssertEqual(0, [self.escrowServer.controller.stateMachine.paused wait:10*NSEC_PER_SEC], @"State machine should pause");
257 [self.escrowServer.controller.stateMachine haltOperation];
258
259 XCTAssertTrue([self.escrowRequest triggerEscrowUpdate:@"test" error:&error], @"Should be able to trigger an update");
260 XCTAssertNil(error, @"Should be no error triggering an escrow update");
261
262 // First, let's ensure that we properly handle the case where the device is locked when we go to ask CloudServices about things
263
264 [SecMockAKS lockClassA];
265 [self.lockStateTracker recheck];
266 [self.escrowServer.controller.stateMachine startOperation];
267 XCTAssertEqual(0, [self.escrowServer.controller.stateMachine.stateConditions[EscrowRequestStateWaitForUnlock] wait:10*NSEC_PER_SEC], "State machine enters waitforunlock");
268
269 // Now, we should call CloudServices, but the device locks between CS success and us writing it down
270 OCMExpect([self.escrowRequestInformCloudServicesOperationMock triggerCloudServicesPasscodeRequest:[OCMArg any] error:[OCMArg anyObjectRef]]).andCall(self, @selector(mockTriggerCloudServicesPasscodeRequestAndLockClassA:error:));
271 [SecMockAKS unlockAllClasses];
272 [self.lockStateTracker recheck];
273
274 OCMVerifyAllWithDelay(self.escrowRequestInformCloudServicesOperationMock, 10);
275 // and we should be back in wait for unlock:
276 XCTAssertEqual(0, [self.escrowServer.controller.stateMachine.stateConditions[EscrowRequestStateWaitForUnlock] wait:10*NSEC_PER_SEC], "State machine enters waitforunlock");
277
278 // Then, unlock one last time, and let everything succeed
279 OCMExpect([self.escrowRequestInformCloudServicesOperationMock triggerCloudServicesPasscodeRequest:[OCMArg any] error:[OCMArg anyObjectRef]]).andCall(self, @selector(mockTriggerCloudServicesPasscodeRequest:error:));
280 [SecMockAKS unlockAllClasses];
281 [self.lockStateTracker recheck];
282
283 OCMVerifyAllWithDelay(self.escrowRequestInformCloudServicesOperationMock, 10);
284 }
285
286 - (void)testRateLimitSBDTriggerWhenFailing {
287
288 // Trigger an escrow update, which should call CloudServices and fail
289 OCMExpect([self.escrowRequestInformCloudServicesOperationMock triggerCloudServicesPasscodeRequest:[OCMArg any] error:[OCMArg anyObjectRef]]).andCall(self, @selector(mockFailTriggerCloudServicesPasscodeRequest:error:));
290
291 NSError* error = nil;
292 XCTAssertTrue([self.escrowRequest triggerEscrowUpdate:@"test" error:&error], @"Should be able to trigger an update");
293 XCTAssertNil(error, @"Should be no error triggering an escrow update");
294
295 OCMVerifyAll(self.escrowRequestServerClassMock);
296
297 // And, the state machine should pause
298 XCTAssertEqual(0, [self.escrowServer.controller.stateMachine.paused wait:10*NSEC_PER_SEC], @"State machine should rest");
299
300 // But, there should be no pending requests (as we need to retry CloudServices)
301 NSString* pendingUUID = [self.escrowRequest fetchRequestWaitingOnPasscode:&error];
302 XCTAssertNil(error, @"Should be no error fetching a pending UUID");
303 XCTAssertNil(pendingUUID, "Should be no pending request UUID after a failed trigger");
304
305
306 // But, the controller will recover at some point (for now, disable rate limiting)
307 [self allCloudServicesCallsSucceed];
308 self.escrowServer.controller.forceIgnoreCloudServicesRateLimiting = true;
309 [self.escrowServer.controller.stateMachine pokeStateMachine];
310
311 XCTAssertEqual(0, [self.escrowServer.controller.stateMachine.paused wait:10*NSEC_PER_SEC], @"State machine should rest");
312
313 pendingUUID = [self.escrowRequest fetchRequestWaitingOnPasscode:&error];
314 XCTAssertNil(error, @"Should be no error fetching a pending UUID");
315 XCTAssertNotNil(pendingUUID, "Should be a pending request UUID after the state machine retries certificate caching");
316 }
317
318 - (void)testRateLimitSBDUploadWhenFailing {
319
320 // Trigger an escrow update, which should call CloudServices
321 OCMExpect([self.escrowRequestInformCloudServicesOperationMock triggerCloudServicesPasscodeRequest:[OCMArg any] error:[OCMArg anyObjectRef]]).andCall(self, @selector(mockTriggerCloudServicesPasscodeRequest:error:));
322
323 NSError* error = nil;
324 XCTAssertTrue([self.escrowRequest triggerEscrowUpdate:@"test" error:&error], @"Should be able to trigger an update");
325 XCTAssertNil(error, @"Should be no error triggering an escrow update");
326
327 OCMVerifyAllWithDelay(self.escrowRequestInformCloudServicesOperationMock, 5);
328
329 XCTAssertEqual(0, [self.escrowServer.controller.stateMachine.paused wait:10*NSEC_PER_SEC], @"State machine should pause within some time");
330
331 NSString* pendingUUID = [self.escrowRequest fetchRequestWaitingOnPasscode:&error];
332 XCTAssertNil(error, @"Should be no error fetching a pending UUID");
333 XCTAssertNotNil(pendingUUID, "Should be a pending request UUID after a trigger");
334
335
336 [[[[self.escrowRequestPerformEscrowEnrollOperationMock expect] andCall:@selector(mockFailcdpUploadPrerecord:secretType:reply:)
337 onObject:self] ignoringNonObjectArgs]
338 cdpUploadPrerecord:[OCMArg any] secretType:0 reply:[OCMArg any]];
339
340
341 NSData* d = [[NSData alloc]initWithBase64EncodedString:@"YXNkZgo=" options:0];
342 XCTAssertTrue([self.escrowRequest cachePrerecord:pendingUUID
343 serializedPrerecord:d
344 error:&error], @"Should be able to cache a prerecord");
345 XCTAssertNil(error, @"Should be no error caching a prerecord");
346
347 // Wait for the upload to fail...
348 OCMVerifyAllWithDelay(self.escrowRequestPerformEscrowEnrollOperationMock, 10);
349
350 XCTAssertEqual(0, [self.escrowServer.controller.stateMachine.paused wait:10*NSEC_PER_SEC], @"State machine should rest");
351
352 //////////////////////////
353 // Trigger another update, to check coalescing
354 OCMExpect([self.escrowRequestInformCloudServicesOperationMock triggerCloudServicesPasscodeRequest:[OCMArg any] error:[OCMArg anyObjectRef]]).andCall(self, @selector(mockTriggerCloudServicesPasscodeRequest:error:));
355
356 XCTAssertTrue([self.escrowRequest triggerEscrowUpdate:@"test2" error:&error], @"Should be able to trigger an update");
357 XCTAssertNil(error, @"Should be no error triggering an escrow update");
358 OCMVerifyAllWithDelay(self.escrowRequestInformCloudServicesOperationMock, 5);
359
360 XCTAssertEqual(0, [self.escrowServer.controller.stateMachine.paused wait:10*NSEC_PER_SEC], @"State machine should rest");
361
362 NSString* pendingUUIDTheSecond = [self.escrowRequest fetchRequestWaitingOnPasscode:&error];
363 XCTAssertNotNil(pendingUUIDTheSecond, @"Should have a request waiting on passcode");
364 XCTAssertEqualObjects(pendingUUID, pendingUUIDTheSecond, @"In-flight request should have been restarted");
365 //////////////
366
367 [[[[self.escrowRequestPerformEscrowEnrollOperationMock expect] andCall:@selector(mockFailcdpUploadPrerecord:secretType:reply:)
368 onObject:self] ignoringNonObjectArgs]
369 cdpUploadPrerecord:[OCMArg any] secretType:0 reply:[OCMArg any]];
370 XCTAssertTrue([self.escrowRequest cachePrerecord:pendingUUIDTheSecond
371 serializedPrerecord:d
372 error:&error], @"Should be able to cache a prerecord");
373 XCTAssertNil(error, @"Should be no error caching a prerecord");
374
375 XCTAssertEqual(0, [self.escrowServer.controller.stateMachine.paused wait:10*NSEC_PER_SEC], @"State machine should rest");
376
377 NSDictionary<NSString*, NSString*>* statuses = [self.escrowRequest fetchStatuses:&error];
378 XCTAssertNotNil(statuses, @"Should be able to fetch statuses");
379 XCTAssertNil(error, @"Should be no error fetching statuses");
380
381 XCTAssertEqual(statuses.count, 1, @"Should be one in-flight status");
382
383 // But if escrow plays nice again, kick state machine, and make sure we don't touch escrow proxy
384 [[[[self.escrowRequestPerformEscrowEnrollOperationMock stub] andCall:@selector(mockcdpUploadPrerecord:secretType:reply:)
385 onObject:self] ignoringNonObjectArgs]
386 cdpUploadPrerecord:[OCMArg any] secretType:0 reply:[OCMArg any]];
387
388
389 [self.escrowServer.controller.stateMachine startOperation];
390
391 XCTAssertEqual(0, [self.escrowServer.controller.stateMachine.paused wait:10*NSEC_PER_SEC], @"State machine should rest");
392
393 /* item should still be in flight */
394 statuses = [self.escrowRequest fetchStatuses:&error];
395 XCTAssertNotNil(statuses, @"Should be able to fetch statuses");
396 XCTAssertNil(error, @"Should be no error fetching statuses");
397
398 XCTAssertEqual(statuses.count, 1, @"Should be one in-flight status");
399 }
400
401
402 - (void)testPrerecordCaching {
403 NSError* error = nil;
404
405 // Trigger an escrow update, which should call CloudServices
406 OCMExpect([self.escrowRequestInformCloudServicesOperationMock triggerCloudServicesPasscodeRequest:[OCMArg any] error:[OCMArg anyObjectRef]]).andCall(self, @selector(mockTriggerCloudServicesPasscodeRequest:error:));
407
408 XCTAssertTrue([self.escrowRequest triggerEscrowUpdate:@"test" error:&error], @"Should be able to trigger an update");
409 XCTAssertNil(error, @"Should be no error triggering an escrow update");
410
411 OCMVerifyAll(self.escrowRequestServerClassMock);
412
413 XCTAssertEqual(0, [self.escrowServer.controller.stateMachine.paused wait:10*NSEC_PER_SEC], @"State machine should rest");
414
415 // Now, there should be a pending request
416
417 NSString* pendingUUID = [self.escrowRequest fetchRequestWaitingOnPasscode:&error];
418 XCTAssertNil(error, @"Should be no error fetching a pending UUID");
419 XCTAssertNotNil(pendingUUID, "Should be a pending request UUID after a trigger");
420
421 // Caching a prerecord will attempt an upload via the state machine. But, since we're testing prerecord fetching, we don't want that to happen.
422 // So, let the upload fail.
423 [[[[self.escrowRequestPerformEscrowEnrollOperationMock expect] andCall:@selector(mockFailcdpUploadPrerecord:secretType:reply:)
424 onObject:self] ignoringNonObjectArgs]
425 cdpUploadPrerecord:[OCMArg any] secretType:0 reply:[OCMArg any]];
426
427
428 NSData* d = [[NSData alloc]initWithBase64EncodedString:@"YXNkZgo=" options:0];
429 XCTAssertTrue([self.escrowRequest cachePrerecord:pendingUUID
430 serializedPrerecord:d
431 error:&error], @"Should be able to cache a prerecord");
432 XCTAssertNil(error, @"Should be no error caching a prerecord");
433
434 // Wait for the upload to fail...
435 OCMVerifyAllWithDelay(self.escrowRequestPerformEscrowEnrollOperationMock, 10);
436
437 NSString* nowPendingUUID = [self.escrowRequest fetchRequestWaitingOnPasscode:&error];
438 XCTAssertNil(error, @"Should be no error fetching a pending UUID");
439 XCTAssertNil(nowPendingUUID, "Should be no pending request UUID after a prerecord cache");
440
441 NSData* d2 = [self.escrowRequest fetchPrerecord:pendingUUID
442 error:&error];
443 XCTAssertNotNil(d2, @"Should be able to retrieve a cached prerecord");
444 XCTAssertNil(error, @"Should be no error fetching a cached prerecord");
445
446 XCTAssertEqualObjects(d,d2, @"Cached prerecord should be equal to original");
447 }
448
449 - (void)testPrerecordCachingWhileLocked {
450 NSError* error = nil;
451
452 // Trigger an escrow update, which should call CloudServices
453 OCMExpect([self.escrowRequestInformCloudServicesOperationMock triggerCloudServicesPasscodeRequest:[OCMArg any] error:[OCMArg anyObjectRef]]).andCall(self, @selector(mockTriggerCloudServicesPasscodeRequest:error:));
454
455 XCTAssertTrue([self.escrowRequest triggerEscrowUpdate:@"test" error:&error], @"Should be able to trigger an update");
456 XCTAssertNil(error, @"Should be no error triggering an escrow update");
457
458 OCMVerifyAll(self.escrowRequestServerClassMock);
459
460 XCTAssertEqual(0, [self.escrowServer.controller.stateMachine.paused wait:10*NSEC_PER_SEC], @"State machine should rest");
461
462 // Now, there should be a pending request
463 NSString* pendingUUID = [self.escrowRequest fetchRequestWaitingOnPasscode:&error];
464 XCTAssertNil(error, @"Should be no error fetching a pending UUID");
465 XCTAssertNotNil(pendingUUID, "Should be a pending request UUID after a trigger");
466
467 [SecMockAKS lockClassA];
468 // Caching a prerecord will fail, and should not invoke CDP.
469 // So, let the upload fail.
470 [[[self.escrowRequestPerformEscrowEnrollOperationMock reject] ignoringNonObjectArgs]
471 cdpUploadPrerecord:[OCMArg any] secretType:0 reply:[OCMArg any]];
472
473 NSData* d = [[NSData alloc]initWithBase64EncodedString:@"YXNkZgo=" options:0];
474 XCTAssertFalse([self.escrowRequest cachePrerecord:pendingUUID
475 serializedPrerecord:d
476 error:&error], @"Should be not able to cache a prerecord while the device is locked");
477 XCTAssertNotNil(error, @"Should be an error caching a prerecord while the device is locked");
478
479 // Ensure we don't invoke CDP
480 XCTAssertEqual(0, [self.escrowServer.controller.stateMachine.paused wait:10*NSEC_PER_SEC], @"State machine should rest");
481 OCMVerifyAll(self.escrowRequestPerformEscrowEnrollOperationMock);
482
483 // And now, when we unlock, there's still a pending request
484 error = nil;
485 [SecMockAKS unlockAllClasses];
486 pendingUUID = [self.escrowRequest fetchRequestWaitingOnPasscode:&error];
487 XCTAssertNil(error, @"Should be no error fetching a pending UUID");
488 XCTAssertNotNil(pendingUUID, "Should be a pending request UUID after a trigger");
489 }
490
491 - (void)testEscrowUploadViaStateMachine {
492 // This test should call CloudServices, which will succeed. It should then tell CoreCDP to upload the record.
493 [self allCloudServicesCallsSucceed];
494 [[[[self.escrowRequestPerformEscrowEnrollOperationMock expect] andCall:@selector(mockcdpUploadPrerecord:secretType:reply:)
495 onObject:self] ignoringNonObjectArgs]
496 cdpUploadPrerecord:[OCMArg any] secretType:0 reply:[OCMArg any]];
497
498 [self.escrowServer.controller.stateMachine startOperation];
499 XCTAssertEqual(0, [self.escrowServer.controller.stateMachine.stateConditions[EscrowRequestStateNothingToDo] wait:10*NSEC_PER_SEC], "State machine enters NothingToDo when started");
500
501 NSError* error = nil;
502 OCMExpect([self.escrowRequestInformCloudServicesOperationMock triggerCloudServicesPasscodeRequest:[OCMArg any] error:[OCMArg anyObjectRef]]).andCall(self, @selector(mockTriggerCloudServicesPasscodeRequest:error:));
503 XCTAssertTrue([self.escrowRequest triggerEscrowUpdate:@"test" error:&error], @"Should be able to trigger an update");
504 XCTAssertNil(error, @"Should be no error triggering an escrow update");
505 OCMVerifyAll(self.escrowRequestServerClassMock);
506
507
508 XCTAssertEqual(0, [self.escrowServer.controller.stateMachine.paused wait:10*NSEC_PER_SEC], @"State machine should pause within some time");
509
510 NSString* pendingUUID = [self.escrowRequest fetchRequestWaitingOnPasscode:&error];
511 XCTAssertNil(error, @"Should be no error fetching a pending UUID");
512 XCTAssertNotNil(pendingUUID, "Should be a pending request UUID after a trigger");
513
514 NSData* d = [[NSData alloc]initWithBase64EncodedString:@"YXNkZgo=" options:0];
515 XCTAssertTrue([self.escrowRequest cachePrerecord:pendingUUID
516 serializedPrerecord:d
517 error:&error], @"Should be able to cache a prerecord");
518 XCTAssertNil(error, @"Should be no error caching a prerecord");
519
520 // Don't call the "do it" RPC, but it should happen anyway
521 OCMVerifyAllWithDelay(self.escrowRequestPerformEscrowEnrollOperationMock, 10);
522 }
523
524 - (void)testEscrowUploadViaRPCAfterFailure {
525 [[[[self.escrowRequestPerformEscrowEnrollOperationMock expect] andCall:@selector(mockFailcdpUploadPrerecord:secretType:reply:)
526 onObject:self] ignoringNonObjectArgs]
527 cdpUploadPrerecord:[OCMArg any] secretType:0 reply:[OCMArg any]];
528
529 NSError* error = nil;
530
531 OCMExpect([self.escrowRequestInformCloudServicesOperationMock triggerCloudServicesPasscodeRequest:[OCMArg any] error:[OCMArg anyObjectRef]]).andCall(self, @selector(mockTriggerCloudServicesPasscodeRequest:error:));
532
533 XCTAssertTrue([self.escrowRequest triggerEscrowUpdate:@"test" error:&error], @"Should be able to trigger an update");
534 XCTAssertNil(error, @"Should be no error triggering an escrow update");
535
536 OCMVerifyAll(self.escrowRequestServerClassMock);
537
538 XCTAssertEqual(0, [self.escrowServer.controller.stateMachine.paused wait:10*NSEC_PER_SEC], @"State machine should pause within some time");
539
540 NSString* pendingUUID = [self.escrowRequest fetchRequestWaitingOnPasscode:&error];
541 XCTAssertNil(error, @"Should be no error fetching a pending UUID");
542 XCTAssertNotNil(pendingUUID, "Should be a pending request UUID after a trigger");
543
544 NSData* d = [[NSData alloc]initWithBase64EncodedString:@"YXNkZgo=" options:0];
545 XCTAssertTrue([self.escrowRequest cachePrerecord:pendingUUID
546 serializedPrerecord:d
547 error:&error], @"Should be able to cache a prerecord");
548 XCTAssertNil(error, @"Should be no error caching a prerecord");
549
550
551 // Now, the state machine should try and fail the upload
552 OCMVerifyAllWithDelay(self.escrowRequestPerformEscrowEnrollOperationMock, 10);
553
554 // And pause for a while
555 XCTAssertEqual(0, [self.escrowServer.controller.stateMachine.paused wait:10*NSEC_PER_SEC], @"State machine should pause within some time");
556
557 // But if escrow plays nice again, a 'try again' RPC should succeed
558 [[[[self.escrowRequestPerformEscrowEnrollOperationMock stub] andCall:@selector(mockcdpUploadPrerecord:secretType:reply:)
559 onObject:self] ignoringNonObjectArgs]
560 cdpUploadPrerecord:[OCMArg any] secretType:0 reply:[OCMArg any]];
561
562 uint64_t records = [self.escrowRequest storePrerecordsInEscrow:&error];
563 XCTAssertNil(error, @"Should be no error storing prerecords in escrow");
564 XCTAssertEqual(records, 1, @"Should have stored one record in escrow");
565
566 NSDictionary<NSString*, NSString*>* statuses = [self.escrowRequest fetchStatuses:&error];
567 XCTAssertNotNil(statuses, @"Should be able to fetch statuses");
568 XCTAssertNil(error, @"Should be no error fetching statuses");
569
570 XCTAssertEqual(statuses.count, 1, @"Should be one in-flight status");
571 for(id key in statuses.keyEnumerator) {
572 XCTAssertEqualObjects(statuses[key], @"complete", "Record should be in 'complete' state");
573 }
574 }
575
576 - (void)testEscrowUploadViaStateMachineAfterFailureDueToLockState {
577 // This test should call CloudServices, which will succeed. It should then tell CDP to upload the record, which will fail due to lock state.
578 [self allCloudServicesCallsSucceed];
579 [[[[self.escrowRequestPerformEscrowEnrollOperationMock expect] andCall:@selector(mockcdpUploadPrerecord:secretType:reply:)
580 onObject:self] ignoringNonObjectArgs]
581 cdpUploadPrerecord:[OCMArg any] secretType:0 reply:[OCMArg any]];
582
583 [self.escrowServer.controller.stateMachine startOperation];
584 XCTAssertEqual(0, [self.escrowServer.controller.stateMachine.stateConditions[EscrowRequestStateNothingToDo] wait:10*NSEC_PER_SEC], "State machine enters NothingToDo when started");
585
586 NSError* error = nil;
587 OCMExpect([self.escrowRequestInformCloudServicesOperationMock triggerCloudServicesPasscodeRequest:[OCMArg any] error:[OCMArg anyObjectRef]]).andCall(self, @selector(mockTriggerCloudServicesPasscodeRequest:error:));
588 XCTAssertTrue([self.escrowRequest triggerEscrowUpdate:@"test" error:&error], @"Should be able to trigger an update");
589 XCTAssertNil(error, @"Should be no error triggering an escrow update");
590 OCMVerifyAll(self.escrowRequestServerClassMock);
591
592 XCTAssertEqual(0, [self.escrowServer.controller.stateMachine.paused wait:10*NSEC_PER_SEC], @"State machine should pause within some time");
593
594 NSString* pendingUUID = [self.escrowRequest fetchRequestWaitingOnPasscode:&error];
595 XCTAssertNil(error, @"Should be no error fetching a pending UUID");
596 XCTAssertNotNil(pendingUUID, "Should be a pending request UUID after a trigger");
597
598 // We need to lock directly after the prerecord is cached, but before the CDP attempt is attempted
599 [self.escrowServer.controller.stateMachine haltOperation];
600
601 NSData* d = [[NSData alloc]initWithBase64EncodedString:@"YXNkZgo=" options:0];
602 XCTAssertTrue([self.escrowRequest cachePrerecord:pendingUUID
603 serializedPrerecord:d
604 error:&error], @"Should be able to cache a prerecord");
605 XCTAssertNil(error, @"Should be no error caching a prerecord");
606
607 [SecMockAKS lockClassA];
608 [self.escrowServer.controller.stateMachine startOperation];
609
610 // The state machine should notice the unlock, and try again
611 XCTAssertEqual(0, [self.escrowServer.controller.stateMachine.stateConditions[EscrowRequestStateWaitForUnlock] wait:10*NSEC_PER_SEC], "State machine enters waitforunlock");
612
613 // The upload should be tried after an unlock
614 [SecMockAKS unlockAllClasses];
615 [self.lockStateTracker recheck];
616
617 OCMVerifyAllWithDelay(self.escrowRequestPerformEscrowEnrollOperationMock, 10);
618 }
619
620 - (void)testEscrowUploadBadPeerFailure {
621 [[[[self.escrowRequestPerformEscrowEnrollOperationMock expect] andCall:@selector(mockcdpFailBadPeerUploadPrerecord:secretType:reply:)
622 onObject:self] ignoringNonObjectArgs]
623 cdpUploadPrerecord:[OCMArg any] secretType:0 reply:[OCMArg any]];
624
625 NSError* error = nil;
626
627 OCMExpect([self.escrowRequestInformCloudServicesOperationMock triggerCloudServicesPasscodeRequest:[OCMArg any] error:[OCMArg anyObjectRef]]).andCall(self, @selector(mockTriggerCloudServicesPasscodeRequest:error:));
628
629 XCTAssertTrue([self.escrowRequest triggerEscrowUpdate:@"test" error:&error], @"Should be able to trigger an update");
630 XCTAssertNil(error, @"Should be no error triggering an escrow update");
631
632 OCMVerifyAll(self.escrowRequestServerClassMock);
633
634 XCTAssertEqual(0, [self.escrowServer.controller.stateMachine.paused wait:10*NSEC_PER_SEC], @"State machine should pause within some time");
635
636 NSString* pendingUUID = [self.escrowRequest fetchRequestWaitingOnPasscode:&error];
637 XCTAssertNil(error, @"Should be no error fetching a pending UUID");
638 XCTAssertNotNil(pendingUUID, "Should be a pending request UUID after a trigger");
639
640 NSData* d = [[NSData alloc]initWithBase64EncodedString:@"YXNkZgo=" options:0];
641 XCTAssertTrue([self.escrowRequest cachePrerecord:pendingUUID
642 serializedPrerecord:d
643 error:&error], @"Should be able to cache a prerecord");
644 XCTAssertNil(error, @"Should be no error caching a prerecord");
645
646
647 // Now, the state machine should try and fail the upload
648 OCMVerifyAllWithDelay(self.escrowRequestPerformEscrowEnrollOperationMock, 10);
649
650 NSDictionary<NSString*, NSString*>* statuses = [self.escrowRequest fetchStatuses:&error];
651 XCTAssertNotNil(statuses, @"Should be able to fetch statuses");
652 XCTAssertNil(error, @"Should be no error fetching statuses");
653
654 XCTAssertEqual(statuses.count, 0, @"Should be zero in-flight status");
655 }
656
657 - (void)testEscrowUploadNoSOSPeerFailure {
658 [[[[self.escrowRequestPerformEscrowEnrollOperationMock expect] andCall:@selector(mockcdpFailNoSOSPeerUploadPrerecord:secretType:reply:)
659 onObject:self] ignoringNonObjectArgs]
660 cdpUploadPrerecord:[OCMArg any] secretType:0 reply:[OCMArg any]];
661
662 NSError* error = nil;
663
664 OCMExpect([self.escrowRequestInformCloudServicesOperationMock triggerCloudServicesPasscodeRequest:[OCMArg any] error:[OCMArg anyObjectRef]]).andCall(self, @selector(mockTriggerCloudServicesPasscodeRequest:error:));
665
666 XCTAssertTrue([self.escrowRequest triggerEscrowUpdate:@"test" error:&error], @"Should be able to trigger an update");
667 XCTAssertNil(error, @"Should be no error triggering an escrow update");
668
669 OCMVerifyAll(self.escrowRequestServerClassMock);
670
671 XCTAssertEqual(0, [self.escrowServer.controller.stateMachine.paused wait:10*NSEC_PER_SEC], @"State machine should pause within some time");
672
673 NSString* pendingUUID = [self.escrowRequest fetchRequestWaitingOnPasscode:&error];
674 XCTAssertNil(error, @"Should be no error fetching a pending UUID");
675 XCTAssertNotNil(pendingUUID, "Should be a pending request UUID after a trigger");
676
677 NSData* d = [[NSData alloc]initWithBase64EncodedString:@"YXNkZgo=" options:0];
678 XCTAssertTrue([self.escrowRequest cachePrerecord:pendingUUID
679 serializedPrerecord:d
680 error:&error], @"Should be able to cache a prerecord");
681 XCTAssertNil(error, @"Should be no error caching a prerecord");
682
683
684 // Now, the state machine should try and fail the upload
685 OCMVerifyAllWithDelay(self.escrowRequestPerformEscrowEnrollOperationMock, 10);
686
687 NSDictionary<NSString*, NSString*>* statuses = [self.escrowRequest fetchStatuses:&error];
688 XCTAssertNotNil(statuses, @"Should be able to fetch statuses");
689 XCTAssertNil(error, @"Should be no error fetching statuses");
690
691 XCTAssertEqual(statuses.count, 0, @"Should be zero in-flight status");
692 }
693
694 - (void)testEscrowUploadNoCDPSOSPeerFailure {
695 [[[[self.escrowRequestPerformEscrowEnrollOperationMock expect] andCall:@selector(mockcdpFailNoCDPPeerUploadPrerecord:secretType:reply:)
696 onObject:self] ignoringNonObjectArgs]
697 cdpUploadPrerecord:[OCMArg any] secretType:0 reply:[OCMArg any]];
698
699 NSError* error = nil;
700
701 OCMExpect([self.escrowRequestInformCloudServicesOperationMock triggerCloudServicesPasscodeRequest:[OCMArg any] error:[OCMArg anyObjectRef]]).andCall(self, @selector(mockTriggerCloudServicesPasscodeRequest:error:));
702
703 XCTAssertTrue([self.escrowRequest triggerEscrowUpdate:@"test" error:&error], @"Should be able to trigger an update");
704 XCTAssertNil(error, @"Should be no error triggering an escrow update");
705
706 OCMVerifyAll(self.escrowRequestServerClassMock);
707
708 XCTAssertEqual(0, [self.escrowServer.controller.stateMachine.paused wait:10*NSEC_PER_SEC], @"State machine should pause within some time");
709
710 NSString* pendingUUID = [self.escrowRequest fetchRequestWaitingOnPasscode:&error];
711 XCTAssertNil(error, @"Should be no error fetching a pending UUID");
712 XCTAssertNotNil(pendingUUID, "Should be a pending request UUID after a trigger");
713
714 NSData* d = [[NSData alloc]initWithBase64EncodedString:@"YXNkZgo=" options:0];
715 XCTAssertTrue([self.escrowRequest cachePrerecord:pendingUUID
716 serializedPrerecord:d
717 error:&error], @"Should be able to cache a prerecord");
718 XCTAssertNil(error, @"Should be no error caching a prerecord");
719
720
721 // Now, the state machine should try and fail the upload
722 OCMVerifyAllWithDelay(self.escrowRequestPerformEscrowEnrollOperationMock, 10);
723
724 XCTAssertEqual(0, [self.escrowServer.controller.stateMachine.paused wait:10*NSEC_PER_SEC], @"State machine should pause within some time");
725
726 NSDictionary<NSString*, NSString*>* statuses = [self.escrowRequest fetchStatuses:&error];
727 XCTAssertNotNil(statuses, @"Should be able to fetch statuses");
728 XCTAssertNil(error, @"Should be no error fetching statuses");
729
730 XCTAssertEqual(statuses.count, 0, @"Should be zero in-flight status");
731 }
732
733 - (void)testPendingPreRecordsCheck {
734 [self allCloudServicesCallsSucceed];
735
736 NSError* error = nil;
737
738 XCTAssertTrue([self.escrowRequest triggerEscrowUpdate:@"test" error:&error], @"Should be able to trigger an update");
739 XCTAssertNil(error, @"Should be no error triggering an escrow update");
740
741 XCTAssertTrue([self.escrowRequest triggerEscrowUpdate:@"test" error:&error], @"Should be able to trigger an update");
742 XCTAssertNil(error, @"Should be no error triggering an escrow update");
743
744 XCTAssertTrue([self.escrowRequest triggerEscrowUpdate:@"test" error:&error], @"Should be able to trigger an update");
745 XCTAssertNil(error, @"Should be no error triggering an escrow update");
746
747 NSDictionary<NSString*, NSString*>* statuses = [self.escrowRequest fetchStatuses:&error];
748 XCTAssertNotNil(statuses, @"Should be able to fetch statuses");
749 XCTAssertNil(error, @"Should be no error fetching statuses");
750
751 XCTAssertEqual(statuses.count, 1, @"Should be one in-flight status");
752
753 BOOL result = [self.escrowRequest pendingEscrowUpload:&error];
754 XCTAssertNil(error, @"Should be no error checking for pending uploads");
755 XCTAssertEqual(result, true, @"pendingEscrowUpload should return true");
756
757 }
758
759 - (void)testClearedPreRecordsCheck {
760 [self allCloudServicesCallsSucceed];
761
762 NSError* error = nil;
763
764 [[[[self.escrowRequestPerformEscrowEnrollOperationMock expect] andCall:@selector(mockcdpFailNoCDPPeerUploadPrerecord:secretType:reply:)
765 onObject:self] ignoringNonObjectArgs]
766 cdpUploadPrerecord:[OCMArg any] secretType:0 reply:[OCMArg any]];
767
768 OCMExpect([self.escrowRequestInformCloudServicesOperationMock triggerCloudServicesPasscodeRequest:[OCMArg any] error:[OCMArg anyObjectRef]]).andCall(self, @selector(mockTriggerCloudServicesPasscodeRequest:error:));
769
770 XCTAssertTrue([self.escrowRequest triggerEscrowUpdate:@"test" error:&error], @"Should be able to trigger an update");
771 XCTAssertNil(error, @"Should be no error triggering an escrow update");
772
773 OCMVerifyAll(self.escrowRequestServerClassMock);
774
775 XCTAssertEqual(0, [self.escrowServer.controller.stateMachine.paused wait:10*NSEC_PER_SEC], @"State machine should pause within some time");
776
777 NSString* pendingUUID = [self.escrowRequest fetchRequestWaitingOnPasscode:&error];
778 XCTAssertNil(error, @"Should be no error fetching a pending UUID");
779 XCTAssertNotNil(pendingUUID, "Should be a pending request UUID after a trigger");
780
781 NSData* d = [[NSData alloc]initWithBase64EncodedString:@"YXNkZgo=" options:0];
782 XCTAssertTrue([self.escrowRequest cachePrerecord:pendingUUID
783 serializedPrerecord:d
784 error:&error], @"Should be able to cache a prerecord");
785 XCTAssertNil(error, @"Should be no error caching a prerecord");
786
787
788 // Now, the state machine should try and fail the upload
789 OCMVerifyAllWithDelay(self.escrowRequestPerformEscrowEnrollOperationMock, 10);
790
791 NSDictionary<NSString*, NSString*>* statuses = [self.escrowRequest fetchStatuses:&error];
792 XCTAssertNotNil(statuses, @"Should be able to fetch statuses");
793 XCTAssertNil(error, @"Should be no error fetching statuses");
794
795 XCTAssertEqual(statuses.count, 0, @"Should be zero in-flight status");
796
797 BOOL result = [self.escrowRequest pendingEscrowUpload:&error];
798 XCTAssertNil(error, @"Should be no error checking for pending uploads");
799 XCTAssertEqual(result, false, @"pendingEscrowUpload should return true");
800 }
801
802 - (void)testServerPendingPreRecordsCheck {
803 [self allCloudServicesCallsSucceed];
804
805 NSError* error = nil;
806
807 XCTAssertTrue([self.escrowServer triggerEscrowUpdate:@"test" error:&error], @"Should be able to trigger an update");
808 XCTAssertNil(error, @"Should be no error triggering an escrow update");
809
810 XCTAssertTrue([self.escrowServer triggerEscrowUpdate:@"test" error:&error], @"Should be able to trigger an update");
811 XCTAssertNil(error, @"Should be no error triggering an escrow update");
812
813 XCTAssertTrue([self.escrowServer triggerEscrowUpdate:@"test" error:&error], @"Should be able to trigger an update");
814 XCTAssertNil(error, @"Should be no error triggering an escrow update");
815
816 NSDictionary<NSString*, NSString*>* statuses = [self.escrowServer fetchStatuses:&error];
817 XCTAssertNotNil(statuses, @"Should be able to fetch statuses");
818 XCTAssertNil(error, @"Should be no error fetching statuses");
819
820 XCTAssertEqual(statuses.count, 1, @"Should be one in-flight status");
821
822 BOOL result = [self.escrowServer pendingEscrowUpload:&error];
823 XCTAssertNil(error, @"Should be no error checking for pending uploads");
824 XCTAssertEqual(result, true, @"pendingEscrowUpload should return true");
825
826 }
827
828 - (void)testServerClearedPreRecordsCheck {
829 [self allCloudServicesCallsSucceed];
830
831 NSError* error = nil;
832
833 [[[[self.escrowRequestPerformEscrowEnrollOperationMock expect] andCall:@selector(mockcdpFailNoCDPPeerUploadPrerecord:secretType:reply:)
834 onObject:self] ignoringNonObjectArgs]
835 cdpUploadPrerecord:[OCMArg any] secretType:0 reply:[OCMArg any]];
836
837 OCMExpect([self.escrowRequestInformCloudServicesOperationMock triggerCloudServicesPasscodeRequest:[OCMArg any] error:[OCMArg anyObjectRef]]).andCall(self, @selector(mockTriggerCloudServicesPasscodeRequest:error:));
838
839 XCTAssertTrue([self.escrowRequest triggerEscrowUpdate:@"test" error:&error], @"Should be able to trigger an update");
840 XCTAssertNil(error, @"Should be no error triggering an escrow update");
841
842 OCMVerifyAll(self.escrowRequestServerClassMock);
843
844 XCTAssertEqual(0, [self.escrowServer.controller.stateMachine.paused wait:10*NSEC_PER_SEC], @"State machine should pause within some time");
845
846 NSString* pendingUUID = [self.escrowRequest fetchRequestWaitingOnPasscode:&error];
847 XCTAssertNil(error, @"Should be no error fetching a pending UUID");
848 XCTAssertNotNil(pendingUUID, "Should be a pending request UUID after a trigger");
849
850 NSData* d = [[NSData alloc]initWithBase64EncodedString:@"YXNkZgo=" options:0];
851 XCTAssertTrue([self.escrowRequest cachePrerecord:pendingUUID
852 serializedPrerecord:d
853 error:&error], @"Should be able to cache a prerecord");
854 XCTAssertNil(error, @"Should be no error caching a prerecord");
855
856
857 // Now, the state machine should try and fail the upload
858 OCMVerifyAllWithDelay(self.escrowRequestPerformEscrowEnrollOperationMock, 10);
859
860 NSDictionary<NSString*, NSString*>* statuses = [self.escrowServer fetchStatuses:&error];
861 XCTAssertNotNil(statuses, @"Should be able to fetch statuses");
862 XCTAssertNil(error, @"Should be no error fetching statuses");
863
864 XCTAssertEqual(statuses.count, 0, @"Should be zero in-flight status");
865
866 BOOL result = [self.escrowServer pendingEscrowUpload:&error];
867 XCTAssertNil(error, @"Should be no error checking for pending uploads");
868 XCTAssertEqual(result, false, @"pendingEscrowUpload should return true");
869 }
870
871 @end