]> git.saurik.com Git - apple/security.git/blob - keychain/OctagonTrust/OctagonTrust.m
Security-59754.41.1.tar.gz
[apple/security.git] / keychain / OctagonTrust / OctagonTrust.m
1 /*
2 * Copyright (c) 2020 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 "OctagonTrust.h"
27 #import <Security/OTClique+Private.h>
28 #import <utilities/debugging.h>
29 #import "OTEscrowTranslation.h"
30
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>
40
41 SOFT_LINK_FRAMEWORK(PrivateFrameworks, KeychainCircle);
42 SOFT_LINK_OPTIONAL_FRAMEWORK(PrivateFrameworks, CloudServices);
43
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*);
51
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*);
56
57 #pragma clang diagnostic pop
58
59 static NSString * const kOTEscrowAuthKey = @"kOTEscrowAuthKey";
60 NSString* OTCKContainerName = @"com.apple.security.keychain";
61
62 @implementation OTConfigurationContext(Framework)
63
64 @dynamic escrowAuth;
65
66 -(void) setEscrowAuth:(id)e
67 {
68 objc_setAssociatedObject(self, (__bridge const void * _Nonnull)(kOTEscrowAuthKey), e, OBJC_ASSOCIATION_ASSIGN);
69
70 }
71 - (id) escrowAuth
72 {
73 return objc_getAssociatedObject(self, (__bridge const void * _Nonnull)(kOTEscrowAuthKey));
74 }
75
76 @end
77
78 @implementation OTClique(Framework)
79
80 + (NSArray<OTEscrowRecord*>*)filterViableSOSRecords:(NSArray<OTEscrowRecord*>*)nonViable
81 {
82 NSMutableArray<OTEscrowRecord*>* viableSOS = [NSMutableArray array];
83 for(OTEscrowRecord* record in nonViable) {
84 if (record.viabilityStatus == OTEscrowRecord_SOSViability_SOS_VIABLE) {
85 [viableSOS addObject:record];
86 }
87 }
88
89 return viableSOS;
90 }
91
92 /*
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
99
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
106 */
107
108 + (NSArray<OTEscrowRecord*>* _Nullable)filterRecords:(NSArray<OTEscrowRecord*>*)allRecords
109 {
110 NSMutableArray<OTEscrowRecord*>* viable = [NSMutableArray array];
111 NSMutableArray<OTEscrowRecord*>* partial = [NSMutableArray array];
112 NSMutableArray<OTEscrowRecord*>* nonViable = [NSMutableArray array];
113
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];
123 } else {
124 [nonViable addObject:record];
125 }
126 }
127
128 for(OTEscrowRecord* record in viable) {
129 secnotice("octagontrust-fetchescrowrecords", "viable record: %@ serial:%@ bottleID:%@ silent allowed:%d",
130 record.label,
131 record.escrowInformationMetadata.serial,
132 record.escrowInformationMetadata.bottleId,
133 (int)record.silentAttemptAllowed);
134 }
135 for(OTEscrowRecord* record in partial) {
136 secnotice("octagontrust-fetchescrowrecords", "partially viable record: %@ serial:%@ bottleID:%@ silent allowed:%d",
137 record.label,
138 record.escrowInformationMetadata.serial,
139 record.escrowInformationMetadata.bottleId,
140 (int)record.silentAttemptAllowed);
141 }
142 for(OTEscrowRecord* record in nonViable) {
143 secnotice("octagontrust-fetchescrowrecords", "nonviable record: %@ serial:%@ bottleID:%@ silent allowed:%d",
144 record.label,
145 record.escrowInformationMetadata.serial,
146 record.escrowInformationMetadata.bottleId,
147 (int)record.silentAttemptAllowed);
148 }
149
150 if ([viable count] > 0) {
151 secnotice("octagontrust-fetchescrowrecords", "Returning %d viable records", (int)[viable count]);
152 return viable;
153 }
154
155 if ([partial count] > 0) {
156 secnotice("octagontrust-fetchescrowrecords", "Returning %d partially viable records", (int)[partial count]);
157 return partial;
158 }
159
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;
164 }
165
166 secnotice("octagontrust-fetchescrowrecords", "no viable records!");
167
168 return nil;
169 }
170
171 + (NSArray<OTEscrowRecord*>* _Nullable)fetchAndHandleEscrowRecords:(OTConfigurationContext*)data shouldFilter:(BOOL)shouldFiler error:(NSError**)error
172 {
173 OctagonSignpost signPost = OctagonSignpostBegin(OctagonSignpostNameFetchEscrowRecords);
174 bool subTaskSuccess = false;
175
176 NSError* localError = nil;
177 NSArray* escrowRecordDatas = [OTClique fetchEscrowRecordsInternal:data error:&localError];
178 if(localError) {
179 secerror("octagontrust-fetchAndHandleEscrowRecords: failed to fetch escrow records: %@", localError);
180 if(error){
181 *error = localError;
182 }
183 OctagonSignpostEnd(signPost, OctagonSignpostNameFetchEscrowRecords, OctagonSignpostNumber1(OctagonSignpostNameFetchEscrowRecords), (int)subTaskSuccess);
184 return nil;
185 }
186
187 NSMutableArray<OTEscrowRecord*>* escrowRecords = [NSMutableArray array];
188
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";
195 break;
196 case OTEscrowRecord_RecordViability_RECORD_VIABILITY_PARTIALLY_VIABLE:
197 translated.escrowInformationMetadata.bottleValidity = @"valid";
198 break;
199 case OTEscrowRecord_RecordViability_RECORD_VIABILITY_LEGACY:
200 translated.escrowInformationMetadata.bottleValidity = @"invalid";
201 break;
202 }
203 }
204 if(translated.recordId == nil || [translated.recordId isEqualToString:@""]){
205 translated.recordId = [[translated.label stringByReplacingOccurrencesOfString:OTEscrowRecordPrefix withString:@""] mutableCopy];
206 }
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);
210 if(cfError != NULL){
211 secnotice("octagontrust-handleEscrowRecords", "failed to create sos peer info: %@", cfError);
212 } else {
213 translated.serialNumber = (NSString*)CFBridgingRelease(SOSPeerInfoCopySerialNumber(peer));
214 }
215 }
216
217 [escrowRecords addObject:translated];
218 }
219 subTaskSuccess = true;
220 OctagonSignpostEnd(signPost, OctagonSignpostNameFetchEscrowRecords, OctagonSignpostNumber1(OctagonSignpostNameFetchEscrowRecords), (int)subTaskSuccess);
221
222 if (shouldFiler == YES) {
223 return [OTClique filterRecords:escrowRecords];
224 }
225
226 return escrowRecords;
227 }
228
229 + (NSArray<OTEscrowRecord*>* _Nullable)fetchAllEscrowRecords:(OTConfigurationContext*)data error:(NSError**)error
230 {
231 #if OCTAGON
232 secnotice("octagontrust-fetchallescrowrecords", "fetching all escrow records for context :%@, altdsid:%@", data.context, data.altDSID);
233 return [OTClique fetchAndHandleEscrowRecords:data shouldFilter:NO error:error];
234 #else
235 if (error) {
236 *error = [NSError errorWithDomain:NSOSStatusErrorDomain code:errSecUnimplemented userInfo:nil];
237 }
238 return nil;
239 #endif
240 }
241
242 + (NSArray<OTEscrowRecord*>* _Nullable)fetchEscrowRecords:(OTConfigurationContext*)data error:(NSError**)error
243 {
244 #if OCTAGON
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];
248 } else {
249 if ([OTClique isCloudServicesAvailable] == NO) {
250 if (error) {
251 *error = [NSError errorWithDomain:NSOSStatusErrorDomain code:errSecUnimplemented userInfo:nil];
252 }
253 return NULL;
254 }
255
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];
259
260 OTEscrowAuthenticationInformation *escrowAuth = nil;
261 NSDictionary* escrowAuthDictionary = nil;
262 if(data.escrowAuth != nil) {
263 escrowAuth = data.escrowAuth;
264 escrowAuthDictionary = [OTEscrowTranslation escrowAuthenticationInfoToDictionary:escrowAuth];
265 }
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);
271 if(error){
272 *error = recoverError;
273 }
274 OctagonSignpostEnd(signPost, OctagonSignpostNameGetAccountInfo, OctagonSignpostNumber1(OctagonSignpostNameGetAccountInfo), (int)subTaskSuccess);
275 return nil;
276 } else {
277 secnotice("octagontrust-fetchescrowrecords", "recovered accountWithInfo results: %@", results);
278 NSArray *icdpRecords = results[getkSecureBackupiCDPRecordsKey()];
279 secnotice("octagontrust-fetchescrowrecords", "recovered iCDP records: %@", icdpRecords);
280
281 NSMutableArray<OTEscrowRecord*>* records = [NSMutableArray array];
282 for (NSDictionary *dictionaryRecord in icdpRecords) {
283 OTEscrowRecord* otEscrowRecord = [OTEscrowTranslation dictionaryToEscrowRecord:dictionaryRecord];
284 [records addObject:otEscrowRecord];
285 }
286 secnotice("octagontrust-fetchescrowrecords", "translated dictionary records to escrow record protos: %@", records);
287
288 subTaskSuccess = true;
289 OctagonSignpostEnd(signPost, OctagonSignpostNameGetAccountInfo, OctagonSignpostNumber1(OctagonSignpostNameGetAccountInfo), (int)subTaskSuccess);
290
291 return records;
292 }
293 }
294 #else
295 if (error) {
296 *error = [NSError errorWithDomain:NSOSStatusErrorDomain code:errSecUnimplemented userInfo:nil];
297 }
298 return nil;
299 #endif
300 }
301
302 + (OTClique* _Nullable)handleRecoveryResults:(OTConfigurationContext*)data recoveredInformation:(NSDictionary*)recoveredInformation sosViability:(OTEscrowRecord_SOSViability)sosViability performedSilentBurn:(BOOL)performedSilentBurn recoverError:(NSError*)recoverError error:(NSError**)error
303 {
304 if ([self isCloudServicesAvailable] == NO) {
305 if (error) {
306 *error = [NSError errorWithDomain:NSOSStatusErrorDomain code:errSecUnimplemented userInfo:nil];
307 }
308 return nil;
309 }
310
311 OTClique* clique = [[OTClique alloc] initWithContextData:data];
312 BOOL resetToOfferingOccured = NO;
313
314 if(recoverError) {
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);
324 } else {
325 secnotice("octagontrust-handleRecoveryResults", "resetting SOS circle successful");
326 }
327 CFReleaseNull(resetToOfferingError);
328 } else {
329 secnotice("octagontrust-handleRecoveryResults", "Legacy restore failed on a non-SOS platform");
330 }
331 } else {
332 if(error) {
333 *error = recoverError;
334 }
335 return nil;
336 }
337 }
338
339 NSError* localError = nil;
340 OTControl* control = nil;
341
342 if (data.otControl) {
343 control = data.otControl;
344 } else {
345 control = [data makeOTControl:&localError];
346 }
347 if (!control) {
348 secerror("octagontrust-handleRecoveryResults: unable to create otcontrol: %@", localError);
349 if (error) {
350 *error = localError;
351 }
352 return nil;
353 }
354
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;
360
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);
365
366 //restore bottle!
367 [control restore:OTCKContainerName
368 contextID:data.context
369 bottleSalt:data.altDSID
370 entropy:bottledPeerEntropy
371 bottleID:bottleID
372 reply:^(NSError * _Nullable restoreError) {
373 if(restoreError) {
374 secnotice("octagontrust-handleRecoveryResults", "restore bottle errored: %@", restoreError);
375 } else {
376 secnotice("octagontrust-handleRecoveryResults", "restoring bottle succeeded");
377 }
378 restoreBottleError = restoreError;
379 if (performedSilentBurn) {
380 OctagonSignpostEnd(bottleRestoreSignPost, OctagonSignpostNamePerformOctagonJoinForSilent, OctagonSignpostNumber1(OctagonSignpostNamePerformOctagonJoinForSilent), (int)true);
381 } else {
382 OctagonSignpostEnd(bottleRestoreSignPost, OctagonSignpostNamePerformOctagonJoinForNonSilent, OctagonSignpostNumber1(OctagonSignpostNamePerformOctagonJoinForNonSilent), (int)true);
383 }
384 }];
385
386 if(restoreBottleError) {
387 if(error){
388 *error = restoreBottleError;
389 }
390 return nil;
391 }
392 } else {
393 shouldResetOctagon = true;
394 }
395
396 if(shouldResetOctagon) {
397 secnotice("octagontrust-handleRecoveryResults", "bottle %@ is not valid, resetting octagon", bottleID);
398 NSError* resetError = nil;
399 [clique resetAndEstablish:CuttlefishResetReasonNoBottleDuringEscrowRecovery error:&resetError];
400 if(resetError) {
401 secerror("octagontrust-handleRecoveryResults: failed to reset octagon: %@", resetError);
402 if(error){
403 *error = resetError;
404 }
405 return nil;
406 } else{
407 secnotice("octagontrust-handleRecoveryResults", "reset octagon succeeded");
408 }
409 }
410
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);
419 } else {
420 secnotice("octagontrust-handleRecoveryResults", "resetting SOS circle successful");
421 }
422 CFReleaseNull(resetToOfferingError);
423 } else {
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);
429 } else {
430 secnotice("octagontrust-handleRecoveryResults", "joinAfterRestore succeeded");
431 }
432 }
433 }
434
435
436 // call SBD to kick off keychain data restore
437 id<OctagonEscrowRecovererPrococol> sb = data.sbd ?: [[getSecureBackupClass() alloc] init];
438 NSError* restoreError = nil;
439
440 NSMutableSet <NSString *> *viewsNotToBeRestored = [NSMutableSet set];
441 [viewsNotToBeRestored addObject:@"iCloudIdentity"];
442 [viewsNotToBeRestored addObject:@"PCS-MasterKey"];
443 [viewsNotToBeRestored addObject:@"KeychainV0"];
444
445 NSDictionary *escrowRecords = recoveredInformation[getkEscrowServiceRecordDataKey()];
446 if (escrowRecords == nil) {
447 secnotice("octagontrust-handleRecoveryResults", "unable to request keychain restore, record data missing");
448 return clique;
449 }
450
451
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");
456 return clique;
457 }
458
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];
465
466 if (restoreError) {
467 secerror("octagontrust-handleRecoveryResults: error restoring keychain items: %@", restoreError);
468 }
469
470 return clique;
471 }
472
473 + (instancetype _Nullable)performEscrowRecovery:(OTConfigurationContext*)data
474 cdpContext:(OTICDPRecordContext*)cdpContext
475 escrowRecord:(OTEscrowRecord*)escrowRecord
476 error:(NSError**)error
477 {
478 #if OCTAGON
479 secnotice("octagontrust-performEscrowRecovery", "performEscrowRecovery invoked for context:%@, altdsid:%@", data.context, data.altDSID);
480
481 if ([self isCloudServicesAvailable] == NO) {
482 if (error) {
483 *error = [NSError errorWithDomain:NSOSStatusErrorDomain code:errSecUnimplemented userInfo:nil];
484 }
485 return nil;
486 }
487
488 OctagonSignpost performEscrowRecoverySignpost = OctagonSignpostBegin(OctagonSignpostNamePerformEscrowRecovery);
489 bool subTaskSuccess = true;
490
491 id<OctagonEscrowRecovererPrococol> sb = data.sbd ?: [[getSecureBackupClass() alloc] init];
492 NSDictionary* recoveredInformation = nil;
493 NSError* recoverError = nil;
494
495 BOOL supportedRestorePath = [OTEscrowTranslation supportedRestorePath:cdpContext];
496 secnotice("octagontrust-performEscrowRecovery", "restore path is supported? %@", supportedRestorePath ? @"YES" : @"NO");
497
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);
504 } else {
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);
511
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);
516 }
517
518 OTEscrowRecord_SOSViability viableForSOS = escrowRecord.viabilityStatus;
519
520 OTClique* clique = [OTClique handleRecoveryResults:data recoveredInformation:recoveredInformation sosViability:viableForSOS performedSilentBurn:NO recoverError:recoverError error:error];
521
522 if(recoverError) {
523 subTaskSuccess = false;
524 OctagonSignpostEnd(performEscrowRecoverySignpost, OctagonSignpostNamePerformEscrowRecovery, OctagonSignpostNumber1(OctagonSignpostNamePerformEscrowRecovery), (int)subTaskSuccess);
525 } else {
526 OctagonSignpostEnd(performEscrowRecoverySignpost, OctagonSignpostNamePerformEscrowRecovery, OctagonSignpostNumber1(OctagonSignpostNamePerformEscrowRecovery), (int)subTaskSuccess);
527 }
528 return clique;
529
530 #else
531 if (error) {
532 *error = [NSError errorWithDomain:NSOSStatusErrorDomain code:errSecUnimplemented userInfo:nil];
533 }
534 return nil;
535 #endif
536 }
537
538 + (OTEscrowRecord* _Nullable) recordMatchingLabel:(NSString*)label allRecords:(NSArray<OTEscrowRecord*>*)allRecords
539 {
540 for(OTEscrowRecord* record in allRecords) {
541 if ([record.label isEqualToString:label]) {
542 return record;
543 }
544 }
545 return nil;
546 }
547
548 + (instancetype _Nullable)performSilentEscrowRecovery:(OTConfigurationContext*)data
549 cdpContext:(OTICDPRecordContext*)cdpContext
550 allRecords:(NSArray<OTEscrowRecord*>*)allRecords
551 error:(NSError**)error
552 {
553 #if OCTAGON
554 secnotice("octagontrust-performSilentEscrowRecovery", "performSilentEscrowRecovery invoked for context:%@, altdsid:%@", data.context, data.altDSID);
555
556 if ([self isCloudServicesAvailable] == NO) {
557 if (error) {
558 *error = [NSError errorWithDomain:NSOSStatusErrorDomain code:errSecUnimplemented userInfo:nil];
559 }
560 return nil;
561 }
562
563 OctagonSignpost performSilentEscrowRecoverySignpost = OctagonSignpostBegin(OctagonSignpostNamePerformSilentEscrowRecovery);
564 bool subTaskSuccess = true;
565
566 id<OctagonEscrowRecovererPrococol> sb = data.sbd ?: [[getSecureBackupClass() alloc] init];
567 NSDictionary* recoveredInformation = nil;
568 NSError* recoverError = nil;
569
570 BOOL supportedRestorePath = [OTEscrowTranslation supportedRestorePath:cdpContext];
571 secnotice("octagontrust-performSilentEscrowRecovery", "restore path is supported? %@", supportedRestorePath ? @"YES" : @"NO");
572
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);
579 } else {
580 secnotice("octagontrust-performSilentEscrowRecovery", "optimization flag turned off");
581 NSDictionary* sbdRecoveryArguments = [OTEscrowTranslation CDPRecordContextToDictionary:cdpContext];
582
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);
587 }
588
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;
592
593 OTClique *clique = [OTClique handleRecoveryResults:data recoveredInformation:recoveredInformation sosViability:viableForSOS performedSilentBurn:YES recoverError:recoverError error:error];
594
595 if(recoverError) {
596 subTaskSuccess = false;
597 OctagonSignpostEnd(performSilentEscrowRecoverySignpost, OctagonSignpostNamePerformSilentEscrowRecovery, OctagonSignpostNumber1(OctagonSignpostNamePerformSilentEscrowRecovery), (int)subTaskSuccess);
598 } else {
599 OctagonSignpostEnd(performSilentEscrowRecoverySignpost, OctagonSignpostNamePerformSilentEscrowRecovery, OctagonSignpostNumber1(OctagonSignpostNamePerformSilentEscrowRecovery), (int)subTaskSuccess);
600 }
601
602 return clique;
603
604 #else
605 if (error) {
606 *error = [NSError errorWithDomain:NSOSStatusErrorDomain code:errSecUnimplemented userInfo:nil];
607 }
608 return nil;
609 #endif
610 }
611
612 + (BOOL) invalidateEscrowCache:(OTConfigurationContext*)configurationContext error:(NSError**)error
613 {
614 #if OCTAGON
615 secnotice("octagontrust-invalidateEscrowCache", "invalidateEscrowCache invoked for context:%@, altdsid:%@", configurationContext.context, configurationContext.altDSID);
616 __block NSError* localError = nil;
617 __block BOOL invalidatedCache = NO;
618
619 OTControl *control = [configurationContext makeOTControl:&localError];
620 if (!control) {
621 secnotice("clique-invalidateEscrowCache", "unable to create otcontrol: %@", localError);
622 if (error) {
623 *error = localError;
624 }
625 return invalidatedCache;
626 }
627
628 [control invalidateEscrowCache:OTCKContainerName
629 contextID:configurationContext.context
630 reply:^(NSError * _Nullable invalidateError) {
631 if(invalidateError) {
632 secnotice("clique-invalidateEscrowCache", "invalidateEscrowCache errored: %@", invalidateError);
633 } else {
634 secnotice("clique-invalidateEscrowCache", "invalidateEscrowCache succeeded");
635 invalidatedCache = YES;
636 }
637 localError = invalidateError;
638 }];
639
640 if(error && localError) {
641 *error = localError;
642 }
643
644 secnotice("clique-invalidateEscrowCache", "invalidateEscrowCache complete");
645
646 return invalidatedCache;
647 #else
648 if (error) {
649 *error = [NSError errorWithDomain:NSOSStatusErrorDomain code:errSecUnimplemented userInfo:nil];
650 }
651 return NO;
652 #endif
653 }
654
655 @end
656
657 #endif