OctagonCheckTrustStateOperation.m [plain text]
#if OCTAGON
#import "utilities/debugging.h"
#import "keychain/ot/OTConstants.h"
#import "keychain/ot/OctagonCheckTrustStateOperation.h"
#import "keychain/ot/OctagonCKKSPeerAdapter.h"
#import "keychain/ot/OTCuttlefishAccountStateHolder.h"
#import "keychain/ot/OTFetchCKKSKeysOperation.h"
#import "keychain/ot/OTStates.h"
#import "keychain/ckks/CKKSCurrentKeyPointer.h"
#import "keychain/ckks/CKKSKeychainView.h"
#import "keychain/ot/categories/OTAccountMetadataClassC+KeychainSupport.h"
#import "keychain/TrustedPeersHelper/TrustedPeersHelperProtocol.h"
#import "keychain/ot/ObjCImprovements.h"
@interface OctagonCheckTrustStateOperation ()
@property OTOperationDependencies* deps;
@property NSOperation* finishedOp;
@end
@implementation OctagonCheckTrustStateOperation
@synthesize intendedState = _intendedState;
- (instancetype)initWithDependencies:(OTOperationDependencies*)dependencies
intendedState:(OctagonState*)intendedState
errorState:(OctagonState*)errorState
{
if((self = [super init])) {
_deps = dependencies;
_intendedState = intendedState;
_nextState = errorState;
}
return self;
}
- (void)groupStart
{
self.finishedOp = [[NSOperation alloc] init];
[self dependOnBeforeGroupFinished:self.finishedOp];
// Make sure we're in agreement with TPH about _which_ peer ID we should have
WEAKIFY(self);
[self.deps.cuttlefishXPCWrapper fetchTrustStateWithContainer:self.deps.containerName
context:self.deps.contextID
reply:^(TrustedPeersHelperPeerState * _Nullable selfPeerState,
NSArray<TrustedPeersHelperPeer *> * _Nullable trustedPeers,
NSError * _Nullable error) {
STRONGIFY(self);
if(error || !selfPeerState || !trustedPeers) {
secerror("octagon: TPH was unable to determine current peer state: self.error = error;
self.nextState = OctagonStateError;
[self runBeforeGroupFinished:self.finishedOp];
} else {
[self afterTPHTrustState:selfPeerState trustedPeers:trustedPeers];
}
}];
}
- (void)afterTPHTrustState:(TrustedPeersHelperPeerState*)peerState
trustedPeers:(NSArray<TrustedPeersHelperPeer *> *)trustedPeers
{
NSError* localError = nil;
if (peerState.memberChanges) {
secnotice("octagon", "Member list changed");
[self.deps.octagonAdapter sendTrustedPeerSetChangedUpdate];
}
bool changedCurrentAccountMetadata = false;
OTAccountMetadataClassC* currentAccountMetadata = [self.deps.stateHolder loadOrCreateAccountMetadata:&localError];
if(localError) {
if([self.deps.lockStateTracker isLockedError:localError]) {
secerror("octagon-consistency: Unable to fetch current account state due to lock state: self.nextState = OctagonStateWaitForClassCUnlock;
[self runBeforeGroupFinished:self.finishedOp];
return;
}
secerror("octagon-consistency: Unable to fetch current account state. Can't ensure consistency: [self runBeforeGroupFinished:self.finishedOp];
return;
}
// What state does TPH think it's in?
// This code is slightly duplicated with rpcTrustStatus; we should probably fix that
OTAccountMetadataClassC_TrustState trustState = OTAccountMetadataClassC_TrustState_UNKNOWN;
if(peerState.peerStatus & TPPeerStatusExcluded) {
trustState = OTAccountMetadataClassC_TrustState_UNTRUSTED;
} else if(peerState.peerStatus & TPPeerStatusUnknown) {
trustState = OTAccountMetadataClassC_TrustState_UNTRUSTED;
} else if((peerState.peerStatus & TPPeerStatusSelfTrust) ||
(peerState.peerStatus & TPPeerStatusFullyReciprocated) ||
(peerState.peerStatus & TPPeerStatusPartiallyReciprocated)) {
trustState = OTAccountMetadataClassC_TrustState_TRUSTED;
}
secnotice("octagon-consistency", "TPH's trust state ( TPPeerStatusToString(peerState.peerStatus),
OTAccountMetadataClassC_TrustStateAsString(trustState));
if(trustState == currentAccountMetadata.trustState) {
secnotice("octagon-consistency", "TPH peer status matches cache: ( [[CKKSAnalytics logger] logSuccessForEventNamed:OctagonEventCheckTrustState];
} else {
secerror("octagon-consistency: Locally cached status ( OTAccountMetadataClassC_TrustStateAsString(currentAccountMetadata.trustState),
OTAccountMetadataClassC_TrustStateAsString(trustState));
if (currentAccountMetadata.trustState == OTAccountMetadataClassC_TrustState_TRUSTED && trustState == OTAccountMetadataClassC_TrustState_UNTRUSTED) {
[[CKKSAnalytics logger] logHardFailureForEventNamed:OctagonEventCheckTrustState withAttributes:nil];
}
currentAccountMetadata.trustState = trustState;
changedCurrentAccountMetadata = true;
}
if([peerState.peerID isEqualToString:currentAccountMetadata.peerID] ||
(peerState.peerID == nil && currentAccountMetadata.peerID == nil)) {
secnotice("octagon-consistency", "TPH peer ID matches cache: ( } else {
secerror("octagon-consistency: Locally cached peer ID ( currentAccountMetadata.peerID,
peerState.peerID);
currentAccountMetadata.peerID = peerState.peerID;
changedCurrentAccountMetadata = true;
}
if(changedCurrentAccountMetadata) {
NSError* localError = nil;
BOOL persisted = [self.deps.stateHolder persistAccountChanges:^OTAccountMetadataClassC * _Nonnull(OTAccountMetadataClassC * _Nonnull metadata) {
metadata.trustState = currentAccountMetadata.trustState;
metadata.peerID = currentAccountMetadata.peerID;
return metadata;
} error:&localError];
if(!persisted || localError) {
if([self.deps.lockStateTracker isLockedError:localError]) {
secerror("octagon-consistency: Unable to save new account state due to lock state: self.nextState = OctagonStateWaitForClassCUnlock;
[self runBeforeGroupFinished:self.finishedOp];
return;
}
secerror("octagon-consistency: Unable to save new account state. Erroring: self.error = localError;
self.nextState = OctagonStateError;
[self runBeforeGroupFinished:self.finishedOp];
return;
}
secnotice("octagon-consistency", "Saved new account metadata");
}
// See if we possibly should do an update because peer data in Octagon
// is different from local state, ie we upgraded
if (peerState.osVersion) {
NSString *osVersion = self.deps.deviceInformationAdapter.osVersion;
if (osVersion && ![osVersion isEqualToString:peerState.osVersion]) {
OctagonPendingFlag *pendingFlag = [[OctagonPendingFlag alloc] initWithFlag:OctagonFlagCuttlefishNotification
conditions:OctagonPendingConditionsDeviceUnlocked];
[self.deps.flagHandler handlePendingFlag:pendingFlag];
}
}
// And determine where to go from here!
if(currentAccountMetadata.peerID && currentAccountMetadata.trustState == OTAccountMetadataClassC_TrustState_TRUSTED) {
secnotice("octagon", "Appear to be trusted for peer self.nextState = OctagonStateEnsureConsistency;
} else if(self.deps.sosAdapter.sosEnabled &&
currentAccountMetadata.trustState != OTAccountMetadataClassC_TrustState_TRUSTED &&
OctagonPerformSOSUpgrade()) {
secnotice("octagon", "Have iCloud account but not trusted in Octagon yet: [currentAccountMetadata trustStateAsString:currentAccountMetadata.trustState]);
self.nextState = OctagonStateAttemptSOSUpgrade;
} else if(currentAccountMetadata.trustState != OTAccountMetadataClassC_TrustState_TRUSTED) {
secnotice("octagon", "Have iCloud account but not trusted in Octagon ( OTAccountMetadataClassC_TrustStateAsString(currentAccountMetadata.trustState));
#if TARGET_OS_WATCH
self.nextState = OctagonStateStartCompanionPairing;
#else
self.nextState = OctagonStateBecomeUntrusted;
#endif
} else {
secnotice("octagon", "Unknown trust state ( OTAccountMetadataClassC_TrustStateAsString(currentAccountMetadata.trustState));
self.nextState = OctagonStateBecomeUntrusted;
}
[self runBeforeGroupFinished:self.finishedOp];
}
@end
#endif // OCTAGON