4 #import "utilities/debugging.h"
6 #import "keychain/ot/OTConstants.h"
7 #import "keychain/ot/OctagonCheckTrustStateOperation.h"
8 #import "keychain/ot/OctagonCKKSPeerAdapter.h"
9 #import "keychain/ot/OTCuttlefishAccountStateHolder.h"
10 #import "keychain/ot/OTFetchCKKSKeysOperation.h"
11 #import "keychain/ot/OTStates.h"
12 #import "keychain/ckks/CKKSCurrentKeyPointer.h"
13 #import "keychain/ckks/CKKSKeychainView.h"
15 #import "keychain/ot/categories/OTAccountMetadataClassC+KeychainSupport.h"
16 #import "keychain/TrustedPeersHelper/TrustedPeersHelperProtocol.h"
17 #import "keychain/ot/ObjCImprovements.h"
19 @interface OctagonCheckTrustStateOperation ()
20 @property OTOperationDependencies* deps;
22 @property NSOperation* finishedOp;
25 @implementation OctagonCheckTrustStateOperation
26 @synthesize intendedState = _intendedState;
28 - (instancetype)initWithDependencies:(OTOperationDependencies*)dependencies
29 intendedState:(OctagonState*)intendedState
30 errorState:(OctagonState*)errorState
32 if((self = [super init])) {
35 _intendedState = intendedState;
36 _nextState = errorState;
43 self.finishedOp = [[NSOperation alloc] init];
44 [self dependOnBeforeGroupFinished:self.finishedOp];
46 // Make sure we're in agreement with TPH about _which_ peer ID we should have
48 [[self.deps.cuttlefishXPC remoteObjectProxyWithErrorHandler:^(NSError * _Nonnull error) {
50 secerror("octagon: Can't talk with TrustedPeersHelper, can't ensure check trust state: %@", error);
52 [self runBeforeGroupFinished:self.finishedOp];
54 }] fetchTrustStateWithContainer:self.deps.containerName
55 context:self.deps.contextID
56 reply:^(TrustedPeersHelperPeerState * _Nullable selfPeerState,
57 NSArray<TrustedPeersHelperPeer *> * _Nullable trustedPeers,
58 NSError * _Nullable error) {
60 if(error || !selfPeerState || !trustedPeers) {
61 secerror("octagon: TPH was unable to determine current peer state: %@", error);
63 self.nextState = OctagonStateError;
64 [self runBeforeGroupFinished:self.finishedOp];
67 [self afterTPHTrustState:selfPeerState trustedPeers:trustedPeers];
72 - (void)afterTPHTrustState:(TrustedPeersHelperPeerState*)peerState
73 trustedPeers:(NSArray<TrustedPeersHelperPeer *> *)trustedPeers
75 NSError* localError = nil;
77 if (peerState.memberChanges) {
78 secnotice("octagon", "Member list changed");
79 [self.deps.octagonAdapter sendTrustedPeerSetChangedUpdate];
82 bool changedCurrentAccountMetadata = false;
83 OTAccountMetadataClassC* currentAccountMetadata = [self.deps.stateHolder loadOrCreateAccountMetadata:&localError];
86 if([self.deps.lockStateTracker isLockedError:localError]) {
87 secerror("octagon-consistency: Unable to fetch current account state due to lock state: %@", localError);
88 self.nextState = OctagonStateWaitForUnlock;
89 [self runBeforeGroupFinished:self.finishedOp];
93 secerror("octagon-consistency: Unable to fetch current account state. Can't ensure consistency: %@", localError);
94 [self runBeforeGroupFinished:self.finishedOp];
98 // What state does TPH think it's in?
99 // This code is slightly duplicated with rpcTrustStatus; we should probably fix that
100 OTAccountMetadataClassC_TrustState trustState = OTAccountMetadataClassC_TrustState_UNKNOWN;
101 if(peerState.peerStatus & TPPeerStatusExcluded) {
102 trustState = OTAccountMetadataClassC_TrustState_UNTRUSTED;
104 } else if(peerState.peerStatus & TPPeerStatusUnknown) {
105 trustState = OTAccountMetadataClassC_TrustState_UNTRUSTED;
107 } else if((peerState.peerStatus & TPPeerStatusSelfTrust) ||
108 (peerState.peerStatus & TPPeerStatusFullyReciprocated) ||
109 (peerState.peerStatus & TPPeerStatusPartiallyReciprocated)) {
110 trustState = OTAccountMetadataClassC_TrustState_TRUSTED;
113 secnotice("octagon-consistency", "TPH's trust state (%@) is considered %@",
114 TPPeerStatusToString(peerState.peerStatus),
115 OTAccountMetadataClassC_TrustStateAsString(trustState));
117 if(trustState == currentAccountMetadata.trustState) {
118 secnotice("octagon-consistency", "TPH peer status matches cache: (%@)", TPPeerStatusToString(peerState.peerStatus));
119 [[CKKSAnalytics logger] logSuccessForEventNamed:OctagonEventCheckTrustState];
121 secerror("octagon-consistency: Locally cached status (%@) does not match TPH's current peer status (%@)",
122 OTAccountMetadataClassC_TrustStateAsString(currentAccountMetadata.trustState),
123 OTAccountMetadataClassC_TrustStateAsString(trustState));
125 if (currentAccountMetadata.trustState == OTAccountMetadataClassC_TrustState_TRUSTED && trustState == OTAccountMetadataClassC_TrustState_UNTRUSTED) {
126 [[CKKSAnalytics logger] logHardFailureForEventNamed:OctagonEventCheckTrustState withAttributes:nil];
129 currentAccountMetadata.trustState = trustState;
130 changedCurrentAccountMetadata = true;
133 if([peerState.peerID isEqualToString:currentAccountMetadata.peerID] ||
134 (peerState.peerID == nil && currentAccountMetadata.peerID == nil)) {
135 secnotice("octagon-consistency", "TPH peer ID matches cache: (%@)", peerState.peerID);
137 secerror("octagon-consistency: Locally cached peer ID (%@) does not match TPH's current peer ID (%@)",
138 currentAccountMetadata.peerID,
141 currentAccountMetadata.peerID = peerState.peerID;
142 changedCurrentAccountMetadata = true;
145 if(changedCurrentAccountMetadata) {
147 NSError* localError = nil;
148 BOOL persisted = [self.deps.stateHolder persistAccountChanges:^OTAccountMetadataClassC * _Nonnull(OTAccountMetadataClassC * _Nonnull metadata) {
149 metadata.trustState = currentAccountMetadata.trustState;
150 metadata.peerID = currentAccountMetadata.peerID;
152 } error:&localError];
154 if(!persisted || localError) {
155 if([self.deps.lockStateTracker isLockedError:localError]) {
156 secerror("octagon-consistency: Unable to save new account state due to lock state: %@", localError);
157 self.nextState = OctagonStateWaitForUnlock;
158 [self runBeforeGroupFinished:self.finishedOp];
162 secerror("octagon-consistency: Unable to save new account state. Erroring: %@", localError);
163 self.error = localError;
164 self.nextState = OctagonStateError;
165 [self runBeforeGroupFinished:self.finishedOp];
169 secnotice("octagon-consistency", "Saved new account metadata");
172 // And determine where to go from here!
174 if(currentAccountMetadata.peerID && currentAccountMetadata.trustState == OTAccountMetadataClassC_TrustState_TRUSTED) {
175 secnotice("octagon", "Appear to be trusted for peer %@; ensuring correct state", currentAccountMetadata.peerID);
176 self.nextState = OctagonStateEnsureConsistency;
178 } else if(self.deps.sosAdapter.sosEnabled &&
179 currentAccountMetadata.trustState != OTAccountMetadataClassC_TrustState_TRUSTED &&
180 OctagonPerformSOSUpgrade()) {
181 secnotice("octagon", "Have iCloud account but not trusted in Octagon yet; inspecting SOS status: %@",
182 [currentAccountMetadata trustStateAsString:currentAccountMetadata.trustState]);
184 NSError* circleError = nil;
185 SOSCCStatus sosStatus = [self.deps.sosAdapter circleStatus:&circleError];
187 if(sosStatus == kSOSCCInCircle) {
188 secnotice("octagon", "SOS status is 'trusted'; requesting SOS upgrade");
189 [self.deps.flagHandler handleFlag:OctagonFlagAttemptSOSUpgrade];
191 secnotice("octagon", "SOS status is %d (error: %@)", (int)sosStatus, circleError);
193 self.nextState = OctagonStateBecomeUntrusted;
195 } else if(currentAccountMetadata.trustState != OTAccountMetadataClassC_TrustState_TRUSTED) {
196 secnotice("octagon", "Have iCloud account but not trusted in Octagon (%@)",
197 OTAccountMetadataClassC_TrustStateAsString(currentAccountMetadata.trustState));
199 self.nextState = OctagonStateStartCompanionPairing;
201 self.nextState = OctagonStateBecomeUntrusted;
204 secnotice("octagon", "Unknown trust state (%@). Assuming untrusted...",
205 OTAccountMetadataClassC_TrustStateAsString(currentAccountMetadata.trustState));
206 self.nextState = OctagonStateBecomeUntrusted;
209 [self runBeforeGroupFinished:self.finishedOp];