2 #import <CloudServices/SecureBackup.h>
4 #import "utilities/debugging.h"
5 #import "keychain/ckks/CKKSLockStateTracker.h"
7 #import "keychain/escrowrequest/EscrowRequestController.h"
8 #import "keychain/escrowrequest/operations/EscrowRequestInformCloudServicesOperation.h"
9 #import "keychain/escrowrequest/generated_source/SecEscrowPendingRecord.h"
10 #import "keychain/escrowrequest/SecEscrowPendingRecord+KeychainSupport.h"
12 @interface EscrowRequestInformCloudServicesOperation()
13 @property CKKSLockStateTracker* lockStateTracker;
16 @implementation EscrowRequestInformCloudServicesOperation
17 @synthesize nextState = _nextState;
18 @synthesize intendedState = _intendedState;
20 - (instancetype)initWithIntendedState:(OctagonState*)intendedState
21 errorState:(OctagonState*)errorState
22 lockStateTracker:(CKKSLockStateTracker*)lockStateTracker
24 if((self = [super init])) {
25 _intendedState = intendedState;
26 _nextState = errorState;
27 _lockStateTracker = lockStateTracker;
34 secnotice("escrowrequest", "Telling CloudServices about any pending requests");
37 NSArray<SecEscrowPendingRecord*>* records = [SecEscrowPendingRecord loadAllFromKeychain:&error];
38 if(error && !([error.domain isEqualToString:NSOSStatusErrorDomain] && error.code == errSecItemNotFound)) {
39 secnotice("escrowrequest", "failed to fetch records from keychain: %@", error);
40 if([self.lockStateTracker isLockedError:error]) {
41 secnotice("escrowrequest", "Trying again after unlock");
42 self.nextState = EscrowRequestStateWaitForUnlock;
44 self.nextState = EscrowRequestStateNothingToDo;
51 SecEscrowPendingRecord* record = nil;
53 for(SecEscrowPendingRecord* existingRecord in records) {
54 if(!existingRecord.hasCertCached) {
55 record = existingRecord;
61 secnotice("escrowrequest", "No pending escrow request needs a certificate");
62 self.nextState = EscrowRequestStateNothingToDo;
66 // Next, see if CloudServices can cache a certificate
67 NSData* cachedCert = [EscrowRequestInformCloudServicesOperation triggerCloudServicesPasscodeRequest:record.uuid error:&error];
68 record.lastCloudServicesTriggerTime = (uint64_t) ([[NSDate date] timeIntervalSince1970] * 1000);
70 if(!cachedCert || error) {
71 secerror("escrowrequest: cloudservices reports an issue caching the certificate, so we'll have to try again later: %@", error);
73 // TODO: wait for network?
74 self.nextState = EscrowRequestStateNothingToDo;
76 NSError* saveCacheTimeError = nil;
77 [record saveToKeychain:&saveCacheTimeError];
78 if(saveCacheTimeError) {
79 secerror("escrowrequest: unable to save the last attempt time: %@", saveCacheTimeError);
85 record.certCached = true;
86 [record saveToKeychain:&error];
89 // Ignore this error, since we've successfully triggered the update. We'll probably re-cache the certificate later, but that's okay.
90 secerror("escrowrequest: unable to save escrow update request certificate status, so we'll have to try again later: %@", error);
93 if([self.lockStateTracker isLockedError:error]) {
94 secnotice("escrowrequest", "Trying again after unlock");
95 self.nextState = EscrowRequestStateWaitForUnlock;
97 self.nextState = EscrowRequestStateNothingToDo;
103 secnotice("escrowrequest", "CloudService successfully cached a certificate; request is ready for passcode");
104 self.nextState = EscrowRequestStateNothingToDo;
107 // Separated into a class method for mocking
108 // Returns any cert that CS has cached
109 + (NSData* _Nullable)triggerCloudServicesPasscodeRequest:(NSString*)uuid error:(NSError**)error
111 SecureBackup* sb = [[SecureBackup alloc] init];
113 NSError* localError = nil;
114 SecureBackupBeginPasscodeRequestResults* results = [sb beginHSA2PasscodeRequest:true
118 if(!results || localError) {
119 secerror("escrowrequest: unable to begin passcode request: %@", localError);
127 secerror("escrowrequest: sbd failed to cache a certificate");
128 // TODO fill in error