5 #import <TargetConditionals.h>
6 #import <Security/SecKey.h>
7 #import <Security/SecKeyPriv.h>
8 #import <CloudKit/CloudKit_Private.h>
10 #import "keychain/ot/ObjCImprovements.h"
11 #import "keychain/ot/OTSOSUpdatePreapprovalsOperation.h"
12 #import "keychain/ot/OTOperationDependencies.h"
13 #import "keychain/ot/OTCuttlefishAccountStateHolder.h"
14 #import "keychain/TrustedPeersHelper/TrustedPeersHelperProtocol.h"
15 #import "keychain/ot/categories/OTAccountMetadataClassC+KeychainSupport.h"
16 #import "keychain/ckks/CKKSAnalytics.h"
17 #import "keychain/ckks/CloudKitCategories.h"
19 @interface OTSOSUpdatePreapprovalsOperation ()
20 @property OTOperationDependencies* deps;
22 // Since we're making callback based async calls, use this operation trick to hold off the ending of this operation
23 @property NSOperation* finishedOp;
26 @implementation OTSOSUpdatePreapprovalsOperation
27 @synthesize nextState = _nextState;
28 @synthesize intendedState = _intendedState;
30 - (instancetype)initWithDependencies:(OTOperationDependencies*)dependencies
31 intendedState:(OctagonState*)intendedState
32 sosNotPresentState:(OctagonState*)sosNotPresentState
33 errorState:(OctagonState*)errorState
35 if((self = [super init])) {
38 _intendedState = intendedState;
39 _sosNotPresentState = sosNotPresentState;
40 _nextState = errorState;
49 if(!self.deps.sosAdapter.sosEnabled) {
50 secnotice("octagon-sos", "SOS not enabled on this platform?");
54 self.finishedOp = [NSBlockOperation blockOperationWithBlock:^{
55 // If we errored in some unknown way, ask to try again!
59 // Is this a very scary error?
62 NSTimeInterval ckdelay = CKRetryAfterSecondsForError(self.error);
63 NSTimeInterval delay = 30;
68 if([self.error isCuttlefishError:CuttlefishErrorResultGraphNotFullyReachable]) {
69 secnotice("octagon-sos", "SOS update preapproval error is 'result graph not reachable'; retrying is useless: %@", self.error);
73 if([self.error.domain isEqualToString:TrustedPeersHelperErrorDomain] && self.error.code == TrustedPeersHelperErrorNoPreparedIdentity) {
74 secnotice("octagon-sos", "SOS update preapproval error is 'no prepared identity'; retrying immediately is useless: %@", self.error);
79 secnotice("octagon-sos", "SOS update preapproval error is not fatal: requesting retry in %0.2fs: %@", delay, self.error);
80 [self.deps.flagHandler handlePendingFlag:[[OctagonPendingFlag alloc] initWithFlag:OctagonFlagAttemptSOSUpdatePreapprovals
81 delayInSeconds:delay]];
85 [self dependOnBeforeGroupFinished:self.finishedOp];
88 NSSet<id<CKKSRemotePeerProtocol>>* peerSet = [self.deps.sosAdapter fetchTrustedPeers:&error];
90 if(!peerSet || error) {
91 secerror("octagon-sos: Can't fetch trusted peers; stopping preapproved key update: %@", error);
93 self.nextState = self.sosNotPresentState;
94 [self runBeforeGroupFinished:self.finishedOp];
98 NSArray<NSData*>* publicSigningSPKIs = [OTSOSActualAdapter peerPublicSigningKeySPKIs:peerSet];
99 secnotice("octagon-sos", "Updating SOS preapproved keys to %@", publicSigningSPKIs);
101 [[self.deps.cuttlefishXPC remoteObjectProxyWithErrorHandler:^(NSError * _Nonnull error) {
103 secerror("octagon: Can't talk with TrustedPeersHelper, update of preapproved keys is lost: %@", error);
105 [self runBeforeGroupFinished:self.finishedOp];
107 }] setPreapprovedKeysWithContainer:self.deps.containerName
108 context:self.deps.contextID
109 preapprovedKeys:publicSigningSPKIs
110 reply:^(NSError* error) {
113 secerror("octagon-sos: unable to update preapproved keys: %@", error);
116 secnotice("octagon-sos", "Updated SOS preapproved keys");
117 self.nextState = self.intendedState;
119 [self runBeforeGroupFinished:self.finishedOp];