]> git.saurik.com Git - apple/security.git/blob - keychain/ot/OTClique.m
Security-59306.101.1.tar.gz
[apple/security.git] / keychain / ot / OTClique.m
1 /*
2 * Copyright (c) 2018 Apple Inc. All Rights Reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24 #if __OBJC2__
25
26 #import <TargetConditionals.h>
27 #import <Foundation/Foundation.h>
28
29 #import "keychain/ot/OTClique.h"
30 #import "keychain/ot/OTConstants.h"
31 #import "keychain/ot/OTDefines.h"
32 #import "keychain/SigninMetrics/OctagonSignPosts.h"
33
34 #import <utilities/SecCFWrappers.h>
35 #import <utilities/debugging.h>
36
37 #import "keychain/SecureObjectSync/SOSCloudCircle.h"
38 #import "KeychainCircle/PairingChannel.h"
39 #import <Security/SecBase.h>
40
41 const NSString* kSecEntitlementPrivateOctagonEscrow = @"com.apple.private.octagon.escrow-content";
42
43 #if OCTAGON
44 #import <AuthKit/AuthKit.h>
45 #import <AuthKit/AuthKit_Private.h>
46 #import <SoftLinking/SoftLinking.h>
47 #import <CloudServices/SecureBackup.h>
48 #import <CloudServices/SecureBackupConstants.h>
49 #import "keychain/ot/OTControl.h"
50 #import "keychain/ckks/CKKSControl.h"
51 #import "keychain/ot/categories/OctagonEscrowRecoverer.h"
52
53 SOFT_LINK_FRAMEWORK(PrivateFrameworks, KeychainCircle);
54 SOFT_LINK_FRAMEWORK(PrivateFrameworks, CloudServices);
55
56 #pragma clang diagnostic push
57 #pragma clang diagnostic ignored "-Wstrict-prototypes"
58 SOFT_LINK_CLASS(KeychainCircle, KCPairingChannel);
59 SOFT_LINK_CLASS(KeychainCircle, OTPairingChannel);
60 SOFT_LINK_CLASS(CloudServices, SecureBackup);
61 SOFT_LINK_CONSTANT(CloudServices, kSecureBackupErrorDomain, NSErrorDomain);
62 SOFT_LINK_CONSTANT(CloudServices, kSecureBackupAuthenticationAppleID, NSString*);
63 SOFT_LINK_CONSTANT(CloudServices, kSecureBackupAuthenticationPassword, NSString*);
64 SOFT_LINK_CONSTANT(CloudServices, kSecureBackupiCloudDataProtectionDeleteAllRecordsKey, NSString*);
65 SOFT_LINK_CONSTANT(CloudServices, kSecureBackupContainsiCDPDataKey, NSString*);
66
67 #pragma clang diagnostic pop
68 #endif
69
70 OTCliqueCDPContextType OTCliqueCDPContextTypeNone = @"cdpContextTypeNone";
71 OTCliqueCDPContextType OTCliqueCDPContextTypeSignIn = @"cdpContextTypeSignIn";
72 OTCliqueCDPContextType OTCliqueCDPContextTypeRepair = @"cdpContextTypeRepair";
73 OTCliqueCDPContextType OTCliqueCDPContextTypeFinishPasscodeChange = @"cdpContextTypeFinishPasscodeChange";
74 OTCliqueCDPContextType OTCliqueCDPContextTypeRecoveryKeyGenerate = @"cdpContextTypeRecoveryKeyGenerate";
75 OTCliqueCDPContextType OTCliqueCDPContextTypeRecoveryKeyNew = @"cdpContextTypeRecoveryKeyNew";
76 OTCliqueCDPContextType OTCliqueCDPContextTypeUpdatePasscode = @"cdpContextTypeUpdatePasscode";
77
78 NSString* OTCliqueStatusToString(CliqueStatus status)
79 {
80 switch(status) {
81 case CliqueStatusIn:
82 return @"CliqueStatusIn";
83 case CliqueStatusNotIn:
84 return @"CliqueStatusNotIn";
85 case CliqueStatusPending:
86 return @"CliqueStatusPending";
87 case CliqueStatusAbsent:
88 return @"CliqueStatusAbsent";
89 case CliqueStatusNoCloudKitAccount:
90 return @"CliqueStatusNoCloudKitAccount";
91 case CliqueStatusError:
92 return @"CliqueStatusError";
93 };
94 }
95 CliqueStatus OTCliqueStatusFromString(NSString* str)
96 {
97 if([str isEqualToString: @"CliqueStatusIn"]) {
98 return CliqueStatusIn;
99 } else if([str isEqualToString: @"CliqueStatusNotIn"]) {
100 return CliqueStatusNotIn;
101 } else if([str isEqualToString: @"CliqueStatusPending"]) {
102 return CliqueStatusPending;
103 } else if([str isEqualToString: @"CliqueStatusAbsent"]) {
104 return CliqueStatusAbsent;
105 } else if([str isEqualToString: @"CliqueStatusNoCloudKitAccount"]) {
106 return CliqueStatusNoCloudKitAccount;
107 } else if([str isEqualToString: @"CliqueStatusError"]) {
108 return CliqueStatusError;
109 }
110
111 return CliqueStatusError;
112 }
113
114 NSString* OTCDPStatusToString(OTCDPStatus status) {
115 switch(status) {
116 case OTCDPStatusUnknown:
117 return @"unknown";
118 case OTCDPStatusDisabled:
119 return @"disabled";
120 case OTCDPStatusEnabled:
121 return @"enabled";
122 }
123 }
124
125
126 @implementation OTConfigurationContext
127 - (OTControl* _Nullable)makeOTControl:(NSError**)error
128 {
129 #if OCTAGON
130 if (self.otControl) {
131 return self.otControl;
132 }
133 return [OTControl controlObject:true error:error];
134 #else
135 return nil;
136 #endif
137 }
138
139 - (CKKSControl* _Nullable)makeCKKSControl:(NSError**)error
140 {
141 #if OCTAGON
142 if(self.ckksControl) {
143 return self.ckksControl;
144 }
145 return [CKKSControl CKKSControlObject:true error:error];
146 #else
147 return nil;
148 #endif
149 }
150
151 - (instancetype)init
152 {
153 if((self = [super init])) {
154 _context = OTDefaultContext;
155 }
156 return self;
157 }
158 @end
159
160 @implementation OTBottleIDs
161 @end
162
163 @implementation OTOperationConfiguration
164
165 - (instancetype)init {
166 if ((self = [super init]) == nil) {
167 return nil;
168 }
169 _timeoutWaitForCKAccount = 10 * NSEC_PER_SEC;
170 _qualityOfService = NSQualityOfServiceDefault;
171 _discretionaryNetwork = NO;
172 _useCachedAccountStatus = NO;
173 return self;
174 }
175
176 + (BOOL)supportsSecureCoding {
177 return YES;
178 }
179
180 - (void)encodeWithCoder:(nonnull NSCoder *)coder {
181 [coder encodeObject:@(_timeoutWaitForCKAccount) forKey:@"timeoutWaitForCKAccount"];
182 [coder encodeObject:@(_qualityOfService) forKey:@"qualityOfService"];
183 [coder encodeObject:@(_discretionaryNetwork) forKey:@"discretionaryNetwork"];
184 [coder encodeObject:@(_useCachedAccountStatus) forKey:@"useCachedAccountStatus"];
185 }
186
187 - (nullable instancetype)initWithCoder:(nonnull NSCoder *)coder {
188 _timeoutWaitForCKAccount = [[coder decodeObjectOfClass:[NSNumber class] forKey:@"timeoutWaitForCKAccount"] unsignedLongLongValue];
189 _qualityOfService = [[coder decodeObjectOfClass:[NSNumber class] forKey:@"qualityOfService"] integerValue];
190 _discretionaryNetwork = [[coder decodeObjectOfClass:[NSNumber class] forKey:@"discretionaryNetwork"] boolValue];
191 _useCachedAccountStatus = [[coder decodeObjectOfClass:[NSNumber class] forKey:@"useCachedAccountStatus"] boolValue];
192 return self;
193 }
194
195 @end
196
197
198 @interface OTClique ()
199 @property (nonatomic, copy) NSString* cliqueMemberIdentifier;
200 @property (nonatomic, strong) OTConfigurationContext *ctx;
201 @property (nonatomic, strong) NSMutableDictionary *defaults;
202 @end
203
204 @implementation OTClique
205
206 + (BOOL)platformSupportsSOS
207 {
208 return (OctagonPlatformSupportsSOS() && OctagonIsSOSFeatureEnabled());
209 }
210
211 // defaults write com.apple.security.octagon enable -bool YES
212 -(BOOL)isOctagonPairingEnabled {
213 BOOL nsDefaults = self.defaults[OTDefaultsOctagonEnable] ? [self.defaults[OTDefaultsOctagonEnable] boolValue] : OctagonIsEnabled();
214 secnotice("octagon", "pairing is %@", nsDefaults ? @"on" : @"off");
215 return nsDefaults;
216 }
217
218 - (void)setPairingDefault:(BOOL)defaults
219 {
220 self.defaults[OTDefaultsOctagonEnable] = @(defaults);
221 }
222
223 - (void)removePairingDefault
224 {
225 [self.defaults removeObjectForKey:OTDefaultsOctagonEnable];
226 }
227
228 - (instancetype)initWithContextData:(OTConfigurationContext *)ctx error:(NSError * __autoreleasing *)error
229 {
230 return [self initWithContextData:ctx];
231 }
232
233 - (instancetype)initWithContextData:(OTConfigurationContext *)ctx
234 {
235 #if OCTAGON
236 self = [super init];
237 if(self){
238 _ctx = [[OTConfigurationContext alloc]init];
239 _ctx.context = ctx.context ?: OTDefaultContext;
240 _ctx.dsid = [ctx.dsid copy];
241 _ctx.altDSID = [ctx.altDSID copy];
242 _ctx.analytics = ctx.analytics;
243 _ctx.otControl = ctx.otControl;
244 _ctx.ckksControl = ctx.ckksControl;
245
246 self.defaults = [NSMutableDictionary dictionary];
247 }
248 return self;
249 #else
250 NSAssert(false, @"OTClique is not implemented on this platform");
251
252 // make the build analyzer happy
253 self = [super init];
254 return self;
255 #endif // OCTAGON
256 }
257
258 - (NSString* _Nullable)cliqueMemberIdentifier
259 {
260 #if OCTAGON
261 __block NSString* retPeerID = nil;
262 __block bool subTaskSuccess = false;
263
264 OctagonSignpost fetchEgoPeerSignPost = OctagonSignpostBegin(OctagonSignpostNameFetchEgoPeer);
265 if(OctagonIsEnabled()) {
266 NSError* localError = nil;
267 OTControl* control = [self makeOTControl:&localError];
268 if(!control) {
269 secerror("octagon: Failed to create OTControl: %@", localError);
270 OctagonSignpostEnd(fetchEgoPeerSignPost, OctagonSignpostNameFetchEgoPeer, OctagonSignpostNumber1(OctagonSignpostNameFetchEgoPeer), (int)subTaskSuccess);
271 return nil;
272 }
273
274 [control fetchEgoPeerID:nil
275 context:self.ctx.context
276 reply:^(NSString* peerID, NSError* error) {
277 if(error) {
278 secerror("octagon: Failed to fetch octagon peer ID: %@", error);
279 }
280 retPeerID = peerID;
281 }];
282 secnotice("clique", "cliqueMemberIdentifier(octagon) received %@", retPeerID);
283 }
284
285 if([OTClique platformSupportsSOS]) {
286 CFErrorRef error = NULL;
287 SOSPeerInfoRef me = SOSCCCopyMyPeerInfo(&error);
288 retPeerID = (NSString*)CFBridgingRelease(CFRetainSafe(SOSPeerInfoGetPeerID(me)));
289 CFReleaseNull(me);
290 CFBridgingRelease(error);
291 }
292
293 secnotice("clique", "cliqueMemberIdentifier complete: %@", retPeerID);
294 subTaskSuccess = retPeerID ? true : false;
295 OctagonSignpostEnd(fetchEgoPeerSignPost, OctagonSignpostNameFetchEgoPeer, OctagonSignpostNumber1(OctagonSignpostNameFetchEgoPeer), (int)subTaskSuccess);
296 return retPeerID;
297 #else
298 return nil;
299 #endif
300 }
301
302 #if OCTAGON
303 - (OTControl* _Nullable)makeOTControl:(NSError**)error
304 {
305 return [self.ctx makeOTControl:error];
306 }
307
308 - (BOOL)establish:(NSError**)error
309 {
310 secnotice("clique-establish", "establish started");
311 OctagonSignpost establishSignPost = OctagonSignpostBegin(OctagonSignpostNameEstablish);
312 bool subTaskSuccess = false;
313 OTControl* control = [self makeOTControl:error];
314 if(!control) {
315 OctagonSignpostEnd(establishSignPost, OctagonSignpostNameEstablish, OctagonSignpostNumber1(OctagonSignpostNameEstablish), (int)subTaskSuccess);
316 return false;
317 }
318
319 __block BOOL success = NO;
320 __block NSError* localError = nil;
321
322 //only establish
323 [control establish:nil context:self.ctx.context altDSID:self.ctx.altDSID reply:^(NSError * _Nullable operationError) {
324 if(operationError) {
325 secnotice("clique-establish", "establish returned an error: %@", operationError);
326 }
327 success = operationError == nil;
328 localError = operationError;
329 }];
330
331 if(localError && error) {
332 *error = localError;
333 }
334 secnotice("clique-establish", "establish complete: %@", success ? @"YES" : @"NO");
335 subTaskSuccess = success ? true : false;
336 OctagonSignpostEnd(establishSignPost, OctagonSignpostNameEstablish, OctagonSignpostNumber1(OctagonSignpostNameEstablish), (int)subTaskSuccess);
337
338 return success;
339 }
340
341 - (BOOL)resetAndEstablish:(CuttlefishResetReason)resetReason error:(NSError**)error
342 {
343 secnotice("clique-resetandestablish", "resetAndEstablish started");
344 bool subTaskSuccess = false;
345 OctagonSignpost resetAndEstablishSignPost = OctagonSignpostBegin(OctagonSignpostNameResetAndEstablish);
346
347 OTControl* control = [self makeOTControl:error];
348
349 if(!control) {
350 OctagonSignpostEnd(resetAndEstablishSignPost, OctagonSignpostNameResetAndEstablish, OctagonSignpostNumber1(OctagonSignpostNameResetAndEstablish), (int)subTaskSuccess);
351 return NO;
352 }
353
354 __block BOOL success = NO;
355 __block NSError* localError = nil;
356 [control resetAndEstablish:nil context:self.ctx.context altDSID:self.ctx.altDSID resetReason:resetReason reply:^(NSError * _Nullable operationError) {
357
358 if(operationError) {
359 secnotice("clique-resetandestablish", "resetAndEstablish returned an error: %@", operationError);
360 }
361 success = operationError == nil;
362 localError = operationError;
363 }];
364
365 if(localError && error) {
366 *error = localError;
367 }
368
369 secnotice("clique-resetandestablish", "establish complete: %@", success ? @"YES" : @"NO");
370 subTaskSuccess = success ? true : false;
371 OctagonSignpostEnd(resetAndEstablishSignPost, OctagonSignpostNameResetAndEstablish, OctagonSignpostNumber1(OctagonSignpostNameResetAndEstablish), (int)subTaskSuccess);
372
373 return success;
374 }
375 #endif // OCTAGON
376
377 + (OTClique*)newFriendsWithContextData:(OTConfigurationContext*)data error:(NSError * __autoreleasing *)error
378 {
379 return [OTClique newFriendsWithContextData:data resetReason:CuttlefishResetReasonUserInitiatedReset error:error];
380 }
381
382 + (OTClique*)newFriendsWithContextData:(OTConfigurationContext*)data resetReason:(CuttlefishResetReason)resetReason error:(NSError * __autoreleasing *)error
383 {
384 #if OCTAGON
385 secnotice("clique-newfriends", "makeNewFriends invoked using context: %@, dsid: %@", data.context, data.dsid);
386 bool result = false;
387 bool subTaskSuccess = false;
388 OctagonSignpost performEscrowRecoverySignpost = OctagonSignpostBegin(OctagonSignpostNameMakeNewFriends);
389
390 OTClique* clique = [[OTClique alloc] initWithContextData:data];
391
392 if(OctagonIsEnabled()) {
393 NSError* localError = nil;
394 [clique resetAndEstablish:resetReason error:&localError];
395
396 if(localError) {
397 secnotice("clique-newfriends", "account reset failed: %@", localError);
398 if(error) {
399 *error = localError;
400 }
401 OctagonSignpostEnd(performEscrowRecoverySignpost, OctagonSignpostNameMakeNewFriends, OctagonSignpostNumber1(OctagonSignpostNameMakeNewFriends), (int)subTaskSuccess);
402 return nil;
403 } else {
404 secnotice("clique-newfriends", "Octagon account reset succeeded");
405 }
406 }
407
408 if([OTClique platformSupportsSOS]) {
409 CFErrorRef resetError = NULL;
410 result = SOSCCResetToOffering(&resetError);
411
412 if(!result || resetError){
413 secnotice("clique-newfriends", "newFriendsWithContextData: resetToOffering failed: %@", resetError);
414 if(error) {
415 *error = CFBridgingRelease(resetError);
416 } else {
417 CFBridgingRelease(resetError);
418 }
419 OctagonSignpostEnd(performEscrowRecoverySignpost, OctagonSignpostNameMakeNewFriends, OctagonSignpostNumber1(OctagonSignpostNameMakeNewFriends), (int)subTaskSuccess);
420 return nil;
421 }
422 secnotice("clique-newfriends", "newFriendsWithContextData: reset the SOS circle");
423 } else {
424 secnotice("clique-newfriends", "newFriendsWithContextData: SOS disabled on this platform");
425 }
426 secnotice("clique-newfriends", "makeNewFriends complete");
427
428 subTaskSuccess = true;
429 OctagonSignpostEnd(performEscrowRecoverySignpost, OctagonSignpostNameMakeNewFriends, OctagonSignpostNumber1(OctagonSignpostNameMakeNewFriends), (int)subTaskSuccess);
430
431 return clique;
432
433 #else // !OCTAGON
434 if (error)
435 *error = [NSError errorWithDomain:NSOSStatusErrorDomain code:errSecUnimplemented userInfo:nil];
436 return NULL;
437 #endif
438 }
439
440 + (OTClique* _Nullable)performEscrowRecoveryWithContextData:(OTConfigurationContext*)data
441 escrowArguments:(NSDictionary*)sbdRecoveryArguments
442 error:(NSError**)error
443 {
444 #if OCTAGON
445 OctagonSignpost performEscrowRecoverySignpost = OctagonSignpostBegin(OctagonSignpostNamePerformEscrowRecovery);
446 bool subTaskSuccess = false;
447 NSError* localError = nil;
448
449 OTClique* clique = [[OTClique alloc] initWithContextData:data];
450
451 // Attempt the recovery from sbd
452 secnotice("clique-recovery", "attempting an escrow recovery for context:%@, altdsid:%@", data.context, data.altDSID);
453 id<OctagonEscrowRecovererPrococol> sb = data.sbd ?: [[getSecureBackupClass() alloc] init];
454 NSDictionary* recoveredInformation = nil;
455
456 OctagonSignpost recoverFromSBDSignPost = OctagonSignpostBegin(OctagonSignpostNamePerformRecoveryFromSBD);
457 NSError* recoverError = [sb recoverWithInfo:sbdRecoveryArguments results:&recoveredInformation];
458 subTaskSuccess = (recoverError == nil) ? true : false;
459 OctagonSignpostEnd(recoverFromSBDSignPost, OctagonSignpostNamePerformRecoveryFromSBD, OctagonSignpostNumber1(OctagonSignpostNamePerformRecoveryFromSBD), (int)subTaskSuccess);
460
461 if(recoverError) {
462 secnotice("clique-recovery", "sbd escrow recovery failed: %@", recoverError);
463 if(recoverError.code == 17 /* kSecureBackupRestoringLegacyBackupKeychainError */ && [recoverError.domain isEqualToString:getkSecureBackupErrorDomain()]) { /* XXX */
464 if([OTClique platformSupportsSOS]) {
465 secnotice("clique-recovery", "Can't restore legacy backup with no keybag. Resetting SOS to offering");
466 CFErrorRef blowItAwayError = NULL;
467 bool successfulReset = SOSCCResetToOffering(&blowItAwayError);
468 if(!successfulReset || blowItAwayError) {
469 secerror("clique-recovery: failed to reset to offering:%@", blowItAwayError);
470 } else {
471 secnotice("clique-recovery", "resetting SOS circle successful");
472 }
473 CFBridgingRelease(blowItAwayError);
474 } else {
475 secnotice("clique-recovery", "Legacy restore failed on a non-SOS platform");
476 }
477 } else {
478 if(error) {
479 *error = recoverError;
480 }
481 subTaskSuccess = false;
482 OctagonSignpostEnd(performEscrowRecoverySignpost, OctagonSignpostNamePerformEscrowRecovery, OctagonSignpostNumber1(OctagonSignpostNamePerformEscrowRecovery), (int)subTaskSuccess);
483 return nil;
484 }
485 } else {
486 if(OctagonPlatformSupportsSOS()) { // Join if the legacy restore is complete now.
487 secnotice("clique-recovery", "attempting joinAfterRestore");
488 [clique joinAfterRestore:&localError];
489 secnotice("clique-recovery", "joinAfterRestore: %@", localError);
490 }
491 }
492
493 // look for OT Bottles
494 OTControl* control = [clique makeOTControl:&localError];
495 if (!control) {
496 secnotice("clique-recovery", "unable to create otcontrol: %@", localError);
497 if (error) {
498 *error = localError;
499 }
500 subTaskSuccess = false;
501 OctagonSignpostEnd(performEscrowRecoverySignpost, OctagonSignpostNamePerformEscrowRecovery, OctagonSignpostNumber1(OctagonSignpostNamePerformEscrowRecovery), (int)subTaskSuccess);
502 return nil;
503 }
504
505 NSString *bottleID = recoveredInformation[@"bottleID"];
506 NSString *isValid = recoveredInformation[@"bottleValid"];
507 NSData *bottledPeerEntropy = recoveredInformation[@"EscrowServiceEscrowData"][@"BottledPeerEntropy"];
508 bool shouldResetOctagon = false;
509
510 if(bottledPeerEntropy && bottleID && [isValid isEqualToString:@"valid"]){
511 secnotice("clique-recovery", "recovering from bottle: %@", bottleID);
512 __block NSError* restoreBottleError = nil;
513
514 OctagonSignpost bottleRecoverySignPost = OctagonSignpostBegin(OctagonSignpostNamePerformBottleRecovery);
515 //restore bottle!
516 [control restore:OTCKContainerName
517 contextID:data.context
518 bottleSalt:data.altDSID
519 entropy:bottledPeerEntropy
520 bottleID:bottleID
521 reply:^(NSError * _Nullable restoreError) {
522 if(restoreError) {
523 secnotice("clique-recovery", "restore bottle errored: %@", restoreError);
524 } else {
525 secnotice("clique-recovery", "restoring bottle succeeded");
526 }
527 restoreBottleError = restoreError;
528 }];
529
530 subTaskSuccess = (restoreBottleError == nil) ? true : false;
531 OctagonSignpostEnd(bottleRecoverySignPost, OctagonSignpostNamePerformBottleRecovery, OctagonSignpostNumber1(OctagonSignpostNamePerformBottleRecovery), (int)subTaskSuccess);
532
533 if(restoreBottleError) {
534 if(error){
535 *error = restoreBottleError;
536 }
537 subTaskSuccess = false;
538 OctagonSignpostEnd(performEscrowRecoverySignpost, OctagonSignpostNamePerformEscrowRecovery, OctagonSignpostNumber1(OctagonSignpostNamePerformEscrowRecovery), (int)subTaskSuccess);
539 return nil;
540 }
541 } else {
542 shouldResetOctagon = true;
543 }
544
545 if(shouldResetOctagon) {
546 secnotice("clique-recovery", "bottle %@ is not valid, resetting octagon", bottleID);
547 NSError* resetError = nil;
548
549 OctagonSignpost resetSignPost = OctagonSignpostBegin(OctagonSignpostNamePerformResetAndEstablishAfterFailedBottle);
550 [clique resetAndEstablish:CuttlefishResetReasonNoBottleDuringEscrowRecovery error:&resetError];
551 subTaskSuccess = (resetError == nil) ? true : false;
552 OctagonSignpostEnd(resetSignPost, OctagonSignpostNamePerformResetAndEstablishAfterFailedBottle, OctagonSignpostNumber1(OctagonSignpostNamePerformResetAndEstablishAfterFailedBottle), (int)subTaskSuccess);
553
554 if(resetError) {
555 secnotice("clique-recovery", "failed to reset octagon: %@", resetError);
556 } else{
557 secnotice("clique-recovery", "reset octagon succeeded");
558 }
559 }
560
561 secnotice("clique-recovery", "recovery complete: %@", clique);
562
563 subTaskSuccess = clique ? true : false;
564 OctagonSignpostEnd(performEscrowRecoverySignpost, OctagonSignpostNamePerformEscrowRecovery, OctagonSignpostNumber1(OctagonSignpostNamePerformEscrowRecovery), (int)subTaskSuccess);
565
566 return clique;
567 #else
568 if (error) {
569 *error = [NSError errorWithDomain:NSOSStatusErrorDomain code:errSecUnimplemented userInfo:nil];
570 }
571 return NULL;
572 #endif
573 }
574
575
576 - (KCPairingChannel *)setupPairingChannelAsInitiator:(KCPairingChannelContext *)ctx
577 {
578 #if OCTAGON
579 return [getKCPairingChannelClass() pairingChannelInitiator:ctx];
580 #else
581 return NULL;
582 #endif
583 }
584
585 - (KCPairingChannel * _Nullable)setupPairingChannelAsInitator:(KCPairingChannelContext *)ctx error:(NSError * __autoreleasing *)error
586 {
587 if (error) {
588 *error = nil;
589 }
590 return [self setupPairingChannelAsInitiator:ctx];
591 }
592
593 - (KCPairingChannel *)setupPairingChannelAsAcceptor:(KCPairingChannelContext *)ctx
594 {
595 #if OCTAGON
596 return [getKCPairingChannelClass() pairingChannelAcceptor:ctx];
597 #else
598 return NULL;
599 #endif
600 }
601
602 - (KCPairingChannel * _Nullable)setupPairingChannelAsAcceptor:(KCPairingChannelContext *)ctx error:(NSError * __autoreleasing *)error
603 {
604 if (error) {
605 *error = nil;
606 }
607
608 return [self setupPairingChannelAsAcceptor:ctx];
609 }
610
611
612 - (CliqueStatus)_fetchCliqueStatus:(OTOperationConfiguration *)configuration error:(NSError * __autoreleasing *)error
613 {
614 #if OCTAGON
615 __block CliqueStatus sosStatus = CliqueStatusError;
616 __block CliqueStatus octagonStatus = CliqueStatusError;
617 bool subTaskSuccess = false;
618
619 OctagonSignpost fetchCliqueStatusSignPost = OctagonSignpostBegin(OctagonSignpostNameFetchCliqueStatus);
620
621 // Octagon is supreme.
622
623 if(OctagonIsEnabled()) {
624 OTControl* control = [self makeOTControl:error];
625 if(!control) {
626 secnotice("clique-status", "cliqueStatus noOTControl");
627 OctagonSignpostEnd(fetchCliqueStatusSignPost, OctagonSignpostNameFetchCliqueStatus, OctagonSignpostNumber1(OctagonSignpostNameFetchCliqueStatus), (int)subTaskSuccess);
628
629 return CliqueStatusError;
630 }
631
632 __block NSError* localError = nil;
633 [control fetchCliqueStatus:nil context:self.ctx.context configuration:configuration reply:^(CliqueStatus cliqueStatus, NSError * _Nullable fetchError) {
634 if(fetchError){
635 octagonStatus = CliqueStatusError;
636 localError = fetchError;
637 secnotice("clique-status", "octagon clique status errored: %@", fetchError);
638 } else {
639 octagonStatus = cliqueStatus;
640 }
641 }];
642
643 if(OctagonAuthoritativeTrustIsEnabled() || !OctagonPlatformSupportsSOS()) {
644 secnotice("clique-status", "cliqueStatus(%{public}scached)(context:%@, altDSID:%@) returning %@ (error: %@)",
645 configuration.useCachedAccountStatus ? "" : "non-",
646 self.ctx.context, self.ctx.altDSID,
647 OTCliqueStatusToString(octagonStatus), localError);
648 if (localError && error) {
649 *error = localError;
650 subTaskSuccess = false;
651 } else {
652 subTaskSuccess = true;
653 }
654 OctagonSignpostEnd(fetchCliqueStatusSignPost, OctagonSignpostNameFetchCliqueStatus, OctagonSignpostNumber1(OctagonSignpostNameFetchCliqueStatus), (int)subTaskSuccess);
655 return octagonStatus;
656 }
657 }
658
659 if([OTClique platformSupportsSOS]) {
660 CFErrorRef circleStatusError = NULL;
661 sosStatus = kSOSCCError;
662 if(configuration.useCachedAccountStatus){
663 sosStatus = SOSCCThisDeviceIsInCircle(&circleStatusError);
664 } else {
665 sosStatus = SOSCCThisDeviceIsInCircleNonCached(&circleStatusError);
666 }
667 secnotice("clique-status", "sos clique status is %d (%@)", (int)sosStatus, circleStatusError);
668
669 if (error) {
670 *error = (NSError*)CFBridgingRelease(circleStatusError);
671 } else {
672 CFBridgingRelease(circleStatusError);
673 }
674 }
675 secnotice("clique-status", "cliqueStatus(%{public}scached)(context:%@, altDSID:%@) complete: %@",
676 configuration.useCachedAccountStatus ? "" : "non-",
677 self.ctx.context, self.ctx.altDSID,
678 OTCliqueStatusToString(octagonStatus));
679
680 subTaskSuccess = true;
681 OctagonSignpostEnd(fetchCliqueStatusSignPost, OctagonSignpostNameFetchCliqueStatus, OctagonSignpostNumber1(OctagonSignpostNameFetchCliqueStatus), (int)subTaskSuccess);
682
683 return octagonStatus;
684 #else // !OCTAGON
685 if(error){
686 *error = [NSError errorWithDomain:NSOSStatusErrorDomain code:errSecUnimplemented userInfo:nil];
687 }
688 return (CliqueStatus)kSOSCCError;
689 #endif
690 }
691
692 // Don't change rules for CoreCDP, and preserve legacy behavior for now
693 // preserve old behavior until CoreCDP can move to -fetchCliqueStatus:error:
694 #define LEGACY_WAITING_BEHAVIOR (TARGET_OS_OSX || TARGET_OS_IOS)
695
696 - (CliqueStatus)fetchCliqueStatus:(OTOperationConfiguration *)configuration error:(NSError * __autoreleasing * _Nonnull)error
697 {
698 return [self _fetchCliqueStatus:configuration error:error];
699 }
700
701 - (CliqueStatus)fetchCliqueStatus:(NSError * __autoreleasing *)error
702 {
703 OTOperationConfiguration *configuration = [[OTOperationConfiguration alloc] init];
704 #if LEGACY_WAITING_BEHAVIOR
705 configuration.timeoutWaitForCKAccount = 0;
706 #endif
707 return [self _fetchCliqueStatus:configuration error:error];
708 }
709
710 - (CliqueStatus)cachedCliqueStatus:(BOOL)usedCached error:(NSError * __autoreleasing *)error
711 {
712 OTOperationConfiguration *configuration = [[OTOperationConfiguration alloc] init];
713 #if LEGACY_WAITING_BEHAVIOR
714 configuration.timeoutWaitForCKAccount = 0;
715 #endif
716 if (usedCached) {
717 configuration.useCachedAccountStatus = YES;
718 }
719 return [self _fetchCliqueStatus:configuration error:error];
720 }
721
722
723 - (BOOL)removeFriendsInClique:(NSArray<NSString*>*)friendIdentifiers error:(NSError * __autoreleasing *)error
724 {
725 #if OCTAGON
726 secnotice("clique-removefriends", "removeFriendsInClique invoked using context:%@, altdsid:%@", self.ctx.context, self.ctx.altDSID);
727 OctagonSignpost removeFriendsSignPost = OctagonSignpostBegin(OctagonSignpostNameRemoveFriendsInClique);
728 bool subTaskSuccess = false;
729
730 // Annoying: we must sort friendIdentifiers into octagon/sos lists.
731 NSMutableArray<NSString*>* octagonIdentifiers = [NSMutableArray array];
732 NSMutableArray<NSString*>* sosIdentifiers = [NSMutableArray array];
733
734 for(NSString* friendIdentifier in friendIdentifiers) {
735 if([friendIdentifier hasPrefix:@"SHA256:"]) {
736 [octagonIdentifiers addObject: friendIdentifier];
737 } else {
738 [sosIdentifiers addObject: friendIdentifier];
739 }
740 }
741
742 // Ensure that we don't have any peers on the wrong platform
743 if(!OctagonIsEnabled() && octagonIdentifiers.count > 0) {
744 NSError *localError = [NSError errorWithDomain:NSOSStatusErrorDomain
745 code:errSecUnimplemented
746 userInfo:@{NSLocalizedDescriptionKey: @"Octagon is disabled; can't distrust any Octagon peers"}];
747 secnotice("clique-removefriends", "removeFriendsInClique failed:%@", localError);
748 if(error) {
749 *error = localError;
750 }
751 OctagonSignpostEnd(removeFriendsSignPost, OctagonSignpostNameRemoveFriendsInClique, OctagonSignpostNumber1(OctagonSignpostNameRemoveFriendsInClique), (int)subTaskSuccess);
752 return NO;
753 }
754
755 if(!OctagonPlatformSupportsSOS() && sosIdentifiers.count > 0) {
756 NSError *localError = [NSError errorWithDomain:NSOSStatusErrorDomain
757 code:errSecUnimplemented
758 userInfo:@{NSLocalizedDescriptionKey: @"SOS is not available on this platform; can't distrust any SOS peers"}];
759 secnotice("clique-removefriends", "removeFriendsInClique failed:%@", localError);
760 if(error) {
761 *error = localError;
762 }
763 OctagonSignpostEnd(removeFriendsSignPost, OctagonSignpostNameRemoveFriendsInClique, OctagonSignpostNumber1(OctagonSignpostNameRemoveFriendsInClique), (int)subTaskSuccess);
764 return NO;
765 }
766
767
768 __block NSError* localError = nil;
769 bool result = true;
770
771 if(OctagonIsEnabled() && octagonIdentifiers.count > 0) {
772 OTControl* control = [self makeOTControl:error];
773 if(!control) {
774 OctagonSignpostEnd(removeFriendsSignPost, OctagonSignpostNameRemoveFriendsInClique, OctagonSignpostNumber1(OctagonSignpostNameRemoveFriendsInClique), (int)subTaskSuccess);
775 return NO;
776 }
777
778 secnotice("clique-removefriends", "octagon: removing octagon friends: %@", octagonIdentifiers);
779 [control removeFriendsInClique:nil
780 context:self.ctx.context
781 peerIDs:octagonIdentifiers
782 reply:^(NSError* replyError) {
783 if(replyError) {
784 secnotice("clique-removefriends", "removeFriendsInClique failed: unable to remove friends: %@", replyError);
785 localError = replyError;
786 } else {
787 secnotice("clique-removefriends", "octagon: friends removed: %@", octagonIdentifiers);
788 }
789 }];
790 }
791
792 if([OTClique platformSupportsSOS] && sosIdentifiers.count >0) {
793 CFErrorRef removeFriendError = NULL;
794 NSData* analyticsData = nil;
795
796 secnotice("clique-removefriends", "removing sos friends: %@", sosIdentifiers);
797
798 if(self.ctx.analytics){
799 NSError* encodingError = nil;
800 analyticsData = [NSKeyedArchiver archivedDataWithRootObject:self.ctx.analytics requiringSecureCoding:YES error:&encodingError];
801 }
802
803 if(analyticsData) {
804 result = SOSCCRemovePeersFromCircleWithAnalytics((__bridge CFArrayRef)friendIdentifiers, (__bridge CFDataRef)analyticsData, &removeFriendError);
805 } else {
806 result = SOSCCRemovePeersFromCircle((__bridge CFArrayRef)friendIdentifiers, &removeFriendError);
807 }
808
809 if(removeFriendError) {
810 secnotice("clique-removefriends", "removeFriendsInClique failed: unable to remove friends: %@", removeFriendError);
811 localError = CFBridgingRelease(removeFriendError);
812 }
813 }
814
815 if(error && localError) {
816 *error = localError;
817 }
818 secnotice("clique-removefriends", "removeFriendsInClique complete: %d", result);
819
820 subTaskSuccess = result;
821 OctagonSignpostEnd(removeFriendsSignPost, OctagonSignpostNameRemoveFriendsInClique, OctagonSignpostNumber1(OctagonSignpostNameRemoveFriendsInClique), (int)subTaskSuccess);
822
823 return result && localError == nil;
824 #else // !OCTAGON
825 return NO;
826 #endif
827 }
828
829 - (BOOL)leaveClique:(NSError * __autoreleasing *)error
830 {
831 #if OCTAGON
832 secnotice("clique-leaveClique", "leaveClique invoked using context:%@, altdsid:%@", self.ctx.context, self.ctx.altDSID);
833 CFErrorRef removeThisDeviceError = NULL;
834 bool result = false;
835 bool subTaskSuccess = false;
836
837 OctagonSignpost leaveCliqueSignPost = OctagonSignpostBegin(OctagonSignpostNameLeaveClique);
838
839 if(OctagonIsEnabled()) {
840 OTControl* control = [self makeOTControl:error];
841 if(!control) {
842 OctagonSignpostEnd(leaveCliqueSignPost, OctagonSignpostNameLeaveClique, OctagonSignpostNumber1(OctagonSignpostNameLeaveClique), (int)subTaskSuccess);
843 return NO;
844 }
845
846 // We only want to issue a "leave" command if we're actively in a clique
847 __block NSError* localError = nil;
848 CliqueStatus currentStatus = [self fetchCliqueStatus:[[OTOperationConfiguration alloc] init]
849 error:&localError];
850
851 if(localError) {
852 secnotice("clique-leaveClique", "fetching current status errored: %@", localError);
853 if(error) {
854 *error = localError;
855 }
856 OctagonSignpostEnd(leaveCliqueSignPost, OctagonSignpostNameLeaveClique, OctagonSignpostNumber1(OctagonSignpostNameLeaveClique), (int)subTaskSuccess);
857 return NO;
858 }
859
860 if(currentStatus == CliqueStatusNotIn) {
861 secnotice("clique-leaveClique", "current status is Not In; no need to leave");
862 subTaskSuccess = true;
863 OctagonSignpostEnd(leaveCliqueSignPost, OctagonSignpostNameLeaveClique, OctagonSignpostNumber1(OctagonSignpostNameLeaveClique), (int)subTaskSuccess);
864 return YES;
865 }
866 [control leaveClique:nil context:self.ctx.context reply:^(NSError * _Nullable leaveError) {
867 if(leaveError) {
868 secnotice("clique-leaveClique", "leaveClique errored: %@", leaveError);
869 localError = leaveError;
870 } else {
871 secnotice("clique-leaveClique", "leaveClique success.");
872 }
873 }];
874
875 if(error) {
876 *error = localError;
877 }
878 result = !localError;
879 }
880
881 if([OTClique platformSupportsSOS]) {
882 NSData* analyticsData = nil;
883
884 if(self.ctx.analytics) {
885 NSError* encodingError = nil;
886 analyticsData = [NSKeyedArchiver archivedDataWithRootObject:self.ctx.analytics requiringSecureCoding:YES error:&encodingError];
887 if(!analyticsData){
888 secnotice("clique-leaveClique", "leaveClique unable to archive analytics object: %@", encodingError);
889 }
890 }
891
892 if(analyticsData) {
893 result &= SOSCCRemoveThisDeviceFromCircleWithAnalytics((__bridge CFDataRef)analyticsData, &removeThisDeviceError);
894 } else {
895 result &= SOSCCRemoveThisDeviceFromCircle(&removeThisDeviceError);
896 }
897 if (error) {
898 *error = (NSError*)CFBridgingRelease(removeThisDeviceError);
899 } else {
900 CFBridgingRelease(removeThisDeviceError);
901 }
902 }
903 secnotice("clique-leaveClique", "leaveClique complete: %d", result);
904
905 subTaskSuccess = result;
906 OctagonSignpostEnd(leaveCliqueSignPost, OctagonSignpostNameLeaveClique, OctagonSignpostNumber1(OctagonSignpostNameLeaveClique), (int)subTaskSuccess);
907
908 return result ? YES : NO;
909 #else // !OCTAGON
910 return NO;
911 #endif
912 }
913
914 - (NSDictionary<NSString*,NSString*>* _Nullable)peerDeviceNamesByPeerID:(NSError * __autoreleasing *)error
915 {
916 #if OCTAGON
917 secnotice("clique", "peerDeviceNamesByPeerID invoked using context:%@, altdsid:%@", self.ctx.context, self.ctx.altDSID);
918 OctagonSignpost peerNamesSignPost = OctagonSignpostBegin(OctagonSignpostNamePeerDeviceNamesByPeerID);
919 __block bool subTaskSuccess = false;
920 NSMutableDictionary<NSString*, NSString*>* retPeers = [NSMutableDictionary dictionary];
921
922 if(OctagonIsEnabled()) {
923 OTControl* control = [self makeOTControl:error];
924 if(!control) {
925 OctagonSignpostEnd(peerNamesSignPost, OctagonSignpostNamePeerDeviceNamesByPeerID, OctagonSignpostNumber1(OctagonSignpostNamePeerDeviceNamesByPeerID), (int)subTaskSuccess);
926 return nil;
927 }
928
929 __block NSError* localError = nil;
930 __block NSDictionary<NSString*, NSString*>* localPeers = nil;
931
932 [control peerDeviceNamesByPeerID:nil context:OTDefaultContext reply:^(NSDictionary<NSString*,NSString*>* peers, NSError* controlError) {
933 if(controlError) {
934 secnotice("clique", "peerDeviceNamesByPeerID errored: %@", controlError);
935 } else {
936 secnotice("clique", "peerDeviceNamesByPeerID succeeded: %@", peers);
937 }
938 localError = controlError;
939 localPeers = peers;
940 }];
941
942 if(error && localError) {
943 *error = localError;
944 }
945 if(localError) {
946 OctagonSignpostEnd(peerNamesSignPost, OctagonSignpostNamePeerDeviceNamesByPeerID, OctagonSignpostNumber1(OctagonSignpostNamePeerDeviceNamesByPeerID), (int)subTaskSuccess);
947 return nil;
948 }
949 [retPeers addEntriesFromDictionary:localPeers];
950 secnotice("clique", "Received %lu Octagon peers", (unsigned long)localPeers.count);
951 }
952
953 if([OTClique platformSupportsSOS]) {
954 CFErrorRef peerErrorRef = NULL;
955 NSMutableDictionary<NSString*,NSString*>* peerMapping = [NSMutableDictionary dictionary];
956 NSArray* arrayOfPeerRefs = CFBridgingRelease(SOSCCCopyPeerPeerInfo(&peerErrorRef));
957 if(arrayOfPeerRefs){
958 [arrayOfPeerRefs enumerateObjectsUsingBlock:^(id peerRef, NSUInteger idx, BOOL * stop) {
959 SOSPeerInfoRef peer = (__bridge SOSPeerInfoRef)peerRef;
960 if(peer){
961 [peerMapping setObject:(__bridge NSString*)SOSPeerInfoGetPeerName(peer) forKey:(__bridge NSString*)SOSPeerInfoGetPeerID(peer)];
962 }
963 }];
964 }
965 subTaskSuccess = (peerErrorRef == NULL || [retPeers count] == 0) ? true : false;
966
967 if (error) {
968 *error = (NSError*)CFBridgingRelease(peerErrorRef);
969 } else {
970 CFBridgingRelease(peerErrorRef);
971 }
972 [retPeers addEntriesFromDictionary:peerMapping];
973 secnotice("clique", "Received %lu SOS peers", (unsigned long)peerMapping.count);
974 }
975
976 OctagonSignpostEnd(peerNamesSignPost, OctagonSignpostNamePeerDeviceNamesByPeerID, OctagonSignpostNumber1(OctagonSignpostNamePeerDeviceNamesByPeerID), (int)subTaskSuccess);
977 return retPeers;
978 #else // !OCTAGON
979 return NULL;
980 #endif
981 }
982
983 - (BOOL)joinAfterRestore:(NSError * __autoreleasing *)error
984 {
985 secnotice("clique-recovery", "joinAfterRestore for context:%@, altdsid:%@", self.ctx.context, self.ctx.altDSID);
986 OctagonSignpost joinAfterRestoreSignPost = OctagonSignpostBegin(OctagonSignpostNameJoinAfterRestore);
987 bool subTaskSuccess = false;
988
989 if([OTClique platformSupportsSOS]) {
990 CFErrorRef restoreError = NULL;
991 bool res = SOSCCRequestToJoinCircleAfterRestore(&restoreError);
992 if (error) {
993 *error = (NSError*)CFBridgingRelease(restoreError);
994 } else {
995 CFBridgingRelease(restoreError);
996 }
997 secnotice("clique-recovery", "joinAfterRestore complete: %d %@", res, error ? *error : @"no error pointer provided");
998
999 subTaskSuccess = res;
1000 OctagonSignpostEnd(joinAfterRestoreSignPost, OctagonSignpostNameJoinAfterRestore, OctagonSignpostNumber1(OctagonSignpostNameJoinAfterRestore), (int)subTaskSuccess);
1001
1002 return res ? YES : NO;
1003 } else {
1004 secnotice("clique-recovery", "SOS disabled for this platform, returning NO");
1005 if(error){
1006 *error = [NSError errorWithDomain:NSOSStatusErrorDomain
1007 code:errSecUnimplemented
1008 userInfo:@{NSLocalizedDescriptionKey: @"join after restore unimplemented"}];
1009 }
1010 OctagonSignpostEnd(joinAfterRestoreSignPost, OctagonSignpostNameJoinAfterRestore, OctagonSignpostNumber1(OctagonSignpostNameJoinAfterRestore), (int)subTaskSuccess);
1011 return NO;
1012 }
1013 }
1014
1015 - (BOOL)safariPasswordSyncingEnabled:(NSError **)error
1016 {
1017 secnotice("clique-safari", "safariPasswordSyncingEnabled for context:%@, altdsid:%@", self.ctx.context, self.ctx.altDSID);
1018 OctagonSignpost safariSyncingEnabledSignPost = OctagonSignpostBegin(OctagonSignpostNameSafariPasswordSyncingEnabled);
1019 bool subTaskSuccess = false;
1020
1021 if([OTClique platformSupportsSOS]) {
1022 CFErrorRef viewErrorRef = NULL;
1023
1024 SOSViewResultCode result = SOSCCView(kSOSViewAutofillPasswords, kSOSCCViewQuery, &viewErrorRef);
1025 subTaskSuccess = (viewErrorRef == NULL) ? true : false;
1026
1027 BOOL viewMember = result == kSOSCCViewMember;
1028 if (error) {
1029 *error = (NSError*)CFBridgingRelease(viewErrorRef);
1030 } else {
1031 CFBridgingRelease(viewErrorRef);
1032 }
1033 OctagonSignpostEnd(safariSyncingEnabledSignPost, OctagonSignpostNameSafariPasswordSyncingEnabled, OctagonSignpostNumber1(OctagonSignpostNameSafariPasswordSyncingEnabled), (int)subTaskSuccess);
1034
1035 secnotice("clique-safari", "safariPasswordSyncingEnabled complete: %@", viewMember ? @"YES" : @"NO");
1036 return viewMember;
1037 } else {
1038 secnotice("clique-safari", "SOS disabled for this platform, returning NO");
1039 if(error){
1040 *error = [NSError errorWithDomain:NSOSStatusErrorDomain
1041 code:errSecUnimplemented
1042 userInfo:@{NSLocalizedDescriptionKey: @"safari password syncing enabled unimplemented"}];
1043 }
1044 OctagonSignpostEnd(safariSyncingEnabledSignPost, OctagonSignpostNameSafariPasswordSyncingEnabled, OctagonSignpostNumber1(OctagonSignpostNameSafariPasswordSyncingEnabled), (int)subTaskSuccess);
1045 return NO;
1046 }
1047 }
1048
1049 - (BOOL)isLastFriend:(NSError **)error
1050 {
1051 secnotice("clique-isLastFriend", "is last friend");
1052 return NO;
1053 }
1054
1055 - (BOOL)waitForInitialSync:(NSError *__autoreleasing*)error
1056 {
1057 secnotice("clique-legacy", "waitForInitialSync for context:%@, altdsid:%@", self.ctx.context, self.ctx.altDSID);
1058
1059 OctagonSignpost waitForInitialSyncSignPost = OctagonSignpostBegin(OctagonSignpostNameWaitForInitialSync);
1060 bool subTaskSuccess = false;
1061
1062 if([OTClique platformSupportsSOS]) {
1063 CFErrorRef initialSyncErrorRef = NULL;
1064 bool result = false;
1065 if(self.ctx.analytics){
1066 NSError* encodingError = nil;
1067 NSData* analyticsData = [NSKeyedArchiver archivedDataWithRootObject:self.ctx.analytics requiringSecureCoding:YES error:&encodingError];
1068 if(!encodingError && analyticsData){
1069 result = SOSCCWaitForInitialSyncWithAnalytics((__bridge CFDataRef)analyticsData, &initialSyncErrorRef);
1070 }else{
1071 result = SOSCCWaitForInitialSync(&initialSyncErrorRef);
1072 }
1073 }else{
1074 result = SOSCCWaitForInitialSync(&initialSyncErrorRef);
1075 }
1076
1077 BOOL initialSyncResult = result ? YES : NO;
1078 if (error) {
1079 *error = (NSError*)CFBridgingRelease(initialSyncErrorRef);
1080 } else {
1081 CFBridgingRelease(initialSyncErrorRef);
1082 }
1083 secnotice("clique-legacy", "waitForInitialSync waited: %d %@", initialSyncResult, error ? *error : @"no error pointer provided");
1084
1085 subTaskSuccess = initialSyncResult ? true : false;
1086 OctagonSignpostEnd(waitForInitialSyncSignPost, OctagonSignpostNameWaitForInitialSync, OctagonSignpostNumber1(OctagonSignpostNameWaitForInitialSync), (int)subTaskSuccess);
1087
1088 return initialSyncResult;
1089 } else {
1090 secnotice("clique-legacy", "SOS disabled for this platform, returning NO");
1091 if(error){
1092 *error = [NSError errorWithDomain:NSOSStatusErrorDomain
1093 code:errSecUnimplemented
1094 userInfo:@{NSLocalizedDescriptionKey: @"wait for initial sync unimplemented"}];
1095 }
1096 OctagonSignpostEnd(waitForInitialSyncSignPost, OctagonSignpostNameWaitForInitialSync, OctagonSignpostNumber1(OctagonSignpostNameWaitForInitialSync), (int)subTaskSuccess);
1097 return NO;
1098 }
1099 }
1100
1101 - (NSArray* _Nullable)copyViewUnawarePeerInfo:(NSError *__autoreleasing*)error
1102 {
1103 secnotice("clique-legacy", "copyViewUnawarePeerInfo for context:%@, altdsid:%@", self.ctx.context, self.ctx.altDSID);
1104
1105 OctagonSignpost signPost = OctagonSignpostBegin(OctagonSignpostNameCopyViewUnawarePeerInfo);
1106 bool subTaskSuccess = false;
1107
1108 if([OTClique platformSupportsSOS]) {
1109 CFErrorRef copyViewUnawarePeerInfoErrorRef = NULL;
1110 CFArrayRef peerListRef = SOSCCCopyViewUnawarePeerInfo(&copyViewUnawarePeerInfoErrorRef);
1111
1112 NSArray* peerList = (peerListRef ? (NSArray*)(CFBridgingRelease(peerListRef)) : nil);
1113 if (error) {
1114 *error = (NSError*)CFBridgingRelease(copyViewUnawarePeerInfoErrorRef);
1115 } else {
1116 CFBridgingRelease(copyViewUnawarePeerInfoErrorRef);
1117 }
1118 subTaskSuccess = (peerList != nil) ? true : false;
1119 OctagonSignpostEnd(signPost, OctagonSignpostNameCopyViewUnawarePeerInfo, OctagonSignpostNumber1(OctagonSignpostNameCopyViewUnawarePeerInfo), (int)subTaskSuccess);
1120
1121 return peerList;
1122 } else {
1123 secnotice("clique-legacy", "SOS disabled for this platform, returning NULL");
1124 if(error){
1125 *error = [NSError errorWithDomain:NSOSStatusErrorDomain
1126 code:errSecUnimplemented
1127 userInfo:@{NSLocalizedDescriptionKey: @"copy view unaware peer info unimplemented"}];
1128 }
1129 OctagonSignpostEnd(signPost, OctagonSignpostNameCopyViewUnawarePeerInfo, OctagonSignpostNumber1(OctagonSignpostNameCopyViewUnawarePeerInfo), (int)subTaskSuccess);
1130 return nil;
1131 }
1132 }
1133
1134 - (BOOL)viewSet:(NSSet*)enabledViews disabledViews:(NSSet*)disabledViews
1135 {
1136 secnotice("clique-legacy", "viewSet for context:%@, altdsid:%@", self.ctx.context, self.ctx.altDSID);
1137 OctagonSignpost signPost = OctagonSignpostBegin(OctagonSignpostNameViewSet);
1138 bool subTaskSuccess = false;
1139
1140 if([OTClique platformSupportsSOS]) {
1141 bool result = false;
1142 if(self.ctx.analytics){
1143 NSError* encodingError = nil;
1144 NSData* analyticsData = [NSKeyedArchiver archivedDataWithRootObject:self.ctx.analytics requiringSecureCoding:YES error:&encodingError];
1145 if(!encodingError && analyticsData){
1146 result = SOSCCViewSetWithAnalytics((__bridge CFSetRef)enabledViews, (__bridge CFSetRef)disabledViews, (__bridge CFDataRef)analyticsData);
1147 }else{
1148 result = SOSCCViewSet((__bridge CFSetRef)enabledViews, (__bridge CFSetRef)disabledViews);
1149 }
1150 }else{
1151 result = SOSCCViewSet((__bridge CFSetRef)enabledViews, (__bridge CFSetRef)disabledViews);
1152 }
1153
1154 BOOL viewSetResult = result ? YES : NO;
1155 subTaskSuccess = result;
1156 OctagonSignpostEnd(signPost, OctagonSignpostNameViewSet, OctagonSignpostNumber1(OctagonSignpostNameViewSet), (int)subTaskSuccess);
1157 return viewSetResult;
1158 } else {
1159 secnotice("clique-legacy", "SOS disabled for this platform, returning NO");
1160 OctagonSignpostEnd(signPost, OctagonSignpostNameViewSet, OctagonSignpostNumber1(OctagonSignpostNameViewSet), (int)subTaskSuccess);
1161 return NO;
1162 }
1163 }
1164
1165 - (BOOL)setUserCredentialsAndDSID:(NSString*)userLabel
1166 password:(NSData*)userPassword
1167 error:(NSError *__autoreleasing*)error
1168 {
1169 secnotice("clique-legacy", "setUserCredentialsAndDSID for context:%@, altdsid:%@", self.ctx.context, self.ctx.altDSID);
1170 OctagonSignpost signPost = OctagonSignpostBegin(OctagonSignpostNameSetUserCredentialsAndDSID);
1171 bool subTaskSuccess = false;
1172
1173 if([OTClique platformSupportsSOS]) {
1174 CFErrorRef setCredentialsErrorRef = NULL;
1175 bool result = false;
1176 if(self.ctx.analytics){
1177 NSError* encodingError = nil;
1178 NSData* analyticsData = [NSKeyedArchiver archivedDataWithRootObject:self.ctx.analytics requiringSecureCoding:YES error:&encodingError];
1179 if(!encodingError && analyticsData){
1180 result = SOSCCSetUserCredentialsAndDSIDWithAnalytics((__bridge CFStringRef)userLabel,
1181 (__bridge CFDataRef)userPassword,
1182 (__bridge CFStringRef)self.ctx.dsid,
1183 (__bridge CFDataRef)analyticsData,
1184 &setCredentialsErrorRef);
1185 }else{
1186 result = SOSCCSetUserCredentialsAndDSID((__bridge CFStringRef)userLabel,
1187 (__bridge CFDataRef)userPassword,
1188 (__bridge CFStringRef)self.ctx.dsid,
1189 &setCredentialsErrorRef);
1190 }
1191 }else{
1192 result = SOSCCSetUserCredentialsAndDSID((__bridge CFStringRef)userLabel,
1193 (__bridge CFDataRef)userPassword,
1194 (__bridge CFStringRef)self.ctx.dsid,
1195 &setCredentialsErrorRef);
1196 }
1197
1198 BOOL setCredentialsResult = result ? YES : NO;
1199 secnotice("clique-legacy", "setUserCredentialsAndDSID results: %d %@", setCredentialsResult, setCredentialsErrorRef);
1200 if (error) {
1201 *error = (NSError*)CFBridgingRelease(setCredentialsErrorRef);
1202 } else {
1203 CFBridgingRelease(setCredentialsErrorRef);
1204 }
1205 subTaskSuccess = result;
1206 OctagonSignpostEnd(signPost, OctagonSignpostNameSetUserCredentialsAndDSID, OctagonSignpostNumber1(OctagonSignpostNameSetUserCredentialsAndDSID), (int)subTaskSuccess);
1207
1208 return setCredentialsResult;
1209 } else {
1210 secnotice("clique-legacy", "SOS disabled for this platform, returning NO");
1211 if(error){
1212 *error = [NSError errorWithDomain:NSOSStatusErrorDomain
1213 code:errSecUnimplemented
1214 userInfo:@{NSLocalizedDescriptionKey: @"set user credentials unimplemented"}];
1215 }
1216 OctagonSignpostEnd(signPost, OctagonSignpostNameSetUserCredentialsAndDSID, OctagonSignpostNumber1(OctagonSignpostNameSetUserCredentialsAndDSID), (int)subTaskSuccess);
1217 return NO;
1218 }
1219 }
1220
1221 - (BOOL)tryUserCredentialsAndDSID:(NSString*)userLabel
1222 password:(NSData*)userPassword
1223 error:(NSError *__autoreleasing*)error
1224 {
1225 secnotice("clique-legacy", "tryUserCredentialsAndDSID for context:%@, altdsid:%@", self.ctx.context, self.ctx.altDSID);
1226 OctagonSignpost signPost = OctagonSignpostBegin(OctagonSignpostNameTryUserCredentialsAndDSID);
1227 bool subTaskSuccess = false;
1228
1229 if([OTClique platformSupportsSOS]) {
1230 CFErrorRef tryCredentialsErrorRef = NULL;
1231 bool result = SOSCCTryUserCredentialsAndDSID((__bridge CFStringRef)userLabel,
1232 (__bridge CFDataRef)userPassword,
1233 (__bridge CFStringRef)self.ctx.dsid,
1234 &tryCredentialsErrorRef);
1235
1236 BOOL tryCredentialsResult = result ? YES : NO;
1237 secnotice("clique-legacy", "tryUserCredentialsAndDSID results: %d %@", tryCredentialsResult, tryCredentialsErrorRef);
1238 if (error) {
1239 *error = (NSError*)CFBridgingRelease(tryCredentialsErrorRef);
1240 } else {
1241 CFBridgingRelease(tryCredentialsErrorRef);
1242 }
1243 subTaskSuccess = result;
1244 OctagonSignpostEnd(signPost, OctagonSignpostNameTryUserCredentialsAndDSID, OctagonSignpostNumber1(OctagonSignpostNameTryUserCredentialsAndDSID), (int)subTaskSuccess);
1245 return tryCredentialsResult;
1246
1247 } else {
1248 secnotice("clique-legacy", "SOS disabled for this platform, returning NO");
1249 if(error){
1250 *error = [NSError errorWithDomain:NSOSStatusErrorDomain
1251 code:errSecUnimplemented
1252 userInfo:@{NSLocalizedDescriptionKey: @"try user credentials unimplemented"}];
1253 }
1254 OctagonSignpostEnd(signPost, OctagonSignpostNameTryUserCredentialsAndDSID, OctagonSignpostNumber1(OctagonSignpostNameTryUserCredentialsAndDSID), (int)subTaskSuccess);
1255 return NO;
1256 }
1257 }
1258
1259 - (NSArray* _Nullable)copyPeerPeerInfo:(NSError *__autoreleasing*)error
1260 {
1261 secnotice("clique-legacy", "copyPeerPeerInfo for context:%@, altdsid:%@", self.ctx.context, self.ctx.altDSID);
1262 OctagonSignpost signPost = OctagonSignpostBegin(OctagonSignpostNameCopyPeerPeerInfo);
1263 bool subTaskSuccess = false;
1264
1265 if([OTClique platformSupportsSOS]) {
1266 CFErrorRef copyPeerErrorRef = NULL;
1267 CFArrayRef result = SOSCCCopyPeerPeerInfo(&copyPeerErrorRef);
1268
1269 NSArray* peerList = (result ? (NSArray*)(CFBridgingRelease(result)) : nil);
1270
1271 secnotice("clique-legacy", "copyPeerPeerInfo results: %@ (%@)", peerList, copyPeerErrorRef);
1272 if (error) {
1273 *error = (NSError*)CFBridgingRelease(copyPeerErrorRef);
1274 } else {
1275 CFBridgingRelease(copyPeerErrorRef);
1276 }
1277 subTaskSuccess = (peerList != nil) ? true : false;
1278 OctagonSignpostEnd(signPost, OctagonSignpostNameCopyPeerPeerInfo, OctagonSignpostNumber1(OctagonSignpostNameCopyPeerPeerInfo), (int)subTaskSuccess);
1279 return peerList;
1280 } else {
1281 secnotice("clique-legacy", "SOS disabled for this platform, returning NO");
1282 if(error){
1283 *error = [NSError errorWithDomain:NSOSStatusErrorDomain
1284 code:errSecUnimplemented
1285 userInfo:@{NSLocalizedDescriptionKey: @"copy peer peer info unimplemented"}];
1286 }
1287 OctagonSignpostEnd(signPost, OctagonSignpostNameCopyPeerPeerInfo, OctagonSignpostNumber1(OctagonSignpostNameCopyPeerPeerInfo), (int)subTaskSuccess);
1288 return nil;
1289 }
1290 }
1291
1292 - (BOOL)peersHaveViewsEnabled:(NSArray<NSString*>*)viewNames error:(NSError *__autoreleasing*)error
1293 {
1294 secnotice("clique-legacy", "peersHaveViewsEnabled for context:%@, altdsid:%@", self.ctx.context, self.ctx.altDSID);
1295 OctagonSignpost signPost = OctagonSignpostBegin(OctagonSignpostNamePeersHaveViewsEnabled);
1296 bool subTaskSuccess = false;
1297
1298 if([OTClique platformSupportsSOS]) {
1299 CFErrorRef viewsEnabledErrorRef = NULL;
1300 BOOL viewsEnabledResult = NO;
1301
1302 CFBooleanRef result = SOSCCPeersHaveViewsEnabled((__bridge CFArrayRef)viewNames, &viewsEnabledErrorRef);
1303 if(result){
1304 viewsEnabledResult = CFBooleanGetValue(result) ? YES : NO;
1305 }
1306 secnotice("clique-legacy", "peersHaveViewsEnabled results: %@ (%@)", viewsEnabledResult ? @"YES" : @"NO",
1307 viewsEnabledErrorRef);
1308 if (error) {
1309 *error = (NSError*)CFBridgingRelease(viewsEnabledErrorRef);
1310 } else {
1311 CFBridgingRelease(viewsEnabledErrorRef);
1312 }
1313 subTaskSuccess = viewsEnabledResult ? true : false;
1314 OctagonSignpostEnd(signPost, OctagonSignpostNamePeersHaveViewsEnabled, OctagonSignpostNumber1(OctagonSignpostNamePeersHaveViewsEnabled), (int)subTaskSuccess);
1315 return viewsEnabledResult;
1316 } else {
1317 secnotice("clique-legacy", "SOS disabled for this platform, returning NO");
1318 if(error){
1319 *error = [NSError errorWithDomain:NSOSStatusErrorDomain
1320 code:errSecUnimplemented
1321 userInfo:@{NSLocalizedDescriptionKey: @"peers have views enabled unimplemented"}];
1322 }
1323 OctagonSignpostEnd(signPost, OctagonSignpostNamePeersHaveViewsEnabled, OctagonSignpostNumber1(OctagonSignpostNamePeersHaveViewsEnabled), (int)subTaskSuccess);
1324 return NO;
1325 }
1326 }
1327
1328 - (BOOL)requestToJoinCircle:(NSError *__autoreleasing*)error
1329 {
1330 bool result = false;
1331 bool subTaskSuccess = false;
1332 OctagonSignpost signPost = OctagonSignpostBegin(OctagonSignpostNameRequestToJoinCircle);
1333
1334 #if OCTAGON
1335 secnotice("clique-legacy", "requestToJoinCircle for context:%@, altdsid:%@", self.ctx.context, self.ctx.altDSID);
1336
1337 if(OctagonIsEnabled()) {
1338 // Sometimes, CoreCDP calls this to cause a circle creation to occur.
1339 // So, for Octagon, we might want to request a establish, but not a reset.
1340
1341 // Fetch the current trust status, so we know if we should fire off the establish.
1342 NSError* localError = nil;
1343 CliqueStatus status = [self fetchCliqueStatus: &localError];
1344
1345 if(localError) {
1346 secnotice("clique-legacy", "fetching clique status failed: %@", localError);
1347 if(error) {
1348 *error = localError;
1349 }
1350 OctagonSignpostEnd(signPost, OctagonSignpostNameRequestToJoinCircle, OctagonSignpostNumber1(OctagonSignpostNameRequestToJoinCircle), (int)subTaskSuccess);
1351 return NO;
1352 }
1353
1354 if(status == CliqueStatusAbsent) {
1355 secnotice("clique-legacy", "clique status is %@; beginning an establish", OTCliqueStatusToString(status));
1356 [self establish:&localError];
1357
1358 if(localError) {
1359 if(error) {
1360 *error = localError;
1361 }
1362 OctagonSignpostEnd(signPost, OctagonSignpostNameRequestToJoinCircle, OctagonSignpostNumber1(OctagonSignpostNameRequestToJoinCircle), (int)subTaskSuccess);
1363 return NO;
1364 } else {
1365 secnotice("clique-legacy", "establish succeeded");
1366 }
1367 } else {
1368 secnotice("clique-legacy", "clique status is %@; performing no Octagon actions", OTCliqueStatusToString(status));
1369 }
1370
1371 // If we didn't early-exit, and we aren't going to invoke SOS below, we succeeded.
1372 if(!OctagonPlatformSupportsSOS()) {
1373 secnotice("clique-legacy", "requestToJoinCircle platform does not support SOS");
1374 subTaskSuccess = true;
1375 OctagonSignpostEnd(signPost, OctagonSignpostNameRequestToJoinCircle, OctagonSignpostNumber1(OctagonSignpostNameRequestToJoinCircle), (int)subTaskSuccess);
1376 return YES;
1377 }
1378 }
1379 #endif // OCTAGON
1380
1381 if([OTClique platformSupportsSOS]) {
1382 NSData* analyticsData = nil;
1383 CFErrorRef joinErrorRef = NULL;
1384 if(self.ctx.analytics){
1385 NSError* encodingError = nil;
1386 analyticsData = [NSKeyedArchiver archivedDataWithRootObject:self.ctx.analytics requiringSecureCoding:YES error:&encodingError];
1387 }
1388
1389 if(analyticsData){
1390 result = SOSCCRequestToJoinCircleWithAnalytics((__bridge CFDataRef)analyticsData, &joinErrorRef);
1391 } else {
1392 result = SOSCCRequestToJoinCircle(&joinErrorRef);
1393 }
1394
1395 secnotice("clique-legacy", "sos requestToJoinCircle complete: %d %@", result, joinErrorRef);
1396 if (error) {
1397 *error = (NSError*)CFBridgingRelease(joinErrorRef);
1398 } else {
1399 CFBridgingRelease(joinErrorRef);
1400 }
1401 }
1402
1403 subTaskSuccess = result;
1404 OctagonSignpostEnd(signPost, OctagonSignpostNameRequestToJoinCircle, OctagonSignpostNumber1(OctagonSignpostNameRequestToJoinCircle), (int)subTaskSuccess);
1405
1406 return result ? YES : NO;
1407 }
1408
1409 - (BOOL)accountUserKeyAvailable
1410 {
1411 secnotice("clique-legacy", "accountUserKeyAvailable for context:%@, altdsid:%@", self.ctx.context, self.ctx.altDSID);
1412 OctagonSignpost signPost = OctagonSignpostBegin(OctagonSignpostNameAccountUserKeyAvailable);
1413 bool subTaskSuccess = false;
1414
1415 if([OTClique platformSupportsSOS]) {
1416 BOOL canAuthenticate = SOSCCCanAuthenticate(NULL) ? YES : NO;
1417 if (canAuthenticate == NO) {
1418 secnotice("clique-legacy", "Security requires credentials...");
1419 }
1420 subTaskSuccess = canAuthenticate ? true : false;
1421 OctagonSignpostEnd(signPost, OctagonSignpostNameAccountUserKeyAvailable, OctagonSignpostNumber1(OctagonSignpostNameAccountUserKeyAvailable), (int)subTaskSuccess);
1422 return canAuthenticate;
1423 } else {
1424 secnotice("clique-legacy", "SOS disabled for this platform, returning NO");
1425 OctagonSignpostEnd(signPost, OctagonSignpostNameAccountUserKeyAvailable, OctagonSignpostNumber1(OctagonSignpostNameAccountUserKeyAvailable), (int)subTaskSuccess);
1426 return NO;
1427 }
1428 }
1429
1430 // MARK: SBD interfaces
1431 + (OTBottleIDs* _Nullable)findOptimalBottleIDsWithContextData:(OTConfigurationContext*)data
1432 error:(NSError**)error
1433 {
1434 #if OCTAGON
1435 secnotice("clique-findbottle", "finding optimal bottles for context:%@, altdsid:%@", data.context, data.altDSID);
1436 OctagonSignpost signPost = OctagonSignpostBegin(OctagonSignpostNameFindOptimalBottleIDsWithContextData);
1437 bool subTaskSuccess = false;
1438
1439 if(OctagonIsEnabled()) {
1440 __block NSError* localError = nil;
1441 __block NSArray<NSString*>* localViableBottleIDs = nil;
1442 __block NSArray<NSString*>* localPartiallyViableBottleIDs = nil;
1443
1444 OTControl *control = [data makeOTControl:&localError];
1445 if (!control) {
1446 secnotice("clique-findbottle", "unable to create otcontrol: %@", localError);
1447 if (error) {
1448 *error = localError;
1449 }
1450 OctagonSignpostEnd(signPost, OctagonSignpostNameFindOptimalBottleIDsWithContextData, OctagonSignpostNumber1(OctagonSignpostNameFindOptimalBottleIDsWithContextData), (int)subTaskSuccess);
1451 return nil;
1452 }
1453 [control fetchAllViableBottles:OTCKContainerName
1454 context:data.context
1455 reply:^(NSArray<NSString *> * _Nullable sortedBottleIDs,
1456 NSArray<NSString*> * _Nullable sortedPartialBottleIDs,
1457 NSError * _Nullable fetchError) {
1458 if(fetchError) {
1459 secnotice("clique-findbottle", "findOptimalBottleIDsWithContextData errored: %@", fetchError);
1460 } else {
1461 secnotice("clique-findbottle", "findOptimalBottleIDsWithContextData succeeded: %@, %@", sortedBottleIDs, sortedPartialBottleIDs);
1462 }
1463 localError = fetchError;
1464 localViableBottleIDs = sortedBottleIDs;
1465 localPartiallyViableBottleIDs = sortedPartialBottleIDs;
1466 }];
1467
1468 if(error && localError) {
1469 *error = localError;
1470 }
1471 OTBottleIDs* bottleIDs = [[OTBottleIDs alloc] init];
1472 bottleIDs.preferredBottleIDs = localViableBottleIDs;
1473 bottleIDs.partialRecoveryBottleIDs = localPartiallyViableBottleIDs;
1474
1475 secnotice("clique-findbottle", "findOptimalBottleIDsWithContextData complete");
1476
1477 subTaskSuccess = (localError == nil) ? true : false;
1478 OctagonSignpostEnd(signPost, OctagonSignpostNameFindOptimalBottleIDsWithContextData, OctagonSignpostNumber1(OctagonSignpostNameFindOptimalBottleIDsWithContextData), (int)subTaskSuccess);
1479 return bottleIDs;
1480 } else {
1481 // With octagon off, fail with 'unimplemented'
1482 if(error) {
1483 *error = [NSError errorWithDomain:NSOSStatusErrorDomain
1484 code:errSecUnimplemented
1485 userInfo:@{NSLocalizedDescriptionKey: @"optimal bottle IDs unimplemented"}];
1486 }
1487 OctagonSignpostEnd(signPost, OctagonSignpostNameFindOptimalBottleIDsWithContextData, OctagonSignpostNumber1(OctagonSignpostNameFindOptimalBottleIDsWithContextData), (int)subTaskSuccess);
1488 return nil;
1489 }
1490 #else // !OCTAGON
1491 if (error) {
1492 *error = [NSError errorWithDomain:NSOSStatusErrorDomain code:errSecUnimplemented userInfo:nil];
1493 }
1494 return NULL;
1495 #endif
1496 }
1497
1498 + (OTClique* _Nullable)recoverWithContextData:(OTConfigurationContext*)data
1499 bottleID:(NSString*)bottleID
1500 escrowedEntropy:(NSData*)entropy
1501 error:(NSError**)error
1502 {
1503 #if OCTAGON
1504 secnotice("octagon", "replaced by performEscrowRecoveryWithContextData:escrowArguments:error: remove call");
1505 return nil;
1506 #else // !OCTAGON
1507 if (error) {
1508 *error = [NSError errorWithDomain:NSOSStatusErrorDomain code:errSecUnimplemented userInfo:nil];
1509 }
1510 return NULL;
1511 #endif
1512 }
1513
1514 // used by sbd to fill in the escrow record
1515 // TODO: what extra entitlement do you need to call this?
1516 - (void)fetchEscrowContents:(void (^)(NSData* _Nullable entropy,
1517 NSString* _Nullable bottleID,
1518 NSData* _Nullable signingPublicKey,
1519 NSError* _Nullable error))reply
1520 {
1521 #if OCTAGON
1522 secnotice("clique-fetchescrow", "fetching entropy for bottling for context:%@, altdsid:%@", self.ctx.context, self.ctx.altDSID);
1523 OctagonSignpost signPost = OctagonSignpostBegin(OctagonSignpostNameFetchEscrowContents);
1524 __block bool subTaskSuccess = false;
1525 if(OctagonIsEnabled()) {
1526 NSError* controlError = nil;
1527 OTControl* control = [self makeOTControl:&controlError];
1528 if (!control) {
1529 OctagonSignpostEnd(signPost, OctagonSignpostNameFetchEscrowContents, OctagonSignpostNumber1(OctagonSignpostNameFetchEscrowContents), (int)subTaskSuccess);
1530 reply(nil, nil, nil, controlError);
1531 return;
1532 }
1533 [control fetchEscrowContents:OTCKContainerName
1534 contextID:self.ctx.context
1535 reply:^(NSData * _Nullable entropy,
1536 NSString * _Nullable bottleID,
1537 NSData * _Nullable signingPublicKey,
1538 NSError * _Nullable error) {
1539 if(error){
1540 secnotice("clique-fetchescrow", "fetchEscrowContents errored: %@", error);
1541 } else{
1542 secnotice("clique-fetchescrow","fetchEscrowContents succeeded");
1543 }
1544 subTaskSuccess = (error == nil) ? true : false;
1545 OctagonSignpostEnd(signPost, OctagonSignpostNameFetchEscrowContents, OctagonSignpostNumber1(OctagonSignpostNameFetchEscrowContents), (int)subTaskSuccess);
1546 reply (entropy, bottleID, signingPublicKey, error);
1547 }];
1548 } else {
1549 // With octagon off, fail with 'unimplemented'
1550 OctagonSignpostEnd(signPost, OctagonSignpostNameFetchEscrowContents, OctagonSignpostNumber1(OctagonSignpostNameFetchEscrowContents), (int)subTaskSuccess);
1551 reply(nil, nil, nil, [NSError errorWithDomain:NSOSStatusErrorDomain
1552 code:errSecUnimplemented
1553 userInfo:@{NSLocalizedDescriptionKey: @"fetchEscrowRecordContents unimplemented"}]);
1554 }
1555 #else // !OCTAGON
1556 reply(nil, nil, nil, [NSError errorWithDomain:NSOSStatusErrorDomain code:errSecUnimplemented userInfo:nil]);
1557 #endif
1558 }
1559
1560 + (void)setNewRecoveryKeyWithData:(OTConfigurationContext *)ctx
1561 recoveryKey:(NSString*)recoveryKey reply:(nonnull void (^)(SecRecoveryKey *rk, NSError *error))reply
1562 {
1563 #if OCTAGON
1564 secnotice("octagon-setrecoverykey", "setNewRecoveryKeyWithData invoked for context: %@", ctx.context);
1565 //set the recovery key for SOS
1566 NSError* createRecoveryKeyError = nil;
1567 NSMutableDictionary *userInfo = [NSMutableDictionary new];
1568 NSError* retError = nil;
1569 OctagonSignpost signPost = OctagonSignpostBegin(OctagonSignpostNameSetNewRecoveryKeyWithData);
1570 __block bool subTaskSuccess = false;
1571
1572 SecRecoveryKey *rk = SecRKCreateRecoveryKeyWithError(recoveryKey, &createRecoveryKeyError);
1573 if (rk == nil) {
1574 secerror("octagon-setrecoverykey, SecRKCreateRecoveryKeyWithError() failed: %@", createRecoveryKeyError);
1575 userInfo[NSLocalizedDescriptionKey] = @"SecRKCreateRecoveryKeyWithError() failed";
1576 userInfo[NSUnderlyingErrorKey] = createRecoveryKeyError;
1577 retError = [NSError errorWithDomain:getkSecureBackupErrorDomain() code:kSecureBackupInternalError userInfo:userInfo];
1578 OctagonSignpostEnd(signPost, OctagonSignpostNameSetNewRecoveryKeyWithData, OctagonSignpostNumber1(OctagonSignpostNameSetNewRecoveryKeyWithData), (int)subTaskSuccess);
1579 reply(nil, retError);
1580 return;
1581 }
1582 if([OTClique platformSupportsSOS]) {
1583 CFErrorRef registerError = nil;
1584 if (!SecRKRegisterBackupPublicKey(rk, &registerError)) {
1585 secerror("octagon-setrecoverykey, SecRKRegisterBackupPublicKey() failed: %@", registerError);
1586 NSError *underlyingError = CFBridgingRelease(registerError);
1587 userInfo[NSLocalizedDescriptionKey] = @"SecRKRegisterBackupPublicKey() failed";
1588 userInfo[NSUnderlyingErrorKey] = underlyingError;
1589 retError = [NSError errorWithDomain:getkSecureBackupErrorDomain() code:kSecureBackupInternalError userInfo:userInfo];
1590 OctagonSignpostEnd(signPost, OctagonSignpostNameSetNewRecoveryKeyWithData, OctagonSignpostNumber1(OctagonSignpostNameSetNewRecoveryKeyWithData), (int)subTaskSuccess);
1591 reply(nil,retError);
1592 return;
1593 } else {
1594 secnotice("octagon-setrecoverykey", "successfully registered recovery key for SOS");
1595 }
1596 }
1597
1598 //set the recovery key for Octagon
1599 if(OctagonRecoveryKeyIsEnabled()) {
1600 NSError* controlError = nil;
1601 OTControl* control = [ctx makeOTControl:&controlError];
1602 if(!control) {
1603 secnotice("octagon-setrecoverykey", "failed to fetch OTControl object: %@", controlError);
1604 OctagonSignpostEnd(signPost, OctagonSignpostNameSetNewRecoveryKeyWithData, OctagonSignpostNumber1(OctagonSignpostNameSetNewRecoveryKeyWithData), (int)subTaskSuccess);
1605 reply(nil, controlError);
1606 return;
1607 }
1608 [control createRecoveryKey:OTCKContainerName contextID:ctx.context recoveryKey:recoveryKey reply:^(NSError * createError) {
1609 if(createError){
1610 secerror("octagon-setrecoverykey, failed to create octagon recovery key");
1611 OctagonSignpostEnd(signPost, OctagonSignpostNameSetNewRecoveryKeyWithData, OctagonSignpostNumber1(OctagonSignpostNameSetNewRecoveryKeyWithData), (int)subTaskSuccess);
1612 reply(nil, createError);
1613 return;
1614 } else {
1615 secnotice("octagon-setrecoverykey", "successfully set octagon recovery key");
1616 subTaskSuccess = true;
1617 OctagonSignpostEnd(signPost, OctagonSignpostNameSetNewRecoveryKeyWithData, OctagonSignpostNumber1(OctagonSignpostNameSetNewRecoveryKeyWithData), (int)subTaskSuccess);
1618 reply(rk, nil);
1619 return;
1620 }
1621 }];
1622 }
1623 #else // !OCTAGON
1624 reply(nil, [NSError errorWithDomain:NSOSStatusErrorDomain code:errSecUnimplemented userInfo:nil]);
1625 #endif
1626 }
1627
1628 + (void)recoverOctagonUsingData:(OTConfigurationContext *)ctx
1629 recoveryKey:(NSString*)recoveryKey
1630 reply:(void(^)(NSError* _Nullable error))reply
1631 {
1632 #if OCTAGON
1633 OctagonSignpost signpost = OctagonSignpostBegin(OctagonSignpostNameRecoverOctagonUsingData);
1634 __block bool subTaskSuccess = false;
1635
1636 if(OctagonRecoveryKeyIsEnabled()) {
1637 NSError* controlError = nil;
1638 OTControl* control = [ctx makeOTControl:&controlError];
1639
1640 secnotice("clique-recoverykey", "join using recovery key");
1641
1642 if(!control) {
1643 secnotice("clique-recoverykey", "failed to fetch OTControl object: %@", controlError);
1644 OctagonSignpostEnd(signpost, OctagonSignpostNameRecoverOctagonUsingData, OctagonSignpostNumber1(OctagonSignpostNameRecoverOctagonUsingData), (int)subTaskSuccess);
1645 reply(controlError);
1646 return;
1647 }
1648 [control joinWithRecoveryKey:OTCKContainerName contextID:ctx.context recoveryKey:recoveryKey reply:^(NSError *joinError) {
1649 if(joinError){
1650 secnotice("clique-recoverykey", "failed to join using recovery key: %@", joinError);
1651 OctagonSignpostEnd(signpost, OctagonSignpostNameRecoverOctagonUsingData, OctagonSignpostNumber1(OctagonSignpostNameRecoverOctagonUsingData), (int)subTaskSuccess);
1652 reply(joinError);
1653 return;
1654 }
1655 secnotice("clique-recoverykey", "successfully joined using recovery key");
1656 subTaskSuccess = true;
1657 OctagonSignpostEnd(signpost, OctagonSignpostNameRecoverOctagonUsingData, OctagonSignpostNumber1(OctagonSignpostNameRecoverOctagonUsingData), (int)subTaskSuccess);
1658 reply(nil);
1659 }];
1660 }
1661
1662 #else // !OCTAGON
1663 reply([NSError errorWithDomain:NSOSStatusErrorDomain code:errSecUnimplemented userInfo:nil]);
1664 #endif
1665 }
1666
1667 - (void)performedCDPStateMachineRun:(OTCliqueCDPContextType)type
1668 success:(BOOL)success
1669 error:(NSError * _Nullable)error
1670 reply:(void(^)(NSError* _Nullable error))reply
1671 {
1672 #if OCTAGON
1673 OctagonSignpost signPost = OctagonSignpostBegin(OctagonSignpostNamePerformedCDPStateMachineRun);
1674 NSError* controlError = nil;
1675 __block bool subTaskSuccess = false;
1676
1677 OTControl* control = [self makeOTControl:&controlError];
1678 if(!control) {
1679 secnotice("clique-cdp-sm", "octagon, failed to fetch OTControl object: %@", controlError);
1680 OctagonSignpostEnd(signPost, OctagonSignpostNamePerformedCDPStateMachineRun, OctagonSignpostNumber1(OctagonSignpostNamePerformedCDPStateMachineRun), (int)subTaskSuccess);
1681 reply(controlError);
1682 return;
1683 }
1684
1685 [control postCDPFollowupResult:success type:type error:error containerName:OTCKContainerName contextName:OTDefaultContext reply:^(NSError *postError) {
1686 if(postError){
1687 secnotice("clique-cdp-sm", "failed to post %@ result: %@ ", type, postError);
1688 OctagonSignpostEnd(signPost, OctagonSignpostNamePerformedCDPStateMachineRun, OctagonSignpostNumber1(OctagonSignpostNamePerformedCDPStateMachineRun), (int)subTaskSuccess);
1689 reply(postError);
1690 return;
1691 }
1692 if (success) {
1693 secnotice("clique-cdp-sm", "posted success: %@", type);
1694 } else {
1695 secnotice("clique-cdp-sm", "posted error: %@: %@", type, error);
1696 }
1697 subTaskSuccess = success ? true : false;
1698 OctagonSignpostEnd(signPost, OctagonSignpostNamePerformedCDPStateMachineRun, OctagonSignpostNumber1(OctagonSignpostNamePerformedCDPStateMachineRun), (int)subTaskSuccess);
1699 reply(nil);
1700 }];
1701 #else // !OCTAGON
1702 reply([NSError errorWithDomain:NSOSStatusErrorDomain code:errSecUnimplemented userInfo:nil]);
1703 #endif
1704 }
1705
1706 - (BOOL)waitForOctagonUpgrade:(NSError** _Nullable)error
1707 {
1708 #if OCTAGON
1709 OTControl* control = nil;
1710 OctagonSignpost signPost = OctagonSignpostBegin(OctagonSignpostNameWaitForOctagonUpgrade);
1711 __block bool subTaskSuccess = false;
1712
1713 if (!OctagonIsEnabled()) {
1714 secnotice("clique-waitforoctagonupgrade", "cannot upgrade, octagon is not enabled");
1715 if (error) {
1716 *error = [NSError errorWithDomain:NSOSStatusErrorDomain code:errSecUnimplemented userInfo:@{NSLocalizedDescriptionKey: @"Octagon is not enabled"}];
1717 }
1718 OctagonSignpostEnd(signPost, OctagonSignpostNameWaitForOctagonUpgrade, OctagonSignpostNumber1(OctagonSignpostNameWaitForOctagonUpgrade), (int)subTaskSuccess);
1719
1720 return NO;
1721 }
1722
1723 NSError *controlError = nil;
1724 control = [self makeOTControl:&controlError];
1725 if (!control) {
1726 secnotice("clique-waitforoctagonupgrade", "octagon, failed to fetch OTControl object: %@", controlError);
1727 if (error) {
1728 *error = controlError;
1729 }
1730 OctagonSignpostEnd(signPost, OctagonSignpostNameWaitForOctagonUpgrade, OctagonSignpostNumber1(OctagonSignpostNameWaitForOctagonUpgrade), (int)subTaskSuccess);
1731 return NO;
1732 }
1733
1734 __block BOOL ret = NO;
1735 __block NSError* blockError = nil;
1736
1737 [control waitForOctagonUpgrade:OTCKContainerName context:OTDefaultContext reply:^(NSError *postError) {
1738 if(postError){
1739 secnotice("clique-waitforoctagonupgrade", "error from control: %@", postError);
1740 blockError = postError;
1741 ret = NO;
1742 } else {
1743 secnotice("clique-waitforoctagonupgrade", "successfully upgraded to octagon");
1744 ret = YES;
1745 }
1746 }];
1747
1748 if (blockError && error) {
1749 *error = blockError;
1750 }
1751 subTaskSuccess = ret ? true : false;
1752 OctagonSignpostEnd(signPost, OctagonSignpostNameWaitForOctagonUpgrade, OctagonSignpostNumber1(OctagonSignpostNameWaitForOctagonUpgrade), (int)subTaskSuccess);
1753 return ret;
1754 #else // !OCTAGON
1755 if(error) {
1756 *error = [NSError errorWithDomain:NSOSStatusErrorDomain code:errSecUnimplemented userInfo:nil];
1757 }
1758 return NO;
1759 #endif
1760 }
1761
1762 - (void)performedFailureCDPStateMachineRun:(OTCliqueCDPContextType)type
1763 error:(NSError * _Nullable)error
1764 reply:(void(^)(NSError* _Nullable error))reply
1765 {
1766 [self performedCDPStateMachineRun:type success:NO error:error reply:reply];
1767 }
1768
1769 - (void)performedSuccessfulCDPStateMachineRun:(OTCliqueCDPContextType)type
1770 reply:(void(^)(NSError* _Nullable error))reply
1771 {
1772 [self performedCDPStateMachineRun:type success:YES error:nil reply:reply];
1773 }
1774
1775 + (BOOL)setCDPEnabled:(OTConfigurationContext*)arguments
1776 error:(NSError* __autoreleasing*)error
1777 {
1778 #if OCTAGON
1779 NSError *controlError = nil;
1780 OTControl* control = [arguments makeOTControl:&controlError];
1781 if (!control) {
1782 secerror("octagon-setcdpenabled: failed to fetch OTControl object: %@", controlError);
1783 if (error) {
1784 *error = controlError;
1785 }
1786 return NO;
1787 }
1788
1789 __block NSError* reterror = nil;
1790
1791 [control setCDPEnabled:nil
1792 contextID:arguments.context
1793 reply:^(NSError * _Nullable resultError) {
1794 if(resultError) {
1795 secnotice("octagon-setcdpenabled", "failed to set CDP bit: %@", resultError);
1796 reterror = resultError;
1797 } else {
1798 secnotice("octagon-setcdpenabled", "successfully set CDP bit");
1799 }
1800 }];
1801
1802 if(reterror && error) {
1803 *error = reterror;
1804 }
1805
1806 return (reterror == nil);
1807 #else // !OCTAGON
1808 if(error) {
1809 *error = [NSError errorWithDomain:NSOSStatusErrorDomain code:errSecUnimplemented userInfo:nil];
1810 }
1811 return NO;
1812 #endif
1813 }
1814
1815 + (OTCDPStatus)getCDPStatus:(OTConfigurationContext*)arguments
1816 error:(NSError* __autoreleasing *)error
1817 {
1818 #if OCTAGON
1819 NSError *controlError = nil;
1820 OTControl* control = [arguments makeOTControl:&controlError];
1821 if (!control) {
1822 secerror("octagon-cdp-status: failed to fetch OTControl object: %@", controlError);
1823 if (error) {
1824 *error = controlError;
1825 }
1826 return OTCDPStatusUnknown;
1827 }
1828
1829 __block NSError* reterror = nil;
1830 __block OTCDPStatus retcdpstatus = OTCDPStatusUnknown;
1831
1832 [control getCDPStatus:nil
1833 contextID:arguments.context
1834 reply:^(OTCDPStatus status, NSError * _Nullable resultError) {
1835 if(resultError) {
1836 secnotice("octagon-cdp-status", "failed to fetch CDP status: %@", resultError);
1837 reterror = resultError;
1838
1839 } else {
1840 secnotice("octagon-cdp-status", "successfully fetched CDP status as %@", OTCDPStatusToString(status));
1841 retcdpstatus = status;
1842 }
1843 }];
1844
1845 if(reterror && error) {
1846 *error = reterror;
1847 }
1848
1849 return retcdpstatus;
1850 #else // !OCTAGON
1851 if(error) {
1852 *error = [NSError errorWithDomain:NSOSStatusErrorDomain code:errSecUnimplemented userInfo:nil];
1853 }
1854 return OTCDPStatusDisabled;
1855 #endif
1856 }
1857
1858 + (OTClique* _Nullable)resetProtectedData:(OTConfigurationContext*)data error:(NSError**)error
1859 {
1860 #if OCTAGON
1861 NSError *controlError = nil;
1862 OTControl *control = [data makeOTControl:&controlError];
1863 if (!control) {
1864 secerror("clique-reset-protected-data: unable to create otcontrol: %@", controlError);
1865 if (error) {
1866 *error = controlError;
1867 }
1868 return nil;
1869 }
1870
1871 __block NSError* localError = nil;
1872
1873 //delete all records
1874 id<OctagonEscrowRecovererPrococol> sb = data.sbd ?: [[getSecureBackupClass() alloc] init];
1875
1876 NSDictionary* deletionInformation = @{ getkSecureBackupAuthenticationAppleID() : data.authenticationAppleID,
1877 getkSecureBackupAuthenticationPassword() : data.passwordEquivalentToken,
1878 getkSecureBackupiCloudDataProtectionDeleteAllRecordsKey() : @YES,
1879 getkSecureBackupContainsiCDPDataKey() : @YES};
1880
1881 NSError* sbError = [sb disableWithInfo:deletionInformation];
1882 if(sbError) {
1883 secerror("clique-reset-protected-data: secure backup escrow record deletion failed: %@", sbError);
1884 if(error) {
1885 *error = sbError;
1886 }
1887 return nil;
1888 } else {
1889 secnotice("clique-reset-protected-data", "sbd disableWithInfo succeeded");
1890 }
1891
1892 //reset sos
1893 if(OctagonPlatformSupportsSOS()) {
1894 //reset SOS
1895 CFErrorRef sosError = NULL;
1896 bool resetSuccess = SOSCCResetToOffering(&sosError);
1897
1898 if(sosError || !resetSuccess) {
1899 secerror("clique-reset-protected-data: sos reset failed: %@", sosError);
1900 if(error) {
1901 *error = (NSError*)CFBridgingRelease(sosError);
1902 }
1903 return nil;
1904 } else {
1905 secnotice("clique-reset-protected-data", "sos reset succeeded");
1906 }
1907 } else {
1908 secnotice("clique-reset-protected-data", "platform does not support sos");
1909 }
1910
1911 //reset octagon
1912 OTClique* clique = [[OTClique alloc] initWithContextData:data];
1913 if(OctagonIsEnabled()) {
1914 [clique resetAndEstablish:CuttlefishResetReasonUserInitiatedReset error:&localError];
1915
1916 if(localError) {
1917 secerror("clique-reset-protected-data: account reset failed: %@", localError);
1918 if(error) {
1919 *error = localError;
1920 }
1921 return nil;
1922 } else {
1923 secnotice("clique-reset-protected-data", "Octagon account reset succeeded");
1924 }
1925 }
1926
1927 //reset ckks
1928 CKKSControl* ckksControl = [data makeCKKSControl:&localError];
1929 [ckksControl rpcResetCloudKit:nil reason:@"clique-reset-protected-data" reply:^(NSError* resetError){
1930 localError = resetError;
1931 }];
1932
1933 if(localError) {
1934 secerror("clique-reset-protected-data: ckks cloudkit reset failed: %@", localError);
1935 if(error) {
1936 *error = localError;
1937 }
1938 return nil;
1939 } else {
1940 secnotice("clique-reset-protected-data", "ckks cloudkit reset succeeded");
1941 }
1942
1943 return clique;
1944 #else // !OCTAGON
1945 if(error) {
1946 *error = [NSError errorWithDomain:NSOSStatusErrorDomain code:errSecUnimplemented userInfo:nil];
1947 }
1948 return nil;
1949 #endif
1950
1951 }
1952
1953 @end
1954
1955 #endif /* OBJC2 */