]> git.saurik.com Git - apple/security.git/blob - keychain/ot/OTClique.m
Security-59306.11.20.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
33 #import <utilities/SecCFWrappers.h>
34 #import <utilities/debugging.h>
35
36 #import "keychain/SecureObjectSync/SOSCloudCircle.h"
37 #import "KeychainCircle/PairingChannel.h"
38 #import <Security/SecBase.h>
39
40 const NSString* kSecEntitlementPrivateOctagonEscrow = @"com.apple.private.octagon.escrow-content";
41
42 #if OCTAGON
43 #import <AuthKit/AuthKit.h>
44 #import <AuthKit/AuthKit_Private.h>
45 #import <SoftLinking/SoftLinking.h>
46 #import <CloudServices/SecureBackup.h>
47 #import <CloudServices/SecureBackupConstants.h>
48 #import "keychain/ot/OTControl.h"
49 #import "keychain/ot/categories/OctagonEscrowRecoverer.h"
50
51 SOFT_LINK_FRAMEWORK(PrivateFrameworks, KeychainCircle);
52 SOFT_LINK_FRAMEWORK(PrivateFrameworks, CloudServices);
53
54 #pragma clang diagnostic push
55 #pragma clang diagnostic ignored "-Wstrict-prototypes"
56 SOFT_LINK_CLASS(KeychainCircle, KCPairingChannel);
57 SOFT_LINK_CLASS(KeychainCircle, OTPairingChannel);
58 SOFT_LINK_CLASS(CloudServices, SecureBackup);
59 SOFT_LINK_CONSTANT(CloudServices, kSecureBackupErrorDomain, NSErrorDomain);
60
61 #pragma clang diagnostic pop
62 #endif
63
64 OTCliqueCFUType OTCliqueCFTypeRepair = @"typeRepair";
65 OTCliqueCFUType OTCliqueCFTypePasscode = @"typePasscode";
66 OTCliqueCFUType OTCliqueCFTypeUpgrade = @"typeUpgrade";
67
68 OTCliqueCDPContextType OTCliqueCDPContextTypeNone = @"cdpContextTypeNone";
69 OTCliqueCDPContextType OTCliqueCDPContextTypeSignIn = @"cdpContextTypeSignIn";
70 OTCliqueCDPContextType OTCliqueCDPContextTypeRepair = @"cdpContextTypeRepair";
71 OTCliqueCDPContextType OTCliqueCDPContextTypeFinishPasscodeChange = @"cdpContextTypeFinishPasscodeChange";
72 OTCliqueCDPContextType OTCliqueCDPContextTypeRecoveryKeyGenerate = @"cdpContextTypeRecoveryKeyGenerate";
73 OTCliqueCDPContextType OTCliqueCDPContextTypeRecoveryKeyNew = @"cdpContextTypeRecoveryKeyNew";
74 OTCliqueCDPContextType OTCliqueCDPContextTypeUpdatePasscode = @"cdpContextTypeUpdatePasscode";
75
76 NSString* OTCliqueStatusToString(CliqueStatus status)
77 {
78 switch(status) {
79 case CliqueStatusIn:
80 return @"CliqueStatusIn";
81 case CliqueStatusNotIn:
82 return @"CliqueStatusNotIn";
83 case CliqueStatusPending:
84 return @"CliqueStatusPending";
85 case CliqueStatusAbsent:
86 return @"CliqueStatusAbsent";
87 case CliqueStatusNoCloudKitAccount:
88 return @"CliqueStatusNoCloudKitAccount";
89 case CliqueStatusError:
90 return @"CliqueStatusError";
91 };
92 }
93 CliqueStatus OTCliqueStatusFromString(NSString* str)
94 {
95 if([str isEqualToString: @"CliqueStatusIn"]) {
96 return CliqueStatusIn;
97 } else if([str isEqualToString: @"CliqueStatusNotIn"]) {
98 return CliqueStatusNotIn;
99 } else if([str isEqualToString: @"CliqueStatusPending"]) {
100 return CliqueStatusPending;
101 } else if([str isEqualToString: @"CliqueStatusAbsent"]) {
102 return CliqueStatusAbsent;
103 } else if([str isEqualToString: @"CliqueStatusNoCloudKitAccount"]) {
104 return CliqueStatusNoCloudKitAccount;
105 } else if([str isEqualToString: @"CliqueStatusError"]) {
106 return CliqueStatusError;
107 }
108
109 return CliqueStatusError;
110 }
111
112
113 @implementation OTConfigurationContext
114 - (OTControl* _Nullable)makeOTControl:(NSError**)error
115 {
116 #if OCTAGON
117 if (self.otControl) {
118 return self.otControl;
119 }
120 return [OTControl controlObject:true error:error];
121 #else
122 return nil;
123 #endif
124 }
125 @end
126
127 @implementation OTBottleIDs
128 @end
129
130 @implementation OTOperationConfiguration
131
132 - (instancetype)init {
133 if ((self = [super init]) == nil) {
134 return nil;
135 }
136 _timeoutWaitForCKAccount = 10 * NSEC_PER_SEC;
137 _qualityOfService = NSQualityOfServiceDefault;
138 _discretionaryNetwork = NO;
139 _useCachedAccountStatus = NO;
140 return self;
141 }
142
143 + (BOOL)supportsSecureCoding {
144 return YES;
145 }
146
147 - (void)encodeWithCoder:(nonnull NSCoder *)coder {
148 [coder encodeObject:@(_timeoutWaitForCKAccount) forKey:@"timeoutWaitForCKAccount"];
149 [coder encodeObject:@(_qualityOfService) forKey:@"qualityOfService"];
150 [coder encodeObject:@(_discretionaryNetwork) forKey:@"discretionaryNetwork"];
151 [coder encodeObject:@(_useCachedAccountStatus) forKey:@"useCachedAccountStatus"];
152 }
153
154 - (nullable instancetype)initWithCoder:(nonnull NSCoder *)coder {
155 _timeoutWaitForCKAccount = [[coder decodeObjectOfClass:[NSNumber class] forKey:@"timeoutWaitForCKAccount"] unsignedLongLongValue];
156 _qualityOfService = [[coder decodeObjectOfClass:[NSNumber class] forKey:@"qualityOfService"] integerValue];
157 _discretionaryNetwork = [[coder decodeObjectOfClass:[NSNumber class] forKey:@"discretionaryNetwork"] boolValue];
158 _useCachedAccountStatus = [[coder decodeObjectOfClass:[NSNumber class] forKey:@"useCachedAccountStatus"] boolValue];
159 return self;
160 }
161
162 @end
163
164
165 @interface OTClique ()
166 @property (nonatomic, copy) NSString* cliqueMemberIdentifier;
167 @property (nonatomic, strong) OTConfigurationContext *ctx;
168 @property (nonatomic, strong) NSMutableDictionary *defaults;
169 @end
170
171 @implementation OTClique
172
173 + (BOOL)platformSupportsSOS
174 {
175 return OctagonPlatformSupportsSOS();
176 }
177
178 // defaults write com.apple.security.octagon enable -bool YES
179 -(BOOL)isOctagonPairingEnabled {
180 BOOL nsDefaults = self.defaults[OTDefaultsOctagonEnable] ? [self.defaults[OTDefaultsOctagonEnable] boolValue] : OctagonIsEnabled();
181 secnotice("octagon", "pairing is %@", nsDefaults ? @"on" : @"off");
182 return nsDefaults;
183 }
184
185 - (void)setPairingDefault:(BOOL)defaults
186 {
187 self.defaults[OTDefaultsOctagonEnable] = @(defaults);
188 }
189
190 - (void)removePairingDefault
191 {
192 [self.defaults removeObjectForKey:OTDefaultsOctagonEnable];
193 }
194
195 - (instancetype)initWithContextData:(OTConfigurationContext *)ctx error:(NSError * __autoreleasing *)error
196 {
197 #if OCTAGON
198 self = [super init];
199 if(self){
200 _ctx = [[OTConfigurationContext alloc]init];
201 _ctx.context = ctx.context ?: OTDefaultContext;
202 _ctx.dsid = [ctx.dsid copy];
203 _ctx.altDSID = [ctx.altDSID copy];
204 _ctx.analytics = ctx.analytics;
205 _ctx.otControl = ctx.otControl;
206
207 self.defaults = [NSMutableDictionary dictionary];
208 }
209 return self;
210 #else
211 NSAssert(false, @"OTClique is not implemented on this platform");
212 return nil;
213 #endif // OCTAGON
214 }
215
216 - (NSString* _Nullable)cliqueMemberIdentifier
217 {
218 #if OCTAGON
219 __block NSString* retPeerID = nil;
220
221 if(OctagonIsEnabled()) {
222 NSError* localError = nil;
223 OTControl* control = [self makeOTControl:&localError];
224 if(!control) {
225 secerror("octagon: Failed to create OTControl: %@", localError);
226 return nil;
227 }
228
229 [control fetchEgoPeerID:nil
230 context:self.ctx.context
231 reply:^(NSString* peerID, NSError* error) {
232 if(error) {
233 secerror("octagon: Failed to fetch octagon peer ID: %@", error);
234 }
235 retPeerID = peerID;
236 }];
237 secnotice("clique", "cliqueMemberIdentifier(octagon) received %@", retPeerID);
238 }
239
240 if(OctagonPlatformSupportsSOS()) {
241 CFErrorRef error = NULL;
242 SOSPeerInfoRef me = SOSCCCopyMyPeerInfo(&error);
243 retPeerID = (NSString*)CFBridgingRelease(CFRetainSafe(SOSPeerInfoGetPeerID(me)));
244 CFReleaseNull(me);
245 }
246
247 secnotice("clique", "cliqueMemberIdentifier complete: %@", retPeerID);
248 return retPeerID;
249 #else
250 return nil;
251 #endif
252 }
253
254 #if OCTAGON
255 - (OTControl* _Nullable)makeOTControl:(NSError**)error
256 {
257 return [self.ctx makeOTControl:error];
258 }
259
260 - (BOOL)establish:(NSError**)error
261 {
262 secnotice("clique-establish", "establish started");
263
264 OTControl* control = [self makeOTControl:error];
265 if(!control) {
266 return false;
267 }
268
269 __block BOOL success = NO;
270 __block NSError* localError = nil;
271 //only establish
272 [control establish:nil context:self.ctx.context altDSID:self.ctx.altDSID reply:^(NSError * _Nullable operationError) {
273
274 if(operationError) {
275 secnotice("clique-establish", "reenact establish returned an error: %@", operationError);
276 }
277 success = !!operationError;
278 localError = operationError;
279 }];
280
281 if(localError && error) {
282 *error = localError;
283 }
284 secnotice("clique-establish", "establish complete: %@", success ? @"YES" : @"NO");
285
286 return success;
287 }
288
289 - (BOOL)resetAndEstablish:(NSError**)error
290 {
291 secnotice("clique-resetandestablish", "resetAndEstablish started");
292
293 OTControl* control = [self makeOTControl:error];
294
295 if(!control) {
296 return false;
297 }
298
299 __block BOOL success = NO;
300 __block NSError* localError = nil;
301 [control resetAndEstablish:nil context:self.ctx.context altDSID:self.ctx.altDSID reply:^(NSError * _Nullable operationError) {
302
303 if(operationError) {
304 secnotice("clique-resetandestablish", "resetAndEstablish returned an error: %@", operationError);
305 }
306 success = !!operationError;
307 localError = operationError;
308 }];
309
310 if(localError && error) {
311 *error = localError;
312 }
313
314 secnotice("clique-resetandestablish", "establish complete: %@", success ? @"YES" : @"NO");
315
316 return success;
317 }
318 #endif // OCTAGON
319
320 + (OTClique*)newFriendsWithContextData:(OTConfigurationContext*)data error:(NSError * __autoreleasing *)error
321 {
322 #if OCTAGON
323 secnotice("clique-newfriends", "makeNewFriends invoked using context: %@, dsid: %@", data.context, data.dsid);
324 bool result = false;
325
326 OTClique* clique = [[OTClique alloc] initWithContextData:data error:error];
327
328 if(OctagonIsEnabled()) {
329 NSError* localError = nil;
330 [clique resetAndEstablish:&localError];
331
332 if(localError) {
333 secnotice("clique-newfriends", "account reset failed: %@", localError);
334 if(error) {
335 *error = localError;
336 }
337 return nil;
338 } else {
339 secnotice("clique-newfriends", "Octagon account reset succeeded");
340 }
341 }
342
343 if(OctagonPlatformSupportsSOS()) {
344 CFErrorRef resetError = NULL;
345 NSData* analyticsData = nil;
346 if(data.analytics) {
347 NSError* encodingError = nil;
348 analyticsData = [NSKeyedArchiver archivedDataWithRootObject:data.analytics requiringSecureCoding:YES error:&encodingError];
349
350 if(encodingError) {
351 secnotice("clique-newfriends", "newFriendsWithContextData: unable to serialize analytics: %@", encodingError);
352 }
353 }
354
355 if(analyticsData) {
356 result = SOSCCResetToEmptyWithAnalytics((__bridge CFDataRef)analyticsData, &resetError);
357 } else {
358 result = SOSCCResetToEmpty(&resetError);
359 }
360
361 if(!result || resetError){
362 secnotice("clique-newfriends", "newFriendsWithContextData: resetToOffering failed: %@", resetError);
363 if(error) {
364 *error = CFBridgingRelease(resetError);
365 }
366 return nil;
367 }
368 secnotice("clique-newfriends", "newFriendsWithContextData: reset the SOS circle");
369 } else {
370 secnotice("clique-newfriends", "newFriendsWithContextData: SOS disabled on this platform");
371 }
372 secnotice("clique-newfriends", "makeNewFriends complete");
373
374 return clique;
375
376 #else // !OCTAGON
377 if (error)
378 *error = [NSError errorWithDomain:NSOSStatusErrorDomain code:errSecUnimplemented userInfo:nil];
379 return NULL;
380 #endif
381 }
382
383 + (OTClique* _Nullable)performEscrowRecoveryWithContextData:(OTConfigurationContext*)data
384 escrowArguments:(NSDictionary*)sbdRecoveryArguments
385 error:(NSError**)error
386 {
387 #if OCTAGON
388 secnotice("clique-recovery", "attempting an escrow recovery for context:%@, altdsid:%@", data.context, data.altDSID);
389
390 id<OctagonEscrowRecovererPrococol> sb = data.sbd ?: [[getSecureBackupClass() alloc] init];
391
392 NSDictionary* recoveredInformation = nil;
393 NSError* recoverError = [sb recoverWithInfo:sbdRecoveryArguments results:&recoveredInformation];
394
395 if(recoverError) {
396 secnotice("clique-recovery", "sbd escrow recovery failed: %@", recoverError);
397 if(OctagonPlatformSupportsSOS()) {
398 if(recoverError.code == 17 /* kSecureBackupRestoringLegacyBackupKeychainError */ && [recoverError.domain isEqualToString:getkSecureBackupErrorDomain()]) { /* XXX */
399 secnotice("clique-recovery", "Can't restore legacy backup with no keybag. Resetting SOS to offering");
400 CFErrorRef blowItAwayError = NULL;
401 bool successfulReset = SOSCCResetToOffering(&blowItAwayError);
402 if(!successfulReset || blowItAwayError) {
403 secerror("clique-recovery: failed to reset to offering:%@", blowItAwayError);
404 } else {
405 secnotice("clique-recovery", "resetting SOS circle successful");
406 }
407 } else {
408 if(error) {
409 *error = recoverError;
410 }
411 return nil;
412 }
413 } else {
414 if(error){
415 *error = recoverError;
416 }
417 return nil;
418 }
419 }
420
421 NSError* localError = nil;
422 OTClique* clique = [[OTClique alloc] initWithContextData:data
423 error:&localError];
424
425 if(!clique || localError) {
426 secnotice("clique-recovery", "unable to create otclique: %@", localError);
427 if(error) {
428 *error = localError;
429 }
430 return nil;
431 }
432
433 OTControl* control = [clique makeOTControl:&localError];
434 if (!control) {
435 secnotice("clique-recovery", "unable to create otcontrol: %@", localError);
436 if (error) {
437 *error = localError;
438 }
439 return nil;
440 }
441
442 NSString *bottleID = recoveredInformation[@"bottleID"];
443 NSString *isValid = recoveredInformation[@"bottleValid"];
444 NSData *bottledPeerEntropy = recoveredInformation[@"EscrowServiceEscrowData"][@"BottledPeerEntropy"];
445 bool shouldResetOctagon = false;
446
447 if(bottledPeerEntropy && bottleID && [isValid isEqualToString:@"valid"]){
448 secnotice("clique-recovery", "recovering from bottle: %@", bottleID);
449 __block NSError* restoreBottleError = nil;
450
451 //restore bottle!
452 [control restore:OTCKContainerName
453 contextID:data.context
454 bottleSalt:data.altDSID
455 entropy:bottledPeerEntropy
456 bottleID:bottleID
457 reply:^(NSError * _Nullable restoreError) {
458 if(restoreError) {
459 secnotice("clique-recovery", "restore bottle errored: %@", restoreError);
460 } else {
461 secnotice("clique-recovery", "restoring bottle succeeded");
462 }
463 restoreBottleError = restoreError;
464 }];
465
466 if(restoreBottleError) {
467 if(error){
468 *error = restoreBottleError;
469 }
470 return nil;
471 }
472 } else {
473 shouldResetOctagon = true;
474 }
475
476 if(OctagonPlatformSupportsSOS()) {
477 secnotice("clique-recovery", "attempting joinAfterRestore");
478 [clique joinAfterRestore:&localError];
479 secnotice("clique-recovery", "joinAfterRestore: %@", localError);
480 }
481
482 if(shouldResetOctagon) {
483 secnotice("clique-recovery", "bottle %@ is not valid, resetting octagon", bottleID);
484 NSError* resetError = nil;
485 [clique resetAndEstablish:&resetError];
486 if(resetError) {
487 secnotice("clique-recovery", "failed to reset octagon: %@", resetError);
488 } else{
489 secnotice("clique-recovery", "reset octagon succeeded");
490 }
491 }
492
493 secnotice("clique-recovery", "recovery complete: %@", clique);
494
495 return clique;
496 #else
497 if (error) {
498 *error = [NSError errorWithDomain:NSOSStatusErrorDomain code:errSecUnimplemented userInfo:nil];
499 }
500 return NULL;
501 #endif
502 }
503
504
505 - (KCPairingChannel *)setupPairingChannelAsInitiator:(KCPairingChannelContext *)ctx
506 {
507 #if OCTAGON
508 return [getKCPairingChannelClass() pairingChannelInitiator:ctx];
509 #else
510 return NULL;
511 #endif
512 }
513
514 - (KCPairingChannel * _Nullable)setupPairingChannelAsInitator:(KCPairingChannelContext *)ctx error:(NSError * __autoreleasing *)error
515 {
516 if (error) {
517 *error = nil;
518 }
519 return [self setupPairingChannelAsInitiator:ctx];
520 }
521
522 - (KCPairingChannel *)setupPairingChannelAsAcceptor:(KCPairingChannelContext *)ctx
523 {
524 #if OCTAGON
525 return [getKCPairingChannelClass() pairingChannelAcceptor:ctx];
526 #else
527 return NULL;
528 #endif
529 }
530
531 - (KCPairingChannel * _Nullable)setupPairingChannelAsAcceptor:(KCPairingChannelContext *)ctx error:(NSError * __autoreleasing *)error
532 {
533 if (error) {
534 *error = nil;
535 }
536
537 return [self setupPairingChannelAsAcceptor:ctx];
538 }
539
540
541 - (CliqueStatus)_fetchCliqueStatus:(OTOperationConfiguration *)configuration error:(NSError * __autoreleasing *)error
542 {
543 #if OCTAGON
544 __block CliqueStatus sosStatus = CliqueStatusError;
545 __block CliqueStatus octagonStatus = CliqueStatusError;
546
547 // Octagon is supreme.
548
549 if(OctagonIsEnabled()) {
550 OTControl* control = [self makeOTControl:error];
551 if(!control) {
552 secnotice("clique-status", "cliqueStatus noOTControl");
553 return CliqueStatusError;
554 }
555
556 __block NSError* localError = nil;
557 [control fetchCliqueStatus:nil context:self.ctx.context configuration:configuration reply:^(CliqueStatus cliqueStatus, NSError * _Nullable fetchError) {
558 if(fetchError){
559 octagonStatus = CliqueStatusError;
560 localError = fetchError;
561 secnotice("clique-status", "octagon clique status errored: %@", fetchError);
562 } else {
563 octagonStatus = cliqueStatus;
564 }
565 }];
566
567 if(OctagonAuthoritativeTrustIsEnabled() || !OctagonPlatformSupportsSOS()) {
568 secnotice("clique-status", "cliqueStatus(%{public}scached)(context:%@, altDSID:%@) returning %@ (error: %@)",
569 configuration.useCachedAccountStatus ? "" : "non-",
570 self.ctx.context, self.ctx.altDSID,
571 OTCliqueStatusToString(octagonStatus), localError);
572 if (localError && error) {
573 *error = localError;
574 }
575 return octagonStatus;
576 }
577 }
578
579 if(OctagonPlatformSupportsSOS()) {
580 CFErrorRef circleStatusError = NULL;
581 sosStatus = kSOSCCError;
582 if(configuration.useCachedAccountStatus){
583 sosStatus = SOSCCThisDeviceIsInCircle(&circleStatusError);
584 } else {
585 sosStatus = SOSCCThisDeviceIsInCircleNonCached(&circleStatusError);
586 }
587 secnotice("clique-status", "sos clique status is %d (%@)", (int)sosStatus, circleStatusError);
588
589 if (error) {
590 *error = (NSError*)CFBridgingRelease(circleStatusError);
591 } else {
592 CFBridgingRelease(circleStatusError);
593 }
594 }
595 secnotice("clique-status", "cliqueStatus(%{public}scached)(context:%@, altDSID:%@) complete: %@",
596 configuration.useCachedAccountStatus ? "" : "non-",
597 self.ctx.context, self.ctx.altDSID,
598 OTCliqueStatusToString(octagonStatus));
599 return octagonStatus;
600 #else // !OCTAGON
601 if(error){
602 *error = [NSError errorWithDomain:NSOSStatusErrorDomain code:errSecUnimplemented userInfo:nil];
603 }
604 return (CliqueStatus)kSOSCCError;
605 #endif
606 }
607
608 // Don't change rules for CoreCDP, and preserve legacy behavior for now
609 // preserve old behavior until CoreCDP can move to -fetchCliqueStatus:error:
610 #define LEGACY_WAITING_BEHAVIOR (TARGET_OS_OSX || TARGET_OS_IOS)
611
612 - (CliqueStatus)fetchCliqueStatus:(OTOperationConfiguration *)configuration error:(NSError * __autoreleasing * _Nonnull)error
613 {
614 return [self _fetchCliqueStatus:configuration error:error];
615 }
616
617 - (CliqueStatus)fetchCliqueStatus:(NSError * __autoreleasing *)error
618 {
619 OTOperationConfiguration *configuration = [[OTOperationConfiguration alloc] init];
620 #if LEGACY_WAITING_BEHAVIOR
621 configuration.timeoutWaitForCKAccount = 0;
622 #endif
623 return [self _fetchCliqueStatus:configuration error:error];
624 }
625
626 - (CliqueStatus)cachedCliqueStatus:(BOOL)usedCached error:(NSError * __autoreleasing *)error
627 {
628 OTOperationConfiguration *configuration = [[OTOperationConfiguration alloc] init];
629 #if LEGACY_WAITING_BEHAVIOR
630 configuration.timeoutWaitForCKAccount = 0;
631 #endif
632 if (usedCached) {
633 configuration.useCachedAccountStatus = YES;
634 }
635 return [self _fetchCliqueStatus:configuration error:error];
636 }
637
638
639 - (BOOL)removeFriendsInClique:(NSArray<NSString*>*)friendIdentifiers error:(NSError * __autoreleasing *)error
640 {
641 #if OCTAGON
642 secnotice("clique-removefriends", "removeFriendsInClique invoked using context:%@, altdsid:%@", self.ctx.context, self.ctx.altDSID);
643
644 // Annoying: we must sort friendIdentifiers into octagon/sos lists.
645 NSMutableArray<NSString*>* octagonIdentifiers = [NSMutableArray array];
646 NSMutableArray<NSString*>* sosIdentifiers = [NSMutableArray array];
647
648 for(NSString* friendIdentifier in friendIdentifiers) {
649 if([friendIdentifier hasPrefix:@"SHA256:"]) {
650 [octagonIdentifiers addObject: friendIdentifier];
651 } else {
652 [sosIdentifiers addObject: friendIdentifier];
653 }
654 }
655
656 // Ensure that we don't have any peers on the wrong platform
657 if(!OctagonIsEnabled() && octagonIdentifiers.count > 0) {
658 NSError *localError = [NSError errorWithDomain:NSOSStatusErrorDomain
659 code:errSecUnimplemented
660 userInfo:@{NSLocalizedDescriptionKey: @"Octagon is disabled; can't distrust any Octagon peers"}];
661 secnotice("clique-removefriends", "removeFriendsInClique failed:%@", localError);
662 if(error) {
663 *error = localError;
664 }
665 return NO;
666 }
667
668 if(!OctagonPlatformSupportsSOS() && sosIdentifiers.count > 0) {
669 NSError *localError = [NSError errorWithDomain:NSOSStatusErrorDomain
670 code:errSecUnimplemented
671 userInfo:@{NSLocalizedDescriptionKey: @"SOS is not available on this platform; can't distrust any SOS peers"}];
672 secnotice("clique-removefriends", "removeFriendsInClique failed:%@", localError);
673 if(error) {
674 *error = localError;
675 }
676 return NO;
677 }
678
679
680 __block NSError* localError = nil;
681 bool result = true;
682
683 if(OctagonIsEnabled() && octagonIdentifiers.count > 0) {
684 OTControl* control = [self makeOTControl:error];
685 if(!control) {
686 return NO;
687 }
688
689 secnotice("clique-removefriends", "octagon: removing octagon friends: %@", octagonIdentifiers);
690
691 [control removeFriendsInClique:nil
692 context:self.ctx.context
693 peerIDs:octagonIdentifiers
694 reply:^(NSError* replyError) {
695 if(replyError) {
696 secnotice("clique-removefriends", "removeFriendsInClique failed: unable to remove friends: %@", replyError);
697 localError = replyError;
698 } else {
699 secnotice("clique-removefriends", "octagon: friends removed: %@", octagonIdentifiers);
700 }
701 }];
702 }
703
704 if(OctagonPlatformSupportsSOS() && sosIdentifiers.count > 0) {
705 CFErrorRef removeFriendError = NULL;
706 NSData* analyticsData = nil;
707
708 secnotice("clique-removefriends", "removing sos friends: %@", sosIdentifiers);
709
710 if(self.ctx.analytics){
711 NSError* encodingError = nil;
712 analyticsData = [NSKeyedArchiver archivedDataWithRootObject:self.ctx.analytics requiringSecureCoding:YES error:&encodingError];
713 }
714
715 if(analyticsData) {
716 result = SOSCCRemovePeersFromCircleWithAnalytics((__bridge CFArrayRef)friendIdentifiers, (__bridge CFDataRef)analyticsData, &removeFriendError);
717 } else {
718 result = SOSCCRemovePeersFromCircle((__bridge CFArrayRef)friendIdentifiers, &removeFriendError);
719 }
720
721 if(removeFriendError) {
722 secnotice("clique-removefriends", "removeFriendsInClique failed: unable to remove friends: %@", removeFriendError);
723 localError = CFBridgingRelease(removeFriendError);
724 }
725 }
726
727 if(error && localError) {
728 *error = localError;
729 }
730 secnotice("clique-removefriends", "removeFriendsInClique complete: %d", result);
731
732 return result && localError == nil;
733 #else // !OCTAGON
734 return NO;
735 #endif
736 }
737
738 - (BOOL)leaveClique:(NSError * __autoreleasing *)error
739 {
740 #if OCTAGON
741 secnotice("clique-leaveClique", "leaveClique invoked using context:%@, altdsid:%@", self.ctx.context, self.ctx.altDSID);
742 CFErrorRef removeThisDeviceError = NULL;
743 bool result = false;
744
745 if(OctagonIsEnabled()) {
746 OTControl* control = [self makeOTControl:error];
747 if(!control) {
748 return false;
749 }
750
751 __block NSError* localError = nil;
752 [control leaveClique:nil context:self.ctx.context reply:^(NSError * _Nullable leaveError) {
753 if(leaveError) {
754 secnotice("clique-leaveClique", "leaveClique errored: %@", leaveError);
755 localError = leaveError;
756 } else {
757 secnotice("clique-leaveClique", "leaveClique success.");
758 }
759 }];
760
761 if(error) {
762 *error = localError;
763 }
764 result = !localError;
765 }
766
767 if(OctagonPlatformSupportsSOS()) {
768 NSData* analyticsData = nil;
769
770 if(self.ctx.analytics) {
771 NSError* encodingError = nil;
772 analyticsData = [NSKeyedArchiver archivedDataWithRootObject:self.ctx.analytics requiringSecureCoding:YES error:&encodingError];
773 if(!analyticsData){
774 secnotice("clique-leaveClique", "leaveClique unable to archive analytics object: %@", encodingError);
775 }
776 }
777
778 if(analyticsData) {
779 result &= SOSCCRemoveThisDeviceFromCircleWithAnalytics((__bridge CFDataRef)analyticsData, &removeThisDeviceError);
780 } else {
781 result &= SOSCCRemoveThisDeviceFromCircle(&removeThisDeviceError);
782 }
783
784 if (error) {
785 *error = (NSError*)CFBridgingRelease(removeThisDeviceError);
786 } else {
787 CFBridgingRelease(removeThisDeviceError);
788 }
789 }
790 secnotice("clique-leaveClique", "leaveClique complete: %d", result);
791
792 return result ? YES : NO;
793 #else // !OCTAGON
794 return NO;
795 #endif
796 }
797
798 - (NSDictionary<NSString*,NSString*>* _Nullable)peerDeviceNamesByPeerID:(NSError * __autoreleasing *)error
799 {
800 #if OCTAGON
801 secnotice("clique", "peerDeviceNamesByPeerID invoked using context:%@, altdsid:%@", self.ctx.context, self.ctx.altDSID);
802
803 NSMutableDictionary<NSString*, NSString*>* retPeers = [NSMutableDictionary dictionary];
804
805 if(OctagonIsEnabled()) {
806 OTControl* control = [self makeOTControl:error];
807 if(!control) {
808 return nil;
809 }
810
811 __block NSError* localError = nil;
812 __block NSDictionary<NSString*, NSString*>* localPeers = nil;
813
814 [control peerDeviceNamesByPeerID:nil context:OTDefaultContext reply:^(NSDictionary<NSString*,NSString*>* peers, NSError* controlError) {
815 if(controlError) {
816 secnotice("clique", "peerDeviceNamesByPeerID errored: %@", controlError);
817 } else {
818 secnotice("clique", "peerDeviceNamesByPeerID succeeded: %@", peers);
819 }
820 localError = controlError;
821 localPeers = peers;
822 }];
823
824 if(error && localError) {
825 *error = localError;
826 }
827 if(localError) {
828 return nil;
829 }
830 [retPeers addEntriesFromDictionary:localPeers];
831 secnotice("clique", "Received %lu Octagon peers", (unsigned long)localPeers.count);
832 }
833
834 if(OctagonPlatformSupportsSOS()) {
835 CFErrorRef peerErrorRef = NULL;
836 NSMutableDictionary<NSString*,NSString*>* peerMapping = [NSMutableDictionary dictionary];
837 NSArray* arrayOfPeerRefs = CFBridgingRelease(SOSCCCopyPeerPeerInfo(&peerErrorRef));
838 if(arrayOfPeerRefs){
839 [arrayOfPeerRefs enumerateObjectsUsingBlock:^(id peerRef, NSUInteger idx, BOOL * stop) {
840 SOSPeerInfoRef peer = (__bridge SOSPeerInfoRef)peerRef;
841 if(peer){
842 [peerMapping setObject:(__bridge NSString*)SOSPeerInfoGetPeerName(peer) forKey:(__bridge NSString*)SOSPeerInfoGetPeerID(peer)];
843 }
844 }];
845 }
846 if (error) {
847 *error = (NSError*)CFBridgingRelease(peerErrorRef);
848 } else {
849 CFBridgingRelease(peerErrorRef);
850 }
851
852 [retPeers addEntriesFromDictionary:peerMapping];
853 secnotice("clique", "Received %lu SOS peers", (unsigned long)peerMapping.count);
854 }
855
856 return retPeers;
857 #else // !OCTAGON
858 return NULL;
859 #endif
860 }
861
862 - (BOOL)joinAfterRestore:(NSError * __autoreleasing *)error
863 {
864 secnotice("clique-recovery", "joinAfterRestore for context:%@, altdsid:%@", self.ctx.context, self.ctx.altDSID);
865 CFErrorRef restoreError = NULL;
866
867 bool res = SOSCCRequestToJoinCircleAfterRestore(&restoreError);
868 if (error) {
869 *error = (NSError*)CFBridgingRelease(restoreError);
870 } else {
871 CFBridgingRelease(restoreError);
872 }
873 secnotice("clique-recovery", "joinAfterRestore complete: %d %@", res, error ? *error : @"no error pointer provided");
874 return res;
875 }
876
877 - (BOOL)safariPasswordSyncingEnabled:(NSError **)error
878 {
879 secnotice("clique-safari", "safariPasswordSyncingEnabled for context:%@, altdsid:%@", self.ctx.context, self.ctx.altDSID);
880
881 CFErrorRef viewErrorRef = NULL;
882
883 SOSViewResultCode result = SOSCCView(kSOSViewAutofillPasswords, kSOSCCViewQuery, &viewErrorRef);
884
885 BOOL viewMember = result == kSOSCCViewMember;
886 if (error) {
887 *error = (NSError*)CFBridgingRelease(viewErrorRef);
888 } else {
889 CFBridgingRelease(viewErrorRef);
890 }
891
892 secnotice("clique-safari", "safariPasswordSyncingEnabled complete: %@", viewMember ? @"YES" : @"NO");
893
894 return viewMember;
895 }
896
897 - (BOOL)isLastFriend:(NSError **)error
898 {
899 secnotice("clique-isLastFriend", "is last friend");
900 return NO;
901 }
902
903 - (BOOL)waitForInitialSync:(NSError *__autoreleasing*)error
904 {
905 secnotice("clique-legacy", "waitForInitialSync for context:%@, altdsid:%@", self.ctx.context, self.ctx.altDSID);
906 CFErrorRef initialSyncErrorRef = NULL;
907 bool result = false;
908 if(self.ctx.analytics){
909 NSError* encodingError = nil;
910 NSData* analyticsData = [NSKeyedArchiver archivedDataWithRootObject:self.ctx.analytics requiringSecureCoding:YES error:&encodingError];
911 if(!encodingError && analyticsData){
912 result = SOSCCWaitForInitialSyncWithAnalytics((__bridge CFDataRef)analyticsData, &initialSyncErrorRef);
913 }else{
914 result = SOSCCWaitForInitialSync(&initialSyncErrorRef);
915 }
916 }else{
917 result = SOSCCWaitForInitialSync(&initialSyncErrorRef);
918 }
919
920 BOOL initialSyncResult = (result == true);
921 if (error) {
922 *error = (NSError*)CFBridgingRelease(initialSyncErrorRef);
923 } else {
924 CFBridgingRelease(initialSyncErrorRef);
925 }
926 secnotice("clique-legacy", "waitForInitialSync waited: %d %@", initialSyncResult, error ? *error : @"no error pointer provided");
927 return initialSyncResult;
928 }
929
930 - (NSArray*)copyViewUnawarePeerInfo:(NSError *__autoreleasing*)error
931 {
932 secnotice("clique-legacy", "copyViewUnawarePeerInfo for context:%@, altdsid:%@", self.ctx.context, self.ctx.altDSID);
933 CFErrorRef copyViewUnawarePeerInfoErrorRef = NULL;
934 CFArrayRef peerListRef = SOSCCCopyViewUnawarePeerInfo(&copyViewUnawarePeerInfoErrorRef);
935
936 NSArray* peerList = (peerListRef ? (NSArray*)(CFBridgingRelease(peerListRef)) : nil);
937 if (error) {
938 *error = (NSError*)CFBridgingRelease(copyViewUnawarePeerInfoErrorRef);
939 } else {
940 CFBridgingRelease(copyViewUnawarePeerInfoErrorRef);
941 }
942 return peerList;
943 }
944
945 - (BOOL)viewSet:(NSSet*)enabledViews disabledViews:(NSSet*)disabledViews
946 {
947 secnotice("clique-legacy", "viewSet for context:%@, altdsid:%@", self.ctx.context, self.ctx.altDSID);
948 bool result = false;
949 if(self.ctx.analytics){
950 NSError* encodingError = nil;
951 NSData* analyticsData = [NSKeyedArchiver archivedDataWithRootObject:self.ctx.analytics requiringSecureCoding:YES error:&encodingError];
952 if(!encodingError && analyticsData){
953 result = SOSCCViewSetWithAnalytics((__bridge CFSetRef)enabledViews, (__bridge CFSetRef)disabledViews, (__bridge CFDataRef)analyticsData);
954 }else{
955 result = SOSCCViewSet((__bridge CFSetRef)enabledViews, (__bridge CFSetRef)disabledViews);
956 }
957 }else{
958 result = SOSCCViewSet((__bridge CFSetRef)enabledViews, (__bridge CFSetRef)disabledViews);
959 }
960
961 BOOL viewSetResult = (result == true);
962 return viewSetResult;
963 }
964
965 - (BOOL)setUserCredentialsAndDSID:(NSString*)userLabel
966 password:(NSData*)userPassword
967 error:(NSError *__autoreleasing*)error
968 {
969 secnotice("clique-legacy", "setUserCredentialsAndDSID for context:%@, altdsid:%@", self.ctx.context, self.ctx.altDSID);
970 CFErrorRef setCredentialsErrorRef = NULL;
971 bool result = false;
972 if(self.ctx.analytics){
973 NSError* encodingError = nil;
974 NSData* analyticsData = [NSKeyedArchiver archivedDataWithRootObject:self.ctx.analytics requiringSecureCoding:YES error:&encodingError];
975 if(!encodingError && analyticsData){
976 result = SOSCCSetUserCredentialsAndDSIDWithAnalytics((__bridge CFStringRef)userLabel,
977 (__bridge CFDataRef)userPassword,
978 (__bridge CFStringRef)self.ctx.dsid,
979 (__bridge CFDataRef)analyticsData,
980 &setCredentialsErrorRef);
981 }else{
982 result = SOSCCSetUserCredentialsAndDSID((__bridge CFStringRef)userLabel,
983 (__bridge CFDataRef)userPassword,
984 (__bridge CFStringRef)self.ctx.dsid,
985 &setCredentialsErrorRef);
986 }
987 }else{
988 result = SOSCCSetUserCredentialsAndDSID((__bridge CFStringRef)userLabel,
989 (__bridge CFDataRef)userPassword,
990 (__bridge CFStringRef)self.ctx.dsid,
991 &setCredentialsErrorRef);
992 }
993
994 BOOL setCredentialsResult = (result == true);
995 if (error) {
996 *error = (NSError*)CFBridgingRelease(setCredentialsErrorRef);
997 } else {
998 CFBridgingRelease(setCredentialsErrorRef);
999 }
1000 secnotice("clique-legacy", "setUserCredentialsAndDSID results: %d %@", setCredentialsResult, setCredentialsErrorRef);
1001 return setCredentialsResult;
1002 }
1003
1004 - (BOOL)tryUserCredentialsAndDSID:(NSString*)userLabel
1005 password:(NSData*)userPassword
1006 error:(NSError *__autoreleasing*)error
1007 {
1008 secnotice("clique-legacy", "tryUserCredentialsAndDSID for context:%@, altdsid:%@", self.ctx.context, self.ctx.altDSID);
1009 CFErrorRef tryCredentialsErrorRef = NULL;
1010 bool result = SOSCCTryUserCredentialsAndDSID((__bridge CFStringRef)userLabel,
1011 (__bridge CFDataRef)userPassword,
1012 (__bridge CFStringRef)self.ctx.dsid,
1013 &tryCredentialsErrorRef);
1014
1015 BOOL tryCredentialsResult = (result == true);
1016 if (error) {
1017 *error = (NSError*)CFBridgingRelease(tryCredentialsErrorRef);
1018 } else {
1019 CFBridgingRelease(tryCredentialsErrorRef);
1020 }
1021 secnotice("clique-legacy", "tryUserCredentialsAndDSID results: %d %@", tryCredentialsResult, tryCredentialsErrorRef);
1022 return tryCredentialsResult;
1023
1024 }
1025
1026 - (NSArray*)copyPeerPeerInfo:(NSError *__autoreleasing*)error
1027 {
1028 secnotice("clique-legacy", "copyPeerPeerInfo for context:%@, altdsid:%@", self.ctx.context, self.ctx.altDSID);
1029 CFErrorRef copyPeerErrorRef = NULL;
1030 CFArrayRef result = SOSCCCopyPeerPeerInfo(&copyPeerErrorRef);
1031
1032 NSArray* peerList = (result ? (NSArray*)(CFBridgingRelease(result)) : nil);
1033
1034 if (error) {
1035 *error = (NSError*)CFBridgingRelease(copyPeerErrorRef);
1036 } else {
1037 CFBridgingRelease(copyPeerErrorRef);
1038 }
1039 secnotice("clique-legacy", "copyPeerPeerInfo results: %@", peerList);
1040
1041 return peerList;
1042
1043 }
1044
1045 - (BOOL)peersHaveViewsEnabled:(NSArray<NSString*>*)viewNames error:(NSError *__autoreleasing*)error
1046 {
1047 secnotice("clique-legacy", "peersHaveViewsEnabled for context:%@, altdsid:%@", self.ctx.context, self.ctx.altDSID);
1048 CFErrorRef viewsEnabledErrorRef = NULL;
1049 BOOL viewsEnabledResult = NO;
1050
1051 CFBooleanRef result = SOSCCPeersHaveViewsEnabled((__bridge CFArrayRef)viewNames, &viewsEnabledErrorRef);
1052 if(result){
1053 viewsEnabledResult = CFBooleanGetValue(result);
1054 }
1055 if (error) {
1056 *error = (NSError*)CFBridgingRelease(viewsEnabledErrorRef);
1057 } else {
1058 CFBridgingRelease(viewsEnabledErrorRef);
1059 }
1060 secnotice("clique-legacy", "peersHaveViewsEnabled results: %@", viewsEnabledResult ? @"YES" : @"NO");
1061
1062 return viewsEnabledResult;
1063 }
1064
1065 - (BOOL)requestToJoinCircle:(NSError *__autoreleasing*)error
1066 {
1067 bool result = false;
1068 CFErrorRef joinErrorRef = NULL;
1069
1070 #if OCTAGON
1071 secnotice("clique-legacy", "requestToJoinCircle for context:%@, altdsid:%@", self.ctx.context, self.ctx.altDSID);
1072
1073 if(OctagonIsEnabled()) {
1074 NSError* localError = nil;
1075 [self resetAndEstablish:&localError];
1076
1077 if(localError) {
1078 secnotice("clique-legacy", "account reset failed: %@", localError);
1079 if(error) {
1080 *error = localError;
1081 }
1082 return NO;
1083 } else {
1084 secnotice("clique-legacy", "account reset succeeded");
1085 }
1086
1087 // If we didn't early-exit, and we aren't going to invoke SOS below, we succeeded.
1088 if(!OctagonPlatformSupportsSOS()) {
1089 secnotice("clique-legacy", "sos requestToJoinCircle results: %d %@", result, joinErrorRef);
1090 return YES;
1091 }
1092 }
1093 #endif // OCTAGON
1094
1095 if(OctagonPlatformSupportsSOS()) {
1096 NSData* analyticsData = nil;
1097 if(self.ctx.analytics){
1098 NSError* encodingError = nil;
1099 analyticsData = [NSKeyedArchiver archivedDataWithRootObject:self.ctx.analytics requiringSecureCoding:YES error:&encodingError];
1100 }
1101
1102 if(analyticsData){
1103 result = SOSCCRequestToJoinCircleWithAnalytics((__bridge CFDataRef)analyticsData, &joinErrorRef);
1104 } else {
1105 result = SOSCCRequestToJoinCircle(&joinErrorRef);
1106 }
1107
1108 secnotice("clique-legacy", "sos requestToJoinCircle complete: %d %@", result, joinErrorRef);
1109 }
1110
1111 if (error) {
1112 *error = (NSError*)CFBridgingRelease(joinErrorRef);
1113 } else {
1114 CFBridgingRelease(joinErrorRef);
1115 }
1116 return result ? YES : NO;
1117 }
1118
1119 - (BOOL)accountUserKeyAvailable
1120 {
1121 secnotice("clique-legacy", "accountUserKeyAvailable for context:%@, altdsid:%@", self.ctx.context, self.ctx.altDSID);
1122 BOOL canAuthenticate = (BOOL)SOSCCCanAuthenticate(NULL);
1123 if (canAuthenticate == NO) {
1124 secnotice("clique-legacy", "Security requires credentials...");
1125 }
1126 return canAuthenticate;
1127 }
1128
1129 // MARK: SBD interfaces
1130 + (OTBottleIDs* _Nullable)findOptimalBottleIDsWithContextData:(OTConfigurationContext*)data
1131 error:(NSError**)error
1132 {
1133 #if OCTAGON
1134 secnotice("clique-findbottle", "finding optimal bottles for context:%@, altdsid:%@", data.context, data.altDSID);
1135
1136 if(OctagonIsEnabled()) {
1137 __block NSError* localError = nil;
1138 __block NSArray<NSString*>* localViableBottleIDs = nil;
1139 __block NSArray<NSString*>* localPartiallyViableBottleIDs = nil;
1140
1141 OTControl *control = [data makeOTControl:&localError];
1142 if (!control) {
1143 secnotice("clique-findbottle", "unable to create otcontrol: %@", localError);
1144 if (error) {
1145 *error = localError;
1146 }
1147 return nil;
1148 }
1149 [control fetchAllViableBottles:OTCKContainerName
1150 context:data.context
1151 reply:^(NSArray<NSString *> * _Nullable sortedBottleIDs,
1152 NSArray<NSString*> * _Nullable sortedPartialBottleIDs,
1153 NSError * _Nullable fetchError) {
1154 if(fetchError) {
1155 secnotice("clique-findbottle", "findOptimalBottleIDsWithContextData errored: %@", fetchError);
1156 } else {
1157 secnotice("clique-findbottle", "findOptimalBottleIDsWithContextData succeeded: %@, %@", sortedBottleIDs, sortedPartialBottleIDs);
1158 }
1159 localError = fetchError;
1160 localViableBottleIDs = sortedBottleIDs;
1161 localPartiallyViableBottleIDs = sortedPartialBottleIDs;
1162 }];
1163
1164 if(error && localError) {
1165 *error = localError;
1166 }
1167 OTBottleIDs* bottleIDs = [[OTBottleIDs alloc] init];
1168 bottleIDs.preferredBottleIDs = localViableBottleIDs;
1169 bottleIDs.partialRecoveryBottleIDs = localPartiallyViableBottleIDs;
1170
1171 secnotice("clique-findbottle", "findOptimalBottleIDsWithContextData complete");
1172
1173 return bottleIDs;
1174 } else {
1175 // With octagon off, fail with 'unimplemented'
1176 if(error) {
1177 *error = [NSError errorWithDomain:NSOSStatusErrorDomain
1178 code:errSecUnimplemented
1179 userInfo:@{NSLocalizedDescriptionKey: @"optimal bottle IDs unimplemented"}];
1180 }
1181 return nil;
1182 }
1183 #else // !OCTAGON
1184 if (error) {
1185 *error = [NSError errorWithDomain:NSOSStatusErrorDomain code:errSecUnimplemented userInfo:nil];
1186 }
1187 return NULL;
1188 #endif
1189 }
1190
1191 + (OTClique* _Nullable)recoverWithContextData:(OTConfigurationContext*)data
1192 bottleID:(NSString*)bottleID
1193 escrowedEntropy:(NSData*)entropy
1194 error:(NSError**)error
1195 {
1196 #if OCTAGON
1197 secnotice("octagon", "replaced by performEscrowRecoveryWithContextData:escrowArguments:error: remove call");
1198 return nil;
1199 #else // !OCTAGON
1200 if (error) {
1201 *error = [NSError errorWithDomain:NSOSStatusErrorDomain code:errSecUnimplemented userInfo:nil];
1202 }
1203 return NULL;
1204 #endif
1205 }
1206
1207 // used by sbd to fill in the escrow record
1208 // TODO: what extra entitlement do you need to call this?
1209 - (void)fetchEscrowContents:(void (^)(NSData* _Nullable entropy,
1210 NSString* _Nullable bottleID,
1211 NSData* _Nullable signingPublicKey,
1212 NSError* _Nullable error))reply
1213 {
1214 #if OCTAGON
1215 secnotice("clique-fetchescrow", "fetching entropy for bottling for context:%@, altdsid:%@", self.ctx.context, self.ctx.altDSID);
1216
1217 if(OctagonIsEnabled()) {
1218 NSError* controlError = nil;
1219 OTControl* control = [self makeOTControl:&controlError];
1220 if (!control) {
1221 reply(nil, nil, nil, controlError);
1222 return;
1223 }
1224 [control fetchEscrowContents:OTCKContainerName
1225 contextID:self.ctx.context
1226 reply:^(NSData * _Nullable entropy,
1227 NSString * _Nullable bottleID,
1228 NSData * _Nullable signingPublicKey,
1229 NSError * _Nullable error) {
1230 if(error){
1231 secnotice("clique-fetchescrow", "fetchEscrowContents errored: %@", error);
1232 } else{
1233 secnotice("clique-fetchescrow","fetchEscrowContents succeeded");
1234 }
1235 reply (entropy, bottleID, signingPublicKey, error);
1236 }];
1237
1238 return;
1239 } else {
1240 // With octagon off, fail with 'unimplemented'
1241 reply(nil, nil, nil, [NSError errorWithDomain:NSOSStatusErrorDomain
1242 code:errSecUnimplemented
1243 userInfo:@{NSLocalizedDescriptionKey: @"fetchEscrowRecordContents unimplemented"}]);
1244 }
1245 #else // !OCTAGON
1246 reply(nil, nil, nil, [NSError errorWithDomain:NSOSStatusErrorDomain code:errSecUnimplemented userInfo:nil]);
1247 #endif
1248 }
1249
1250 + (void)setNewRecoveryKeyWithData:(OTConfigurationContext *)ctx
1251 recoveryKey:(NSString*)recoveryKey reply:(nonnull void (^)(SecRecoveryKey *rk, NSError *error))reply
1252 {
1253 #if OCTAGON
1254 secnotice("octagon-setrecoverykey", "setNewRecoveryKeyWithData invoked for context: %@", ctx.context);
1255 //set the recovery key for SOS
1256 NSError* createRecoveryKeyError = nil;
1257 NSMutableDictionary *userInfo = [NSMutableDictionary new];
1258 NSError* retError = nil;
1259
1260 SecRecoveryKey *rk = SecRKCreateRecoveryKeyWithError(recoveryKey, &createRecoveryKeyError);
1261 if (rk == nil) {
1262 secerror("octagon-setrecoverykey, SecRKCreateRecoveryKeyWithError() failed: %@", createRecoveryKeyError);
1263 userInfo[NSLocalizedDescriptionKey] = @"SecRKCreateRecoveryKeyWithError() failed";
1264 userInfo[NSUnderlyingErrorKey] = createRecoveryKeyError;
1265 retError = [NSError errorWithDomain:getkSecureBackupErrorDomain() code:kSecureBackupInternalError userInfo:userInfo];
1266 reply(nil, retError);
1267 return;
1268 }
1269 if(OctagonPlatformSupportsSOS()) {
1270 CFErrorRef registerError = nil;
1271 if (!SecRKRegisterBackupPublicKey(rk, &registerError)) {
1272 secerror("octagon-setrecoverykey, SecRKRegisterBackupPublicKey() failed: %@", registerError);
1273 NSError *underlyingError = CFBridgingRelease(registerError);
1274 userInfo[NSLocalizedDescriptionKey] = @"SecRKRegisterBackupPublicKey() failed";
1275 userInfo[NSUnderlyingErrorKey] = underlyingError;
1276 retError = [NSError errorWithDomain:getkSecureBackupErrorDomain() code:kSecureBackupInternalError userInfo:userInfo];
1277 reply(nil,retError);
1278 return;
1279 } else {
1280 secnotice("octagon-setrecoverykey", "successfully registered recovery key for SOS");
1281 }
1282 }
1283
1284 //set the recovery key for Octagon
1285 if(OctagonRecoveryKeyIsEnabled()) {
1286 NSError* controlError = nil;
1287 OTControl* control = [ctx makeOTControl:&controlError];
1288 if(!control) {
1289 secnotice("octagon-setrecoverykey", "failed to fetch OTControl object: %@", controlError);
1290 reply(nil, controlError);
1291 return;
1292 }
1293 [control createRecoveryKey:OTCKContainerName contextID:ctx.context recoveryKey:recoveryKey reply:^(NSError * createError) {
1294 if(createError){
1295 secerror("octagon-setrecoverykey, failed to create octagon recovery key");
1296 reply(nil, createError);
1297 return;
1298 } else {
1299 secnotice("octagon-setrecoverykey", "successfully set octagon recovery key");
1300 reply(rk, nil);
1301 return;
1302 }
1303 }];
1304 }
1305 #else // !OCTAGON
1306 reply(nil, [NSError errorWithDomain:NSOSStatusErrorDomain code:errSecUnimplemented userInfo:nil]);
1307 #endif
1308 }
1309
1310 + (void)recoverOctagonUsingData:(OTConfigurationContext *)ctx
1311 recoveryKey:(NSString*)recoveryKey
1312 reply:(void(^)(NSError* _Nullable error))reply
1313 {
1314 #if OCTAGON
1315 if(OctagonRecoveryKeyIsEnabled()) {
1316 NSError* controlError = nil;
1317 OTControl* control = [ctx makeOTControl:&controlError];
1318
1319 secnotice("clique-recoverykey", "join using recovery key");
1320
1321 if(!control) {
1322 secnotice("clique-recoverykey", "failed to fetch OTControl object: %@", controlError);
1323 reply(controlError);
1324 return;
1325 }
1326 [control joinWithRecoveryKey:OTCKContainerName contextID:ctx.context recoveryKey:recoveryKey reply:^(NSError *joinError) {
1327 if(joinError){
1328 secnotice("clique-recoverykey", "failed to join using recovery key: %@", joinError);
1329 reply(joinError);
1330 return;
1331 }
1332 secnotice("clique-recoverykey", "successfully joined using recovery key");
1333 reply(nil);
1334 }];
1335 }
1336
1337 #else // !OCTAGON
1338 reply([NSError errorWithDomain:NSOSStatusErrorDomain code:errSecUnimplemented userInfo:nil]);
1339 #endif
1340 }
1341
1342 - (void)performedCDPStateMachineRun:(OTCliqueCDPContextType)type
1343 success:(BOOL)success
1344 error:(NSError * _Nullable)error
1345 reply:(void(^)(NSError* _Nullable error))reply
1346 {
1347 #if OCTAGON
1348 NSError* controlError = nil;
1349 OTControl* control = [self makeOTControl:&controlError];
1350 if(!control) {
1351 secnotice("clique-cdp-sm", "octagon, failed to fetch OTControl object: %@", controlError);
1352 reply(controlError);
1353 return;
1354 }
1355
1356 [control postCDPFollowupResult:success type:type error:error containerName:OTCKContainerName contextName:OTDefaultContext reply:^(NSError *postError) {
1357 if(postError){
1358 secnotice("clique-cdp-sm", "failed to post %@ result: %@ ", type, postError);
1359 reply(postError);
1360 return;
1361 }
1362 if (success) {
1363 secnotice("clique-cdp-sm", "posted success: %@", type);
1364 } else {
1365 secnotice("clique-cdp-sm", "posted error: %@: %@", type, error);
1366 }
1367 reply(NULL);
1368 }];
1369 #else // !OCTAGON
1370 reply([NSError errorWithDomain:NSOSStatusErrorDomain code:errSecUnimplemented userInfo:nil]);
1371 #endif
1372 }
1373
1374 - (BOOL)waitForOctagonUpgrade:(NSError** _Nullable)error
1375 {
1376 #if OCTAGON
1377 OTControl* control = nil;
1378
1379 if (!OctagonIsEnabled()) {
1380 secnotice("clique-waitforoctagonupgrade", "cannot upgrade, octagon is not enabled");
1381 if (error) {
1382 *error = [NSError errorWithDomain:NSOSStatusErrorDomain code:errSecUnimplemented userInfo:@{NSLocalizedDescriptionKey: @"Octagon is not enabled"}];
1383 }
1384 return NO;
1385 }
1386
1387 NSError *controlError = nil;
1388 control = [self makeOTControl:&controlError];
1389 if (!control) {
1390 secnotice("clique-waitforoctagonupgrade", "octagon, failed to fetch OTControl object: %@", controlError);
1391 if (error) {
1392 *error = controlError;
1393 }
1394 return NO;
1395 }
1396
1397 __block BOOL ret = NO;
1398 __block NSError* blockError = nil;
1399
1400 [control waitForOctagonUpgrade:OTCKContainerName context:OTDefaultContext reply:^(NSError *postError) {
1401 if(postError){
1402 secnotice("clique-waitforoctagonupgrade", "error from control: %@", postError);
1403 blockError = postError;
1404 ret = NO;
1405 } else {
1406 secnotice("clique-waitforoctagonupgrade", "successfully upgraded to octagon");
1407 ret = YES;
1408 }
1409 }];
1410
1411 if (blockError && error) {
1412 *error = blockError;
1413 }
1414
1415 return ret;
1416 #else // !OCTAGON
1417 if(error) {
1418 *error = [NSError errorWithDomain:NSOSStatusErrorDomain code:errSecUnimplemented userInfo:nil];
1419 }
1420 return NO;
1421 #endif
1422 }
1423
1424 - (void)performedFailureCDPStateMachineRun:(OTCliqueCDPContextType)type
1425 error:(NSError * _Nullable)error
1426 reply:(void(^)(NSError* _Nullable error))reply
1427 {
1428 [self performedCDPStateMachineRun:type success:NO error:error reply:reply];
1429 }
1430
1431 - (void)performedSuccessfulCDPStateMachineRun:(OTCliqueCDPContextType)type
1432 reply:(void(^)(NSError* _Nullable error))reply
1433 {
1434 [self performedCDPStateMachineRun:type success:YES error:nil reply:reply];
1435 }
1436
1437 @end
1438
1439 #endif /* OBJC2 */