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 SecSetCustomHomeURLString((__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