2 * Copyright (c) 2020 Apple Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
26 #import "OctagonTrust.h"
27 #import <Security/OTClique+Private.h>
28 #import <utilities/debugging.h>
29 #import "OTEscrowTranslation.h"
31 #import <SoftLinking/SoftLinking.h>
32 #import <CloudServices/SecureBackup.h>
33 #import <CloudServices/SecureBackupConstants.h>
34 #import "keychain/OctagonTrust/categories/OctagonTrustEscrowRecoverer.h"
35 #import "keychain/ot/OTDefines.h"
36 #import "keychain/ot/OTControl.h"
37 #import "keychain/ot/OTClique+Private.h"
38 #import <Security/OctagonSignPosts.h>
39 #include <utilities/SecCFRelease.h>
41 SOFT_LINK_FRAMEWORK(PrivateFrameworks, KeychainCircle);
42 SOFT_LINK_OPTIONAL_FRAMEWORK(PrivateFrameworks, CloudServices);
44 #pragma clang diagnostic push
45 #pragma clang diagnostic ignored "-Wstrict-prototypes"
46 SOFT_LINK_CLASS(CloudServices, SecureBackup);
47 SOFT_LINK_CONSTANT(CloudServices, kSecureBackupErrorDomain, NSErrorDomain);
48 SOFT_LINK_CONSTANT(CloudServices, kSecureBackupiCDPRecordsKey, NSString*);
49 SOFT_LINK_CONSTANT(CloudServices, kSecureBackupMetadataKey, NSString*);
50 SOFT_LINK_CONSTANT(CloudServices, kSecureBackupRecordIDKey, NSString*);
52 SOFT_LINK_CONSTANT(CloudServices, kEscrowServiceRecordDataKey, NSString*);
53 SOFT_LINK_CONSTANT(CloudServices, kSecureBackupKeybagDigestKey, NSString*);
54 SOFT_LINK_CONSTANT(CloudServices, kSecureBackupBagPasswordKey, NSString*);
55 SOFT_LINK_CONSTANT(CloudServices, kSecureBackupRecordLabelKey, NSString*);
57 #pragma clang diagnostic pop
59 static NSString * const kOTEscrowAuthKey = @"kOTEscrowAuthKey";
60 NSString* OTCKContainerName = @"com.apple.security.keychain";
62 @implementation OTConfigurationContext(Framework)
66 -(void) setEscrowAuth:(id)e
68 objc_setAssociatedObject(self, (__bridge const void * _Nonnull)(kOTEscrowAuthKey), e, OBJC_ASSOCIATION_ASSIGN);
73 return objc_getAssociatedObject(self, (__bridge const void * _Nonnull)(kOTEscrowAuthKey));
78 @implementation OTClique(Framework)
80 + (NSArray<OTEscrowRecord*>*)filterViableSOSRecords:(NSArray<OTEscrowRecord*>*)nonViable
82 NSMutableArray<OTEscrowRecord*>* viableSOS = [NSMutableArray array];
83 for(OTEscrowRecord* record in nonViable) {
84 if (record.viabilityStatus == OTEscrowRecord_SOSViability_SOS_VIABLE) {
85 [viableSOS addObject:record];
93 * desired outcome from filtering on an iOS/macOS platform:
94 * Escrow Record data validation outcome Outcome of fetch records
95 Octagon Viable, SOS Viable Record gets recommended, normal Octagon and SOS join
96 Octagon Viable, SOS Doesn't work Record gets recommended, normal Octagon join, SOS reset
97 Octagon Doesn't work, SOS Viable Record gets recommended, Octagon reset, normal SOS join
98 Octagon Doesn't work, SOS Doesn't work no records recommended. Force user to reset all protected data
100 * desired outcome from filtering on an watchOS/tvOS/etc:
101 Escrow Record data validation outcome Outcome of fetch records
102 Octagon Viable, SOS Viable Record gets recommended, normal Octagon and SOS noop
103 Octagon Viable, SOS Doesn't work Record gets recommended, normal Octagon join, SOS noop
104 Octagon Doesn't work, SOS Viable Record gets recommended, Octagon reset, SOS noop
105 Octagon Doesn't work, SOS Doesn't work no records recommended. Force user to reset all protected data
108 + (NSArray<OTEscrowRecord*>* _Nullable)filterRecords:(NSArray<OTEscrowRecord*>*)allRecords
110 NSMutableArray<OTEscrowRecord*>* viable = [NSMutableArray array];
111 NSMutableArray<OTEscrowRecord*>* partial = [NSMutableArray array];
112 NSMutableArray<OTEscrowRecord*>* nonViable = [NSMutableArray array];
114 for (OTEscrowRecord* record in allRecords) {
115 if(record.recordViability == OTEscrowRecord_RecordViability_RECORD_VIABILITY_FULLY_VIABLE &&
116 record.escrowInformationMetadata.bottleId != nil &&
117 [record.escrowInformationMetadata.bottleId length] != 0) {
118 [viable addObject:record];
119 } else if(record.recordViability == OTEscrowRecord_RecordViability_RECORD_VIABILITY_PARTIALLY_VIABLE &&
120 record.escrowInformationMetadata.bottleId != nil &&
121 [record.escrowInformationMetadata.bottleId length] != 0) {
122 [partial addObject:record];
124 [nonViable addObject:record];
128 for(OTEscrowRecord* record in viable) {
129 secnotice("octagontrust-fetchescrowrecords", "viable record: %@ serial:%@ bottleID:%@ silent allowed:%d",
131 record.escrowInformationMetadata.serial,
132 record.escrowInformationMetadata.bottleId,
133 (int)record.silentAttemptAllowed);
135 for(OTEscrowRecord* record in partial) {
136 secnotice("octagontrust-fetchescrowrecords", "partially viable record: %@ serial:%@ bottleID:%@ silent allowed:%d",
138 record.escrowInformationMetadata.serial,
139 record.escrowInformationMetadata.bottleId,
140 (int)record.silentAttemptAllowed);
142 for(OTEscrowRecord* record in nonViable) {
143 secnotice("octagontrust-fetchescrowrecords", "nonviable record: %@ serial:%@ bottleID:%@ silent allowed:%d",
145 record.escrowInformationMetadata.serial,
146 record.escrowInformationMetadata.bottleId,
147 (int)record.silentAttemptAllowed);
150 if ([viable count] > 0) {
151 secnotice("octagontrust-fetchescrowrecords", "Returning %d viable records", (int)[viable count]);
155 if ([partial count] > 0) {
156 secnotice("octagontrust-fetchescrowrecords", "Returning %d partially viable records", (int)[partial count]);
160 if (OctagonPlatformSupportsSOS()) {
161 NSArray<OTEscrowRecord*>* viableSOSRecords = [self filterViableSOSRecords:nonViable];
162 secnotice("octagontrust-fetchescrowrecords", "Returning %d sos viable records", (int)[viableSOSRecords count]);
163 return viableSOSRecords;
166 secnotice("octagontrust-fetchescrowrecords", "no viable records!");
171 + (NSArray<OTEscrowRecord*>* _Nullable)fetchAndHandleEscrowRecords:(OTConfigurationContext*)data shouldFilter:(BOOL)shouldFiler error:(NSError**)error
173 OctagonSignpost signPost = OctagonSignpostBegin(OctagonSignpostNameFetchEscrowRecords);
174 bool subTaskSuccess = false;
176 NSError* localError = nil;
177 NSArray* escrowRecordDatas = [OTClique fetchEscrowRecordsInternal:data error:&localError];
179 secerror("octagontrust-fetchAndHandleEscrowRecords: failed to fetch escrow records: %@", localError);
183 OctagonSignpostEnd(signPost, OctagonSignpostNameFetchEscrowRecords, OctagonSignpostNumber1(OctagonSignpostNameFetchEscrowRecords), (int)subTaskSuccess);
187 NSMutableArray<OTEscrowRecord*>* escrowRecords = [NSMutableArray array];
189 for(NSData* recordData in escrowRecordDatas){
190 OTEscrowRecord* translated = [[OTEscrowRecord alloc] initWithData:recordData];
191 if(translated.escrowInformationMetadata.bottleValidity == nil || [translated.escrowInformationMetadata.bottleValidity isEqualToString:@""]){
192 switch(translated.recordViability) {
193 case OTEscrowRecord_RecordViability_RECORD_VIABILITY_FULLY_VIABLE:
194 translated.escrowInformationMetadata.bottleValidity = @"valid";
196 case OTEscrowRecord_RecordViability_RECORD_VIABILITY_PARTIALLY_VIABLE:
197 translated.escrowInformationMetadata.bottleValidity = @"valid";
199 case OTEscrowRecord_RecordViability_RECORD_VIABILITY_LEGACY:
200 translated.escrowInformationMetadata.bottleValidity = @"invalid";
204 if(translated.recordId == nil || [translated.recordId isEqualToString:@""]){
205 translated.recordId = [[translated.label stringByReplacingOccurrencesOfString:OTEscrowRecordPrefix withString:@""] mutableCopy];
207 if((translated.serialNumber == nil || [translated.serialNumber isEqualToString:@""]) && translated.escrowInformationMetadata.peerInfo != nil && [translated.escrowInformationMetadata.peerInfo length] > 0) {
208 CFErrorRef cfError = NULL;
209 SOSPeerInfoRef peer = SOSPeerInfoCreateFromData(kCFAllocatorDefault, &cfError, (__bridge CFDataRef)translated.escrowInformationMetadata.peerInfo);
211 secnotice("octagontrust-handleEscrowRecords", "failed to create sos peer info: %@", cfError);
213 translated.serialNumber = (NSString*)CFBridgingRelease(SOSPeerInfoCopySerialNumber(peer));
217 [escrowRecords addObject:translated];
219 subTaskSuccess = true;
220 OctagonSignpostEnd(signPost, OctagonSignpostNameFetchEscrowRecords, OctagonSignpostNumber1(OctagonSignpostNameFetchEscrowRecords), (int)subTaskSuccess);
222 if (shouldFiler == YES) {
223 return [OTClique filterRecords:escrowRecords];
226 return escrowRecords;
229 + (NSArray<OTEscrowRecord*>* _Nullable)fetchAllEscrowRecords:(OTConfigurationContext*)data error:(NSError**)error
232 secnotice("octagontrust-fetchallescrowrecords", "fetching all escrow records for context :%@, altdsid:%@", data.context, data.altDSID);
233 return [OTClique fetchAndHandleEscrowRecords:data shouldFilter:NO error:error];
236 *error = [NSError errorWithDomain:NSOSStatusErrorDomain code:errSecUnimplemented userInfo:nil];
242 + (NSArray<OTEscrowRecord*>* _Nullable)fetchEscrowRecords:(OTConfigurationContext*)data error:(NSError**)error
245 if (OctagonIsOptimizationEnabled() && OctagonIsEscrowRecordFetchEnabled()) {
246 secnotice("octagontrust-fetchescrowrecords", "fetching filtered escrow records for context with feature flag enabled:%@, altdsid:%@", data.context, data.altDSID);
247 return [OTClique fetchAndHandleEscrowRecords:data shouldFilter:YES error:error];
249 if ([OTClique isCloudServicesAvailable] == NO) {
251 *error = [NSError errorWithDomain:NSOSStatusErrorDomain code:errSecUnimplemented userInfo:nil];
256 OctagonSignpost signPost = OctagonSignpostBegin(OctagonSignpostNameGetAccountInfo);
257 secnotice("octagontrust-fetchescrowrecords", "fetching escrow records for context with feature flag disabled:%@, altdsid:%@", data.context, data.altDSID);
258 id<OctagonEscrowRecovererPrococol> sb = data.sbd ?: [[getSecureBackupClass() alloc] init];
260 OTEscrowAuthenticationInformation *escrowAuth = nil;
261 NSDictionary* escrowAuthDictionary = nil;
262 if(data.escrowAuth != nil) {
263 escrowAuth = data.escrowAuth;
264 escrowAuthDictionary = [OTEscrowTranslation escrowAuthenticationInfoToDictionary:escrowAuth];
266 NSDictionary* results = nil;
267 NSError* recoverError = [sb getAccountInfoWithInfo:escrowAuthDictionary results:&results];
268 bool subTaskSuccess = false;
269 if(recoverError || results == nil) {
270 secerror("octagontrust-fetchescrowrecords: error fetching escrow records: %@", recoverError);
272 *error = recoverError;
274 OctagonSignpostEnd(signPost, OctagonSignpostNameGetAccountInfo, OctagonSignpostNumber1(OctagonSignpostNameGetAccountInfo), (int)subTaskSuccess);
277 secnotice("octagontrust-fetchescrowrecords", "recovered accountWithInfo results: %@", results);
278 NSArray *icdpRecords = results[getkSecureBackupiCDPRecordsKey()];
279 secnotice("octagontrust-fetchescrowrecords", "recovered iCDP records: %@", icdpRecords);
281 NSMutableArray<OTEscrowRecord*>* records = [NSMutableArray array];
282 for (NSDictionary *dictionaryRecord in icdpRecords) {
283 OTEscrowRecord* otEscrowRecord = [OTEscrowTranslation dictionaryToEscrowRecord:dictionaryRecord];
284 [records addObject:otEscrowRecord];
286 secnotice("octagontrust-fetchescrowrecords", "translated dictionary records to escrow record protos: %@", records);
288 subTaskSuccess = true;
289 OctagonSignpostEnd(signPost, OctagonSignpostNameGetAccountInfo, OctagonSignpostNumber1(OctagonSignpostNameGetAccountInfo), (int)subTaskSuccess);
296 *error = [NSError errorWithDomain:NSOSStatusErrorDomain code:errSecUnimplemented userInfo:nil];
302 + (OTClique* _Nullable)handleRecoveryResults:(OTConfigurationContext*)data recoveredInformation:(NSDictionary*)recoveredInformation sosViability:(OTEscrowRecord_SOSViability)sosViability performedSilentBurn:(BOOL)performedSilentBurn recoverError:(NSError*)recoverError error:(NSError**)error
304 if ([self isCloudServicesAvailable] == NO) {
306 *error = [NSError errorWithDomain:NSOSStatusErrorDomain code:errSecUnimplemented userInfo:nil];
311 OTClique* clique = [[OTClique alloc] initWithContextData:data];
312 BOOL resetToOfferingOccured = NO;
315 secnotice("octagontrust-handleRecoveryResults", "sbd escrow recovery failed: %@", recoverError);
316 if(recoverError.code == 40 /* kSecureBackupRestoringLegacyBackupKeychainError */ && [recoverError.domain isEqualToString:getkSecureBackupErrorDomain()]) {
317 if([OTClique platformSupportsSOS]) {
318 secnotice("octagontrust-handleRecoveryResults", "Can't restore legacy backup with no keybag. Resetting SOS to offering");
319 CFErrorRef resetToOfferingError = NULL;
320 bool successfulReset = SOSCCResetToOffering(&resetToOfferingError);
321 resetToOfferingOccured = YES;
322 if(!successfulReset || resetToOfferingError) {
323 secerror("octagontrust-handleRecoveryResults: failed to reset to offering:%@", resetToOfferingError);
325 secnotice("octagontrust-handleRecoveryResults", "resetting SOS circle successful");
327 CFReleaseNull(resetToOfferingError);
329 secnotice("octagontrust-handleRecoveryResults", "Legacy restore failed on a non-SOS platform");
333 *error = recoverError;
339 NSError* localError = nil;
340 OTControl* control = nil;
342 if (data.otControl) {
343 control = data.otControl;
345 control = [data makeOTControl:&localError];
348 secerror("octagontrust-handleRecoveryResults: unable to create otcontrol: %@", localError);
355 // look for OT Bottles
356 NSString *bottleID = recoveredInformation[@"bottleID"];
357 NSString *isValid = recoveredInformation[@"bottleValid"];
358 NSData *bottledPeerEntropy = recoveredInformation[@"EscrowServiceEscrowData"][@"BottledPeerEntropy"];
359 bool shouldResetOctagon = false;
361 if(bottledPeerEntropy && bottleID && [isValid isEqualToString:@"valid"]){
362 secnotice("octagontrust-handleRecoveryResults", "recovering from bottle: %@", bottleID);
363 __block NSError* restoreBottleError = nil;
364 OctagonSignpost bottleRestoreSignPost = performedSilentBurn ? OctagonSignpostBegin(OctagonSignpostNamePerformOctagonJoinForSilent) : OctagonSignpostBegin(OctagonSignpostNamePerformOctagonJoinForNonSilent);
367 [control restore:OTCKContainerName
368 contextID:data.context
369 bottleSalt:data.altDSID
370 entropy:bottledPeerEntropy
372 reply:^(NSError * _Nullable restoreError) {
374 secnotice("octagontrust-handleRecoveryResults", "restore bottle errored: %@", restoreError);
376 secnotice("octagontrust-handleRecoveryResults", "restoring bottle succeeded");
378 restoreBottleError = restoreError;
379 if (performedSilentBurn) {
380 OctagonSignpostEnd(bottleRestoreSignPost, OctagonSignpostNamePerformOctagonJoinForSilent, OctagonSignpostNumber1(OctagonSignpostNamePerformOctagonJoinForSilent), (int)true);
382 OctagonSignpostEnd(bottleRestoreSignPost, OctagonSignpostNamePerformOctagonJoinForNonSilent, OctagonSignpostNumber1(OctagonSignpostNamePerformOctagonJoinForNonSilent), (int)true);
386 if(restoreBottleError) {
388 *error = restoreBottleError;
393 shouldResetOctagon = true;
396 if(shouldResetOctagon) {
397 secnotice("octagontrust-handleRecoveryResults", "bottle %@ is not valid, resetting octagon", bottleID);
398 NSError* resetError = nil;
399 [clique resetAndEstablish:CuttlefishResetReasonNoBottleDuringEscrowRecovery error:&resetError];
401 secerror("octagontrust-handleRecoveryResults: failed to reset octagon: %@", resetError);
407 secnotice("octagontrust-handleRecoveryResults", "reset octagon succeeded");
411 // Join SOS circle if platform is supported and we didn't previously reset SOS
412 if (OctagonPlatformSupportsSOS() && resetToOfferingOccured == NO) {
413 if (sosViability == OTEscrowRecord_SOSViability_SOS_NOT_VIABLE) {
414 secnotice("octagontrust-handleRecoveryResults", "Record will not allow device to join SOS. Invoking reset to offering");
415 CFErrorRef resetToOfferingError = NULL;
416 bool successfulReset = SOSCCResetToOffering(&resetToOfferingError);
417 if(!successfulReset || resetToOfferingError) {
418 secerror("octagontrust-handleRecoveryResults: failed to reset to offering:%@", resetToOfferingError);
420 secnotice("octagontrust-handleRecoveryResults", "resetting SOS circle successful");
422 CFReleaseNull(resetToOfferingError);
424 NSError* joinAfterRestoreError = nil;
425 secnotice("octagontrust-handleRecoveryResults", "attempting joinAfterRestore");
426 BOOL joinAfterRestoreResult = [clique joinAfterRestore:&joinAfterRestoreError];
427 if(joinAfterRestoreError || joinAfterRestoreResult == NO) {
428 secnotice("octagontrust-handleRecoveryResults", "failed to join after restore: %@", joinAfterRestoreError);
430 secnotice("octagontrust-handleRecoveryResults", "joinAfterRestore succeeded");
436 // call SBD to kick off keychain data restore
437 id<OctagonEscrowRecovererPrococol> sb = data.sbd ?: [[getSecureBackupClass() alloc] init];
438 NSError* restoreError = nil;
440 NSMutableSet <NSString *> *viewsNotToBeRestored = [NSMutableSet set];
441 [viewsNotToBeRestored addObject:@"iCloudIdentity"];
442 [viewsNotToBeRestored addObject:@"PCS-MasterKey"];
443 [viewsNotToBeRestored addObject:@"KeychainV0"];
445 NSDictionary *escrowRecords = recoveredInformation[getkEscrowServiceRecordDataKey()];
446 if (escrowRecords == nil) {
447 secnotice("octagontrust-handleRecoveryResults", "unable to request keychain restore, record data missing");
452 NSData *keybagDigest = escrowRecords[getkSecureBackupKeybagDigestKey()];
453 NSData *password = escrowRecords[getkSecureBackupBagPasswordKey()];
454 if (keybagDigest == nil || password == nil) {
455 secnotice("octagontrust-handleRecoveryResults", "unable to request keychain restore, digest or password missing");
459 BOOL haveBottledPeer = (bottledPeerEntropy && bottleID && [isValid isEqualToString:@"valid"]) || shouldResetOctagon;
460 [sb restoreKeychainAsyncWithPassword:password
461 keybagDigest:keybagDigest
462 haveBottledPeer:haveBottledPeer
463 viewsNotToBeRestored:viewsNotToBeRestored
464 error:&restoreError];
467 secerror("octagontrust-handleRecoveryResults: error restoring keychain items: %@", restoreError);
473 + (instancetype _Nullable)performEscrowRecovery:(OTConfigurationContext*)data
474 cdpContext:(OTICDPRecordContext*)cdpContext
475 escrowRecord:(OTEscrowRecord*)escrowRecord
476 error:(NSError**)error
479 secnotice("octagontrust-performEscrowRecovery", "performEscrowRecovery invoked for context:%@, altdsid:%@", data.context, data.altDSID);
481 if ([self isCloudServicesAvailable] == NO) {
483 *error = [NSError errorWithDomain:NSOSStatusErrorDomain code:errSecUnimplemented userInfo:nil];
488 OctagonSignpost performEscrowRecoverySignpost = OctagonSignpostBegin(OctagonSignpostNamePerformEscrowRecovery);
489 bool subTaskSuccess = true;
491 id<OctagonEscrowRecovererPrococol> sb = data.sbd ?: [[getSecureBackupClass() alloc] init];
492 NSDictionary* recoveredInformation = nil;
493 NSError* recoverError = nil;
495 BOOL supportedRestorePath = [OTEscrowTranslation supportedRestorePath:cdpContext];
496 secnotice("octagontrust-performEscrowRecovery", "restore path is supported? %@", supportedRestorePath ? @"YES" : @"NO");
498 if (OctagonIsOptimizationEnabled() && supportedRestorePath) {
499 secnotice("octagontrust-performEscrowRecovery", "optimization flag turned on");
500 OctagonSignpost recoverFromSBDSignPost = OctagonSignpostBegin(OctagonSignpostNameRecoverWithCDPContext);
501 recoveredInformation = [sb recoverWithCDPContext:cdpContext escrowRecord:escrowRecord error:&recoverError];
502 subTaskSuccess = (recoverError == nil) ? true : false;
503 OctagonSignpostEnd(recoverFromSBDSignPost, OctagonSignpostNameRecoverWithCDPContext, OctagonSignpostNumber1(OctagonSignpostNameRecoverWithCDPContext), (int)subTaskSuccess);
505 secnotice("octagontrust-performEscrowRecovery", "optimization flag turned off");
506 NSMutableDictionary* sbdRecoveryArguments = [[OTEscrowTranslation CDPRecordContextToDictionary:cdpContext] mutableCopy];
507 NSDictionary* metadata = [OTEscrowTranslation metadataToDictionary:escrowRecord.escrowInformationMetadata];
508 sbdRecoveryArguments[getkSecureBackupMetadataKey()] = metadata;
509 sbdRecoveryArguments[getkSecureBackupRecordIDKey()] = escrowRecord.recordId;
510 secnotice("octagontrust-performEscrowRecovery", "using sbdRecoveryArguments: %@", sbdRecoveryArguments);
512 OctagonSignpost recoverFromSBDSignPost = OctagonSignpostBegin(OctagonSignpostNamePerformRecoveryFromSBD);
513 recoverError = [sb recoverWithInfo:sbdRecoveryArguments results:&recoveredInformation];
514 subTaskSuccess = (recoverError == nil) ? true : false;
515 OctagonSignpostEnd(recoverFromSBDSignPost, OctagonSignpostNamePerformRecoveryFromSBD, OctagonSignpostNumber1(OctagonSignpostNamePerformRecoveryFromSBD), (int)subTaskSuccess);
518 OTEscrowRecord_SOSViability viableForSOS = escrowRecord.viabilityStatus;
520 OTClique* clique = [OTClique handleRecoveryResults:data recoveredInformation:recoveredInformation sosViability:viableForSOS performedSilentBurn:NO recoverError:recoverError error:error];
523 subTaskSuccess = false;
524 OctagonSignpostEnd(performEscrowRecoverySignpost, OctagonSignpostNamePerformEscrowRecovery, OctagonSignpostNumber1(OctagonSignpostNamePerformEscrowRecovery), (int)subTaskSuccess);
526 OctagonSignpostEnd(performEscrowRecoverySignpost, OctagonSignpostNamePerformEscrowRecovery, OctagonSignpostNumber1(OctagonSignpostNamePerformEscrowRecovery), (int)subTaskSuccess);
532 *error = [NSError errorWithDomain:NSOSStatusErrorDomain code:errSecUnimplemented userInfo:nil];
538 + (OTEscrowRecord* _Nullable) recordMatchingLabel:(NSString*)label allRecords:(NSArray<OTEscrowRecord*>*)allRecords
540 for(OTEscrowRecord* record in allRecords) {
541 if ([record.label isEqualToString:label]) {
548 + (instancetype _Nullable)performSilentEscrowRecovery:(OTConfigurationContext*)data
549 cdpContext:(OTICDPRecordContext*)cdpContext
550 allRecords:(NSArray<OTEscrowRecord*>*)allRecords
551 error:(NSError**)error
554 secnotice("octagontrust-performSilentEscrowRecovery", "performSilentEscrowRecovery invoked for context:%@, altdsid:%@", data.context, data.altDSID);
556 if ([self isCloudServicesAvailable] == NO) {
558 *error = [NSError errorWithDomain:NSOSStatusErrorDomain code:errSecUnimplemented userInfo:nil];
563 OctagonSignpost performSilentEscrowRecoverySignpost = OctagonSignpostBegin(OctagonSignpostNamePerformSilentEscrowRecovery);
564 bool subTaskSuccess = true;
566 id<OctagonEscrowRecovererPrococol> sb = data.sbd ?: [[getSecureBackupClass() alloc] init];
567 NSDictionary* recoveredInformation = nil;
568 NSError* recoverError = nil;
570 BOOL supportedRestorePath = [OTEscrowTranslation supportedRestorePath:cdpContext];
571 secnotice("octagontrust-performSilentEscrowRecovery", "restore path is supported? %@", supportedRestorePath ? @"YES" : @"NO");
573 if (OctagonIsOptimizationEnabled() && supportedRestorePath) {
574 secnotice("octagontrust-performSilentEscrowRecovery", "optimization flag turned on");
575 OctagonSignpost recoverFromSBDSignPost = OctagonSignpostBegin(OctagonSignpostNameRecoverSilentWithCDPContext);
576 recoveredInformation = [sb recoverSilentWithCDPContext:cdpContext allRecords:allRecords error:&recoverError];
577 subTaskSuccess = (recoverError == nil) ? true : false;
578 OctagonSignpostEnd(recoverFromSBDSignPost, OctagonSignpostNameRecoverSilentWithCDPContext, OctagonSignpostNumber1(OctagonSignpostNameRecoverSilentWithCDPContext), (int)subTaskSuccess);
580 secnotice("octagontrust-performSilentEscrowRecovery", "optimization flag turned off");
581 NSDictionary* sbdRecoveryArguments = [OTEscrowTranslation CDPRecordContextToDictionary:cdpContext];
583 OctagonSignpost recoverFromSBDSignPost = OctagonSignpostBegin(OctagonSignpostNamePerformRecoveryFromSBD);
584 recoverError = [sb recoverWithInfo:sbdRecoveryArguments results:&recoveredInformation];
585 subTaskSuccess = (recoverError == nil) ? true : false;
586 OctagonSignpostEnd(recoverFromSBDSignPost, OctagonSignpostNamePerformRecoveryFromSBD, OctagonSignpostNumber1(OctagonSignpostNamePerformRecoveryFromSBD), (int)subTaskSuccess);
589 NSString* label = recoveredInformation[getkSecureBackupRecordLabelKey()];
590 OTEscrowRecord* chosenRecord = [OTClique recordMatchingLabel:label allRecords:allRecords];
591 OTEscrowRecord_SOSViability viableForSOS = chosenRecord ? chosenRecord.viabilityStatus : OTEscrowRecord_SOSViability_SOS_VIABLE_UNKNOWN;
593 OTClique *clique = [OTClique handleRecoveryResults:data recoveredInformation:recoveredInformation sosViability:viableForSOS performedSilentBurn:YES recoverError:recoverError error:error];
596 subTaskSuccess = false;
597 OctagonSignpostEnd(performSilentEscrowRecoverySignpost, OctagonSignpostNamePerformSilentEscrowRecovery, OctagonSignpostNumber1(OctagonSignpostNamePerformSilentEscrowRecovery), (int)subTaskSuccess);
599 OctagonSignpostEnd(performSilentEscrowRecoverySignpost, OctagonSignpostNamePerformSilentEscrowRecovery, OctagonSignpostNumber1(OctagonSignpostNamePerformSilentEscrowRecovery), (int)subTaskSuccess);
606 *error = [NSError errorWithDomain:NSOSStatusErrorDomain code:errSecUnimplemented userInfo:nil];
612 + (BOOL) invalidateEscrowCache:(OTConfigurationContext*)configurationContext error:(NSError**)error
615 secnotice("octagontrust-invalidateEscrowCache", "invalidateEscrowCache invoked for context:%@, altdsid:%@", configurationContext.context, configurationContext.altDSID);
616 __block NSError* localError = nil;
617 __block BOOL invalidatedCache = NO;
619 OTControl *control = [configurationContext makeOTControl:&localError];
621 secnotice("clique-invalidateEscrowCache", "unable to create otcontrol: %@", localError);
625 return invalidatedCache;
628 [control invalidateEscrowCache:OTCKContainerName
629 contextID:configurationContext.context
630 reply:^(NSError * _Nullable invalidateError) {
631 if(invalidateError) {
632 secnotice("clique-invalidateEscrowCache", "invalidateEscrowCache errored: %@", invalidateError);
634 secnotice("clique-invalidateEscrowCache", "invalidateEscrowCache succeeded");
635 invalidatedCache = YES;
637 localError = invalidateError;
640 if(error && localError) {
644 secnotice("clique-invalidateEscrowCache", "invalidateEscrowCache complete");
646 return invalidatedCache;
649 *error = [NSError errorWithDomain:NSOSStatusErrorDomain code:errSecUnimplemented userInfo:nil];