OctagonTrustTests-EscrowRecords.m   [plain text]

/*
* Copyright (c) 2020 Apple Inc. All Rights Reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
* This file contains Original Code and/or Modifications of Original Code
* as defined in and that are subject to the Apple Public Source License
* Version 2.0 (the 'License'). You may not use this file except in
* compliance with the License. Please obtain a copy of the License at
* http://www.opensource.apple.com/apsl/ and read it before using this
* file.
*
* The Original Code and all software distributed under the License are
* distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
* EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
* INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
* Please see the License for the specific language governing rights and
* limitations under the License.
*
* @APPLE_LICENSE_HEADER_END@
*/

#import <Foundation/Foundation.h>
#import <XCTest/XCTest.h>

#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wquoted-include-in-framework-header"
#import <OCMock/OCMock.h>
#pragma clang diagnostic pop

#import <OctagonTrust/OctagonTrust.h>
#import "OctagonTrustTests-EscrowTestVectors.h"

#import "keychain/ot/OTConstants.h"
#import "keychain/ot/OTManager.h"
#import "keychain/ot/OTDefines.h"
#import "keychain/ot/OTClique+Private.h"

#import "keychain/OctagonTrust/categories/OctagonTrustEscrowRecoverer.h"

#import <Foundation/NSPropertyList.h>

#import "keychain/ckks/tests/MockCloudKit.h"
#import "keychain/ckks/tests/CKKSMockSOSPresentAdapter.h"
#import "utilities/SecCFError.h"
#import "keychain/categories/NSError+UsefulConstructors.h"

#import "OctagonTrustTests.h"

@implementation ProxyXPCConnection

- (instancetype)initWithInterface:(NSXPCInterface*)interface obj:(id)obj
{
if(self = [super init]) {
self.obj = obj;
self.serverInterface = interface;
self.listener = [NSXPCListener anonymousListener];

self.listener.delegate = self;
[self.listener resume];
}

return self;
}

- (NSXPCConnection*)connection
{
NSXPCConnection* connection = [[NSXPCConnection alloc] initWithListenerEndpoint:self.listener.endpoint];
connection.remoteObjectInterface = self.serverInterface;
[connection resume];
return connection;
}

- (BOOL)listener:(NSXPCListener*)listener newConnection:(NSXPCConnection*)newConnection {
newConnection.exportedInterface = self.serverInterface;
newConnection.exportedObject = self.obj;
[newConnection resume];
return true;
}

@end

@interface FakeOTControlEntitlementBearer : NSObject <OctagonEntitlementBearerProtocol>
@property NSMutableDictionary* entitlements;
-(instancetype)init;
- (nullable id)valueForEntitlement:(NSString *)entitlement;
@end

@implementation FakeOTControlEntitlementBearer

-(instancetype)init
{
if(self = [super init]) {
self.entitlements = [NSMutableDictionary dictionary];
self.entitlements[kSecEntitlementPrivateOctagonEscrow] = @YES;
}
return self;
}
-(id)valueForEntitlement:(NSString*)entitlement
{
return self.entitlements[entitlement];
}

@end

@interface OTMockSecureBackup : NSObject <OctagonEscrowRecovererPrococol>
@property (nonatomic) NSString * bottleID;
@property (nonatomic) NSData* entropy;

@end

@implementation OTMockSecureBackup

-(instancetype) initWithBottleID:(NSString*)b entropy:(NSData*)e {
if(self = [super init]) {
self.bottleID = b;
self.entropy = e;
}
return self;
}

- (NSError *)disableWithInfo:(NSDictionary *)info {
return nil;
}

- (NSError *)getAccountInfoWithInfo:(NSDictionary *)info results:(NSDictionary *__autoreleasing *)results {
NSError* localError = nil;
NSData *testVectorData = [accountInfoWithInfoSample dataUsingEncoding:kCFStringEncodingUTF8];
NSPropertyListFormat format;
NSDictionary *testVectorPlist = [NSPropertyListSerialization propertyListWithData:testVectorData options: NSPropertyListMutableContainersAndLeaves format:&format error:&localError];
*results = testVectorPlist;

return nil;
}

- (NSDictionary *)recoverSilentWithCDPContext:(OTICDPRecordContext *)cdpContext allRecords:(NSArray<OTEscrowRecord *> *)allRecords error:(NSError *__autoreleasing *)error {
return nil;
}

- (NSDictionary *)recoverWithCDPContext:(OTICDPRecordContext *)cdpContext escrowRecord:(OTEscrowRecord *)escrowRecord error:(NSError *__autoreleasing *)error {
return nil;
}

- (NSError *)recoverWithInfo:(NSDictionary *)info results:(NSDictionary *__autoreleasing *)results {
return nil;
}

- (void)restoreKeychainAsyncWithPassword:(id)password keybagDigest:(NSData *)keybagDigest haveBottledPeer:(BOOL)haveBottledPeer viewsNotToBeRestored:(NSMutableSet<NSString *> *)viewsNotToBeRestored error:(NSError *__autoreleasing *)error {
}

@end

@implementation OctagonTrustTests

- (OTEscrowRecord*)createLegacyRecord
{
OTEscrowRecord *nonViableRecord = [[OTEscrowRecord alloc] init];
nonViableRecord.creationDate = [NSDate date].timeIntervalSince1970;
nonViableRecord.label = [[NSUUID UUID] UUIDString];
nonViableRecord.recordStatus = OTEscrowRecord_RecordStatus_RECORD_STATUS_VALID;
nonViableRecord.recordViability = OTEscrowRecord_RecordViability_RECORD_VIABILITY_LEGACY;
nonViableRecord.serialNumber = [[NSUUID UUID] UUIDString];
nonViableRecord.silentAttemptAllowed = 1;
nonViableRecord.escrowInformationMetadata = [[OTEscrowRecordMetadata alloc] init];
nonViableRecord.escrowInformationMetadata.bottleValidity = @"invalid";

// It even has a bottleId! Not a useful one, though.
nonViableRecord.escrowInformationMetadata.bottleId = [[NSUUID UUID] UUIDString];
return nonViableRecord;
}

- (OTEscrowRecord*)createLegacyRecordWithSOSNotViable
{
OTEscrowRecord *nonViableRecord = [[OTEscrowRecord alloc] init];
nonViableRecord.creationDate = [NSDate date].timeIntervalSince1970;
nonViableRecord.label = [[NSUUID UUID] UUIDString];
nonViableRecord.recordStatus = OTEscrowRecord_RecordStatus_RECORD_STATUS_VALID;
nonViableRecord.recordViability = OTEscrowRecord_RecordViability_RECORD_VIABILITY_LEGACY;
nonViableRecord.viabilityStatus = OTEscrowRecord_SOSViability_SOS_NOT_VIABLE;
nonViableRecord.serialNumber = [[NSUUID UUID] UUIDString];
nonViableRecord.silentAttemptAllowed = 1;
nonViableRecord.escrowInformationMetadata = [[OTEscrowRecordMetadata alloc] init];
nonViableRecord.escrowInformationMetadata.bottleValidity = @"invalid";

// It even has a bottleId! Not a useful one, though.
nonViableRecord.escrowInformationMetadata.bottleId = [[NSUUID UUID] UUIDString];
return nonViableRecord;
}

- (OTEscrowRecord*)createPartialRecord
{
OTEscrowRecord *partiallyViableRecord = [[OTEscrowRecord alloc] init];
partiallyViableRecord.creationDate = [NSDate date].timeIntervalSince1970;
partiallyViableRecord.label = [[NSUUID UUID] UUIDString];
partiallyViableRecord.recordStatus = OTEscrowRecord_RecordStatus_RECORD_STATUS_VALID;
partiallyViableRecord.recordViability = OTEscrowRecord_RecordViability_RECORD_VIABILITY_PARTIALLY_VIABLE;
partiallyViableRecord.escrowInformationMetadata = [[OTEscrowRecordMetadata alloc] init];
partiallyViableRecord.escrowInformationMetadata.bottleValidity = @"valid";
partiallyViableRecord.escrowInformationMetadata.bottleId = [[NSUUID UUID] UUIDString];

partiallyViableRecord.serialNumber =[[NSUUID UUID] UUIDString];
partiallyViableRecord.silentAttemptAllowed = 1;
return partiallyViableRecord;
}

- (OTEscrowRecord*)createOctagonViableSOSViableRecord
{
OTEscrowRecord *octagonAndSOSViableRecord = [[OTEscrowRecord alloc] init];
octagonAndSOSViableRecord.creationDate = [NSDate date].timeIntervalSince1970;
octagonAndSOSViableRecord.label = [[NSUUID UUID] UUIDString];
octagonAndSOSViableRecord.recordStatus = OTEscrowRecord_RecordStatus_RECORD_STATUS_VALID;
octagonAndSOSViableRecord.recordViability = OTEscrowRecord_RecordViability_RECORD_VIABILITY_FULLY_VIABLE;
octagonAndSOSViableRecord.viabilityStatus = OTEscrowRecord_SOSViability_SOS_VIABLE;

octagonAndSOSViableRecord.escrowInformationMetadata = [[OTEscrowRecordMetadata alloc] init];
octagonAndSOSViableRecord.escrowInformationMetadata.bottleValidity = @"valid";
octagonAndSOSViableRecord.escrowInformationMetadata.bottleId = [[NSUUID UUID] UUIDString];

octagonAndSOSViableRecord.serialNumber =[[NSUUID UUID] UUIDString];
octagonAndSOSViableRecord.silentAttemptAllowed = 1;
return octagonAndSOSViableRecord;
}

- (OTEscrowRecord*)createOctagonNotViableSOSViableRecord
{
OTEscrowRecord *octagonAndSOSViableRecord = [[OTEscrowRecord alloc] init];
octagonAndSOSViableRecord.creationDate = [NSDate date].timeIntervalSince1970;
octagonAndSOSViableRecord.label = [[NSUUID UUID] UUIDString];
octagonAndSOSViableRecord.recordStatus = OTEscrowRecord_RecordStatus_RECORD_STATUS_VALID;
octagonAndSOSViableRecord.recordViability = OTEscrowRecord_RecordViability_RECORD_VIABILITY_LEGACY;
octagonAndSOSViableRecord.viabilityStatus = OTEscrowRecord_SOSViability_SOS_VIABLE;

octagonAndSOSViableRecord.escrowInformationMetadata = [[OTEscrowRecordMetadata alloc] init];
octagonAndSOSViableRecord.escrowInformationMetadata.bottleValidity = @"invalid";
octagonAndSOSViableRecord.escrowInformationMetadata.bottleId = [[NSUUID UUID] UUIDString];

octagonAndSOSViableRecord.serialNumber =[[NSUUID UUID] UUIDString];
octagonAndSOSViableRecord.silentAttemptAllowed = 1;
return octagonAndSOSViableRecord;
}

- (NSArray<NSData*>* _Nullable)mockFetchEscrowRecordsInternalReturnOneRecord:(OTConfigurationContext*)data
error:(NSError* __autoreleasing *)error
{
NSError* localError = nil;
NSData *testVectorData = [accountInfoWithInfoSample dataUsingEncoding:kCFStringEncodingUTF8];
NSPropertyListFormat format;
NSDictionary *testVectorPlist = [NSPropertyListSerialization propertyListWithData:testVectorData options: NSPropertyListMutableContainersAndLeaves format:&format error:&localError];

NSArray *testVectorICDPRecords = testVectorPlist[@"SecureBackupAlliCDPRecords"];
XCTAssertNotNil(testVectorICDPRecords, "should not be nil");

NSDictionary *testVectorRecord = testVectorICDPRecords[0];

OTEscrowRecord *record = [OTEscrowTranslation dictionaryToEscrowRecord:testVectorRecord];

NSArray *records = @[record.data];
return records;
}

- (NSArray<NSData*>* _Nullable)mockFetchEscrowRecordsInternalReturnMixedRecords:(OTConfigurationContext*)data
error:(NSError* __autoreleasing *)error
{
NSError* localError = nil;
NSData *testVectorData = [accountInfoWithInfoSample dataUsingEncoding:kCFStringEncodingUTF8];
NSPropertyListFormat format;
NSDictionary *testVectorPlist = [NSPropertyListSerialization propertyListWithData:testVectorData options: NSPropertyListMutableContainersAndLeaves format:&format error:&localError];

NSArray *testVectorICDPRecords = testVectorPlist[@"SecureBackupAlliCDPRecords"];
XCTAssertNotNil(testVectorICDPRecords, "should not be nil");

NSDictionary *testVectorRecord = testVectorICDPRecords[0];

OTEscrowRecord *record = [OTEscrowTranslation dictionaryToEscrowRecord:testVectorRecord];

NSArray *records = @[record.data, [self createLegacyRecord].data, [self createLegacyRecord].data];
return records;
}

- (NSArray<NSData*>* _Nullable)mockFetchEscrowRecordsInternalReturnPartiallyViableRecords:(OTConfigurationContext*)data
error:(NSError* __autoreleasing *)error
{
return @[[self createPartialRecord].data, [self createPartialRecord].data];
}

- (NSArray<NSData*>* _Nullable)mockFetchEscrowRecordsInternalReturnOctagonViableSOSViableRecords:(OTConfigurationContext*)data
error:(NSError* __autoreleasing *)error
{
return @[[self createOctagonViableSOSViableRecord].data, [self createOctagonViableSOSViableRecord].data];
}

- (NSArray<NSData*>* _Nullable)mockFetchEscrowRecordsInternalReturnLegacyRecords:(OTConfigurationContext*)data
error:(NSError* __autoreleasing *)error
{
return @[[self createLegacyRecord].data, [self createLegacyRecord].data];
}

- (NSArray<NSData*>* _Nullable)mockFetchEscrowRecordsInternalReturnLegacyNonViableSOSRecords:(OTConfigurationContext*)data
error:(NSError* __autoreleasing *)error
{
return @[[self createLegacyRecordWithSOSNotViable].data, [self createLegacyRecordWithSOSNotViable].data];
}

- (NSArray<NSData*>* _Nullable)mockFetchEscrowRecordsInternalReturnOctagonNotViableSOSViableRecords:(OTConfigurationContext*)data
error:(NSError* __autoreleasing *)error
{
return @[[self createOctagonNotViableSOSViableRecord].data, [self createOctagonNotViableSOSViableRecord].data];
}

- (void)setUp
{
[super setUp];

FakeOTControlEntitlementBearer *otControlEntitlementBearer = [[FakeOTControlEntitlementBearer alloc]init];
id otControlEntitlementChecker = [OctagonXPCEntitlementChecker createWithManager:self.injectedOTManager entitlementBearer:otControlEntitlementBearer];

NSXPCInterface *interface = OTSetupControlProtocol([NSXPCInterface interfaceWithProtocol:@protocol(OTControlProtocol)]);
self.otXPCProxy = [[ProxyXPCConnection alloc] initWithInterface:interface obj: otControlEntitlementChecker];

self.otControl = [[OTControl alloc] initWithConnection:self.otXPCProxy.connection sync: true];

self.mockClique = OCMClassMock([OTClique class]);
}

- (void)testFetchOneViableEscrowRecord
{
OctagonSetOptimizationEnabled(true);

OTConfigurationContext *cliqueContextConfiguration = [[OTConfigurationContext alloc]init];
cliqueContextConfiguration.context = OTDefaultContext;
cliqueContextConfiguration.dsid = @"1234";
cliqueContextConfiguration.altDSID = @"altdsid";
cliqueContextConfiguration.otControl = self.otControl;

OCMStub([self.mockClique fetchEscrowRecordsInternal:[OCMArg any] error:[OCMArg anyObjectRef]]).andCall(self, @selector(mockFetchEscrowRecordsInternalReturnOneRecord:error:));

NSError* localErrror = nil;
NSArray* escrowRecords = [OTClique fetchEscrowRecords:cliqueContextConfiguration error:&localErrror];
XCTAssertNil(localErrror, "error should be nil");

XCTAssertEqual(escrowRecords.count, 1, "should only return 1 record");
OTEscrowRecord *firstRecord = escrowRecords[0];
XCTAssertTrue([firstRecord.label isEqualToString:@"com.apple.icdp.record"], "label should be com.apple.icdp.record");
XCTAssertTrue([firstRecord.serialNumber isEqualToString:@"C39V209AJ9L5"], "serial number should be C39V209AJ9L5");
XCTAssertTrue([firstRecord.escrowInformationMetadata.bottleValidity isEqualToString: @"valid"], "bottle validity should be valid");
XCTAssertEqual(firstRecord.recordViability, OTEscrowRecord_RecordViability_RECORD_VIABILITY_FULLY_VIABLE, "record viability should be fully viable");
}

- (void)testFetchTwoPartiallyViableEscrowRecords
{
OctagonSetOptimizationEnabled(true);

OTConfigurationContext *cliqueContextConfiguration = [[OTConfigurationContext alloc]init];
cliqueContextConfiguration.context = OTDefaultContext;
cliqueContextConfiguration.dsid = @"1234";
cliqueContextConfiguration.altDSID = @"altdsid";
cliqueContextConfiguration.otControl = self.otControl;

OCMStub([self.mockClique fetchEscrowRecordsInternal:[OCMArg any] error:[OCMArg anyObjectRef]]).andCall(self, @selector(mockFetchEscrowRecordsInternalReturnPartiallyViableRecords:error:));

NSError* localErrror = nil;
NSArray* escrowRecords = [OTClique fetchEscrowRecords:cliqueContextConfiguration error:&localErrror];
XCTAssertNil(localErrror, "error should be nil");

XCTAssertEqual(escrowRecords.count, 2, "should only return 2 records");
OTEscrowRecord *firstRecord = escrowRecords[0];
XCTAssertTrue([firstRecord.escrowInformationMetadata.bottleValidity isEqualToString: @"valid"], "bottle validity should be valid");
XCTAssertEqual(firstRecord.recordViability, OTEscrowRecord_RecordViability_RECORD_VIABILITY_PARTIALLY_VIABLE, "record viability should be partial");

OTEscrowRecord *secondRecord = escrowRecords[1];
XCTAssertTrue([secondRecord.escrowInformationMetadata.bottleValidity isEqualToString: @"valid"], "bottle validity should be valid");
XCTAssertEqual(secondRecord.recordViability, OTEscrowRecord_RecordViability_RECORD_VIABILITY_PARTIALLY_VIABLE, "record viability should be partial");
}

- (void)testFetchViableAndLegacyEscrowRecords
{
OctagonSetOptimizationEnabled(true);

OTConfigurationContext *cliqueContextConfiguration = [[OTConfigurationContext alloc]init];
cliqueContextConfiguration.context = OTDefaultContext;
cliqueContextConfiguration.dsid = @"1234";
cliqueContextConfiguration.altDSID = @"altdsid";
cliqueContextConfiguration.otControl = self.otControl;

OCMStub([self.mockClique fetchEscrowRecordsInternal:[OCMArg any] error:[OCMArg anyObjectRef]]).andCall(self, @selector(mockFetchEscrowRecordsInternalReturnMixedRecords:error:));

NSError* localErrror = nil;
NSArray* escrowRecords = [OTClique fetchEscrowRecords:cliqueContextConfiguration error:&localErrror];
XCTAssertEqual(escrowRecords.count, 1, "should only return 1 record");
XCTAssertNil(localErrror, "error should be nil");

OTEscrowRecord *firstRecord = escrowRecords[0];
XCTAssertTrue([firstRecord.label isEqualToString:@"com.apple.icdp.record"], "label should be com.apple.icdp.record");
XCTAssertTrue([firstRecord.serialNumber isEqualToString:@"C39V209AJ9L5"], "serial number should be C39V209AJ9L5");
XCTAssertTrue([firstRecord.escrowInformationMetadata.bottleValidity isEqualToString: @"valid"], "bottle validity should be valid");
XCTAssertEqual(firstRecord.recordViability, OTEscrowRecord_RecordViability_RECORD_VIABILITY_FULLY_VIABLE, "record viability should be fully viable");
}

- (void)testFetchLegacyEscrowRecords
{
OctagonSetOptimizationEnabled(true);

OTConfigurationContext *cliqueContextConfiguration = [[OTConfigurationContext alloc]init];
cliqueContextConfiguration.context = OTDefaultContext;
cliqueContextConfiguration.dsid = @"1234";
cliqueContextConfiguration.altDSID = @"altdsid";
cliqueContextConfiguration.otControl = self.otControl;

OCMStub([self.mockClique fetchEscrowRecordsInternal:[OCMArg any] error:[OCMArg anyObjectRef]]).andCall(self, @selector(mockFetchEscrowRecordsInternalReturnLegacyRecords:error:));

NSError* localErrror = nil;
NSArray* escrowRecords = [OTClique fetchEscrowRecords:cliqueContextConfiguration error:&localErrror];
XCTAssertEqual(escrowRecords.count, 0, "should return zero records");
XCTAssertNil(localErrror, "error should be nil");
}

- (void)testFetchRecordsOctagonViableSOSUnknownViability
{
OctagonSetOptimizationEnabled(true);

OTConfigurationContext *cliqueContextConfiguration = [[OTConfigurationContext alloc]init];
cliqueContextConfiguration.context = OTDefaultContext;
cliqueContextConfiguration.dsid = @"1234";
cliqueContextConfiguration.altDSID = @"altdsid";
cliqueContextConfiguration.otControl = self.otControl;

OCMStub([self.mockClique fetchEscrowRecordsInternal:[OCMArg any] error:[OCMArg anyObjectRef]]).andCall(self, @selector(mockFetchEscrowRecordsInternalReturnOneRecord:error:));

NSError* localErrror = nil;
NSArray* escrowRecords = [OTClique fetchEscrowRecords:cliqueContextConfiguration error:&localErrror];
XCTAssertNil(localErrror, "error should be nil");

XCTAssertEqual(escrowRecords.count, 1, "should only return 1 record");
OTEscrowRecord *firstRecord = escrowRecords[0];
XCTAssertTrue([firstRecord.label isEqualToString:@"com.apple.icdp.record"], "label should be com.apple.icdp.record");
XCTAssertTrue([firstRecord.serialNumber isEqualToString:@"C39V209AJ9L5"], "serial number should be C39V209AJ9L5");
XCTAssertTrue([firstRecord.escrowInformationMetadata.bottleValidity isEqualToString: @"valid"], "bottle validity should be valid");
XCTAssertEqual(firstRecord.recordViability, OTEscrowRecord_RecordViability_RECORD_VIABILITY_FULLY_VIABLE, "record viability should be fully viable");
XCTAssertEqual(firstRecord.viabilityStatus, OTEscrowRecord_SOSViability_SOS_VIABLE_UNKNOWN, "record SOS viability should be unknown");
}

- (void)testFetchRecordsOctagonViableSOSViable
{
OctagonSetOptimizationEnabled(true);

OTConfigurationContext *cliqueContextConfiguration = [[OTConfigurationContext alloc]init];
cliqueContextConfiguration.context = OTDefaultContext;
cliqueContextConfiguration.dsid = @"1234";
cliqueContextConfiguration.altDSID = @"altdsid";
cliqueContextConfiguration.otControl = self.otControl;

OCMStub([self.mockClique fetchEscrowRecordsInternal:[OCMArg any] error:[OCMArg anyObjectRef]]).andCall(self, @selector(mockFetchEscrowRecordsInternalReturnOctagonViableSOSViableRecords:error:));

NSError* localErrror = nil;
NSArray* escrowRecords = [OTClique fetchEscrowRecords:cliqueContextConfiguration error:&localErrror];
XCTAssertNil(localErrror, "error should be nil");

XCTAssertEqual(escrowRecords.count, 2, "should only return 2 records");
OTEscrowRecord *firstRecord = escrowRecords[0];
XCTAssertTrue([firstRecord.escrowInformationMetadata.bottleValidity isEqualToString: @"valid"], "bottle validity should be valid");
XCTAssertEqual(firstRecord.recordViability, OTEscrowRecord_RecordViability_RECORD_VIABILITY_FULLY_VIABLE, "record viability should be fully viable");
XCTAssertEqual(firstRecord.viabilityStatus, OTEscrowRecord_SOSViability_SOS_VIABLE, "record sos viability should be fully viable");

OTEscrowRecord *secondRecord = escrowRecords[1];
XCTAssertTrue([secondRecord.escrowInformationMetadata.bottleValidity isEqualToString: @"valid"], "bottle validity should be valid");
XCTAssertEqual(secondRecord.recordViability, OTEscrowRecord_RecordViability_RECORD_VIABILITY_FULLY_VIABLE, "record viability should be fully viable");
XCTAssertEqual(secondRecord.viabilityStatus, OTEscrowRecord_SOSViability_SOS_VIABLE, "record sos viability should be fully viable");
}

- (void)testFetchRecordsOctagonNotViableSOSViable
{
OctagonSetOptimizationEnabled(true);

OTConfigurationContext *cliqueContextConfiguration = [[OTConfigurationContext alloc]init];
cliqueContextConfiguration.context = OTDefaultContext;
cliqueContextConfiguration.dsid = @"1234";
cliqueContextConfiguration.altDSID = @"altdsid";
cliqueContextConfiguration.otControl = self.otControl;

OCMStub([self.mockClique fetchEscrowRecordsInternal:[OCMArg any] error:[OCMArg anyObjectRef]]).andCall(self, @selector(mockFetchEscrowRecordsInternalReturnOctagonNotViableSOSViableRecords:error:));

NSError* localErrror = nil;
NSArray* escrowRecords = [OTClique fetchEscrowRecords:cliqueContextConfiguration error:&localErrror];
XCTAssertNil(localErrror, "error should be nil");

if (OctagonPlatformSupportsSOS()) {
XCTAssertEqual(escrowRecords.count, 2, "should only return 2 records");
OTEscrowRecord *firstRecord = escrowRecords[0];
XCTAssertTrue([firstRecord.escrowInformationMetadata.bottleValidity isEqualToString: @"invalid"], "bottle validity should be invalid");
XCTAssertEqual(firstRecord.recordViability, OTEscrowRecord_RecordViability_RECORD_VIABILITY_LEGACY, "record viability should be set to legacy");
XCTAssertEqual(firstRecord.viabilityStatus, OTEscrowRecord_SOSViability_SOS_VIABLE, "record sos viability should be fully viable");

OTEscrowRecord *secondRecord = escrowRecords[1];
XCTAssertTrue([secondRecord.escrowInformationMetadata.bottleValidity isEqualToString: @"invalid"], "bottle validity should be invalid");
XCTAssertEqual(secondRecord.recordViability, OTEscrowRecord_RecordViability_RECORD_VIABILITY_LEGACY, "record viability should be set to legacy");
XCTAssertEqual(secondRecord.viabilityStatus, OTEscrowRecord_SOSViability_SOS_VIABLE, "record sos viability should be fully viable");
} else {
XCTAssertEqual(escrowRecords.count, 0, "should return 0 records");

}
}

- (void)testFetchRecordsOctagonNotViableSOSNotViable
{
OctagonSetOptimizationEnabled(true);

OTConfigurationContext *cliqueContextConfiguration = [[OTConfigurationContext alloc]init];
cliqueContextConfiguration.context = OTDefaultContext;
cliqueContextConfiguration.dsid = @"1234";
cliqueContextConfiguration.altDSID = @"altdsid";
cliqueContextConfiguration.otControl = self.otControl;

OCMStub([self.mockClique fetchEscrowRecordsInternal:[OCMArg any] error:[OCMArg anyObjectRef]]).andCall(self, @selector(mockFetchEscrowRecordsInternalReturnLegacyNonViableSOSRecords:error:));

NSError* localErrror = nil;
NSArray* escrowRecords = [OTClique fetchEscrowRecords:cliqueContextConfiguration error:&localErrror];
XCTAssertNil(localErrror, "error should be nil");

XCTAssertEqual(escrowRecords.count, 0, "should return 0 records");
}

- (void)testFetchAllRecords
{
OctagonSetOptimizationEnabled(true);

OTConfigurationContext *cliqueContextConfiguration = [[OTConfigurationContext alloc]init];
cliqueContextConfiguration.context = OTDefaultContext;
cliqueContextConfiguration.dsid = @"1234";
cliqueContextConfiguration.altDSID = @"altdsid";
cliqueContextConfiguration.otControl = self.otControl;

OCMStub([self.mockClique fetchEscrowRecordsInternal:[OCMArg any] error:[OCMArg anyObjectRef]]).andCall(self, @selector(mockFetchEscrowRecordsInternalReturnLegacyNonViableSOSRecords:error:));

NSError* localErrror = nil;
NSArray* escrowRecords = [OTClique fetchAllEscrowRecords:cliqueContextConfiguration error:&localErrror];
XCTAssertNil(localErrror, "error should be nil");

XCTAssertEqual(escrowRecords.count, 2, "should return 2 records");
}

- (void)testFetchEscrowRecordOptimizationOff
{
OctagonSetOptimizationEnabled(false);

OTConfigurationContext *cliqueContextConfiguration = [[OTConfigurationContext alloc]init];
cliqueContextConfiguration.context = OTDefaultContext;
cliqueContextConfiguration.dsid = @"1234";
cliqueContextConfiguration.altDSID = @"altdsid";
cliqueContextConfiguration.sbd = [[OTMockSecureBackup alloc]initWithBottleID:@"" entropy:[NSData data]];
cliqueContextConfiguration.otControl = self.otControl;

NSError* localErrror = nil;
NSArray* escrowRecords = [OTClique fetchEscrowRecords:cliqueContextConfiguration error:&localErrror];
XCTAssertNil(localErrror, "error should be nil");
XCTAssertNotNil(escrowRecords, "escrow records should not be nil");

for (OTEscrowRecord* record in escrowRecords) {
XCTAssertEqual(record.creationDate, 1580440060, "escrow record creation date should be 1580440060");
XCTAssertNotNil(record.escrowInformationMetadata.backupKeybagDigest, "escrow record backup key bag digest should not be nil");
XCTAssertTrue([record.label isEqualToString:@"com.apple.icdp.record"], "escrow record label should be com.apple.icdp.record");
XCTAssertEqual(record.recordStatus, OTEscrowRecord_RecordStatus_RECORD_STATUS_VALID, "escrow record record status should be valid");
XCTAssertEqual(record.remainingAttempts, 10, "escrow record remaining attempts should be 10");
XCTAssertEqual(record.silentAttemptAllowed, 1, "escrow record silent attempted allowed should be true");
XCTAssertTrue([record.serialNumber isEqualToString:@"C39V209AJ9L5"], "escrow record serial number should be equal");
XCTAssertTrue([record.recordId isEqualToString:@"sNs6voV0N35D/T91SuGmJnGO29"], "escrow record record id should be true");
XCTAssertEqual(record.silentAttemptAllowed, 1, "escrow record silent attempted allowed should be true");

XCTAssertTrue([record.escrowInformationMetadata.clientMetadata.deviceColor isEqualToString: @"1"], "escrow record device color should be 1");
XCTAssertTrue([record.escrowInformationMetadata.clientMetadata.deviceEnclosureColor isEqualToString:@"1"], "escrow record ");
XCTAssertTrue([record.escrowInformationMetadata.clientMetadata.deviceMid isEqualToString:@"yWnI8vdNg6EWayeW/FP4cDZRse3LMn8Pxg/x/sPzZJIS5cs3RKo4/stOW46nQ98iNlpSHrnR0kfsbR3X"], "escrow record MID should be yWnI8vdNg6EWayeW/FP4cDZRse3LMn8Pxg/x/sPzZJIS5cs3RKo4/stOW46nQ98iNlpSHrnR0kfsbR3X");
XCTAssertTrue([record.escrowInformationMetadata.clientMetadata.deviceModel isEqualToString:@"iPhone 8 Plus"], "escrow record device model should be iPhone 8 Plus");
XCTAssertTrue([record.escrowInformationMetadata.clientMetadata.deviceModelClass isEqualToString: @"iPhone"], "escrow record model calss should be iPhone");
XCTAssertTrue([record.escrowInformationMetadata.clientMetadata.deviceModelVersion isEqualToString:@"iPhone10,5"], "escrow record model version should be iPhone10,5");
XCTAssertTrue([record.escrowInformationMetadata.clientMetadata.deviceName isEqualToString:@"iPhone"], "escrow record device name should be iPhone ");
XCTAssertEqual(record.escrowInformationMetadata.clientMetadata.secureBackupMetadataTimestamp, 1580440060, "escrow record timestamp should be 1580440060");
XCTAssertEqual(record.escrowInformationMetadata.clientMetadata.secureBackupNumericPassphraseLength, 6, "escrow record passphrase length should be 6");
XCTAssertEqual(record.escrowInformationMetadata.clientMetadata.secureBackupUsesComplexPassphrase, 1, "escrow record uses complex passphrase should be 1");
XCTAssertEqual(record.escrowInformationMetadata.clientMetadata.secureBackupUsesNumericPassphrase, 1, "escrow record uses numeric passphrase should be 1");
XCTAssertNotNil(record.escrowInformationMetadata.escrowedSpki, "escrow record escrowed spki should not be nil");
XCTAssertNotNil(record.escrowInformationMetadata.peerInfo, "escrow record peer info should be not nil");
XCTAssertEqual(record.escrowInformationMetadata.secureBackupTimestamp, 1580440060, "escrow record timestamp should be 1580440060");
XCTAssertEqual(record.escrowInformationMetadata.secureBackupUsesMultipleIcscs, 1, "escrow record uses multiple icscs should be 1");

NSData *testVectorData = [accountInfoWithInfoSample dataUsingEncoding:kCFStringEncodingUTF8];
NSPropertyListFormat format;
NSDictionary *testVectorPlist = [NSPropertyListSerialization propertyListWithData:testVectorData options: NSPropertyListMutableContainersAndLeaves format:&format error:&localErrror];

//now translate back to dictionary!
NSDictionary *translatedBackToDictionary = [OTEscrowTranslation escrowRecordToDictionary:record];
XCTAssertNotNil(translatedBackToDictionary, "should not be nil");

NSArray *testVectorICDPRecords = testVectorPlist[@"SecureBackupAlliCDPRecords"];
XCTAssertNotNil(testVectorICDPRecords, "should not be nil");

NSDictionary *testVectorRecord = testVectorICDPRecords[0];
XCTAssertNotNil(testVectorRecord, "should not be nil");
XCTAssertTrue([testVectorRecord[@"SecureBackupEscrowDate"] isEqualToDate: translatedBackToDictionary[@"SecureBackupEscrowDate"]], "should be equal");
XCTAssertTrue([testVectorRecord[@"recordStatus"] isEqualToString:translatedBackToDictionary[@"recordStatus"]], "should be equal");
XCTAssertTrue([testVectorRecord[@"silentAttemptAllowed"] isEqualToNumber:translatedBackToDictionary[@"silentAttemptAllowed"]], "should be equal");

XCTAssertTrue([testVectorRecord[@"peerInfoSerialNumber"] isEqualToString:translatedBackToDictionary[@"peerInfoSerialNumber"]], "should be equal");
XCTAssertTrue([testVectorRecord[@"recordID"] isEqualToString:translatedBackToDictionary[@"recordID"]], "should be equal");

NSDictionary *testVectorMetadata = testVectorRecord[@"metadata"];
XCTAssertNotNil(testVectorMetadata, "should not be nil");

NSDictionary *translatedMetadata = translatedBackToDictionary[@"metadata"];
XCTAssertNotNil(translatedMetadata, "should not be nil");

XCTAssertTrue([testVectorMetadata[@"com.apple.securebackup.timestamp"] isEqualToString: translatedMetadata[@"com.apple.securebackup.timestamp"]], "should be equal");
XCTAssertTrue([testVectorMetadata[@"bottleID"] isEqualToString:translatedMetadata[@"bottleID"]], "should be equal");
XCTAssertTrue([testVectorMetadata[@"escrowedSPKI"] isEqualToData:translatedMetadata[@"escrowedSPKI"]], "should be equal");
XCTAssertTrue([testVectorMetadata[@"SecureBackupUsesMultipleiCSCs"] isEqualToNumber: translatedMetadata[@"SecureBackupUsesMultipleiCSCs"]], "should be equal");
XCTAssertTrue([testVectorMetadata[@"peerInfo"] isEqualToData:translatedMetadata[@"peerInfo"]], "should be equal");
XCTAssertTrue([testVectorMetadata[@"BackupKeybagDigest"] isEqualToData: translatedMetadata[@"BackupKeybagDigest"]], "should be equal");

NSDictionary *testVectorClientMetadata = testVectorMetadata[@"ClientMetadata"];
XCTAssertNotNil(testVectorClientMetadata, "should not be nil");
NSDictionary *translatedClientMetadata = translatedMetadata[@"ClientMetadata"];
XCTAssertNotNil(translatedClientMetadata, "should not be nil");
XCTAssertTrue([testVectorClientMetadata isEqualToDictionary:translatedClientMetadata], "should be equal");
}

}

- (void)testCDPRecordContextTranslation
{
NSError* localError = nil;
NSData *testVectorData = [testCDPRemoteRecordContextTestVector dataUsingEncoding:kCFStringEncodingUTF8];
NSPropertyListFormat format;
NSDictionary *testVectorPlist = [NSPropertyListSerialization propertyListWithData:testVectorData options: NSPropertyListMutableContainersAndLeaves format:&format error:&localError];
XCTAssertNotNil(testVectorPlist, "testVectorPlist should not be nil");
OTICDPRecordContext *cdpContext = [OTEscrowTranslation dictionaryToCDPRecordContext:testVectorPlist];
XCTAssertNotNil(cdpContext, "cdpContext should not be nil");
XCTAssertTrue([cdpContext.authInfo.authenticationAppleid isEqualToString:@"anna.535.paid@icloud.com"], "authenticationAppleid should match");
XCTAssertTrue([cdpContext.authInfo.authenticationAuthToken isEqualToString:@"EAAbAAAABLwIAAAAAF5PGvkRDmdzLmljbG91ZC5hdXRovQBx359KJvlZTwe1q6BwXvK4gQUYo2WQbKT8UDtn8rcA6FvEYBANaAk1ofWx/bcfB4pcLiXR3Y0kncELCwFCEEpqpZS+klD9AY1oT9zW6VtyOgQTZJ4mfWz103+FoMh8nLJAVpYVfM/UjsiNsLfTX+rUmevfeA=="], "authenticationAuthToken should match");
XCTAssertTrue([cdpContext.authInfo.authenticationDsid isEqualToString:@"16187698960"], "authenticationDsid should match");
XCTAssertTrue([cdpContext.authInfo.authenticationEscrowproxyUrl isEqualToString:@"https://p97-escrowproxy.icloud.com:443"], "authenticationEscrowproxyUrl should match");
XCTAssertTrue([cdpContext.authInfo.authenticationIcloudEnvironment isEqualToString:@"PROD"], "authenticationIcloudEnvironment should match");
XCTAssertTrue([cdpContext.authInfo.authenticationPassword isEqualToString: @"PETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPET"], "authenticationPassword should match");
XCTAssertFalse(cdpContext.authInfo.fmipRecovery, "fmipRecovery should be false");
XCTAssertFalse(cdpContext.authInfo.idmsRecovery, "idmsRecovery should be false");

XCTAssertTrue(cdpContext.cdpInfo.containsIcdpData, "containsIcdpData should be true");
XCTAssertFalse(cdpContext.cdpInfo.silentRecoveryAttempt, "silentRecoveryAttempt should be false");
XCTAssertTrue(cdpContext.cdpInfo.usesMultipleIcsc, "usesMultipleIcsc should match");
XCTAssertFalse(cdpContext.cdpInfo.useCachedSecret, "useCachedSecret should be false");
XCTAssertFalse(cdpContext.cdpInfo.usePreviouslyCachedRecoveryKey, "usePreviouslyCachedRecoveryKey should be false");
XCTAssertNil(cdpContext.cdpInfo.recoveryKey, "recoveryKey should be nil");
XCTAssertTrue([cdpContext.cdpInfo.recoverySecret isEqualToString:@"333333"], "recoverySecret should be 333333");

NSDictionary *translateBack = [OTEscrowTranslation CDPRecordContextToDictionary:cdpContext];
XCTAssertNotNil(translateBack, "translateBack should not be nil");

XCTAssertTrue([((NSString*)translateBack[@"SecureBackupAuthenticationAppleID"]) isEqualToString:@"anna.535.paid@icloud.com"], "SecureBackupAuthenticationAppleID should be equal");
XCTAssertTrue([((NSString*)translateBack[@"SecureBackupAuthenticationAuthToken"]) isEqualToString:@"EAAbAAAABLwIAAAAAF5PGvkRDmdzLmljbG91ZC5hdXRovQBx359KJvlZTwe1q6BwXvK4gQUYo2WQbKT8UDtn8rcA6FvEYBANaAk1ofWx/bcfB4pcLiXR3Y0kncELCwFCEEpqpZS+klD9AY1oT9zW6VtyOgQTZJ4mfWz103+FoMh8nLJAVpYVfM/UjsiNsLfTX+rUmevfeA=="], "SecureBackupAuthenticationAuthToken should be equal");
XCTAssertTrue([((NSString*)translateBack[@"SecureBackupAuthenticationDSID"]) isEqualToString:@"16187698960"], "SecureBackupAuthenticationDSIDshould be equal");
XCTAssertTrue([((NSString*)translateBack[@"SecureBackupAuthenticationEscrowProxyURL"]) isEqualToString:@"https://p97-escrowproxy.icloud.com:443"], "SecureBackupAuthenticationEscrowProxyURL be equal");
XCTAssertTrue([((NSString*)translateBack[@"SecureBackupAuthenticationPassword"]) isEqualToString:@"PETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPET"], "SecureBackupAuthenticationPassword be equal");
XCTAssertTrue([((NSString*)translateBack[@"SecureBackupAuthenticationiCloudEnvironment"]) isEqualToString:@"PROD"], "SecureBackupAuthenticationiCloudEnvironment be equal");
XCTAssertTrue(translateBack[@"SecureBackupContainsiCDPData"], "SecureBackupContainsiCDPData true");
XCTAssertTrue([translateBack[@"SecureBackupFMiPRecoveryKey"] isEqualToNumber:@NO], "SecureBackupFMiPRecoveryKey is false");
XCTAssertTrue([translateBack[@"SecureBackupIDMSRecovery"] isEqualToNumber:@NO], "SecureBackupIDMSRecovery false");
XCTAssertTrue([translateBack[@"SecureBackupPassphrase"] isEqualToString: @"333333"], "SecureBackupPassphrase true");
XCTAssertTrue([translateBack[@"SecureBackupSilentRecoveryAttempt"] isEqualToNumber:@NO], "SecureBackupSilentRecoveryAttempt false");
XCTAssertTrue([translateBack[@"SecureBackupUseCachedPassphrase"] isEqualToNumber:@NO], "SecureBackupUseCachedPassphrase false");
XCTAssertTrue([translateBack[@"SecureBackupUsesMultipleiCSCs"] isEqualToNumber:@YES], "SecureBackupUsesMultipleiCSCs true");
XCTAssertTrue([translateBack[@"SecureBackupUsesRecoveryKey"] isEqualToNumber:@NO], "SecureBackupUsesRecoveryKey false");
}

- (void)testCDPSilentRecordContextTranslation
{
NSError* localError = nil;
NSData *testVectorData = [CDPRecordContextSilentTestVector dataUsingEncoding:kCFStringEncodingUTF8];
NSPropertyListFormat format;
NSDictionary *testVectorPlist = [NSPropertyListSerialization propertyListWithData:testVectorData options: NSPropertyListMutableContainersAndLeaves format:&format error:&localError];
XCTAssertNotNil(testVectorPlist, "testVectorPlist should not be nil");
OTICDPRecordContext *cdpContext = [OTEscrowTranslation dictionaryToCDPRecordContext:testVectorPlist];
XCTAssertNotNil(cdpContext, "cdpContext should not be nil");
XCTAssertTrue([cdpContext.authInfo.authenticationAppleid isEqualToString:@"anna.535.paid@icloud.com"], "authenticationAppleid should match");
XCTAssertTrue([cdpContext.authInfo.authenticationAuthToken isEqualToString:@"EAAbAAAABLwIAAAAAF5PHOERDmdzLmljbG91ZC5hdXRovQDwjwm2kXoklEtO/xeL3YCPlBr7IkVuV26y2BfLco+QhJFm4VhgFZSBFUg5l4g/uV2DG95xadgk0+rWLhyXDGZwHN2V9jju3eo6sRwGVj4g5iBFStuj4unTKylu3iFkNSKtTMXAyBXpn4EiRX+8dwumC2FKkA=="], "authenticationAuthToken should match");
XCTAssertTrue([cdpContext.authInfo.authenticationDsid isEqualToString:@"16187698960"], "authenticationDsid should match");
XCTAssertTrue([cdpContext.authInfo.authenticationEscrowproxyUrl isEqualToString:@"https://p97-escrowproxy.icloud.com:443"], "authenticationEscrowproxyUrl should match");
XCTAssertTrue([cdpContext.authInfo.authenticationIcloudEnvironment isEqualToString:@"PROD"], "authenticationIcloudEnvironment should match");
XCTAssertTrue([cdpContext.authInfo.authenticationPassword isEqualToString: @"PETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPET"], "authenticationPassword should match");
XCTAssertFalse(cdpContext.authInfo.fmipRecovery, "fmipRecovery should be false");
XCTAssertFalse(cdpContext.authInfo.idmsRecovery, "idmsRecovery should be false");

XCTAssertTrue(cdpContext.cdpInfo.containsIcdpData, "containsIcdpData should be true");
XCTAssertTrue(cdpContext.cdpInfo.silentRecoveryAttempt, "silentRecoveryAttempt should be true");
XCTAssertTrue(cdpContext.cdpInfo.usesMultipleIcsc, "usesMultipleIcsc should match");
XCTAssertFalse(cdpContext.cdpInfo.useCachedSecret, "useCachedSecret should be false");
XCTAssertFalse(cdpContext.cdpInfo.usePreviouslyCachedRecoveryKey, "usePreviouslyCachedRecoveryKey should be false");
XCTAssertNil(cdpContext.cdpInfo.recoveryKey, "recoveryKey should be nil");
XCTAssertTrue([cdpContext.cdpInfo.recoverySecret isEqualToString:@"333333"], "recoverySecret should be 333333");

NSDictionary *translateBack = [OTEscrowTranslation CDPRecordContextToDictionary:cdpContext];
XCTAssertNotNil(translateBack, "translateBack should not be nil");

XCTAssertTrue([((NSString*)translateBack[@"SecureBackupAuthenticationAppleID"]) isEqualToString:@"anna.535.paid@icloud.com"], "SecureBackupAuthenticationAppleID should be equal");
XCTAssertTrue([((NSString*)translateBack[@"SecureBackupAuthenticationAuthToken"]) isEqualToString:@"EAAbAAAABLwIAAAAAF5PHOERDmdzLmljbG91ZC5hdXRovQDwjwm2kXoklEtO/xeL3YCPlBr7IkVuV26y2BfLco+QhJFm4VhgFZSBFUg5l4g/uV2DG95xadgk0+rWLhyXDGZwHN2V9jju3eo6sRwGVj4g5iBFStuj4unTKylu3iFkNSKtTMXAyBXpn4EiRX+8dwumC2FKkA=="], "SecureBackupAuthenticationAuthToken should be equal");
XCTAssertTrue([((NSString*)translateBack[@"SecureBackupAuthenticationDSID"]) isEqualToString:@"16187698960"], "SecureBackupAuthenticationDSIDshould be equal");
XCTAssertTrue([((NSString*)translateBack[@"SecureBackupAuthenticationEscrowProxyURL"]) isEqualToString:@"https://p97-escrowproxy.icloud.com:443"], "SecureBackupAuthenticationEscrowProxyURL be equal");
XCTAssertTrue([((NSString*)translateBack[@"SecureBackupAuthenticationPassword"]) isEqualToString:@"PETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPETPET"], "SecureBackupAuthenticationPassword be equal");
XCTAssertTrue([((NSString*)translateBack[@"SecureBackupAuthenticationiCloudEnvironment"]) isEqualToString:@"PROD"], "SecureBackupAuthenticationiCloudEnvironment be equal");
XCTAssertTrue(translateBack[@"SecureBackupContainsiCDPData"], "SecureBackupContainsiCDPData true");
XCTAssertTrue([translateBack[@"SecureBackupFMiPRecoveryKey"] isEqualToNumber:@NO], "SecureBackupFMiPRecoveryKey is false");
XCTAssertTrue([translateBack[@"SecureBackupIDMSRecovery"] isEqualToNumber:@NO], "SecureBackupIDMSRecovery false");
XCTAssertTrue([translateBack[@"SecureBackupPassphrase"] isEqualToString: @"333333"], "SecureBackupPassphrase true");
XCTAssertTrue([translateBack[@"SecureBackupSilentRecoveryAttempt"] isEqualToNumber:@YES], "SecureBackupSilentRecoveryAttempt true");
XCTAssertTrue([translateBack[@"SecureBackupUseCachedPassphrase"] isEqualToNumber:@NO], "SecureBackupUseCachedPassphrase false");
XCTAssertTrue([translateBack[@"SecureBackupUsesMultipleiCSCs"] isEqualToNumber:@YES], "SecureBackupUsesMultipleiCSCs true");
XCTAssertTrue([translateBack[@"SecureBackupUsesRecoveryKey"] isEqualToNumber:@NO], "SecureBackupUsesRecoveryKey false");
}

@end