]> git.saurik.com Git - apple/security.git/commitdiff
Security-59306.101.1.tar.gz macos-10154 v59306.101.1
authorApple <opensource@apple.com>
Fri, 1 May 2020 18:19:48 +0000 (18:19 +0000)
committerApple <opensource@apple.com>
Fri, 1 May 2020 18:19:48 +0000 (18:19 +0000)
319 files changed:
Analytics/SFAnalytics+Internal.h
Analytics/SFAnalytics.m
Analytics/SFAnalytics.plist
Analytics/SFAnalyticsDefines.h
CircleJoinRequested/CircleJoinRequested.m
KeychainCircle/KCJoiningAcceptSession+Internal.h
KeychainCircle/KCJoiningAcceptSession.m
KeychainCircle/KCJoiningMessages.h
KeychainCircle/KCJoiningRequestCircleSession.m
KeychainCircle/KCJoiningRequestSecretSession.m
KeychainCircle/KCJoiningRequestSession+Internal.h
KeychainCircle/PairingChannel.h
KeychainCircle/PairingChannel.m
KeychainCircle/Tests/FakeSOSControl.m
KeychainCircle/Tests/KCJoiningSessionTest.m
KeychainCircle/Tests/KCTLKRequestTest.m [new file with mode: 0644]
OSX/Keychain Circle Notification/KNAppDelegate.m
OSX/authd/authorization.plist
OSX/lib/en.lproj/authorization.prompts.strings
OSX/libsecurity_cdsa_utilities/lib/cssmdb.h
OSX/libsecurity_codesigning/lib/diskimagerep.cpp
OSX/libsecurity_codesigning/lib/diskimagerep.h
OSX/libsecurity_codesigning/lib/notarization.cpp
OSX/libsecurity_codesigning/lib/signer.cpp
OSX/libsecurity_keychain/lib/SecItem.cpp
OSX/libsecurity_smime/lib/cmsdigest.c
OSX/libsecurity_smime/lib/cmspriv.h
OSX/libsecurity_smime/lib/cmsutil.c
OSX/libsecurityd/lib/sstransit.cpp
OSX/sec/Security/Regressions/secitem/si-27-sectrust-exceptions.c [deleted file]
OSX/sec/Security/SecAccessControl.m
OSX/sec/Security/SecCTKKey.m
OSX/sec/Security/SecCertificate.c
OSX/sec/Security/SecExports.exp-in
OSX/sec/Security/SecItem.c
OSX/sec/Security/SecItemInternal.h
OSX/sec/Security/SecOTRPacketData.h
OSX/sec/Security/SecPolicy.c
OSX/sec/Security/SecPolicy.list
OSX/sec/Security/SecuritydXPC.c
OSX/sec/ipc/securityd_client.h
OSX/sec/ipc/server.c
OSX/shared_regressions/shared_regressions.h
OSX/shared_regressions/si-44-seckey-aks.m
OSX/utilities/SecCFWrappers.h
OSX/utilities/SecDb.c
OSX/utilities/SecDb.h
OSX/utilities/SecXPCHelper.h
OSX/utilities/SecXPCHelper.m
Security.exp-in
Security.xcodeproj/project.pbxproj
Security.xcodeproj/xcshareddata/xcschemes/CKKSTests.xcscheme
Security.xcodeproj/xcshareddata/xcschemes/OctagonTests.xcscheme
Security.xcodeproj/xcshareddata/xcschemes/TrustTests_ios.xcscheme
Security.xcodeproj/xcshareddata/xcschemes/TrustTests_macos.xcscheme
Security.xcodeproj/xcshareddata/xcschemes/TrustedPeers.xcscheme
Security.xcodeproj/xcshareddata/xcschemes/ios - Debug.xcscheme
Security.xcodeproj/xcshareddata/xcschemes/ios - Release.xcscheme
Security.xcodeproj/xcshareddata/xcschemes/ios - secdtests.xcscheme
Security.xcodeproj/xcshareddata/xcschemes/osx - World.xcscheme
Security.xcodeproj/xcshareddata/xcschemes/osx - secdtests.xcscheme
Security.xcodeproj/xcshareddata/xcschemes/osx - sectests.xcscheme
Security.xcodeproj/xcshareddata/xcschemes/secdmockaks.xcscheme
SecurityTool/sharedTool/sos.m
SecurityTool/sharedTool/sub_commands.h
experiment/SecExperimentInternal.h
keychain/ResetCloudKeychainAccount/reset_ick_account [new file with mode: 0644]
keychain/SecureObjectSync/Regressions/SOSCircle_regressions.h
keychain/SecureObjectSync/Regressions/sc-150-ring.m [deleted file]
keychain/SecureObjectSync/Regressions/sc-kvstool.m [deleted file]
keychain/SecureObjectSync/SOSAccount.h
keychain/SecureObjectSync/SOSAccount.m
keychain/SecureObjectSync/SOSAccountBackup.m
keychain/SecureObjectSync/SOSAccountConfiguration.proto
keychain/SecureObjectSync/SOSAccountCredentials.m
keychain/SecureObjectSync/SOSAccountPeers.m
keychain/SecureObjectSync/SOSAccountPersistence.m
keychain/SecureObjectSync/SOSAccountPriv.h
keychain/SecureObjectSync/SOSAccountRecovery.m
keychain/SecureObjectSync/SOSAccountRings.m
keychain/SecureObjectSync/SOSAccountTransaction.m
keychain/SecureObjectSync/SOSAccountUpdate.m
keychain/SecureObjectSync/SOSBackupInformation.h [deleted file]
keychain/SecureObjectSync/SOSBackupInformation.m [deleted file]
keychain/SecureObjectSync/SOSCircle.c
keychain/SecureObjectSync/SOSCloudCircle.h
keychain/SecureObjectSync/SOSCloudCircle.m
keychain/SecureObjectSync/SOSCloudCircleInternal.h
keychain/SecureObjectSync/SOSControlHelper.m
keychain/SecureObjectSync/SOSControlServer.m
keychain/SecureObjectSync/SOSExports.exp-in
keychain/SecureObjectSync/SOSFullPeerInfo.h
keychain/SecureObjectSync/SOSFullPeerInfo.m
keychain/SecureObjectSync/SOSPeerInfo.h
keychain/SecureObjectSync/SOSPeerInfo.m
keychain/SecureObjectSync/SOSPeerOTRTimer.m
keychain/SecureObjectSync/SOSRing.h
keychain/SecureObjectSync/SOSRingTypes.m
keychain/SecureObjectSync/SOSRingUtils.c
keychain/SecureObjectSync/SOSRingV0.m
keychain/SecureObjectSync/SOSTypes.h
keychain/SecureObjectSync/Tool/keychain_sync.h
keychain/SecureObjectSync/Tool/keychain_sync.m
keychain/SecureObjectSync/Tool/recovery_key.m
keychain/SecureObjectSync/Tool/syncbackup.h [deleted file]
keychain/SecureObjectSync/Tool/syncbackup.m [deleted file]
keychain/SecureObjectSync/generated_source/SOSAccountConfiguration.h
keychain/SecureObjectSync/generated_source/SOSAccountConfiguration.m
keychain/Trieste/.swiftlint.yml [new file with mode: 0644]
keychain/Trieste/OctagonTriesteTests/Tests/OctagonTriesteTests/OctagonTests.swift
keychain/TrustedPeersHelper/BottledPeer/BottledPeer.swift
keychain/TrustedPeersHelper/BottledPeer/EscrowKeys.swift
keychain/TrustedPeersHelper/Client.swift
keychain/TrustedPeersHelper/Container.swift
keychain/TrustedPeersHelper/ContainerMap.swift
keychain/TrustedPeersHelper/Container_BottledPeers.swift [new file with mode: 0644]
keychain/TrustedPeersHelper/Container_MachineIDs.swift
keychain/TrustedPeersHelper/Container_RecoveryKey.swift [new file with mode: 0644]
keychain/TrustedPeersHelper/CuttlefishErrors.swift
keychain/TrustedPeersHelper/Policy.swift
keychain/TrustedPeersHelper/RecoveryKey/RecoverKeySet.swift
keychain/TrustedPeersHelper/SetValueTransformer.swift
keychain/TrustedPeersHelper/TrustedPeersHelper.xcdatamodeld/TrustedPeersHelper_2.xcdatamodel/contents
keychain/TrustedPeersHelper/TrustedPeersHelperProtocol.h
keychain/TrustedPeersHelper/TrustedPeersHelperProtocol.m
keychain/TrustedPeersHelper/main.swift
keychain/TrustedPeersHelperUnitTests/.swiftlint.yml [new file with mode: 0644]
keychain/TrustedPeersHelperUnitTests/ContainerSync.swift
keychain/TrustedPeersHelperUnitTests/FakeCuttlefish.swift
keychain/TrustedPeersHelperUnitTests/MockCuttlefish.swift
keychain/TrustedPeersHelperUnitTests/TrustedPeersHelperUnitTests.swift
keychain/ckks/CKKS.m
keychain/ckks/CKKSAnalytics.h
keychain/ckks/CKKSAnalytics.m
keychain/ckks/CKKSIncomingQueueEntry.m
keychain/ckks/CKKSIncomingQueueOperation.m
keychain/ckks/CKKSKey.m
keychain/ckks/CKKSKeychainView.h
keychain/ckks/CKKSKeychainView.m
keychain/ckks/CKKSNewTLKOperation.m
keychain/ckks/CKKSOutgoingQueueOperation.m
keychain/ckks/CKKSSQLDatabaseObject.h
keychain/ckks/CKKSSQLDatabaseObject.m
keychain/ckks/CKKSScanLocalItemsOperation.h
keychain/ckks/CKKSScanLocalItemsOperation.m
keychain/ckks/CKKSViewManager.h
keychain/ckks/CKKSViewManager.m
keychain/ckks/CKKSZone.m
keychain/ckks/tests/CKKSCloudKitTests.m
keychain/ckks/tests/CKKSDispatchTests.m [deleted file]
keychain/ckks/tests/CKKSMockSOSPresentAdapter.h
keychain/ckks/tests/CKKSMockSOSPresentAdapter.m
keychain/ckks/tests/CKKSSQLTests.m
keychain/ckks/tests/CKKSTests+API.m
keychain/ckks/tests/CKKSTests+CurrentPointerAPI.m
keychain/ckks/tests/CKKSTests+MultiZone.h
keychain/ckks/tests/CKKSTests+MultiZone.m
keychain/ckks/tests/CKKSTests.m
keychain/ckks/tests/CloudKitKeychainSyncingFixupTests.m
keychain/ckks/tests/CloudKitKeychainSyncingMockXCTest.h
keychain/ckks/tests/CloudKitKeychainSyncingMockXCTest.m
keychain/ckks/tests/CloudKitKeychainSyncingTestsBase.h
keychain/ckks/tests/CloudKitKeychainSyncingTestsBase.m
keychain/ckks/tests/CloudKitMockXCTest.h
keychain/ckks/tests/CloudKitMockXCTest.m
keychain/ckks/tests/gen_test_plist.py [new file with mode: 0644]
keychain/ckks/tests/testrunner/KeychainCKKS.plist [deleted file]
keychain/ckksctl/ckksctl.m
keychain/escrowrequest/generated_source/SecEscrowPendingRecord.m
keychain/headers/SecItem.h
keychain/headers/SecKey.h
keychain/ot/CuttlefishXPCWrapper.m
keychain/ot/OT.m
keychain/ot/OTAuthKitAdapter.h
keychain/ot/OTAuthKitAdapter.m
keychain/ot/OTCheckHealthOperation.h
keychain/ot/OTCheckHealthOperation.m
keychain/ot/OTClientStateMachine.m
keychain/ot/OTClique.h
keychain/ot/OTClique.m
keychain/ot/OTControl.h
keychain/ot/OTControl.m
keychain/ot/OTControlProtocol.h
keychain/ot/OTControlProtocol.m
keychain/ot/OTCuttlefishAccountStateHolder.h
keychain/ot/OTCuttlefishAccountStateHolder.m
keychain/ot/OTCuttlefishContext.h
keychain/ot/OTCuttlefishContext.m
keychain/ot/OTDefines.h
keychain/ot/OTDetermineCDPBitStatusOperation.h [new file with mode: 0644]
keychain/ot/OTDetermineCDPBitStatusOperation.m [new file with mode: 0644]
keychain/ot/OTEstablishOperation.m
keychain/ot/OTFetchCKKSKeysOperation.m
keychain/ot/OTFetchViewsOperation.h
keychain/ot/OTFetchViewsOperation.m
keychain/ot/OTFollowup.h
keychain/ot/OTFollowup.m
keychain/ot/OTJoinWithVoucherOperation.h
keychain/ot/OTJoinWithVoucherOperation.m
keychain/ot/OTJoiningConfiguration.h
keychain/ot/OTJoiningConfiguration.m
keychain/ot/OTManager.h
keychain/ot/OTManager.m
keychain/ot/OTOperationDependencies.h
keychain/ot/OTPrepareOperation.h
keychain/ot/OTPrepareOperation.m
keychain/ot/OTResetCKKSZonesLackingTLKsOperation.m
keychain/ot/OTSOSAdapter.h
keychain/ot/OTSOSAdapter.m
keychain/ot/OTSOSUpdatePreapprovalsOperation.m
keychain/ot/OTSOSUpgradeOperation.h
keychain/ot/OTSOSUpgradeOperation.m
keychain/ot/OTSetCDPBitOperation.h [new file with mode: 0644]
keychain/ot/OTSetCDPBitOperation.m [new file with mode: 0644]
keychain/ot/OTStates.h
keychain/ot/OTStates.m
keychain/ot/OTUpdateTPHOperation.h
keychain/ot/OTUpdateTPHOperation.m
keychain/ot/OTUpdateTrustedDeviceListOperation.m
keychain/ot/OTUploadNewCKKSTLKsOperation.m
keychain/ot/OTVouchWithBottleOperation.m
keychain/ot/OTVouchWithRecoveryKeyOperation.h
keychain/ot/OTVouchWithRecoveryKeyOperation.m
keychain/ot/OctagonStateMachine.m
keychain/ot/categories/OTAccountMetadataClassC+KeychainSupport.h
keychain/ot/categories/OTAccountMetadataClassC+KeychainSupport.m
keychain/ot/categories/OctagonEscrowRecoverer.h
keychain/ot/proto/OTAccountMetadataClassC.proto
keychain/ot/proto/OTPairingMessage.proto
keychain/ot/proto/generated_source/OTAccountMetadataClassC.h
keychain/ot/proto/generated_source/OTAccountMetadataClassC.m
keychain/ot/proto/generated_source/OTPairingMessage.h
keychain/ot/proto/generated_source/OTPairingMessage.m
keychain/ot/proto/generated_source/OTSOSMessage.h [deleted file]
keychain/ot/proto/generated_source/OTSOSMessage.m [deleted file]
keychain/ot/proto/generated_source/OTSponsorToApplicantRound2M2.h
keychain/ot/proto/generated_source/OTSponsorToApplicantRound2M2.m
keychain/ot/proto/source/OTSOSMessage.m [deleted file]
keychain/ot/tests/octagon/.swiftlint.yml [new file with mode: 0644]
keychain/ot/tests/octagon/OctagonDataPersistenceTests.swift
keychain/ot/tests/octagon/OctagonTestMocks.swift
keychain/ot/tests/octagon/OctagonTests+Account.swift [new file with mode: 0644]
keychain/ot/tests/octagon/OctagonTests+CKKS.swift
keychain/ot/tests/octagon/OctagonTests+CKKSConfiguration.swift [new file with mode: 0644]
keychain/ot/tests/octagon/OctagonTests+CloudKitAccount.swift
keychain/ot/tests/octagon/OctagonTests+CoreFollowUp.swift
keychain/ot/tests/octagon/OctagonTests+DeviceList.swift
keychain/ot/tests/octagon/OctagonTests+ErrorHandling.swift
keychain/ot/tests/octagon/OctagonTests+EscrowRecovery.swift
keychain/ot/tests/octagon/OctagonTests+ForwardCompatibility.swift [new file with mode: 0644]
keychain/ot/tests/octagon/OctagonTests+HealthCheck.swift
keychain/ot/tests/octagon/OctagonTests+Helpers.swift [new file with mode: 0644]
keychain/ot/tests/octagon/OctagonTests+RecoveryKey.swift
keychain/ot/tests/octagon/OctagonTests+Reset.swift
keychain/ot/tests/octagon/OctagonTests+SOS.swift
keychain/ot/tests/octagon/OctagonTests+SOSUpgrade.swift
keychain/ot/tests/octagon/OctagonTests-BridgingHeader.h
keychain/ot/tests/octagon/OctagonTests.swift
keychain/ot/tests/octagon/Pairing/OctagonPairingTests+Piggybacking.swift
keychain/ot/tests/octagon/Pairing/OctagonPairingTests+ProxMultiClients.swift
keychain/ot/tests/octagon/Pairing/OctagonPairingTests+ProximitySetup.swift
keychain/ot/tests/octagon/Pairing/OctagonPairingTests.swift
keychain/otctl/OTControlCLI.h
keychain/otctl/OTControlCLI.m
keychain/otctl/otctl-Entitlements.plist
keychain/otctl/otctl.m
keychain/securityd/Regressions/SOSAccountTesting.h
keychain/securityd/Regressions/secd-62-account-backup.m
keychain/securityd/Regressions/secd-65-account-retirement-reset.m
keychain/securityd/Regressions/secd-66-account-recovery.m
keychain/securityd/Regressions/secd-76-idstransport.m [deleted file]
keychain/securityd/Regressions/secd-95-escrow-persistence.m [deleted file]
keychain/securityd/Regressions/secd_77_ids_messaging.m [deleted file]
keychain/securityd/Regressions/secd_regressions.h
keychain/securityd/SOSCloudCircleServer.h
keychain/securityd/SOSCloudCircleServer.m
keychain/securityd/SecDbBackupManager-protobufs/generated_source/SecDbBackupKeyClassSigningKey.m
keychain/securityd/SecDbBackupManager-protobufs/generated_source/SecDbBackupMetadataClassKey.m
keychain/securityd/SecDbBackupManager-protobufs/generated_source/SecDbBackupRecoverySet.m
keychain/securityd/SecDbKeychainItem.m
keychain/securityd/SecItemDataSource.c
keychain/securityd/SecItemDataSource.h
keychain/securityd/SecItemServer.c
keychain/securityd/SecItemServer.h
keychain/securityd/spi.c
keychain/tpctl/main.swift
libsecurity_smime/lib/cmsdigest.c
libsecurity_smime/lib/cmspriv.h
libsecurity_smime/lib/cmsutil.c
protocol/SecProtocolPriv.h
rio.yml [deleted file]
securityd/etc/com.apple.securityd.sb
securityd/securityd_service/securityd_service.xcodeproj/project.pbxproj
securityd/src/kckey.cpp
securityd/src/securityd.entitlements
supd/Tests/SFAnalyticsTests.m
supd/Tests/SupdTests.m
supd/supd.h
supd/supd.m
supd/supdProtocol.h
supdctl/main.m
tests/TrustTests/EvaluationTests/ExceptionTests.m [new file with mode: 0644]
tests/TrustTests/EvaluationTests/ExceptionTests_data.h [new file with mode: 0644]
tests/TrustTests/EvaluationTests/RevocationTests.m
tests/TrustTests/EvaluationTests/RevocationTests_data.h
tests/TrustTests/EvaluationTests/TrustEvaluationTestCase.h
tests/TrustTests/EvaluationTests/TrustEvaluationTestCase.m
tests/TrustTests/TrustEvaluationTestHelpers.h
tests/TrustTests/TrustEvaluationTestHelpers.m
tests/secdmockaks/mockaksKeychain.m
trust/headers/SecPolicyPriv.h
trust/trustd/SecCertificateServer.c
trust/trustd/SecCertificateServer.h
trust/trustd/SecOCSPCache.c
trust/trustd/SecPolicyServer.c
trust/trustd/SecPolicyServer.h
trust/trustd/SecRevocationServer.c
trust/trustd/SecRevocationServer.h
trust/trustd/SecTrustServer.c

index 05aa19a794d6514acbd6a231b880c9080abc5912..2d5554db6c155c74c82f227e113b33fc97fd8671 100644 (file)
@@ -31,6 +31,7 @@
 @interface SFAnalytics (Internal)
 
 - (void)logMetric:(NSNumber*)metric withName:(NSString*)metricName oncePerReport:(BOOL)once;
 @interface SFAnalytics (Internal)
 
 - (void)logMetric:(NSNumber*)metric withName:(NSString*)metricName oncePerReport:(BOOL)once;
++ (NSString*)hwModelID;
 
 @end
 
 
 @end
 
index 4f6933a4bcdc3bc466054c32f8423f19a3f50126..a51eb5f90dafa0aa4d96589847e3d83ec1443d55 100644 (file)
 
 #import <utilities/SecCoreAnalytics.h>
 
 
 #import <utilities/SecCoreAnalytics.h>
 
+#if TARGET_OS_OSX
+#include <sys/sysctl.h>
+#else
+#import <sys/utsname.h>
+#endif
+
 // SFAnalyticsDefines constants
 NSString* const SFAnalyticsTableSuccessCount = @"success_count";
 NSString* const SFAnalyticsTableHardFailures = @"hard_failures";
 // SFAnalyticsDefines constants
 NSString* const SFAnalyticsTableSuccessCount = @"success_count";
 NSString* const SFAnalyticsTableHardFailures = @"hard_failures";
@@ -53,6 +59,7 @@ NSString* const SFAnalyticsColumnSoftFailureCount = @"soft_failure_count";
 NSString* const SFAnalyticsColumnSampleValue = @"value";
 NSString* const SFAnalyticsColumnSampleName = @"name";
 
 NSString* const SFAnalyticsColumnSampleValue = @"value";
 NSString* const SFAnalyticsColumnSampleName = @"name";
 
+NSString* const SFAnalyticsPostTime = @"postTime";
 NSString* const SFAnalyticsEventTime = @"eventTime";
 NSString* const SFAnalyticsEventType = @"eventType";
 NSString* const SFAnalyticsEventTypeErrorEvent = @"errorEvent";
 NSString* const SFAnalyticsEventTime = @"eventTime";
 NSString* const SFAnalyticsEventType = @"eventType";
 NSString* const SFAnalyticsEventTypeErrorEvent = @"errorEvent";
@@ -130,6 +137,7 @@ NSString* const SFAnalyticsErrorDomain = @"com.apple.security.sfanalytics";
 // Local constants
 NSString* const SFAnalyticsEventBuild = @"build";
 NSString* const SFAnalyticsEventProduct = @"product";
 // Local constants
 NSString* const SFAnalyticsEventBuild = @"build";
 NSString* const SFAnalyticsEventProduct = @"product";
+NSString* const SFAnalyticsEventModelID = @"modelid";
 NSString* const SFAnalyticsEventInternal = @"internal";
 const NSTimeInterval SFAnalyticsSamplerIntervalOncePerReport = -1.0;
 
 NSString* const SFAnalyticsEventInternal = @"internal";
 const NSTimeInterval SFAnalyticsSamplerIntervalOncePerReport = -1.0;
 
@@ -314,11 +322,37 @@ const NSTimeInterval SFAnalyticsSamplerIntervalOncePerReport = -1.0;
     return result;
 }
 
     return result;
 }
 
++ (NSString*)hwModelID
+{
+    static NSString *hwModel = nil;
+    static dispatch_once_t onceToken;
+    dispatch_once(&onceToken, ^{
+#if TARGET_OS_SIMULATOR
+        // Asking for a real value in the simulator gives the results for the underlying mac. Not particularly useful.
+        hwModel = [NSString stringWithFormat:@"%s", getenv("SIMULATOR_MODEL_IDENTIFIER")];
+#elif TARGET_OS_OSX
+        size_t size;
+        sysctlbyname("hw.model", NULL, &size, NULL, 0);
+        char *sysctlString = malloc(size);
+        sysctlbyname("hw.model", sysctlString, &size, NULL, 0);
+        hwModel = [[NSString alloc] initWithUTF8String:sysctlString];
+        free(sysctlString);
+#else
+        struct utsname systemInfo;
+        uname(&systemInfo);
+
+        hwModel = [NSString stringWithCString:systemInfo.machine
+                                     encoding:NSUTF8StringEncoding];
+#endif
+    });
+    return hwModel;
+}
 
 + (void)addOSVersionToEvent:(NSMutableDictionary*)eventDict {
     static dispatch_once_t onceToken;
     static NSString *build = NULL;
     static NSString *product = NULL;
 
 + (void)addOSVersionToEvent:(NSMutableDictionary*)eventDict {
     static dispatch_once_t onceToken;
     static NSString *build = NULL;
     static NSString *product = NULL;
+    static NSString *modelID = nil;
     static BOOL internal = NO;
     dispatch_once(&onceToken, ^{
         NSDictionary *version = CFBridgingRelease(_CFCopySystemVersionDictionary());
     static BOOL internal = NO;
     dispatch_once(&onceToken, ^{
         NSDictionary *version = CFBridgingRelease(_CFCopySystemVersionDictionary());
@@ -327,6 +361,8 @@ const NSTimeInterval SFAnalyticsSamplerIntervalOncePerReport = -1.0;
         build = version[(__bridge NSString *)_kCFSystemVersionBuildVersionKey];
         product = version[(__bridge NSString *)_kCFSystemVersionProductNameKey];
         internal = os_variant_has_internal_diagnostics("com.apple.security");
         build = version[(__bridge NSString *)_kCFSystemVersionBuildVersionKey];
         product = version[(__bridge NSString *)_kCFSystemVersionProductNameKey];
         internal = os_variant_has_internal_diagnostics("com.apple.security");
+
+        modelID = [self hwModelID];
     });
     if (build) {
         eventDict[SFAnalyticsEventBuild] = build;
     });
     if (build) {
         eventDict[SFAnalyticsEventBuild] = build;
@@ -334,6 +370,9 @@ const NSTimeInterval SFAnalyticsSamplerIntervalOncePerReport = -1.0;
     if (product) {
         eventDict[SFAnalyticsEventProduct] = product;
     }
     if (product) {
         eventDict[SFAnalyticsEventProduct] = product;
     }
+    if (modelID) {
+        eventDict[SFAnalyticsEventModelID] = modelID;
+    }
     if (internal) {
         eventDict[SFAnalyticsEventInternal] = @YES;
     }
     if (internal) {
         eventDict[SFAnalyticsEventInternal] = @YES;
     }
index 76b0892bf60a163780307c048d6e6613349b28c9..ec44cc55ab946e30c508e8cb58c4739e0e8d6534 100644 (file)
@@ -4,6 +4,8 @@
 <dict>
        <key>KeySyncTopic</key>
        <dict>
 <dict>
        <key>KeySyncTopic</key>
        <dict>
+               <key>uploadSizeLimit</key>
+               <real>1000000</real>
                <key>splunk_allowInsecureCertificate</key>
                <false/>
                <key>splunk_topic</key>
                <key>splunk_allowInsecureCertificate</key>
                <false/>
                <key>splunk_topic</key>
@@ -13,6 +15,8 @@
        </dict>
        <key>CloudServicesTopic</key>
        <dict>
        </dict>
        <key>CloudServicesTopic</key>
        <dict>
+               <key>uploadSizeLimit</key>
+               <real>1000000</real>
                <key>splunk_allowInsecureCertificate</key>
                <false/>
                <key>splunk_topic</key>
                <key>splunk_allowInsecureCertificate</key>
                <false/>
                <key>splunk_topic</key>
@@ -24,6 +28,8 @@
        </dict>
        <key>TrustTopic</key>
        <dict>
        </dict>
        <key>TrustTopic</key>
        <dict>
+               <key>uploadSizeLimit</key>
+               <real>1000000</real>
                <key>splunk_allowInsecureCertificate</key>
                <false/>
                <key>splunk_topic</key>
                <key>splunk_allowInsecureCertificate</key>
                <false/>
                <key>splunk_topic</key>
@@ -35,6 +41,8 @@
        </dict>
        <key>TransparencyTopic</key>
        <dict>
        </dict>
        <key>TransparencyTopic</key>
        <dict>
+               <key>uploadSizeLimit</key>
+               <real>10000</real>
                <key>splunk_allowInsecureCertificate</key>
                <false/>
                <key>splunk_topic</key>
                <key>splunk_allowInsecureCertificate</key>
                <false/>
                <key>splunk_topic</key>
index 2291ca02441734b97d5603c025186e53a6977d4b..abf89eec69046ddf08351bbdfbb10cd7069672e0 100644 (file)
@@ -38,6 +38,7 @@ extern NSString* const SFAnalyticsColumnSoftFailureCount;
 extern NSString* const SFAnalyticsColumnSampleValue;
 extern NSString* const SFAnalyticsColumnSampleName;
 
 extern NSString* const SFAnalyticsColumnSampleValue;
 extern NSString* const SFAnalyticsColumnSampleName;
 
+extern NSString* const SFAnalyticsPostTime;
 extern NSString* const SFAnalyticsEventTime;
 extern NSString* const SFAnalyticsEventType;
 extern NSString* const SFAnalyticsEventTypeErrorEvent;
 extern NSString* const SFAnalyticsEventTime;
 extern NSString* const SFAnalyticsEventType;
 extern NSString* const SFAnalyticsEventTypeErrorEvent;
index 38e7c8eab8b38639fab47a0709ff3663e7b8fb40..2d9caabd1430b7f15908dfa2628d477638998407 100644 (file)
@@ -144,6 +144,8 @@ static void keybagDidUnlock()
         NSError *localError = nil;
         CDPFollowUpContext *context = [CDPFollowUpContext contextForStateRepair];
         CDPFollowUpController *cdpd = [[CDPFollowUpController alloc] init];
         NSError *localError = nil;
         CDPFollowUpContext *context = [CDPFollowUpContext contextForStateRepair];
         CDPFollowUpController *cdpd = [[CDPFollowUpController alloc] init];
+
+        secnotice("followup", "Posting a follow up (for SOS) of type repair");
         [cdpd postFollowUpWithContext:context error:&localError ];
         secnotice("cjr", "account is icdp");
         if(localError){
         [cdpd postFollowUpWithContext:context error:&localError ];
         secnotice("cjr", "account is icdp");
         if(localError){
@@ -628,6 +630,8 @@ static void kickOutChoice(CFUserNotificationRef userNotification, CFOptionFlags
               CDPFollowUpController *cdpd = [[CDPFollowUpController alloc] init];
                 NSError *localError = nil;
                 CDPFollowUpContext *context = [CDPFollowUpContext contextForStateRepair];
               CDPFollowUpController *cdpd = [[CDPFollowUpController alloc] init];
                 NSError *localError = nil;
                 CDPFollowUpContext *context = [CDPFollowUpContext contextForStateRepair];
+
+                secnotice("followup", "Posting a follow up (for SOS) of type repair");
                 [cdpd postFollowUpWithContext:context error:&localError ];
                 if(localError){
                     secnotice("cjr", "request to CoreCDP to follow up failed: %@", localError);
                 [cdpd postFollowUpWithContext:context error:&localError ];
                 if(localError){
                     secnotice("cjr", "request to CoreCDP to follow up failed: %@", localError);
@@ -772,6 +776,8 @@ static void askForCDPFollowup() {
         NSError *localError = nil;
         CDPFollowUpController *cdpd = [[CDPFollowUpController alloc] init];
         CDPFollowUpContext *context = [CDPFollowUpContext contextForStateRepair];
         NSError *localError = nil;
         CDPFollowUpController *cdpd = [[CDPFollowUpController alloc] init];
         CDPFollowUpContext *context = [CDPFollowUpContext contextForStateRepair];
+
+        secnotice("followup", "Posting a follow up (for SOS) of type repair");
         [cdpd postFollowUpWithContext:context error:&localError ];
         if(localError){
             secnotice("cjr", "request to CoreCDP to follow up failed: %@", localError);
         [cdpd postFollowUpWithContext:context error:&localError ];
         if(localError){
             secnotice("cjr", "request to CoreCDP to follow up failed: %@", localError);
@@ -910,6 +916,8 @@ static bool processEvents()
                 NSError *localError = nil;
                 CDPFollowUpController *cdpd = [[CDPFollowUpController alloc] init];
                 CDPFollowUpContext *context = [CDPFollowUpContext contextForStateRepair];
                 NSError *localError = nil;
                 CDPFollowUpController *cdpd = [[CDPFollowUpController alloc] init];
                 CDPFollowUpContext *context = [CDPFollowUpContext contextForStateRepair];
+
+                secnotice("followup", "Posting a follow up (for SOS) of type repair");
                 [cdpd postFollowUpWithContext:context error:&localError ];
                 if(localError){
                     secnotice("cjr", "request to CoreCDP to follow up failed: %@", localError);
                 [cdpd postFollowUpWithContext:context error:&localError ];
                 if(localError){
                     secnotice("cjr", "request to CoreCDP to follow up failed: %@", localError);
index ea2c1be2f7215ac634ef707824178d9d23550f56..a0a9d584744277b62c6ae5ee036af071d97e77bc 100644 (file)
@@ -31,6 +31,9 @@
 #import "KCJoiningSession.h"
 
 @interface KCJoiningAcceptSession (Internal)
 #import "KCJoiningSession.h"
 
 @interface KCJoiningAcceptSession (Internal)
+
+- (KCAESGCMDuplexSession*)accessSession;
+
 -(void)setControlObject:(OTControl*)control;
 - (void)setConfiguration:(OTJoiningConfiguration *)config;
 @end
 -(void)setControlObject:(OTControl*)control;
 - (void)setConfiguration:(OTJoiningConfiguration *)config;
 @end
index 01285024d7b1539cc9d8e476a8bba9c79364c9fc..931144a8e065ca5d519dc1b7364305146cfba666 100644 (file)
@@ -127,6 +127,7 @@ typedef enum {
     self->_joiningConfiguration = [[OTJoiningConfiguration alloc]initWithProtocolType:@"OctagonPiggybacking"
                                                                        uniqueDeviceID:@"acceptor-deviceid"
                                                                        uniqueClientID:@"requester-deviceid"
     self->_joiningConfiguration = [[OTJoiningConfiguration alloc]initWithProtocolType:@"OctagonPiggybacking"
                                                                        uniqueDeviceID:@"acceptor-deviceid"
                                                                        uniqueClientID:@"requester-deviceid"
+                                                                          pairingUUID:[[NSUUID UUID] UUIDString]
                                                                         containerName:nil
                                                                             contextID:OTDefaultContext
                                                                                 epoch:0
                                                                         containerName:nil
                                                                             contextID:OTDefaultContext
                                                                                 epoch:0
@@ -402,7 +403,38 @@ typedef enum {
 }
 #endif
 
 }
 #endif
 
+- (NSData*) createTLKRequestResponse: (NSError**) error {
+    NSError* localError = NULL;
+    NSData* initialSync = [self.circleDelegate circleGetInitialSyncViews:kSOSInitialSyncFlagTLKs error:&localError];
+    if (!initialSync) {
+        secnotice("joining", "Failed to get initial sync view: %@", localError);
+        if ( error!=NULL && localError != NULL )
+            *error = localError;
+        return nil;
+    }
+    
+    NSData* encryptedOutgoing = [self.session encrypt:initialSync error:&localError];
+    if (!encryptedOutgoing) {
+        secnotice("joining", "TLK request failed to encrypt: %@", localError);
+        if ( error!=NULL && localError != NULL )
+            *error = localError;
+        return nil;
+    }
+    self->_state = kAcceptDone;
+
+    secnotice("joining", "TLKRequest done.");
+
+    return [[KCJoiningMessage messageWithType:kTLKRequest
+                                         data:encryptedOutgoing
+                                        error:error] der];
+}
+
 - (NSData*) processApplication: (KCJoiningMessage*) message error:(NSError**) error {
 - (NSData*) processApplication: (KCJoiningMessage*) message error:(NSError**) error {
+    
+    if ([message type] == kTLKRequest) {
+        return [self createTLKRequestResponse: error];
+    }
+    
     if ([message type] != kPeerInfo) {
         KCJoiningErrorCreate(kUnexpectedMessage, error, @"Expected peerInfo!");
         return nil;
     if ([message type] != kPeerInfo) {
         KCJoiningErrorCreate(kUnexpectedMessage, error, @"Expected peerInfo!");
         return nil;
@@ -531,6 +563,11 @@ typedef enum {
 {
     self.joiningConfiguration = config;
 }
 {
     self.joiningConfiguration = config;
 }
+
+- (KCAESGCMDuplexSession*)accessSession
+{
+    return self.session;
+}
 #endif
 
 @end
 #endif
 
 @end
index a6ad9e385fb611702e3b232b80882a53baa7edf3..5dc66c706f896b06ae2dd20e064f1329e66e35be 100644 (file)
@@ -84,6 +84,8 @@ typedef enum {
     kPeerInfo = 4,
     kCircleBlob = 5,
 
     kPeerInfo = 4,
     kCircleBlob = 5,
 
+    kTLKRequest = 6,
+    
     kError = 0,
 
     kUnknown = 255,
     kError = 0,
 
     kUnknown = 255,
index ed9db95082fbb6f570b575024d521c393dafe276..a8778b2935352ffb87655caebd6954eb1e2e7e70 100644 (file)
@@ -52,9 +52,13 @@ typedef enum {
 - (void)setControlObject:(OTControl *)control{
     self.otControl = control;
 }
 - (void)setControlObject:(OTControl *)control{
     self.otControl = control;
 }
-- (void)setJoiningConfigurationObject:(OTJoiningConfiguration *)joiningConfiguration
+- (void)setContextIDOnJoiningConfiguration:(NSString*)contextID
 {
 {
-    self.joiningConfiguration = joiningConfiguration;
+    self.joiningConfiguration.contextID = contextID;
+}
+- (KCAESGCMDuplexSession*)accessSession
+{
+    return self.session;
 }
 
 #endif
 }
 
 #endif
@@ -205,7 +209,10 @@ typedef enum {
         OTSponsorToApplicantRound2M2 *voucher = pairingMessage.voucher;
 
         //handle voucher message then join octagon
         OTSponsorToApplicantRound2M2 *voucher = pairingMessage.voucher;
 
         //handle voucher message then join octagon
-        [self.otControl rpcJoinWithConfiguration:self.joiningConfiguration vouchData:voucher.voucher vouchSig:voucher.voucherSignature preapprovedKeys:voucher.preapprovedKeys reply:^(NSError * _Nullable err) {
+        [self.otControl rpcJoinWithConfiguration:self.joiningConfiguration
+                                       vouchData:voucher.voucher
+                                        vouchSig:voucher.voucherSignature
+                                           reply:^(NSError * _Nullable err) {
             if(err){
                 secerror("octagon: error joining octagon: %@", err);
                 localError = err;
             if(err){
                 secerror("octagon: error joining octagon: %@", err);
                 localError = err;
index 2cb52bcfe7aee3e80bb8b1f41b489d6b9b2c2f3a..81b9aac8acf62b64b4ea8157e9b8133d9d2238d1 100644 (file)
@@ -71,7 +71,8 @@ bool KCJoiningOctagonPiggybackingEnabled() {
 @property (readwrite) NSData* challenge;
 @property (readwrite) NSData* salt;
 #if OCTAGON
 @property (readwrite) NSData* challenge;
 @property (readwrite) NSData* salt;
 #if OCTAGON
-@property (nonatomic, strong) OTJoiningConfiguration* joiningConfiguration;
+@property (readwrite) NSString* sessionUUID;
+
 @property (nonatomic, strong) OTControl *otControl;
 #endif
 @property (nonatomic, strong) NSMutableDictionary *defaults;
 @property (nonatomic, strong) OTControl *otControl;
 #endif
 @property (nonatomic, strong) NSMutableDictionary *defaults;
@@ -147,7 +148,7 @@ bool KCJoiningOctagonPiggybackingEnabled() {
     }
 
     self->_session = [KCAESGCMDuplexSession sessionAsSender:key context:self.dsid];
     }
 
     self->_session = [KCAESGCMDuplexSession sessionAsSender:key context:self.dsid];
-    self.session.pairingUUID = self.joiningConfiguration.pairingUUID;
+    self.session.pairingUUID = self.sessionUUID;
     self.session.piggybackingVersion = self.piggy_version;
 
     return self.session != nil;
     self.session.piggybackingVersion = self.piggy_version;
 
     return self.session != nil;
@@ -219,9 +220,8 @@ bool KCJoiningOctagonPiggybackingEnabled() {
 
         if(self.piggy_version == kPiggyV2){
             OTPairingMessage* pairingMessage = [[OTPairingMessage alloc]initWithData: [message secondData]];
 
         if(self.piggy_version == kPiggyV2){
             OTPairingMessage* pairingMessage = [[OTPairingMessage alloc]initWithData: [message secondData]];
-
-            if(pairingMessage.epoch.epoch){
-                secnotice("octagon", "received epoch");
+            if(pairingMessage.hasEpoch) {
+                secnotice("octagon", "received epoch message: %@", [pairingMessage.epoch dictionaryRepresentation]);
                 self.epoch = pairingMessage.epoch.epoch;
             }
             else{
                 self.epoch = pairingMessage.epoch.epoch;
             }
             else{
@@ -350,18 +350,13 @@ bool KCJoiningOctagonPiggybackingEnabled() {
 #if OCTAGON
     self->_piggy_version = KCJoiningOctagonPiggybackingEnabled() ? kPiggyV2 : kPiggyV1;
     self->_otControl = [OTControl controlObject:true error:error];
 #if OCTAGON
     self->_piggy_version = KCJoiningOctagonPiggybackingEnabled() ? kPiggyV2 : kPiggyV1;
     self->_otControl = [OTControl controlObject:true error:error];
-    self->_joiningConfiguration = [[OTJoiningConfiguration alloc]initWithProtocolType:OTProtocolPiggybacking
-                                                                       uniqueDeviceID:@"requester-id"
-                                                                       uniqueClientID:@"requester-id"
-                                                                        containerName:nil
-                                                                            contextID:OTDefaultContext
-                                                                                epoch:0
-                                                                          isInitiator:true];
+
+    _sessionUUID = [[NSUUID UUID] UUIDString];
 #else
     self->_piggy_version = kPiggyV1;
 #endif
 
 #else
     self->_piggy_version = kPiggyV1;
 #endif
 
-    secnotice("joining", "joining: initWithSecretDelegate called, uuid=%@", self.joiningConfiguration.pairingUUID);
+    secnotice("joining", "joining: initWithSecretDelegate called, uuid=%@", self.sessionUUID);
 
     NSString* name = [NSString stringWithFormat: @"%llu", dsid];
     
 
     NSString* name = [NSString stringWithFormat: @"%llu", dsid];
     
@@ -391,11 +386,6 @@ bool KCJoiningOctagonPiggybackingEnabled() {
 {
     self.otControl = control;
 }
 {
     self.otControl = control;
 }
-
-- (void)setConfiguration:(OTJoiningConfiguration *)config
-{
-    self.joiningConfiguration = config;
-}
 #endif
 
 @end
 #endif
 
 @end
index 917529c334f607fd6c701c63ec641f33b07f6f57..340d23cb8f3311f423b811b86e0abf0f3f44327c 100644 (file)
 
 @interface KCJoiningRequestSecretSession (Internal)
 - (void)setControlObject:(OTControl*)control;
 
 @interface KCJoiningRequestSecretSession (Internal)
 - (void)setControlObject:(OTControl*)control;
-- (void)setConfiguration:(OTJoiningConfiguration *)config;
 @end
 
 @interface KCJoiningRequestCircleSession (Internal)
 
 @end
 
 @interface KCJoiningRequestCircleSession (Internal)
 
+- (KCAESGCMDuplexSession*)accessSession;
+
 - (void)setControlObject:(OTControl*)control;
 - (void)setControlObject:(OTControl*)control;
-- (void)setJoiningConfigurationObject:(OTJoiningConfiguration *)config;
+- (void)setContextIDOnJoiningConfiguration:(NSString*)contextID;
 @end
 #endif /* Header_h */
 #endif
 @end
 #endif /* Header_h */
 #endif
index 6f65dd6fcbfa646e1d506caaa602f37508d1dc08..d6c190332f88ed1c3edee8e0eb08009cb552db6f 100644 (file)
@@ -61,5 +61,9 @@ extern KCPairingIntent_Type KCPairingIntent_Type_UserDriven;
 - (void)setOctagonMessageFailForTesting:(BOOL)value;
 + (bool)isSupportedPlatform;
 - (void)setSessionSupportsOctagonForTesting:(bool)value;
 - (void)setOctagonMessageFailForTesting:(BOOL)value;
 + (bool)isSupportedPlatform;
 - (void)setSessionSupportsOctagonForTesting:(bool)value;
+
++ (NSData *)pairingChannelCompressData:(NSData *)data;
++ (NSData *)pairingChannelDecompressData:(NSData *)data;
+
 @end
 
 @end
 
index b65d3f6b399bba2de86282e3bf5bee515b36bce4..3627feae34c86598c4102e17db5c33de3c2f8766 100644 (file)
@@ -18,7 +18,6 @@
 #import "keychain/ot/OctagonControlServer.h"
 #import "keychain/ot/OTJoiningConfiguration.h"
 #import "keychain/ot/proto/generated_source/OTPairingMessage.h"
 #import "keychain/ot/OctagonControlServer.h"
 #import "keychain/ot/OTJoiningConfiguration.h"
 #import "keychain/ot/proto/generated_source/OTPairingMessage.h"
-#import "keychain/ot/proto/generated_source/OTSOSMessage.h"
 #import "keychain/ot/proto/generated_source/OTApplicantToSponsorRound2M1.h"
 #import "keychain/ot/proto/generated_source/OTSponsorToApplicantRound2M2.h"
 #import "keychain/ot/proto/generated_source/OTSponsorToApplicantRound1M2.h"
 #import "keychain/ot/proto/generated_source/OTApplicantToSponsorRound2M1.h"
 #import "keychain/ot/proto/generated_source/OTSponsorToApplicantRound2M2.h"
 #import "keychain/ot/proto/generated_source/OTSponsorToApplicantRound1M2.h"
@@ -109,6 +108,7 @@ typedef void(^OTNextState)(NSData *inData, OTPairingInternalCompletion complete)
 @property (assign) bool initiator;
 @property (assign) unsigned counter;
 @property (assign) bool acceptorWillSendInitialSyncCredentials;
 @property (assign) bool initiator;
 @property (assign) unsigned counter;
 @property (assign) bool acceptorWillSendInitialSyncCredentials;
+@property (assign) uint32_t acceptorInitialSyncCredentialsFlags;
 @property (strong) NSXPCConnection *connection;
 @property (strong) OTControl *otControl;
 @property (strong) NSString* contextID;
 @property (strong) NSXPCConnection *connection;
 @property (strong) OTControl *otControl;
 @property (strong) NSString* contextID;
@@ -170,6 +170,7 @@ typedef void(^OTNextState)(NSData *inData, OTPairingInternalCompletion complete)
         _joiningConfiguration = [[OTJoiningConfiguration alloc]initWithProtocolType:OTProtocolPairing
                                                                      uniqueDeviceID:peerVersionContext.uniqueDeviceID
                                                                      uniqueClientID:peerVersionContext.uniqueClientID
         _joiningConfiguration = [[OTJoiningConfiguration alloc]initWithProtocolType:OTProtocolPairing
                                                                      uniqueDeviceID:peerVersionContext.uniqueDeviceID
                                                                      uniqueClientID:peerVersionContext.uniqueClientID
+                                                                        pairingUUID:[[NSUUID UUID] UUIDString]
                                                                       containerName:nil
                                                                           contextID:OTDefaultContext
                                                                               epoch:0
                                                                       containerName:nil
                                                                           contextID:OTDefaultContext
                                                                               epoch:0
@@ -207,7 +208,7 @@ typedef void(^OTNextState)(NSData *inData, OTPairingInternalCompletion complete)
 const compression_algorithm pairingCompression = COMPRESSION_LZFSE;
 #define EXTRA_SIZE 100
 
 const compression_algorithm pairingCompression = COMPRESSION_LZFSE;
 #define EXTRA_SIZE 100
 
-- (NSData *)compressData:(NSData *)data
++ (NSData *)pairingChannelCompressData:(NSData *)data
 {
     NSMutableData *scratch = [NSMutableData dataWithLength:compression_encode_scratch_buffer_size(pairingCompression)];
 
 {
     NSMutableData *scratch = [NSMutableData dataWithLength:compression_encode_scratch_buffer_size(pairingCompression)];
 
@@ -226,7 +227,7 @@ const compression_algorithm pairingCompression = COMPRESSION_LZFSE;
     return o;
 }
 
     return o;
 }
 
-- (NSData *)decompressData:(NSData *)data
++ (NSData *)pairingChannelDecompressData:(NSData *)data
 {
     NSMutableData *scratch = [NSMutableData dataWithLength:compression_decode_scratch_buffer_size(pairingCompression)];
 
 {
     NSMutableData *scratch = [NSMutableData dataWithLength:compression_decode_scratch_buffer_size(pairingCompression)];
 
@@ -499,7 +500,10 @@ const compression_algorithm pairingCompression = COMPRESSION_LZFSE;
         OTSponsorToApplicantRound2M2 *voucher = pairingMessage.voucher;
 
         //handle voucher and join octagon
         OTSponsorToApplicantRound2M2 *voucher = pairingMessage.voucher;
 
         //handle voucher and join octagon
-        [self.otControl rpcJoinWithConfiguration:self.joiningConfiguration vouchData:voucher.voucher vouchSig:voucher.voucherSignature preapprovedKeys:voucher.preapprovedKeys reply:^(NSError *error) {
+        [self.otControl rpcJoinWithConfiguration:self.joiningConfiguration
+                                       vouchData:voucher.voucher
+                                        vouchSig:voucher.voucherSignature
+                                           reply:^(NSError *error) {
             if (error || self.testFailOctagon) {
                 secerror("ot-pairing: failed to create %d message: %@", self.counter, error);
                 complete(true, NULL, error);
             if (error || self.testFailOctagon) {
                 secerror("ot-pairing: failed to create %d message: %@", self.counter, error);
                 complete(true, NULL, error);
@@ -507,7 +511,7 @@ const compression_algorithm pairingCompression = COMPRESSION_LZFSE;
             }else{
                 secnotice(pairingScope, "initiatorThirdPacket successfully joined Octagon");
                 typeof(self) strongSelf = weakSelf;
             }else{
                 secnotice(pairingScope, "initiatorThirdPacket successfully joined Octagon");
                 typeof(self) strongSelf = weakSelf;
-                if(OctagonPlatformSupportsSOS() && strongSelf->_acceptorWillSendInitialSyncCredentials == true) {
+                if(OctagonPlatformSupportsSOS() && strongSelf->_acceptorWillSendInitialSyncCredentials) {
                     strongSelf.nextState = ^(NSDictionary *nsdata, KCPairingInternalCompletion kscomplete){
                         [weakSelf initiatorFourthPacket:nsdata complete:kscomplete];
                     };
                     strongSelf.nextState = ^(NSDictionary *nsdata, KCPairingInternalCompletion kscomplete){
                         [weakSelf initiatorFourthPacket:nsdata complete:kscomplete];
                     };
@@ -567,6 +571,11 @@ const compression_algorithm pairingCompression = COMPRESSION_LZFSE;
     if (self.sessionSupportsSOS && indata[@"d"]) {
         secnotice("pairing", "acceptor initialSyncCredentials requested");
         self.acceptorWillSendInitialSyncCredentials = true;
     if (self.sessionSupportsSOS && indata[@"d"]) {
         secnotice("pairing", "acceptor initialSyncCredentials requested");
         self.acceptorWillSendInitialSyncCredentials = true;
+        self.acceptorInitialSyncCredentialsFlags =
+            SOSControlInitialSyncFlagTLK|
+            SOSControlInitialSyncFlagPCS|
+            SOSControlInitialSyncFlagBluetoothMigration;
+
     }
 
     if (indata[@"o"] == nil) {
     }
 
     if (indata[@"o"] == nil) {
@@ -749,6 +758,12 @@ const compression_algorithm pairingCompression = COMPRESSION_LZFSE;
             response.voucher = [[OTSponsorToApplicantRound2M2 alloc] init];
             response.voucher.voucher = voucher;
             response.voucher.voucherSignature = voucherSig;
             response.voucher = [[OTSponsorToApplicantRound2M2 alloc] init];
             response.voucher.voucher = voucher;
             response.voucher.voucherSignature = voucherSig;
+
+            if (self.acceptorWillSendInitialSyncCredentials) {
+                // no need to share TLKs over the pairing channel, that's provided by octagon
+                self.acceptorInitialSyncCredentialsFlags &= ~(SOSControlInitialSyncFlagTLK | SOSControlInitialSyncFlagPCS);
+            }
+
             reply[@"o"] = response.data;
 
             secnotice("pairing", "acceptor reply to packet 2");
             reply[@"o"] = response.data;
 
             secnotice("pairing", "acceptor reply to packet 2");
@@ -761,10 +776,7 @@ const compression_algorithm pairingCompression = COMPRESSION_LZFSE;
 {
     secnotice("pairing", "acceptor packet 3");
 
 {
     secnotice("pairing", "acceptor packet 3");
 
-    const uint32_t initialSyncCredentialsFlags =
-        SOSControlInitialSyncFlagTLK|
-        SOSControlInitialSyncFlagPCS|
-        SOSControlInitialSyncFlagBluetoothMigration;
+    const uint32_t initialSyncCredentialsFlags = self.acceptorInitialSyncCredentialsFlags;
 
     [[self.connection remoteObjectProxyWithErrorHandler:^(NSError * _Nonnull error) {
         complete(true, NULL, error);
 
     [[self.connection remoteObjectProxyWithErrorHandler:^(NSError * _Nonnull error) {
         complete(true, NULL, error);
@@ -824,7 +836,7 @@ const compression_algorithm pairingCompression = COMPRESSION_LZFSE;
 
     if (inputCompressedData) {
 
 
     if (inputCompressedData) {
 
-        NSData *data = [self decompressData:inputCompressedData];
+        NSData *data = [[self class] pairingChannelDecompressData:inputCompressedData];
         if (data == NULL) {
             secnotice("pairing", "failed to decompress");
             complete(true, NULL, NULL);
         if (data == NULL) {
             secnotice("pairing", "failed to decompress");
             complete(true, NULL, NULL);
@@ -849,7 +861,7 @@ const compression_algorithm pairingCompression = COMPRESSION_LZFSE;
             if (outdata == NULL && error)
                 error = error2;
             if (outdata)
             if (outdata == NULL && error)
                 error = error2;
             if (outdata)
-                compressedData = [self compressData:outdata];
+                compressedData = [[self class] pairingChannelCompressData:outdata];
 
             if (compressedData) {
                 NSString *key = [NSString stringWithFormat:@"com.apple.ckks.pairing.packet-size.%s.%u",
 
             if (compressedData) {
                 NSString *key = [NSString stringWithFormat:@"com.apple.ckks.pairing.packet-size.%s.%u",
index b8edbae990b3aa87dbe2a82e1adfdbe0eef4b20b..eafb80c6fa1802c1c631fa5e2e3e7a0f3f65ddf3 100644 (file)
 
 - (void)initialSyncCredentials:(uint32_t)flags complete:(void (^)(NSArray *, NSError *))complete
 {
 
 - (void)initialSyncCredentials:(uint32_t)flags complete:(void (^)(NSArray *, NSError *))complete
 {
-    complete(@[], NULL);
+    // Make up a fake TLK
+    NSMutableArray<NSDictionary *> *items = [NSMutableArray array];
+    if (flags & SOSControlInitialSyncFlagTLK) {
+        NSString *tlkUUID = [[NSUUID UUID] UUIDString];
+        NSDictionary *fakeTLK = @{
+            @"class": @"inet",
+            @"agrp": @"com.apple.security.ckks",
+            @"vwht": @"PCS-master",
+            @"pdmn": @"ck",
+            @"desc": @"tlk",
+            @"srvr": @"fakeZone",
+            @"acct": tlkUUID,
+            @"path": tlkUUID,
+            @"v_Data": [NSData data],
+        };
+        [items addObject:fakeTLK];
+    }
+    complete(items, nil);
 }
 
 - (void)importInitialSyncCredentials:(NSArray *)items complete:(void (^)(bool success, NSError *))complete
 {
 }
 
 - (void)importInitialSyncCredentials:(NSArray *)items complete:(void (^)(bool success, NSError *))complete
 {
-    complete(true, NULL);
+    complete(true, nil);
 }
 
 }
 
-- (void)rpcTriggerSync:(NSArray<NSString *> *)peers complete:(void(^)(bool success, NSError *))complete
+- (void)triggerSync:(NSArray<NSString *> *)peers complete:(void(^)(bool success, NSError *))complete
 {
     complete(true, NULL);
 }
 {
     complete(true, NULL);
 }
     complete(nil, nil);
 }
 
     complete(nil, nil);
 }
 
-- (void)rpcTriggerBackup:(NSArray<NSString *> *)backupPeers complete:(void (^)(NSError *))complete {
-    complete(nil);
-}
-
-- (void)rpcTriggerRingUpdate:(void (^)(NSError *))complete {
+- (void)triggerBackup:(NSArray<NSString *> *)backupPeers complete:(void (^)(NSError *))complete {
     complete(nil);
 }
 
     complete(nil);
 }
 
index a3d71d0f2eedd54b63f76b937689063dc119ece1..2cd57264e3fdca48d33e23e072faf64a4f00029e 100644 (file)
 #include <CommonCrypto/CommonRandomSPI.h>
 
 
 #include <CommonCrypto/CommonRandomSPI.h>
 
 
-__unused static SOSFullPeerInfoRef SOSNSFullPeerInfoCreate(NSDictionary* gestalt,
-                                                           NSData* backupKey, SecKeyRef signingKey,
-                                                           SecKeyRef octagonSigningKey,
-                                                           SecKeyRef octagonEncryptionKey,
-                                                           NSError**error)
-{
-    CFErrorRef errorRef = NULL;
-
-    SOSFullPeerInfoRef result = SOSFullPeerInfoCreate(NULL, (__bridge CFDictionaryRef) gestalt, (__bridge CFDataRef) backupKey, signingKey, octagonSigningKey, octagonEncryptionKey, &errorRef);
-
-    if (errorRef && error) {
-        *error = (__bridge_transfer NSError*) errorRef;
-        errorRef = NULL;
-    }
-
-    return result;
-}
-
 static SecKeyRef GenerateFullECKey_internal(int keySize,  NSError** error)
 {
     SecKeyRef full_key = NULL;
 static SecKeyRef GenerateFullECKey_internal(int keySize,  NSError** error)
 {
     SecKeyRef full_key = NULL;
diff --git a/KeychainCircle/Tests/KCTLKRequestTest.m b/KeychainCircle/Tests/KCTLKRequestTest.m
new file mode 100644 (file)
index 0000000..325f198
--- /dev/null
@@ -0,0 +1,323 @@
+#import <XCTest/XCTest.h>
+
+#import <Foundation/Foundation.h>
+
+#import <KeychainCircle/KCJoiningSession.h>
+#import <KeychainCircle/KCAccountKCCircleDelegate.h>
+#import <KeychainCircle/KCError.h>
+#import <KeychainCircle/KCJoiningMessages.h>
+#import <KeychainCircle/NSError+KCCreationHelpers.h>
+#import <KeychainCircle/KCAESGCMDuplexSession.h>
+
+#include <Security/SecBase.h>
+#include "keychain/SecureObjectSync/SOSFullPeerInfo.h"
+#include "keychain/SecureObjectSync/SOSPeerInfoInternal.h"
+
+#include <CommonCrypto/CommonRandomSPI.h>
+
+
+static SecKeyRef GenerateFullECKey_internal(int keySize,  NSError** error)
+{
+    SecKeyRef full_key = NULL;
+
+    NSDictionary* keygen_parameters = @{ (__bridge NSString*)kSecAttrKeyType:(__bridge NSString*) kSecAttrKeyTypeEC,
+                                         (__bridge NSString*)kSecAttrKeySizeInBits: [NSNumber numberWithInt: keySize] };
+
+
+    (void) OSStatusError(SecKeyGeneratePair((__bridge CFDictionaryRef)keygen_parameters, NULL, &full_key), error, @"Generate Key failed");
+
+    return full_key;
+}
+
+static SecKeyRef GenerateFullECKey(int keySize, NSError** error) {
+    return GenerateFullECKey_internal(keySize, error);
+}
+
+static NSData* createTlkRequestMessage (KCAESGCMDuplexSession* aesSession) {
+    char someData[] = {1,2,3,4,5,6};
+    NSError* error = NULL;
+    NSData* rndPadding = [NSData dataWithBytes:(void*)someData length:sizeof(someData)];
+    KCJoiningMessage* tlkRequestMessage = [KCJoiningMessage messageWithType: kTLKRequest data:rndPadding error:&error];
+    return [tlkRequestMessage der];
+}
+
+@interface KCJoiningRequestTestDelegate : NSObject <KCJoiningRequestSecretDelegate, KCJoiningRequestCircleDelegate>
+@property (readwrite) NSString* sharedSecret;
+
+@property (readonly) NSString* accountCode;
+@property (readonly) NSData* circleJoinData;
+
+@property (readwrite) NSString* incorrectSecret;
+@property (readwrite) int incorrectTries;
+
+
++ (id) requestDelegateWithSecret:(NSString*) secret;
+- (id) init NS_UNAVAILABLE;
+- (id) initWithSecret: (NSString*) secret
+      incorrectSecret: (NSString*) wrongSecret
+       incorrectTries: (int) retries NS_DESIGNATED_INITIALIZER;
+- (NSString*) secret;
+- (NSString*) verificationFailed: (bool) codeChanged;
+- (SOSPeerInfoRef) copyPeerInfoError: (NSError**) error;
+- (bool) processCircleJoinData: (NSData*) circleJoinData version:(PiggyBackProtocolVersion)version error: (NSError**)error ;
+- (bool) processAccountCode: (NSString*) accountCode error: (NSError**)error;
+
+@end
+
+@implementation KCJoiningRequestTestDelegate
+
++ (id) requestDelegateWithSecret:(NSString*) secret {
+    return [[KCJoiningRequestTestDelegate alloc] initWithSecret:secret
+                                                incorrectSecret:@""
+                                                 incorrectTries:0];
+}
+
++ (id) requestDelegateWithSecret:(NSString*) secret
+                 incorrectSecret:(NSString*) wrongSecret
+                  incorrectTries:(int) retries {
+    return [[KCJoiningRequestTestDelegate alloc] initWithSecret:secret
+                                                incorrectSecret:wrongSecret
+                                                 incorrectTries:retries];
+}
+
+
+- (id) initWithSecret: (NSString*) secret
+      incorrectSecret: (NSString*) incorrectSecret
+       incorrectTries: (int) retries {
+    if ( self = [super init] ) {
+        self.sharedSecret = secret;
+        self.incorrectSecret = incorrectSecret;
+        self.incorrectTries = retries;
+    }
+    return self;
+}
+
+- (NSString*) nextSecret {
+    if (self.incorrectTries > 0) {
+        self.incorrectTries -= 1;
+        return self.incorrectSecret;
+    }
+    return self.sharedSecret;
+}
+
+- (NSString*) secret {
+    return [self nextSecret];
+}
+
+- (NSString*) verificationFailed: (bool) codeChanged {
+    return [self nextSecret];
+}
+
+- (SOSPeerInfoRef) copyPeerInfoError: (NSError**) error {
+    return NULL;
+}
+
+- (bool) processCircleJoinData: (NSData*) circleJoinData version:(PiggyBackProtocolVersion)version error: (NSError**)error {
+    self->_circleJoinData = circleJoinData;
+    return true;
+}
+
+- (bool) processAccountCode: (NSString*) accountCode error: (NSError**)error {
+    self->_accountCode = accountCode;
+    return true;
+}
+
+@end
+
+@interface KCJoiningAcceptTestDelegate : NSObject <KCJoiningAcceptSecretDelegate, KCJoiningAcceptCircleDelegate>
+@property (readonly) NSArray<NSString*>* secrets;
+@property (readwrite) NSUInteger currentSecret;
+@property (readwrite) int retriesLeft;
+@property (readwrite) int retriesPerSecret;
+
+@property (readonly) NSString* codeToUse;
+@property (readonly) NSData* circleJoinData;
+@property (readonly) SOSPeerInfoRef peerInfo;
+
++ (id) acceptDelegateWithSecret: (NSString*) secret code: (NSString*) code;
++ (id) acceptDelegateWithSecrets: (NSArray<NSString*>*) secrets retries: (int) retries code: (NSString*) code;
+- (id) initWithSecrets: (NSArray<NSString*>*) secrets retries: (int) retries code: (NSString*) code NS_DESIGNATED_INITIALIZER;
+
+- (NSString*) secret;
+- (NSString*) accountCode;
+
+- (KCRetryOrNot) verificationFailed: (NSError**) error;
+- (NSData*) circleJoinDataFor: (SOSPeerInfoRef) peer
+                        error: (NSError**) error;
+
+- (id) init NS_UNAVAILABLE;
+
+@end
+
+@implementation KCJoiningAcceptTestDelegate
+
++ (id) acceptDelegateWithSecrets: (NSArray<NSString*>*) secrets retries: (int) retries code: (NSString*) code {
+    return [[KCJoiningAcceptTestDelegate alloc] initWithSecrets:secrets retries:retries code:code];
+
+}
+
++ (id) acceptDelegateWithSecret: (NSString*) secret code: (NSString*) code {
+    return [[KCJoiningAcceptTestDelegate alloc] initWithSecret:secret code:code];
+}
+
+- (id) initWithSecret: (NSString*) secret code: (NSString*) code {
+    return [self initWithSecrets:@[secret] retries:3 code:code];
+}
+
+- (id) initWithSecrets: (NSArray<NSString*>*) secrets retries: (int) retries code: (NSString*) code {
+    self = [super init];
+
+    self->_secrets = secrets;
+    self.currentSecret = 0;
+    self->_retriesPerSecret = retries;
+    self->_retriesLeft = self.retriesPerSecret;
+
+    self->_codeToUse = code;
+
+    uint8_t joinDataBuffer[] = { 10, 9, 8, 7, 6, 5, 4, 3, 2, 1 };
+    self->_circleJoinData = [NSData dataWithBytes: joinDataBuffer length: sizeof(joinDataBuffer) ];
+
+    return self;
+}
+
+- (KCRetryOrNot) advanceSecret {
+    if (self.retriesLeft == 0) {
+        self.currentSecret += 1;
+        if (self.currentSecret >= [self.secrets count]) {
+            self.currentSecret = [self.secrets count] - 1;
+        }
+        self.retriesLeft = self.retriesPerSecret;
+        return kKCRetryWithNewChallenge;
+    } else {
+        self.retriesLeft -= 1;
+        return kKCRetryWithSameChallenge;
+    }
+}
+
+- (NSString*) secret {
+    return self.secrets[self.currentSecret];
+}
+- (NSString*) accountCode {
+    return self.codeToUse;
+}
+
+- (KCRetryOrNot) verificationFailed: (NSError**) error {
+    return [self advanceSecret];
+}
+
+- (NSData*) circleJoinDataFor: (SOSPeerInfoRef) peer
+                        error: (NSError**) error {
+    uint8_t joinDataBuffer[] = { 10, 9, 8, 7, 6, 5, 4, 3, 2, 1 };
+
+    self->_peerInfo = peer;
+    return [NSData dataWithBytes: joinDataBuffer length: sizeof(joinDataBuffer) ];
+}
+
+-(NSData*) circleGetInitialSyncViews:(SOSInitialSyncFlags)flags error:(NSError**) error{
+    char testData[] = {0,1,2,3,4,5,6,7,8,9};
+    return [NSData dataWithBytes:testData length:sizeof(testData)];
+    //return [[KCJoiningAcceptAccountCircleDelegate delegate] circleGetInitialSyncViews:flags error:error]; //Need security entitlements!
+}
+
+@end
+
+@interface KCTLKRequestTest : XCTestCase
+
+@end
+
+@implementation KCTLKRequestTest
+
+- (void)setUp {
+    [super setUp];
+    // Put setup code here. This method is called before the invocation of each test method in the class.
+}
+
+- (void)tearDown {
+    // Put teardown code here. This method is called after the invocation of each test method in the class.
+    [super tearDown];
+}
+
+- (void)testTLKRequest {
+    NSError* error = nil;
+
+    NSString* secret = @"123456";
+    NSString* code = @"987654";
+
+    uint64_t dsid = 0x1234567887654321;
+
+    KCJoiningRequestTestDelegate* requestDelegate = [KCJoiningRequestTestDelegate requestDelegateWithSecret: secret];
+    KCJoiningRequestSecretSession *requestSession = [[KCJoiningRequestSecretSession alloc] initWithSecretDelegate:requestDelegate
+                                                                                                             dsid:dsid
+                                                                                                              rng:ccDRBGGetRngState()
+                                                                                                            error:&error];
+
+    NSData* initialMessage = [requestSession initialMessage: &error];
+
+    XCTAssertNotNil(initialMessage, @"No initial message");
+    XCTAssertNil(error, @"Got error %@", error);
+
+    KCJoiningAcceptTestDelegate* acceptDelegate = [KCJoiningAcceptTestDelegate acceptDelegateWithSecret:secret code:code];
+    KCJoiningAcceptSession* acceptSession = [[KCJoiningAcceptSession alloc] initWithSecretDelegate:acceptDelegate
+                                                                                    circleDelegate:acceptDelegate
+                                                                                              dsid:dsid
+                                                                                               rng:ccDRBGGetRngState()
+                                                                                             error:&error];
+    
+    error = nil;
+    NSData* challenge = [acceptSession processMessage: initialMessage error: &error];
+
+    XCTAssertNotNil(challenge, @"No initial message");
+    XCTAssertNil(error, @"Got error %@", error);
+
+    error = nil;
+    NSData* response = [requestSession processMessage: challenge error: &error];
+
+    XCTAssertNotNil(response, @"No response message");
+    XCTAssertNil(error, @"Got error %@", error);
+
+    error = nil;
+    NSData* verification = [acceptSession processMessage: response error: &error];
+
+    XCTAssertNotNil(verification, @"No verification message");
+    XCTAssertNil(error, @"Got error %@", error);
+
+    error = nil;
+    NSData* doneMessage = [requestSession processMessage: verification error: &error];
+
+    XCTAssertNotNil(doneMessage, @"No response message");
+    XCTAssertNil(error, @"Got error %@", error);
+
+    XCTAssertTrue([requestSession isDone], @"SecretSession done");
+    XCTAssertFalse([acceptSession isDone], @"Unexpected accept session done");
+
+    KCAESGCMDuplexSession* aesSession = [requestSession session];
+    requestSession = nil;
+
+    KCJoiningRequestCircleSession* requestSecretSession = [KCJoiningRequestCircleSession sessionWithCircleDelegate:requestDelegate session:aesSession error:&error];
+
+    XCTAssertNotNil(requestSecretSession, @"No request secret session");
+    XCTAssertNil(error, @"Got error %@", error);
+
+    NSData* tlkRequestMessage = createTlkRequestMessage(aesSession);
+    XCTAssertNotNil(tlkRequestMessage, @"No TLKRequest message");
+    
+    NSData* tlkMessage = [acceptSession processMessage:tlkRequestMessage error:&error];
+    XCTAssertNotNil(tlkMessage, @"No tlkData message");
+    XCTAssertNil(error, @"Got error %@", error);
+        
+    KCJoiningMessage* receivedKCJoinMessage = [KCJoiningMessage messageWithDER:tlkMessage error:&error];
+    XCTAssertNotNil(receivedKCJoinMessage, @"No receivedKCJoinMessage message");
+    XCTAssertNil(error, @"Got error %@", error);
+
+    NSData* tlkDecryptedData = [aesSession decryptAndVerify:receivedKCJoinMessage.firstData  error:&error];
+    XCTAssertNotNil(tlkDecryptedData, @"No tlkDecryptedData message");
+    XCTAssertNil(error, @"Got error %@", error);
+    
+    //check for tlkc content
+    NSData* initialSync = [acceptDelegate circleGetInitialSyncViews:kSOSInitialSyncFlagTLKs error:&error];
+    XCTAssertNotNil(initialSync, @"No initialSync data");
+    XCTAssertNil(error, @"Got error %@", error);
+    
+    XCTAssertEqualObjects(initialSync, tlkDecryptedData, @"TLK data is different.");
+}
+@end
index 2c9138260812ee5f48bb34c026e91ab91ff0d14d..742abf4aba708bcbf376cc458c5184bc3ef36755 100644 (file)
@@ -104,6 +104,8 @@ static void PSKeychainSyncIsUsingICDP(void)
     NSError *localError = NULL;
     CDPFollowUpController *cdpd = [[CDPFollowUpController alloc] init];
     CDPFollowUpContext *context = [CDPFollowUpContext contextForStateRepair];
     NSError *localError = NULL;
     CDPFollowUpController *cdpd = [[CDPFollowUpController alloc] init];
     CDPFollowUpContext *context = [CDPFollowUpContext contextForStateRepair];
+
+    secnotice("followup", "Posting a follow up (for SOS) of type repair");
     [cdpd postFollowUpWithContext:context error:&localError ];
     if(localError){
         secnotice("kcn", "request to CoreCDP to follow up failed: %@", localError);
     [cdpd postFollowUpWithContext:context error:&localError ];
     if(localError){
         secnotice("kcn", "request to CoreCDP to follow up failed: %@", localError);
index 2a1930aa5195409319c0a2d2ebbcab1f2f7629df..4377483756bd3fc865206fa8273cf52c142ea889 100644 (file)
@@ -1670,7 +1670,18 @@ See remaining rules for examples.
                        <key>timeout</key>
                        <integer>300</integer>
                </dict>
                        <key>timeout</key>
                        <integer>300</integer>
                </dict>
-               <key>com.apple.system-extensions.admin</key>
+        <key>com.apple.tcc.util.admin</key>
+        <dict>
+             <key>comment</key>
+             <string>For modification of TCC settings.</string>
+             <key>class</key>
+             <string>rule</string>
+             <key>rule</key>
+             <string>authenticate-admin-nonshared</string>
+             <key>shared</key>
+             <false/>
+         </dict>
+        <key>com.apple.system-extensions.admin</key>
                <dict>
                        <key>comment</key>
                        <string>Authorize a 3rd party application which wants to manipulate system extensions.</string>
                <dict>
                        <key>comment</key>
                        <string>Authorize a 3rd party application which wants to manipulate system extensions.</string>
index 1686dbd63897d2832a56026ebbfc9c7b6bd88e09..43870767ceb289b2283145cf0f6871f8b3b24901 100644 (file)
 "com.apple.configurationprofiles.userenrollment.install" = "__APPNAME__ is trying to enroll you in a remote management (MDM) service.";
 
 "com.apple.system-extensions.admin" = "__APPNAME__ is trying to modify a System Extension.";
 "com.apple.configurationprofiles.userenrollment.install" = "__APPNAME__ is trying to enroll you in a remote management (MDM) service.";
 
 "com.apple.system-extensions.admin" = "__APPNAME__ is trying to modify a System Extension.";
+
+"com.apple.tcc.util.admin" = "__APPNAME__ is trying to modify your Security & Privacy settings.";
index 186e766987171bbb381abc81b5993a44ed80ac1e..66c9bcb285e7d5d26fa3cfc585b1f8b58202cff3 100644 (file)
@@ -199,8 +199,13 @@ public:
        CssmDlDbHandle *handles() const { return CssmDlDbHandle::overlay(DLDBHandle); }
        CssmDlDbHandle * &handles()     { return CssmDlDbHandle::overlayVar(DLDBHandle); }
 
        CssmDlDbHandle *handles() const { return CssmDlDbHandle::overlay(DLDBHandle); }
        CssmDlDbHandle * &handles()     { return CssmDlDbHandle::overlayVar(DLDBHandle); }
 
-       CssmDlDbHandle &operator [] (uint32 ix) const
-       { assert(ix < count()); return CssmDlDbHandle::overlay(DLDBHandle[ix]); }
+       CssmDlDbHandle &operator [] (uint32 ix) const {
+               if (ix >= count()) {
+                       secemergency("CssmDlDbList: attempt to index beyond bounds");
+                       abort();
+               }
+               return CssmDlDbHandle::overlay(DLDBHandle[ix]);
+       }
        
        void setDlDbList(uint32 n, CSSM_DL_DB_HANDLE *list)
        { count() = n; handles() = CssmDlDbHandle::overlay(list); }
        
        void setDlDbList(uint32 n, CSSM_DL_DB_HANDLE *list)
        { count() = n; handles() = CssmDlDbHandle::overlay(list); }
@@ -345,8 +350,13 @@ public:
        { return CssmDbAttributeInfo::overlayVar(AttributeInfo); }
        CssmDbAttributeInfo *attributes() const
        { return CssmDbAttributeInfo::overlay(AttributeInfo); }
        { return CssmDbAttributeInfo::overlayVar(AttributeInfo); }
        CssmDbAttributeInfo *attributes() const
        { return CssmDbAttributeInfo::overlay(AttributeInfo); }
-    CssmDbAttributeInfo &at(uint32 ix) const
-       { assert(ix < size()); return attributes()[ix]; }
+    CssmDbAttributeInfo &at(uint32 ix) const {
+               if (ix >= size()) {
+                       secemergency("CssmDbRecordAttributeInfo: attempt to index beyond bounds");
+                       abort();
+               }
+               return attributes()[ix];
+       }
 
     CssmDbAttributeInfo &operator [] (uint32 ix) const { return at(ix); }
 };
 
     CssmDbAttributeInfo &operator [] (uint32 ix) const { return at(ix); }
 };
@@ -481,8 +491,13 @@ public:
        { return CssmDbAttributeData::overlay(AttributeData); }
 
        // Attributes by position
        { return CssmDbAttributeData::overlay(AttributeData); }
 
        // Attributes by position
-    CssmDbAttributeData &at(unsigned int ix) const
-       { assert(ix < size()); return attributes()[ix]; }
+    CssmDbAttributeData &at(unsigned int ix) const {
+               if (ix >= size()) {
+                       secemergency("CssmDbRecordAttributeData: attempt to index beyond bounds");
+                       abort();
+               }
+               return attributes()[ix];
+       }
 
     CssmDbAttributeData &operator [] (unsigned int ix) const { return at(ix); }
 
 
     CssmDbAttributeData &operator [] (unsigned int ix) const { return at(ix); }
 
@@ -596,8 +611,13 @@ public:
        CssmSelectionPredicate *predicates() const
        { return CssmSelectionPredicate::overlay(SelectionPredicate); }
 
        CssmSelectionPredicate *predicates() const
        { return CssmSelectionPredicate::overlay(SelectionPredicate); }
 
-       CssmSelectionPredicate &at(uint32 ix) const
-       { assert(ix < size()); return predicates()[ix]; }
+       CssmSelectionPredicate &at(uint32 ix) const {
+               if (ix >= size()) {
+                  secemergency("CssmDbRecordAttributeData: attempt to index beyond bounds");
+                  abort();
+               }
+               return predicates()[ix];
+       }
 
        CssmSelectionPredicate &operator[] (uint32 ix) const { return at(ix); }
        
 
        CssmSelectionPredicate &operator[] (uint32 ix) const { return at(ix); }
        
index be2443d12951d1125c209818c5ff508ca824f9ef..86b056c063f6ac11d54cc424a14ceaddad3de7ad 100644 (file)
@@ -69,13 +69,19 @@ bool DiskImageRep::readHeader(FileDesc& fd, UDIFFileHeader& header)
 // Object management.
 //
 DiskImageRep::DiskImageRep(const char *path)
 // Object management.
 //
 DiskImageRep::DiskImageRep(const char *path)
-       : SingleDiskRep(path)
+       : SingleDiskRep(path), mSigningData(NULL)
 {
        this->setup();
 }
 
 {
        this->setup();
 }
 
+DiskImageRep::~DiskImageRep()
+{
+       free((void*)mSigningData);
+}
+
 void DiskImageRep::setup()
 {
 void DiskImageRep::setup()
 {
+       free((void*)mSigningData);
        mSigningData = NULL;
        
        // the UDIF "header" is in fact the last 512 bytes of the file, with no particular alignment
        mSigningData = NULL;
        
        // the UDIF "header" is in fact the last 512 bytes of the file, with no particular alignment
@@ -211,7 +217,7 @@ void DiskImageRep::Writer::component(CodeDirectory::SpecialSlot slot, CFDataRef
 //
 void DiskImageRep::Writer::flush()
 {
 //
 void DiskImageRep::Writer::flush()
 {
-       delete mSigningData;                    // ditch previous blob just in case
+       free((void*)mSigningData);              // ditch previous blob just in case
        mSigningData = Maker::make();   // assemble new signature SuperBlob
        
        // write signature superblob
        mSigningData = Maker::make();   // assemble new signature SuperBlob
        
        // write signature superblob
index 91a0919334180bf63944e3128a4b56a459ad02db..06a556e8f0b46a197b2e5646261df783fc778d33 100644 (file)
@@ -43,6 +43,7 @@ namespace CodeSigning {
 class DiskImageRep : public SingleDiskRep {
 public:
        DiskImageRep(const char *path);
 class DiskImageRep : public SingleDiskRep {
 public:
        DiskImageRep(const char *path);
+       virtual ~DiskImageRep();
        
        CFDataRef identification();
        CFDataRef component(CodeDirectory::SpecialSlot slot);
        
        CFDataRef identification();
        CFDataRef component(CodeDirectory::SpecialSlot slot);
@@ -70,7 +71,7 @@ private:
        UDIFFileHeader mHeader;                                         // disk image header (all fields NBO)
        size_t mEndOfDataOffset;                                        // end of payload data (data fork + XML)
        size_t mHeaderOffset;                                           // trailing header offset
        UDIFFileHeader mHeader;                                         // disk image header (all fields NBO)
        size_t mEndOfDataOffset;                                        // end of payload data (data fork + XML)
        size_t mHeaderOffset;                                           // trailing header offset
-       const EmbeddedSignatureBlob *mSigningData;      // pointer to signature SuperBlob (in mapped memory)
+       const EmbeddedSignatureBlob *mSigningData;      // pointer to signature SuperBlob (malloc'd memory during setup)
 };
 
 
 };
 
 
index fa75c0d02c4678e2255cece8008421cadf0516ff..f645fd2eec9130864442b899b1d11ffdf9986e9e 100644 (file)
@@ -49,9 +49,9 @@ registerStapledTicketWithSystem(CFDataRef data)
        secinfo("notarization", "Registering stapled ticket with system");
 
 #if TARGET_OS_OSX
        secinfo("notarization", "Registering stapled ticket with system");
 
 #if TARGET_OS_OSX
-       CFErrorRef error = NULL;
-       if (!SecAssessmentTicketRegister(data, &error)) {
-               secerror("Error registering stapled ticket: %@", data);
+       CFRef<CFErrorRef> error;
+       if (!SecAssessmentTicketRegister(data, &error.aref())) {
+               secerror("Error registering stapled ticket: %@", error.get());
        }
 #endif // TARGET_OS_OSX
 }
        }
 #endif // TARGET_OS_OSX
 }
@@ -208,7 +208,7 @@ registerStapledTicketInPackage(const std::string& path)
                goto lb_exit;
        }
 
                goto lb_exit;
        }
 
-       data = CFDataCreateWithBytesNoCopy(NULL, ticketData, trailer.length, NULL);
+       data.take(makeCFDataMalloc(ticketData, trailer.length));
        if (data.get() == NULL) {
                secerror("unable to create cfdata for notarization");
                goto lb_exit;
        if (data.get() == NULL) {
                secerror("unable to create cfdata for notarization");
                goto lb_exit;
@@ -221,9 +221,6 @@ lb_exit:
        if (fd) {
                close(fd);
        }
        if (fd) {
                close(fd);
        }
-       if (ticketData) {
-               free(ticketData);
-       }
 }
 
 void
 }
 
 void
@@ -277,7 +274,7 @@ registerStapledTicketInBundle(const std::string& path)
                goto lb_exit;
        }
 
                goto lb_exit;
        }
 
-       data = CFDataCreateWithBytesNoCopy(NULL, ticketData, ticketLength, NULL);
+       data.take(makeCFDataMalloc(ticketData, ticketLength));
        if (data.get() == NULL) {
                secerror("unable to create cfdata for notarization");
                goto lb_exit;
        if (data.get() == NULL) {
                secerror("unable to create cfdata for notarization");
                goto lb_exit;
@@ -290,9 +287,6 @@ lb_exit:
        if (fd) {
                close(fd);
        }
        if (fd) {
                close(fd);
        }
-       if (ticketData) {
-               free(ticketData);
-       }
 }
 
 void
 }
 
 void
index 8927923867b57a600c09081f2e6c09af6272c2ae..eff950abc01b261491e1989ea64330a5aa04b904 100644 (file)
@@ -429,11 +429,12 @@ void SecCodeSigner::Signer::buildResources(std::string root, std::string relBase
 
                resources.scan(^(FTSENT *ent, uint32_t ruleFlags, const std::string relpath, Rule *rule) {
                        bool isSymlink = (ent->fts_info == FTS_SL);
 
                resources.scan(^(FTSENT *ent, uint32_t ruleFlags, const std::string relpath, Rule *rule) {
                        bool isSymlink = (ent->fts_info == FTS_SL);
+                       bool isNested = (ruleFlags & ResourceBuilder::nested);
                        const std::string path(ent->fts_path);
                        const std::string accpath(ent->fts_accpath);
                        this->state.mLimitedAsync->perform(groupRef, ^{
                                CFRef<CFMutableDictionaryRef> seal;
                        const std::string path(ent->fts_path);
                        const std::string accpath(ent->fts_accpath);
                        this->state.mLimitedAsync->perform(groupRef, ^{
                                CFRef<CFMutableDictionaryRef> seal;
-                               if (ruleFlags & ResourceBuilder::nested) {
+                               if (isNested) {
                                        seal.take(signNested(path, relpath));
                                } else if (isSymlink) {
                                        char target[PATH_MAX];
                                        seal.take(signNested(path, relpath));
                                } else if (isSymlink) {
                                        char target[PATH_MAX];
@@ -445,6 +446,10 @@ void SecCodeSigner::Signer::buildResources(std::string root, std::string relBase
                                } else {
                                        seal.take(resources.hashFile(accpath.c_str(), digestAlgorithms(), signingFlags() & kSecCSSignStrictPreflight));
                                }
                                } else {
                                        seal.take(resources.hashFile(accpath.c_str(), digestAlgorithms(), signingFlags() & kSecCSSignStrictPreflight));
                                }
+                               if (seal.get() == NULL) {
+                                       secerror("Failed to generate sealed resource: %d, %d, %s", isNested, isSymlink, accpath.c_str());
+                                       MacOSError::throwMe(errSecCSBadResource);
+                               }
                                if (ruleFlags & ResourceBuilder::optional)
                                        CFDictionaryAddValue(seal, CFSTR("optional"), kCFBooleanTrue);
                                CFTypeRef hash;
                                if (ruleFlags & ResourceBuilder::optional)
                                        CFDictionaryAddValue(seal, CFSTR("optional"), kCFBooleanTrue);
                                CFTypeRef hash;
index b1b571c265085dfda78ae643f27d8109d271fe36..2cdb17f942f5d256d6dc24c426f987c6dfa1d390 100644 (file)
@@ -41,6 +41,7 @@
 #include "TrustSettingsSchema.h"
 #include <Security/SecTrustPriv.h>
 #include "utilities/array_size.h"
 #include "TrustSettingsSchema.h"
 #include <Security/SecTrustPriv.h>
 #include "utilities/array_size.h"
+#include "utilities/SecCFWrappers.h"
 
 #include <AssertMacros.h>
 #include <syslog.h>
 
 #include <AssertMacros.h>
 #include <syslog.h>
@@ -4309,7 +4310,7 @@ SecItemCreateFromAttributeDictionary_osx(CFDictionaryRef refAttributes) {
                CFTypeRef v;
 
     Item item = Item(item_class, &attrs, 0, "");
                CFTypeRef v;
 
     Item item = Item(item_class, &attrs, 0, "");
-               v = CFDictionaryGetValue(refAttributes, kSecValuePersistentRef);
+               v = CFCast(CFData, CFDictionaryGetValue(refAttributes, kSecValuePersistentRef));
                if (v) {
                        item->setPersistentRef((CFDataRef)v);
                }
                if (v) {
                        item->setPersistentRef((CFDataRef)v);
                }
index cbdc0912215424698cb8fdfb26e0c5dfa49ec7f3..102b8a4fb85aee7a9fa9a4370986f664bdb865de 100644 (file)
@@ -3,25 +3,25 @@
  * License Version 1.1 (the "License"); you may not use this file
  * except in compliance with the License. You may obtain a copy of
  * the License at http://www.mozilla.org/MPL/
  * License Version 1.1 (the "License"); you may not use this file
  * except in compliance with the License. You may obtain a copy of
  * the License at http://www.mozilla.org/MPL/
- * 
+ *
  * Software distributed under the License is distributed on an "AS
  * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
  * implied. See the License for the specific language governing
  * rights and limitations under the License.
  * Software distributed under the License is distributed on an "AS
  * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
  * implied. See the License for the specific language governing
  * rights and limitations under the License.
- * 
+ *
  * The Original Code is the Netscape security libraries.
  * The Original Code is the Netscape security libraries.
- * 
+ *
  * The Initial Developer of the Original Code is Netscape
  * The Initial Developer of the Original Code is Netscape
- * Communications Corporation.  Portions created by Netscape are 
+ * Communications Corporation.  Portions created by Netscape are
  * Copyright (C) 1994-2000 Netscape Communications Corporation.  All
  * Rights Reserved.
  * Copyright (C) 1994-2000 Netscape Communications Corporation.  All
  * Rights Reserved.
- * 
+ *
  * Contributor(s):
  * Contributor(s):
- * 
+ *
  * Alternatively, the contents of this file may be used under the
  * terms of the GNU General Public License Version 2 or later (the
  * Alternatively, the contents of this file may be used under the
  * terms of the GNU General Public License Version 2 or later (the
- * "GPL"), in which case the provisions of the GPL are applicable 
- * instead of those above.  If you wish to allow use of your 
+ * "GPL"), in which case the provisions of the GPL are applicable
+ * instead of those above.  If you wish to allow use of your
  * version of this file only under the terms of the GPL and not to
  * allow others to use your version of this file under the MPL,
  * indicate your decision by deleting the provisions above and
  * version of this file only under the terms of the GPL and not to
  * allow others to use your version of this file under the MPL,
  * indicate your decision by deleting the provisions above and
 /*
  * CMS digesting.
  */
 /*
  * CMS digesting.
  */
+#include <assert.h>
 
 #include "cmslocal.h"
 
 
 #include "cmslocal.h"
 
-#include "secitem.h"
+#include "SecAsn1Item.h"
 #include "secoid.h"
 
 #include <security_asn1/secerr.h>
 #include "secoid.h"
 
 #include <security_asn1/secerr.h>
-#include <Security/cssmapi.h>
+#include <security_asn1/secport.h>
+
+#include <CommonCrypto/CommonDigest.h>
 
 #include <Security/SecCmsDigestContext.h>
 
 
 #include <Security/SecCmsDigestContext.h>
 
-/* Return the maximum value between S and T */
+/* Return the maximum value between S and T (and U) */
 #define MAX(S, T) ({__typeof__(S) _max_s = S; __typeof__(T) _max_t = T; _max_s > _max_t ? _max_s : _max_t;})
 #define MAX(S, T) ({__typeof__(S) _max_s = S; __typeof__(T) _max_t = T; _max_s > _max_t ? _max_s : _max_t;})
+#define MAX_OF_3(S, T, U) ({__typeof__(U) _max_st = MAX(S,T); MAX(_max_st,U);})
 
 struct SecCmsDigestContextStr {
 
 struct SecCmsDigestContextStr {
-    Boolean            saw_contents;
-    int                        digcnt;
-    CSSM_CC_HANDLE *   digobjs;
+    PLArenaPool *    poolp;
+    Boolean        saw_contents;
+    int                 digcnt;
+    void **             digobjs;
+    SECAlgorithmID **   digestalgs;
 };
 
 /*
 };
 
 /*
@@ -61,25 +67,38 @@ struct SecCmsDigestContextStr {
 SecCmsDigestContextRef
 SecCmsDigestContextStartMultiple(SECAlgorithmID **digestalgs)
 {
 SecCmsDigestContextRef
 SecCmsDigestContextStartMultiple(SECAlgorithmID **digestalgs)
 {
-    SecCmsDigestContextRef cmsdigcx;
-    CSSM_CC_HANDLE digobj;
+    PLArenaPool *poolp;
+    SecCmsDigestContextRef cmsdigcx = NULL;
+    void * digobj;
     int digcnt;
     int i;
 
     int digcnt;
     int i;
 
+    poolp = PORT_NewArena(1024);
+    if (poolp == NULL) {
+        goto loser;
+    }
+
     digcnt = (digestalgs == NULL) ? 0 : SecCmsArrayCount((void **)digestalgs);
 
     digcnt = (digestalgs == NULL) ? 0 : SecCmsArrayCount((void **)digestalgs);
 
-    cmsdigcx = (SecCmsDigestContextRef)PORT_ZAlloc(sizeof(struct SecCmsDigestContextStr));
-    if (cmsdigcx == NULL)
-       return NULL;
+    cmsdigcx = (SecCmsDigestContextRef)PORT_ArenaAlloc(poolp, sizeof(struct SecCmsDigestContextStr));
+    if (cmsdigcx == NULL) {
+        goto loser;
+    }
+    cmsdigcx->poolp = poolp;
 
     if (digcnt > 0) {
         /* Security check to prevent under-allocation */
 
     if (digcnt > 0) {
         /* Security check to prevent under-allocation */
-        if (digcnt >= (int)(INT_MAX/sizeof(CSSM_CC_HANDLE))) {
+        if (digcnt >= (int)((INT_MAX/(MAX(sizeof(void *),sizeof(SECAlgorithmID *))))-1)) {
+            goto loser;
+        }
+        cmsdigcx->digobjs = (void**)PORT_ArenaAlloc(poolp, digcnt * sizeof(void *));
+        if (cmsdigcx->digobjs == NULL) {
+            goto loser;
+        }
+        cmsdigcx->digestalgs = (SECAlgorithmID **)PORT_ArenaZAlloc(poolp, (digcnt + 1) * sizeof(SECAlgorithmID *));
+        if (cmsdigcx->digestalgs == NULL) {
             goto loser;
         }
             goto loser;
         }
-       cmsdigcx->digobjs = (CSSM_CC_HANDLE *)PORT_ZAlloc(digcnt * sizeof(CSSM_CC_HANDLE));
-       if (cmsdigcx->digobjs == NULL)
-           goto loser;
     }
 
     cmsdigcx->digcnt = 0;
     }
 
     cmsdigcx->digcnt = 0;
@@ -88,27 +107,27 @@ SecCmsDigestContextStartMultiple(SECAlgorithmID **digestalgs)
      * Create a digest object context for each algorithm.
      */
     for (i = 0; i < digcnt; i++) {
      * Create a digest object context for each algorithm.
      */
     for (i = 0; i < digcnt; i++) {
-       digobj = SecCmsUtilGetHashObjByAlgID(digestalgs[i]);
-       /*
-        * Skip any algorithm we do not even recognize; obviously,
-        * this could be a problem, but if it is critical then the
-        * result will just be that the signature does not verify.
-        * We do not necessarily want to error out here, because
-        * the particular algorithm may not actually be important,
-        * but we cannot know that until later.
-        */
-       if (digobj)
-        {
-            CSSM_RETURN result;
-           result = CSSM_DigestDataInit(digobj);
-            if (result != CSSM_OK)
-            {
-                goto loser;
-            }
+        digobj = SecCmsUtilGetHashObjByAlgID(digestalgs[i]);
+        /*
+         * Skip any algorithm we do not even recognize; obviously,
+         * this could be a problem, but if it is critical then the
+         * result will just be that the signature does not verify.
+         * We do not necessarily want to error out here, because
+         * the particular algorithm may not actually be important,
+         * but we cannot know that until later.
+         */
+
+        cmsdigcx->digobjs[cmsdigcx->digcnt] = digobj;
+        cmsdigcx->digestalgs[cmsdigcx->digcnt] = PORT_ArenaAlloc(poolp, sizeof(SECAlgorithmID));
+        if (SECITEM_CopyItem(poolp,
+                             &(cmsdigcx->digestalgs[cmsdigcx->digcnt]->algorithm),
+                             &(digestalgs[i]->algorithm))
+            || SECITEM_CopyItem(poolp,
+                                &(cmsdigcx->digestalgs[cmsdigcx->digcnt]->parameters),
+                                &(digestalgs[i]->parameters))) {
+            goto loser;
         }
         }
-        
-       cmsdigcx->digobjs[cmsdigcx->digcnt] = digobj;
-       cmsdigcx->digcnt++;
+        cmsdigcx->digcnt++;
     }
 
     cmsdigcx->saw_contents = PR_FALSE;
     }
 
     cmsdigcx->saw_contents = PR_FALSE;
@@ -116,13 +135,15 @@ SecCmsDigestContextStartMultiple(SECAlgorithmID **digestalgs)
     return cmsdigcx;
 
 loser:
     return cmsdigcx;
 
 loser:
-    if (cmsdigcx) {
-        if (cmsdigcx->digobjs) {
-           PORT_Free(cmsdigcx->digobjs);
-            cmsdigcx->digobjs = NULL;
-            cmsdigcx->digcnt = 0;
-        }
+    if (poolp) {
+        PORT_FreeArena(poolp, PR_FALSE);
+    }
+    if (cmsdigcx && cmsdigcx->digobjs) {
+        PORT_Free(cmsdigcx->digobjs);
+        cmsdigcx->digobjs = NULL;
+        cmsdigcx->digcnt = 0;
     }
     }
+
     return NULL;
 }
 
     return NULL;
 }
 
@@ -133,7 +154,7 @@ loser:
 SecCmsDigestContextRef
 SecCmsDigestContextStartSingle(SECAlgorithmID *digestalg)
 {
 SecCmsDigestContextRef
 SecCmsDigestContextStartSingle(SECAlgorithmID *digestalg)
 {
-    SECAlgorithmID *digestalgs[] = { NULL, NULL };             /* fake array */
+    SECAlgorithmID *digestalgs[] = { NULL, NULL };        /* fake array */
 
     digestalgs[0] = digestalg;
     return SecCmsDigestContextStartMultiple(digestalgs);
 
     digestalgs[0] = digestalg;
     return SecCmsDigestContextStartMultiple(digestalgs);
@@ -145,15 +166,40 @@ SecCmsDigestContextStartSingle(SECAlgorithmID *digestalg)
 void
 SecCmsDigestContextUpdate(SecCmsDigestContextRef cmsdigcx, const unsigned char *data, size_t len)
 {
 void
 SecCmsDigestContextUpdate(SecCmsDigestContextRef cmsdigcx, const unsigned char *data, size_t len)
 {
-    CSSM_DATA dataBuf;
+    SecAsn1Item dataBuf;
     int i;
 
     dataBuf.Length = len;
     int i;
 
     dataBuf.Length = len;
-    dataBuf.Data = (uint8 *)data;
+    dataBuf.Data = (uint8_t *)data;
     cmsdigcx->saw_contents = PR_TRUE;
     cmsdigcx->saw_contents = PR_TRUE;
-    for (i = 0; i < cmsdigcx->digcnt; i++)
-       if (cmsdigcx->digobjs && cmsdigcx->digobjs[i])
-           CSSM_DigestDataUpdate(cmsdigcx->digobjs[i], &dataBuf, 1);
+    for (i = 0; i < cmsdigcx->digcnt; i++) {
+        if (cmsdigcx->digobjs[i]) {
+            /* 64 bits cast: worst case is we truncate the length and we dont hash all the data.
+             This may cause an invalid CMS blob larger than 4GB to be validated. Unlikely, but
+             possible security issue. There is no way to return an error here, but a check at
+             the upper level may happen. */
+            /*
+             rdar://problem/20642513
+             Let's just die a horrible death rather than have the security issue.
+             CMS blob over 4GB?  Oh well.
+             */
+            if (len > UINT32_MAX) {
+                /* Ugh. */
+                abort();
+            }
+            assert(len<=UINT32_MAX); /* Debug check. Correct as long as CC_LONG is uint32_t */
+            switch (SECOID_GetAlgorithmTag(cmsdigcx->digestalgs[i])) {
+                case SEC_OID_SHA1: CC_SHA1_Update((CC_SHA1_CTX *)cmsdigcx->digobjs[i], data, (CC_LONG)len); break;
+                case SEC_OID_MD5: CC_MD5_Update((CC_MD5_CTX *)cmsdigcx->digobjs[i], data, (CC_LONG)len); break;
+                case SEC_OID_SHA224: CC_SHA224_Update((CC_SHA256_CTX *)cmsdigcx->digobjs[i], data, (CC_LONG)len); break;
+                case SEC_OID_SHA256: CC_SHA256_Update((CC_SHA256_CTX *)cmsdigcx->digobjs[i], data, (CC_LONG)len); break;
+                case SEC_OID_SHA384: CC_SHA384_Update((CC_SHA512_CTX *)cmsdigcx->digobjs[i], data, (CC_LONG)len); break;
+                case SEC_OID_SHA512: CC_SHA512_Update((CC_SHA512_CTX *)cmsdigcx->digobjs[i], data, (CC_LONG)len); break;
+                default:
+                    break;
+            }
+        }
+    }
 }
 
 /*
 }
 
 /*
@@ -164,109 +210,113 @@ SecCmsDigestContextCancel(SecCmsDigestContextRef cmsdigcx)
 {
     int i;
 
 {
     int i;
 
-    for (i = 0; i < cmsdigcx->digcnt; i++)
+    for (i = 0; i < cmsdigcx->digcnt; i++) {
         if (cmsdigcx->digobjs && cmsdigcx->digobjs[i]) {
         if (cmsdigcx->digobjs && cmsdigcx->digobjs[i]) {
-           CSSM_DeleteContext(cmsdigcx->digobjs[i]);
-            cmsdigcx->digobjs[i] = 0;
+            free(cmsdigcx->digobjs[i]);
+            cmsdigcx->digobjs[i] = NULL;
         }
         }
+    }
+
+    PORT_FreeArena(cmsdigcx->poolp, PR_TRUE);
 }
 
 /*
 }
 
 /*
- * SecCmsDigestContextFinishMultiple - finish the digests and put them
- *  into an array of CSSM_DATAs (allocated on poolp)
+ * SecCmsDigestContextFinishMultiple - finish the digests
+ * Note that on macOS, this call cancels and frees the digest context (because the digests are allocated from an input arena pool).
+ * The iOS version only frees the digest objects and requires a call to SecCmsDisgestContextDestroy
+ * or SecCmsDisgestContextCancel (because the digests are allocated out of the context's pool).
  */
 OSStatus
  */
 OSStatus
-SecCmsDigestContextFinishMultiple(SecCmsDigestContextRef cmsdigcx, SecArenaPoolRef poolp,
-                           CSSM_DATA_PTR **digestsp)
+SecCmsDigestContextFinishMultiple(SecCmsDigestContextRef cmsdigcx,
+                                  SecArenaPoolRef poolp,
+                                  SecAsn1Item * **digestsp)
 {
 {
-    CSSM_CC_HANDLE digobj;
-    CSSM_DATA_PTR *digests, digest;
+    void * digobj;
+    SecAsn1Item **digests, *digest;
     int i;
     void *mark;
     OSStatus rv = SECFailure;
 
     int i;
     void *mark;
     OSStatus rv = SECFailure;
 
+    assert(cmsdigcx != NULL);
+
     /* no contents? do not update digests */
     if (digestsp == NULL || !cmsdigcx->saw_contents) {
     /* no contents? do not update digests */
     if (digestsp == NULL || !cmsdigcx->saw_contents) {
-       for (i = 0; i < cmsdigcx->digcnt; i++)
+        for (i = 0; i < cmsdigcx->digcnt; i++) {
             if (cmsdigcx->digobjs && cmsdigcx->digobjs[i]) {
             if (cmsdigcx->digobjs && cmsdigcx->digobjs[i]) {
-               CSSM_DeleteContext(cmsdigcx->digobjs[i]);
-                cmsdigcx->digobjs[i] = 0;
+                free(cmsdigcx->digobjs[i]);
             }
             }
-       rv = SECSuccess;
-       goto cleanup;
+        }
+        rv = SECSuccess;
+        goto cleanup;
     }
 
     }
 
+    assert(digestsp != NULL);
+
     mark = PORT_ArenaMark ((PLArenaPool *)poolp);
 
     /* Security check to prevent under-allocation */
     mark = PORT_ArenaMark ((PLArenaPool *)poolp);
 
     /* Security check to prevent under-allocation */
-    if (cmsdigcx->digcnt >= (int)((INT_MAX/(MAX(sizeof(CSSM_DATA_PTR),sizeof(CSSM_DATA))))-1)) {
+    if (cmsdigcx->digcnt >= (int)((INT_MAX/(MAX(sizeof(SecAsn1Item *),sizeof(SecAsn1Item))))-1)) {
         goto loser;
     }
         goto loser;
     }
-    /* allocate digest array & CSSM_DATAs on arena */
-    digests = (CSSM_DATA_PTR *)PORT_ArenaAlloc((PLArenaPool *)poolp, (cmsdigcx->digcnt+1) * sizeof(CSSM_DATA_PTR));
-    digest = (CSSM_DATA_PTR)PORT_ArenaZAlloc((PLArenaPool *)poolp, cmsdigcx->digcnt * sizeof(CSSM_DATA));
+    /* allocate digest array & SecAsn1Items on arena */
+    digests = (SecAsn1Item * *)PORT_ArenaZAlloc((PLArenaPool *)poolp, (cmsdigcx->digcnt+1) * sizeof(SecAsn1Item *));
+    digest = (SecAsn1Item *)PORT_ArenaZAlloc((PLArenaPool *)poolp, cmsdigcx->digcnt * sizeof(SecAsn1Item));
     if (digests == NULL || digest == NULL) {
     if (digests == NULL || digest == NULL) {
-       goto loser;
+        goto loser;
     }
 
     for (i = 0; i < cmsdigcx->digcnt; i++, digest++) {
     }
 
     for (i = 0; i < cmsdigcx->digcnt; i++, digest++) {
-        if (cmsdigcx->digobjs) {
-            digobj = cmsdigcx->digobjs[i];
-        } else {
-            digobj = 0;
+        SECOidTag hash_alg = SECOID_GetAlgorithmTag(cmsdigcx->digestalgs[i]);
+        int diglength = 0;
+
+        switch (hash_alg) {
+            case SEC_OID_SHA1: diglength = CC_SHA1_DIGEST_LENGTH; break;
+            case SEC_OID_MD5: diglength = CC_MD5_DIGEST_LENGTH; break;
+            case SEC_OID_SHA224: diglength = CC_SHA224_DIGEST_LENGTH; break;
+            case SEC_OID_SHA256: diglength = CC_SHA256_DIGEST_LENGTH; break;
+            case SEC_OID_SHA384: diglength = CC_SHA384_DIGEST_LENGTH; break;
+            case SEC_OID_SHA512: diglength = CC_SHA512_DIGEST_LENGTH; break;
+            default: goto loser;
         }
 
         }
 
-       CSSM_QUERY_SIZE_DATA dataSize;
-       rv = CSSM_QuerySize(digobj, CSSM_FALSE, 1, &dataSize);
-        if (rv != CSSM_OK)
-        {
-            goto loser;
-        }
-        
-       int diglength = dataSize.SizeOutputBlock;
-       
-       if (digobj)
-       {
-           digest->Data = (unsigned char*)PORT_ArenaAlloc((PLArenaPool *)poolp, diglength);
-           if (digest->Data == NULL)
-               goto loser;
-           digest->Length = diglength;
-           rv = CSSM_DigestDataFinal(digobj, digest);
-            if (rv != CSSM_OK)
-            {
+        digobj = cmsdigcx->digobjs[i];
+        if (digobj) {
+            digest->Data = (unsigned char*)PORT_ArenaAlloc((PLArenaPool *)poolp, diglength);
+            if (digest->Data == NULL)
                 goto loser;
                 goto loser;
+            digest->Length = diglength;
+            switch (hash_alg) {
+                case SEC_OID_SHA1: CC_SHA1_Final(digest->Data, digobj); break;
+                case SEC_OID_MD5: CC_MD5_Final(digest->Data, digobj); break;
+                case SEC_OID_SHA224: CC_SHA224_Final(digest->Data, digobj); break;
+                case SEC_OID_SHA256: CC_SHA256_Final(digest->Data, digobj); break;
+                case SEC_OID_SHA384: CC_SHA384_Final(digest->Data, digobj); break;
+                case SEC_OID_SHA512: CC_SHA512_Final(digest->Data, digobj); break;
+                default: goto loser;
             }
             }
-            
-           CSSM_DeleteContext(digobj);
-            cmsdigcx->digobjs[i] = 0;
-       }
-       else
-       {
-           digest->Data = NULL;
-           digest->Length = 0;
-       }
-       
-       digests[i] = digest;
-   }
+
+            free(digobj);
+            digests[i] = digest;
+        } else {
+            digest->Data = NULL;
+            digest->Length = 0;
+        }
+    }
     digests[i] = NULL;
     *digestsp = digests;
 
     rv = SECSuccess;
 
 loser:
     digests[i] = NULL;
     *digestsp = digests;
 
     rv = SECSuccess;
 
 loser:
-    if (rv == SECSuccess)
-       PORT_ArenaUnmark((PLArenaPool *)poolp, mark);
-    else
-       PORT_ArenaRelease((PLArenaPool *)poolp, mark);
+    if (rv == SECSuccess) {
+        PORT_ArenaUnmark((PLArenaPool *)poolp, mark);
+    } else {
+        PORT_ArenaRelease((PLArenaPool *)poolp, mark);
+    }
 
 cleanup:
 
 cleanup:
-    if (cmsdigcx->digcnt > 0) {
-        SecCmsDigestContextCancel(cmsdigcx);
-       PORT_Free(cmsdigcx->digobjs);
-        cmsdigcx->digobjs = NULL;
-        cmsdigcx->digcnt = 0;
-    }
-    PORT_Free(cmsdigcx);
+    cmsdigcx->digcnt = 0; // We've already freed the digests above
+    SecCmsDigestContextCancel(cmsdigcx);
 
     return rv;
 }
 
     return rv;
 }
@@ -277,28 +327,30 @@ cleanup:
  */
 OSStatus
 SecCmsDigestContextFinishSingle(SecCmsDigestContextRef cmsdigcx, SecArenaPoolRef poolp,
  */
 OSStatus
 SecCmsDigestContextFinishSingle(SecCmsDigestContextRef cmsdigcx, SecArenaPoolRef poolp,
-                           CSSM_DATA_PTR digest)
+                                SecAsn1Item * digest)
 {
     OSStatus rv = SECFailure;
 {
     OSStatus rv = SECFailure;
-    CSSM_DATA_PTR *dp;
+    SecAsn1Item **dp;
     PLArenaPool *arena = NULL;
 
     if ((arena = PORT_NewArena(1024)) == NULL)
        goto loser;
 
     /* get the digests into arena, then copy the first digest into poolp */
     PLArenaPool *arena = NULL;
 
     if ((arena = PORT_NewArena(1024)) == NULL)
        goto loser;
 
     /* get the digests into arena, then copy the first digest into poolp */
-    if (SecCmsDigestContextFinishMultiple(cmsdigcx, (SecArenaPoolRef)arena, &dp) != SECSuccess)
-       goto loser;
+    if (SecCmsDigestContextFinishMultiple(cmsdigcx, (SecArenaPoolRef)arena, &dp) != SECSuccess) {
+        goto loser;
+    }
 
     /* now copy it into poolp */
 
     /* now copy it into poolp */
-    if (SECITEM_CopyItem((PLArenaPool *)poolp, digest, dp[0]) != SECSuccess)
-       goto loser;
+    if (SECITEM_CopyItem((PLArenaPool *)poolp, digest, dp[0]) != SECSuccess) {
+        goto loser;
+    }
 
     rv = SECSuccess;
 
 loser:
 
     rv = SECSuccess;
 
 loser:
-    if (arena)
-       PORT_FreeArena(arena, PR_FALSE);
-
+    if (arena) {
+        PORT_FreeArena(arena, PR_FALSE);
+    }
     return rv;
 }
     return rv;
 }
index 5e41d83ee98895e67e38b5d8b97127835761a32a..cd7220b67cec2af91027a8cd4981104b87432028 100644 (file)
@@ -95,7 +95,7 @@ SecCmsAlgArrayGetIndexByAlgID(SECAlgorithmID **algorithmArray, SECAlgorithmID *a
 extern int
 SecCmsAlgArrayGetIndexByAlgTag(SECAlgorithmID **algorithmArray, SECOidTag algtag);
 
 extern int
 SecCmsAlgArrayGetIndexByAlgTag(SECAlgorithmID **algorithmArray, SECOidTag algtag);
 
-extern CSSM_CC_HANDLE
+extern void *
 SecCmsUtilGetHashObjByAlgID(SECAlgorithmID *algid);
 
 /*
 SecCmsUtilGetHashObjByAlgID(SECAlgorithmID *algid);
 
 /*
index c0f890547526e66c10e063d249964e263d0906f4..f07f98e4d9599bb78b7d1a1281d894de06a1e45a 100644 (file)
@@ -48,6 +48,7 @@
 #include <Security/cssmapi.h>
 #include <Security/cssmapple.h>
 #include <CoreServices/../Frameworks/CarbonCore.framework/Headers/MacErrors.h>
 #include <Security/cssmapi.h>
 #include <Security/cssmapple.h>
 #include <CoreServices/../Frameworks/CarbonCore.framework/Headers/MacErrors.h>
+#include <CommonCrypto/CommonDigest.h>
 
 
 /*
 
 
 /*
@@ -213,21 +214,42 @@ SecCmsAlgArrayGetIndexByAlgTag(SECAlgorithmID **algorithmArray,
     return i;
 }
 
     return i;
 }
 
-CSSM_CC_HANDLE
+void *
 SecCmsUtilGetHashObjByAlgID(SECAlgorithmID *algid)
 {
     SECOidData *oidData = SECOID_FindOID(&(algid->algorithm));
     if (oidData)
     {
 SecCmsUtilGetHashObjByAlgID(SECAlgorithmID *algid)
 {
     SECOidData *oidData = SECOID_FindOID(&(algid->algorithm));
     if (oidData)
     {
-       CSSM_ALGORITHMS alg = oidData->cssmAlgorithm;
-       if (alg)
-       {
-           CSSM_CC_HANDLE digobj;
-           CSSM_CSP_HANDLE cspHandle = SecCspHandleForAlgorithm(alg);
-
-           if (!CSSM_CSP_CreateDigestContext(cspHandle, alg, &digobj))
-               return digobj;
-       }
+        void *digobj = NULL;
+        switch (oidData->offset) {
+            case SEC_OID_SHA1:
+                digobj = calloc(1, sizeof(CC_SHA1_CTX));
+                CC_SHA1_Init(digobj);
+                break;
+            case SEC_OID_MD5:
+                digobj = calloc(1, sizeof(CC_MD5_CTX));
+                CC_MD5_Init(digobj);
+                break;
+            case SEC_OID_SHA224:
+                digobj = calloc(1, sizeof(CC_SHA256_CTX));
+                CC_SHA224_Init(digobj);
+                break;
+            case SEC_OID_SHA256:
+                digobj = calloc(1, sizeof(CC_SHA256_CTX));
+                CC_SHA256_Init(digobj);
+                break;
+            case SEC_OID_SHA384:
+                digobj = calloc(1, sizeof(CC_SHA512_CTX));
+                CC_SHA384_Init(digobj);
+                break;
+            case SEC_OID_SHA512:
+                digobj = calloc(1, sizeof(CC_SHA512_CTX));
+                CC_SHA512_Init(digobj);
+                break;
+            default:
+                break;
+        }
+        return digobj;
     }
 
     return 0;
     }
 
     return 0;
index a59d7b3fd6b340c1fc8771b154aec3cb9844d8b7..cf209e6743bed6163ca69e420a51631e52eeba37 100644 (file)
@@ -155,7 +155,10 @@ DataRetrieval::~DataRetrieval()
 {
        if (mAddr) {
                relocate(mAddr, mBase);
 {
        if (mAddr) {
                relocate(mAddr, mBase);
-               assert(mAttributes->size() == mAddr->size());
+               if (mAttributes->size() != mAddr->size()) {
+                       secemergency("~DataRetrieval: size mismatch, %u != %u", mAttributes->size(), mAddr->size());
+                       abort();
+               }
                
                // global (per-record) fields
                mAttributes->recordType(mAddr->recordType());
                
                // global (per-record) fields
                mAttributes->recordType(mAddr->recordType());
diff --git a/OSX/sec/Security/Regressions/secitem/si-27-sectrust-exceptions.c b/OSX/sec/Security/Regressions/secitem/si-27-sectrust-exceptions.c
deleted file mode 100644 (file)
index fffb493..0000000
+++ /dev/null
@@ -1,431 +0,0 @@
-/*
- * Copyright (c) 2006-2010,2012-2018 Apple Inc. All Rights Reserved.
- */
-
-#include <CoreFoundation/CoreFoundation.h>
-#include <Security/SecCertificate.h>
-#include <Security/SecCertificatePriv.h>
-#include <Security/SecPolicy.h>
-#include <Security/SecTrustPriv.h>
-#include <utilities/array_size.h>
-#include <stdlib.h>
-#include <unistd.h>
-
-#include "shared_regressions.h"
-
-/*
- Serial Number:
- 01:f1:eb:db:ee:d3:1d:7a:b5:72:54:7d:34:43:4b:87
- Signature Algorithm: sha256WithRSAEncryption
- Issuer: C=US, O=DigiCert Inc, OU=www.digicert.com, CN=DigiCert SHA2 Extended Validation Server CA
- Validity
- Not Before: Mar 22 00:00:00 2018 GMT
- Not After : Mar 23 12:00:00 2019 GMT
- Subject: businessCategory=Private Organization/jurisdictionC=US/jurisdictionST=California/serialNumber=C0806592, C=US, ST=California, L=Cupertino, O=Apple Inc., OU=Internet Services for Akamai, CN=store.apple.com
- */
-static unsigned char _c0[]={
-    0x30,0x82,0x06,0xF7,0x30,0x82,0x05,0xDF,0xA0,0x03,0x02,0x01,0x02,0x02,0x10,0x01,
-    0xF1,0xEB,0xDB,0xEE,0xD3,0x1D,0x7A,0xB5,0x72,0x54,0x7D,0x34,0x43,0x4B,0x87,0x30,
-    0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x0B,0x05,0x00,0x30,0x75,
-    0x31,0x0B,0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x55,0x53,0x31,0x15,0x30,
-    0x13,0x06,0x03,0x55,0x04,0x0A,0x13,0x0C,0x44,0x69,0x67,0x69,0x43,0x65,0x72,0x74,
-    0x20,0x49,0x6E,0x63,0x31,0x19,0x30,0x17,0x06,0x03,0x55,0x04,0x0B,0x13,0x10,0x77,
-    0x77,0x77,0x2E,0x64,0x69,0x67,0x69,0x63,0x65,0x72,0x74,0x2E,0x63,0x6F,0x6D,0x31,
-    0x34,0x30,0x32,0x06,0x03,0x55,0x04,0x03,0x13,0x2B,0x44,0x69,0x67,0x69,0x43,0x65,
-    0x72,0x74,0x20,0x53,0x48,0x41,0x32,0x20,0x45,0x78,0x74,0x65,0x6E,0x64,0x65,0x64,
-    0x20,0x56,0x61,0x6C,0x69,0x64,0x61,0x74,0x69,0x6F,0x6E,0x20,0x53,0x65,0x72,0x76,
-    0x65,0x72,0x20,0x43,0x41,0x30,0x1E,0x17,0x0D,0x31,0x38,0x30,0x33,0x32,0x32,0x30,
-    0x30,0x30,0x30,0x30,0x30,0x5A,0x17,0x0D,0x31,0x39,0x30,0x33,0x32,0x33,0x31,0x32,
-    0x30,0x30,0x30,0x30,0x5A,0x30,0x81,0xF0,0x31,0x1D,0x30,0x1B,0x06,0x03,0x55,0x04,
-    0x0F,0x0C,0x14,0x50,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x4F,0x72,0x67,0x61,0x6E,
-    0x69,0x7A,0x61,0x74,0x69,0x6F,0x6E,0x31,0x13,0x30,0x11,0x06,0x0B,0x2B,0x06,0x01,
-    0x04,0x01,0x82,0x37,0x3C,0x02,0x01,0x03,0x13,0x02,0x55,0x53,0x31,0x1B,0x30,0x19,
-    0x06,0x0B,0x2B,0x06,0x01,0x04,0x01,0x82,0x37,0x3C,0x02,0x01,0x02,0x13,0x0A,0x43,
-    0x61,0x6C,0x69,0x66,0x6F,0x72,0x6E,0x69,0x61,0x31,0x11,0x30,0x0F,0x06,0x03,0x55,
-    0x04,0x05,0x13,0x08,0x43,0x30,0x38,0x30,0x36,0x35,0x39,0x32,0x31,0x0B,0x30,0x09,
-    0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x55,0x53,0x31,0x13,0x30,0x11,0x06,0x03,0x55,
-    0x04,0x08,0x13,0x0A,0x43,0x61,0x6C,0x69,0x66,0x6F,0x72,0x6E,0x69,0x61,0x31,0x12,
-    0x30,0x10,0x06,0x03,0x55,0x04,0x07,0x13,0x09,0x43,0x75,0x70,0x65,0x72,0x74,0x69,
-    0x6E,0x6F,0x31,0x13,0x30,0x11,0x06,0x03,0x55,0x04,0x0A,0x13,0x0A,0x41,0x70,0x70,
-    0x6C,0x65,0x20,0x49,0x6E,0x63,0x2E,0x31,0x25,0x30,0x23,0x06,0x03,0x55,0x04,0x0B,
-    0x13,0x1C,0x49,0x6E,0x74,0x65,0x72,0x6E,0x65,0x74,0x20,0x53,0x65,0x72,0x76,0x69,
-    0x63,0x65,0x73,0x20,0x66,0x6F,0x72,0x20,0x41,0x6B,0x61,0x6D,0x61,0x69,0x31,0x18,
-    0x30,0x16,0x06,0x03,0x55,0x04,0x03,0x13,0x0F,0x73,0x74,0x6F,0x72,0x65,0x2E,0x61,
-    0x70,0x70,0x6C,0x65,0x2E,0x63,0x6F,0x6D,0x30,0x82,0x01,0x22,0x30,0x0D,0x06,0x09,
-    0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x01,0x05,0x00,0x03,0x82,0x01,0x0F,0x00,
-    0x30,0x82,0x01,0x0A,0x02,0x82,0x01,0x01,0x00,0xB5,0xDF,0x48,0x6A,0xE5,0x5F,0x0C,
-    0x42,0xBF,0x71,0x07,0xAB,0xE6,0x48,0xD2,0x2C,0x13,0x7C,0x29,0xF7,0x90,0xC2,0x45,
-    0x39,0x43,0x48,0x2E,0x16,0x22,0xBF,0xAB,0x48,0x20,0x14,0x27,0xE7,0x87,0x52,0x97,
-    0xF6,0x64,0x0C,0x62,0xF5,0x39,0x54,0x64,0x09,0xE8,0x29,0x56,0xF4,0x03,0x40,0x5A,
-    0xFA,0x9A,0x11,0x2B,0xD0,0x01,0x51,0x1E,0xED,0xB8,0x51,0xCB,0xAB,0x6B,0x7A,0x99,
-    0xF3,0xB0,0x6E,0xA8,0xC4,0xB6,0xAF,0x17,0xEC,0xA6,0xD2,0x4D,0xCA,0x63,0x3D,0x59,
-    0x30,0x25,0x30,0x54,0x86,0xDB,0x70,0x7A,0xEC,0x07,0x83,0x19,0xA0,0x44,0xE7,0x3C,
-    0x68,0xE0,0xFD,0xB9,0xEE,0x3F,0xAC,0xF3,0x32,0x5A,0x5F,0x42,0x31,0x94,0x70,0x2C,
-    0xC5,0x73,0xD2,0x79,0x23,0x5B,0x96,0x45,0xB1,0xB3,0x2A,0xA1,0x5A,0x69,0xFE,0xBE,
-    0x52,0x0D,0x5D,0x79,0x18,0xCA,0xF1,0x44,0x92,0xA0,0x27,0x1F,0xAA,0x6E,0x9D,0x6F,
-    0x1B,0x83,0x5B,0x73,0x28,0x1D,0x87,0xB5,0x70,0x0E,0x3D,0xED,0xE2,0xC2,0x34,0x8A,
-    0x81,0xB2,0x22,0x40,0x98,0x77,0x2F,0x34,0x1B,0x70,0xEC,0x96,0x3F,0x91,0xB9,0xFF,
-    0xC9,0xE5,0x7E,0xE7,0x25,0xEF,0xDB,0x9A,0x58,0x4E,0xB2,0x92,0x19,0xA5,0x8D,0xEB,
-    0x76,0xF8,0xA8,0x48,0x9F,0x3D,0x10,0x0C,0xE4,0x69,0x7B,0xE7,0xB7,0xCA,0xF6,0x14,
-    0x5E,0x93,0x1E,0x20,0x37,0x1B,0xB8,0xB1,0x2C,0xD1,0x46,0x5C,0xD7,0x85,0x4A,0x2E,
-    0x19,0xB2,0x3C,0x31,0xDC,0x3D,0xB5,0x3C,0xEC,0x49,0x8D,0x9C,0xCF,0x75,0x0B,0xFC,
-    0x03,0x31,0x37,0xE7,0xA5,0xD6,0x9D,0x19,0x0D,0x02,0x03,0x01,0x00,0x01,0xA3,0x82,
-    0x03,0x05,0x30,0x82,0x03,0x01,0x30,0x1F,0x06,0x03,0x55,0x1D,0x23,0x04,0x18,0x30,
-    0x16,0x80,0x14,0x3D,0xD3,0x50,0xA5,0xD6,0xA0,0xAD,0xEE,0xF3,0x4A,0x60,0x0A,0x65,
-    0xD3,0x21,0xD4,0xF8,0xF8,0xD6,0x0F,0x30,0x1D,0x06,0x03,0x55,0x1D,0x0E,0x04,0x16,
-    0x04,0x14,0x6F,0x89,0xAE,0x9B,0xE4,0x8A,0x21,0x3A,0x3B,0xEA,0xAA,0xBE,0x99,0x90,
-    0xC8,0xDB,0x34,0x80,0x21,0xB9,0x30,0x2F,0x06,0x03,0x55,0x1D,0x11,0x04,0x28,0x30,
-    0x26,0x82,0x0F,0x73,0x74,0x6F,0x72,0x65,0x2E,0x61,0x70,0x70,0x6C,0x65,0x2E,0x63,
-    0x6F,0x6D,0x82,0x13,0x77,0x77,0x77,0x2E,0x73,0x74,0x6F,0x72,0x65,0x2E,0x61,0x70,
-    0x70,0x6C,0x65,0x2E,0x63,0x6F,0x6D,0x30,0x0E,0x06,0x03,0x55,0x1D,0x0F,0x01,0x01,
-    0xFF,0x04,0x04,0x03,0x02,0x05,0xA0,0x30,0x1D,0x06,0x03,0x55,0x1D,0x25,0x04,0x16,
-    0x30,0x14,0x06,0x08,0x2B,0x06,0x01,0x05,0x05,0x07,0x03,0x01,0x06,0x08,0x2B,0x06,
-    0x01,0x05,0x05,0x07,0x03,0x02,0x30,0x75,0x06,0x03,0x55,0x1D,0x1F,0x04,0x6E,0x30,
-    0x6C,0x30,0x34,0xA0,0x32,0xA0,0x30,0x86,0x2E,0x68,0x74,0x74,0x70,0x3A,0x2F,0x2F,
-    0x63,0x72,0x6C,0x33,0x2E,0x64,0x69,0x67,0x69,0x63,0x65,0x72,0x74,0x2E,0x63,0x6F,
-    0x6D,0x2F,0x73,0x68,0x61,0x32,0x2D,0x65,0x76,0x2D,0x73,0x65,0x72,0x76,0x65,0x72,
-    0x2D,0x67,0x32,0x2E,0x63,0x72,0x6C,0x30,0x34,0xA0,0x32,0xA0,0x30,0x86,0x2E,0x68,
-    0x74,0x74,0x70,0x3A,0x2F,0x2F,0x63,0x72,0x6C,0x34,0x2E,0x64,0x69,0x67,0x69,0x63,
-    0x65,0x72,0x74,0x2E,0x63,0x6F,0x6D,0x2F,0x73,0x68,0x61,0x32,0x2D,0x65,0x76,0x2D,
-    0x73,0x65,0x72,0x76,0x65,0x72,0x2D,0x67,0x32,0x2E,0x63,0x72,0x6C,0x30,0x4B,0x06,
-    0x03,0x55,0x1D,0x20,0x04,0x44,0x30,0x42,0x30,0x37,0x06,0x09,0x60,0x86,0x48,0x01,
-    0x86,0xFD,0x6C,0x02,0x01,0x30,0x2A,0x30,0x28,0x06,0x08,0x2B,0x06,0x01,0x05,0x05,
-    0x07,0x02,0x01,0x16,0x1C,0x68,0x74,0x74,0x70,0x73,0x3A,0x2F,0x2F,0x77,0x77,0x77,
-    0x2E,0x64,0x69,0x67,0x69,0x63,0x65,0x72,0x74,0x2E,0x63,0x6F,0x6D,0x2F,0x43,0x50,
-    0x53,0x30,0x07,0x06,0x05,0x67,0x81,0x0C,0x01,0x01,0x30,0x81,0x88,0x06,0x08,0x2B,
-    0x06,0x01,0x05,0x05,0x07,0x01,0x01,0x04,0x7C,0x30,0x7A,0x30,0x24,0x06,0x08,0x2B,
-    0x06,0x01,0x05,0x05,0x07,0x30,0x01,0x86,0x18,0x68,0x74,0x74,0x70,0x3A,0x2F,0x2F,
-    0x6F,0x63,0x73,0x70,0x2E,0x64,0x69,0x67,0x69,0x63,0x65,0x72,0x74,0x2E,0x63,0x6F,
-    0x6D,0x30,0x52,0x06,0x08,0x2B,0x06,0x01,0x05,0x05,0x07,0x30,0x02,0x86,0x46,0x68,
-    0x74,0x74,0x70,0x3A,0x2F,0x2F,0x63,0x61,0x63,0x65,0x72,0x74,0x73,0x2E,0x64,0x69,
-    0x67,0x69,0x63,0x65,0x72,0x74,0x2E,0x63,0x6F,0x6D,0x2F,0x44,0x69,0x67,0x69,0x43,
-    0x65,0x72,0x74,0x53,0x48,0x41,0x32,0x45,0x78,0x74,0x65,0x6E,0x64,0x65,0x64,0x56,
-    0x61,0x6C,0x69,0x64,0x61,0x74,0x69,0x6F,0x6E,0x53,0x65,0x72,0x76,0x65,0x72,0x43,
-    0x41,0x2E,0x63,0x72,0x74,0x30,0x09,0x06,0x03,0x55,0x1D,0x13,0x04,0x02,0x30,0x00,
-    0x30,0x82,0x01,0x03,0x06,0x0A,0x2B,0x06,0x01,0x04,0x01,0xD6,0x79,0x02,0x04,0x02,
-    0x04,0x81,0xF4,0x04,0x81,0xF1,0x00,0xEF,0x00,0x75,0x00,0xA4,0xB9,0x09,0x90,0xB4,
-    0x18,0x58,0x14,0x87,0xBB,0x13,0xA2,0xCC,0x67,0x70,0x0A,0x3C,0x35,0x98,0x04,0xF9,
-    0x1B,0xDF,0xB8,0xE3,0x77,0xCD,0x0E,0xC8,0x0D,0xDC,0x10,0x00,0x00,0x01,0x62,0x4E,
-    0xBC,0xC7,0x43,0x00,0x00,0x04,0x03,0x00,0x46,0x30,0x44,0x02,0x20,0x3E,0xD3,0xFE,
-    0xB4,0x45,0x97,0xCE,0xA3,0x05,0xC9,0x29,0x70,0x55,0x3B,0x77,0x9E,0xCC,0x4B,0x06,
-    0xFD,0x76,0x21,0xD0,0x79,0x69,0xD6,0x60,0x01,0xBB,0xC7,0x43,0x5E,0x02,0x20,0x3D,
-    0xB7,0x73,0x91,0x51,0xB7,0xAC,0x40,0xFB,0xA7,0x36,0xCF,0x10,0xE8,0x63,0x79,0xE6,
-    0x06,0xEC,0xFA,0x60,0xFE,0x44,0x90,0x9A,0x53,0x26,0x04,0x27,0x1A,0x4B,0xD4,0x00,
-    0x76,0x00,0x56,0x14,0x06,0x9A,0x2F,0xD7,0xC2,0xEC,0xD3,0xF5,0xE1,0xBD,0x44,0xB2,
-    0x3E,0xC7,0x46,0x76,0xB9,0xBC,0x99,0x11,0x5C,0xC0,0xEF,0x94,0x98,0x55,0xD6,0x89,
-    0xD0,0xDD,0x00,0x00,0x01,0x62,0x4E,0xBC,0xC8,0x6A,0x00,0x00,0x04,0x03,0x00,0x47,
-    0x30,0x45,0x02,0x20,0x18,0xF4,0x40,0x99,0x83,0xA7,0x53,0x6D,0x11,0xBF,0x7C,0xA5,
-    0x64,0x21,0x52,0xD0,0x7C,0xF5,0x96,0xCD,0xB0,0x56,0xAE,0x1E,0x13,0x9C,0xFC,0x08,
-    0x3B,0x56,0x66,0x0F,0x02,0x21,0x00,0x98,0xBE,0xC7,0x01,0x8E,0x88,0xB7,0xB2,0xF9,
-    0xC7,0xE6,0x08,0x46,0x10,0xEA,0x8D,0x01,0x12,0x0D,0x7D,0xA6,0xBA,0xA6,0xD3,0x1F,
-    0xEC,0xA3,0xD6,0x7A,0xE4,0x85,0x89,0x30,0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,
-    0x0D,0x01,0x01,0x0B,0x05,0x00,0x03,0x82,0x01,0x01,0x00,0x6A,0x52,0xE8,0xB3,0x70,
-    0x25,0xD9,0x95,0x87,0xC4,0xF3,0x14,0xB1,0x8C,0x29,0x6F,0x11,0xA0,0x6D,0xD7,0xE9,
-    0x96,0xFA,0x06,0x57,0x4D,0x86,0xE8,0xD6,0x95,0xC0,0x3A,0x33,0xEC,0x67,0x6C,0x98,
-    0xA2,0x99,0x09,0x28,0x16,0x18,0xEC,0x21,0xED,0x93,0xEF,0xAB,0x69,0x5F,0x98,0x9C,
-    0x62,0x21,0x10,0xEB,0x1C,0xC4,0x20,0x5E,0x3E,0x6F,0x80,0x03,0xC9,0xC5,0xD5,0x2F,
-    0xDA,0x8C,0x86,0x40,0x59,0x71,0x34,0xEE,0xCF,0x54,0x83,0xE1,0x3E,0x11,0x1D,0x12,
-    0x66,0x3D,0x16,0x7F,0xD8,0x1B,0x42,0x35,0x50,0xA6,0xD6,0xE6,0x6B,0x32,0xDE,0xE2,
-    0xD9,0x01,0xD7,0xB1,0xF5,0xE8,0x72,0x44,0x03,0xB5,0xFA,0x19,0x0D,0xF2,0x8D,0x0B,
-    0x75,0xBA,0xD9,0x39,0xFF,0x73,0xF2,0xA0,0xD6,0x49,0xEF,0x9E,0xE9,0x23,0x5D,0xED,
-    0xC5,0x08,0x69,0x10,0xF7,0xF0,0x0B,0x8D,0x80,0xB6,0x43,0xE4,0x2A,0xEC,0x92,0xCF,
-    0x22,0xCA,0xAF,0x1A,0x8A,0x16,0xC7,0xC5,0x88,0xB7,0xCA,0xC6,0x19,0x1A,0xD1,0xFF,
-    0x13,0x93,0x56,0x29,0x1A,0x48,0xA8,0x92,0x21,0xE5,0x7F,0x3E,0x4C,0xB4,0x89,0xD6,
-    0x08,0xF0,0xB0,0x4B,0x22,0x7C,0x1F,0xEC,0xE7,0x09,0x5D,0x23,0xE2,0xD1,0xB8,0xA3,
-    0xDF,0xEC,0x2D,0x87,0x1F,0xCD,0x58,0x1C,0xDD,0x09,0xE8,0xB5,0xD4,0x30,0x1E,0x2A,
-    0x4D,0xA3,0xA1,0x84,0x43,0xD7,0x4A,0x7B,0x15,0x8E,0xEB,0xE2,0x53,0x2B,0x12,0xCB,
-    0xFB,0xF2,0x61,0x7E,0x92,0x0B,0x87,0x07,0x1C,0x8D,0x0A,0xED,0x62,0x10,0x27,0xE3,
-    0xDB,0xC4,0x65,0xDE,0x57,0xFB,0x6D,0x49,0xC8,0x7A,0xF8,
-};
-
-/* subject:/C=US/O=DigiCert Inc/OU=www.digicert.com/CN=DigiCert SHA2 Extended Validation Server CA */
-/* issuer :/C=US/O=DigiCert Inc/OU=www.digicert.com/CN=DigiCert High Assurance EV Root CA */
-static unsigned char _c1[]={
-    0x30,0x82,0x04,0xB6,0x30,0x82,0x03,0x9E,0xA0,0x03,0x02,0x01,0x02,0x02,0x10,0x0C,
-    0x79,0xA9,0x44,0xB0,0x8C,0x11,0x95,0x20,0x92,0x61,0x5F,0xE2,0x6B,0x1D,0x83,0x30,
-    0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x0B,0x05,0x00,0x30,0x6C,
-    0x31,0x0B,0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x55,0x53,0x31,0x15,0x30,
-    0x13,0x06,0x03,0x55,0x04,0x0A,0x13,0x0C,0x44,0x69,0x67,0x69,0x43,0x65,0x72,0x74,
-    0x20,0x49,0x6E,0x63,0x31,0x19,0x30,0x17,0x06,0x03,0x55,0x04,0x0B,0x13,0x10,0x77,
-    0x77,0x77,0x2E,0x64,0x69,0x67,0x69,0x63,0x65,0x72,0x74,0x2E,0x63,0x6F,0x6D,0x31,
-    0x2B,0x30,0x29,0x06,0x03,0x55,0x04,0x03,0x13,0x22,0x44,0x69,0x67,0x69,0x43,0x65,
-    0x72,0x74,0x20,0x48,0x69,0x67,0x68,0x20,0x41,0x73,0x73,0x75,0x72,0x61,0x6E,0x63,
-    0x65,0x20,0x45,0x56,0x20,0x52,0x6F,0x6F,0x74,0x20,0x43,0x41,0x30,0x1E,0x17,0x0D,
-    0x31,0x33,0x31,0x30,0x32,0x32,0x31,0x32,0x30,0x30,0x30,0x30,0x5A,0x17,0x0D,0x32,
-    0x38,0x31,0x30,0x32,0x32,0x31,0x32,0x30,0x30,0x30,0x30,0x5A,0x30,0x75,0x31,0x0B,
-    0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x55,0x53,0x31,0x15,0x30,0x13,0x06,
-    0x03,0x55,0x04,0x0A,0x13,0x0C,0x44,0x69,0x67,0x69,0x43,0x65,0x72,0x74,0x20,0x49,
-    0x6E,0x63,0x31,0x19,0x30,0x17,0x06,0x03,0x55,0x04,0x0B,0x13,0x10,0x77,0x77,0x77,
-    0x2E,0x64,0x69,0x67,0x69,0x63,0x65,0x72,0x74,0x2E,0x63,0x6F,0x6D,0x31,0x34,0x30,
-    0x32,0x06,0x03,0x55,0x04,0x03,0x13,0x2B,0x44,0x69,0x67,0x69,0x43,0x65,0x72,0x74,
-    0x20,0x53,0x48,0x41,0x32,0x20,0x45,0x78,0x74,0x65,0x6E,0x64,0x65,0x64,0x20,0x56,
-    0x61,0x6C,0x69,0x64,0x61,0x74,0x69,0x6F,0x6E,0x20,0x53,0x65,0x72,0x76,0x65,0x72,
-    0x20,0x43,0x41,0x30,0x82,0x01,0x22,0x30,0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,
-    0x0D,0x01,0x01,0x01,0x05,0x00,0x03,0x82,0x01,0x0F,0x00,0x30,0x82,0x01,0x0A,0x02,
-    0x82,0x01,0x01,0x00,0xD7,0x53,0xA4,0x04,0x51,0xF8,0x99,0xA6,0x16,0x48,0x4B,0x67,
-    0x27,0xAA,0x93,0x49,0xD0,0x39,0xED,0x0C,0xB0,0xB0,0x00,0x87,0xF1,0x67,0x28,0x86,
-    0x85,0x8C,0x8E,0x63,0xDA,0xBC,0xB1,0x40,0x38,0xE2,0xD3,0xF5,0xEC,0xA5,0x05,0x18,
-    0xB8,0x3D,0x3E,0xC5,0x99,0x17,0x32,0xEC,0x18,0x8C,0xFA,0xF1,0x0C,0xA6,0x64,0x21,
-    0x85,0xCB,0x07,0x10,0x34,0xB0,0x52,0x88,0x2B,0x1F,0x68,0x9B,0xD2,0xB1,0x8F,0x12,
-    0xB0,0xB3,0xD2,0xE7,0x88,0x1F,0x1F,0xEF,0x38,0x77,0x54,0x53,0x5F,0x80,0x79,0x3F,
-    0x2E,0x1A,0xAA,0xA8,0x1E,0x4B,0x2B,0x0D,0xAB,0xB7,0x63,0xB9,0x35,0xB7,0x7D,0x14,
-    0xBC,0x59,0x4B,0xDF,0x51,0x4A,0xD2,0xA1,0xE2,0x0C,0xE2,0x90,0x82,0x87,0x6A,0xAE,
-    0xEA,0xD7,0x64,0xD6,0x98,0x55,0xE8,0xFD,0xAF,0x1A,0x50,0x6C,0x54,0xBC,0x11,0xF2,
-    0xFD,0x4A,0xF2,0x9D,0xBB,0x7F,0x0E,0xF4,0xD5,0xBE,0x8E,0x16,0x89,0x12,0x55,0xD8,
-    0xC0,0x71,0x34,0xEE,0xF6,0xDC,0x2D,0xEC,0xC4,0x87,0x25,0x86,0x8D,0xD8,0x21,0xE4,
-    0xB0,0x4D,0x0C,0x89,0xDC,0x39,0x26,0x17,0xDD,0xF6,0xD7,0x94,0x85,0xD8,0x04,0x21,
-    0x70,0x9D,0x6F,0x6F,0xFF,0x5C,0xBA,0x19,0xE1,0x45,0xCB,0x56,0x57,0x28,0x7E,0x1C,
-    0x0D,0x41,0x57,0xAA,0xB7,0xB8,0x27,0xBB,0xB1,0xE4,0xFA,0x2A,0xEF,0x21,0x23,0x75,
-    0x1A,0xAD,0x2D,0x9B,0x86,0x35,0x8C,0x9C,0x77,0xB5,0x73,0xAD,0xD8,0x94,0x2D,0xE4,
-    0xF3,0x0C,0x9D,0xEE,0xC1,0x4E,0x62,0x7E,0x17,0xC0,0x71,0x9E,0x2C,0xDE,0xF1,0xF9,
-    0x10,0x28,0x19,0x33,0x02,0x03,0x01,0x00,0x01,0xA3,0x82,0x01,0x49,0x30,0x82,0x01,
-    0x45,0x30,0x12,0x06,0x03,0x55,0x1D,0x13,0x01,0x01,0xFF,0x04,0x08,0x30,0x06,0x01,
-    0x01,0xFF,0x02,0x01,0x00,0x30,0x0E,0x06,0x03,0x55,0x1D,0x0F,0x01,0x01,0xFF,0x04,
-    0x04,0x03,0x02,0x01,0x86,0x30,0x1D,0x06,0x03,0x55,0x1D,0x25,0x04,0x16,0x30,0x14,
-    0x06,0x08,0x2B,0x06,0x01,0x05,0x05,0x07,0x03,0x01,0x06,0x08,0x2B,0x06,0x01,0x05,
-    0x05,0x07,0x03,0x02,0x30,0x34,0x06,0x08,0x2B,0x06,0x01,0x05,0x05,0x07,0x01,0x01,
-    0x04,0x28,0x30,0x26,0x30,0x24,0x06,0x08,0x2B,0x06,0x01,0x05,0x05,0x07,0x30,0x01,
-    0x86,0x18,0x68,0x74,0x74,0x70,0x3A,0x2F,0x2F,0x6F,0x63,0x73,0x70,0x2E,0x64,0x69,
-    0x67,0x69,0x63,0x65,0x72,0x74,0x2E,0x63,0x6F,0x6D,0x30,0x4B,0x06,0x03,0x55,0x1D,
-    0x1F,0x04,0x44,0x30,0x42,0x30,0x40,0xA0,0x3E,0xA0,0x3C,0x86,0x3A,0x68,0x74,0x74,
-    0x70,0x3A,0x2F,0x2F,0x63,0x72,0x6C,0x34,0x2E,0x64,0x69,0x67,0x69,0x63,0x65,0x72,
-    0x74,0x2E,0x63,0x6F,0x6D,0x2F,0x44,0x69,0x67,0x69,0x43,0x65,0x72,0x74,0x48,0x69,
-    0x67,0x68,0x41,0x73,0x73,0x75,0x72,0x61,0x6E,0x63,0x65,0x45,0x56,0x52,0x6F,0x6F,
-    0x74,0x43,0x41,0x2E,0x63,0x72,0x6C,0x30,0x3D,0x06,0x03,0x55,0x1D,0x20,0x04,0x36,
-    0x30,0x34,0x30,0x32,0x06,0x04,0x55,0x1D,0x20,0x00,0x30,0x2A,0x30,0x28,0x06,0x08,
-    0x2B,0x06,0x01,0x05,0x05,0x07,0x02,0x01,0x16,0x1C,0x68,0x74,0x74,0x70,0x73,0x3A,
-    0x2F,0x2F,0x77,0x77,0x77,0x2E,0x64,0x69,0x67,0x69,0x63,0x65,0x72,0x74,0x2E,0x63,
-    0x6F,0x6D,0x2F,0x43,0x50,0x53,0x30,0x1D,0x06,0x03,0x55,0x1D,0x0E,0x04,0x16,0x04,
-    0x14,0x3D,0xD3,0x50,0xA5,0xD6,0xA0,0xAD,0xEE,0xF3,0x4A,0x60,0x0A,0x65,0xD3,0x21,
-    0xD4,0xF8,0xF8,0xD6,0x0F,0x30,0x1F,0x06,0x03,0x55,0x1D,0x23,0x04,0x18,0x30,0x16,
-    0x80,0x14,0xB1,0x3E,0xC3,0x69,0x03,0xF8,0xBF,0x47,0x01,0xD4,0x98,0x26,0x1A,0x08,
-    0x02,0xEF,0x63,0x64,0x2B,0xC3,0x30,0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,
-    0x01,0x01,0x0B,0x05,0x00,0x03,0x82,0x01,0x01,0x00,0x9D,0xB6,0xD0,0x90,0x86,0xE1,
-    0x86,0x02,0xED,0xC5,0xA0,0xF0,0x34,0x1C,0x74,0xC1,0x8D,0x76,0xCC,0x86,0x0A,0xA8,
-    0xF0,0x4A,0x8A,0x42,0xD6,0x3F,0xC8,0xA9,0x4D,0xAD,0x7C,0x08,0xAD,0xE6,0xB6,0x50,
-    0xB8,0xA2,0x1A,0x4D,0x88,0x07,0xB1,0x29,0x21,0xDC,0xE7,0xDA,0xC6,0x3C,0x21,0xE0,
-    0xE3,0x11,0x49,0x70,0xAC,0x7A,0x1D,0x01,0xA4,0xCA,0x11,0x3A,0x57,0xAB,0x7D,0x57,
-    0x2A,0x40,0x74,0xFD,0xD3,0x1D,0x85,0x18,0x50,0xDF,0x57,0x47,0x75,0xA1,0x7D,0x55,
-    0x20,0x2E,0x47,0x37,0x50,0x72,0x8C,0x7F,0x82,0x1B,0xD2,0x62,0x8F,0x2D,0x03,0x5A,
-    0xDA,0xC3,0xC8,0xA1,0xCE,0x2C,0x52,0xA2,0x00,0x63,0xEB,0x73,0xBA,0x71,0xC8,0x49,
-    0x27,0x23,0x97,0x64,0x85,0x9E,0x38,0x0E,0xAD,0x63,0x68,0x3C,0xBA,0x52,0x81,0x58,
-    0x79,0xA3,0x2C,0x0C,0xDF,0xDE,0x6D,0xEB,0x31,0xF2,0xBA,0xA0,0x7C,0x6C,0xF1,0x2C,
-    0xD4,0xE1,0xBD,0x77,0x84,0x37,0x03,0xCE,0x32,0xB5,0xC8,0x9A,0x81,0x1A,0x4A,0x92,
-    0x4E,0x3B,0x46,0x9A,0x85,0xFE,0x83,0xA2,0xF9,0x9E,0x8C,0xA3,0xCC,0x0D,0x5E,0xB3,
-    0x3D,0xCF,0x04,0x78,0x8F,0x14,0x14,0x7B,0x32,0x9C,0xC7,0x00,0xA6,0x5C,0xC4,0xB5,
-    0xA1,0x55,0x8D,0x5A,0x56,0x68,0xA4,0x22,0x70,0xAA,0x3C,0x81,0x71,0xD9,0x9D,0xA8,
-    0x45,0x3B,0xF4,0xE5,0xF6,0xA2,0x51,0xDD,0xC7,0x7B,0x62,0xE8,0x6F,0x0C,0x74,0xEB,
-    0xB8,0xDA,0xF8,0xBF,0x87,0x0D,0x79,0x50,0x91,0x90,0x9B,0x18,0x3B,0x91,0x59,0x27,
-    0xF1,0x35,0x28,0x13,0xAB,0x26,0x7E,0xD5,0xF7,0x7A,
-};
-
-/* subject:/CN=self-signed.ssltest.apple.com/C=US */
-/* issuer :/CN=self-signed.ssltest.apple.com/C=US */
-
-static unsigned char _ss0[]={
-       0x30,0x82,0x03,0x0F,0x30,0x82,0x01,0xF7,0xA0,0x03,0x02,0x01,0x02,0x02,0x01,0x01,
-       0x30,0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x0B,0x05,0x00,0x30,
-       0x35,0x31,0x26,0x30,0x24,0x06,0x03,0x55,0x04,0x03,0x0C,0x1D,0x73,0x65,0x6C,0x66,
-       0x2D,0x73,0x69,0x67,0x6E,0x65,0x64,0x2E,0x73,0x73,0x6C,0x74,0x65,0x73,0x74,0x2E,
-       0x61,0x70,0x70,0x6C,0x65,0x2E,0x63,0x6F,0x6D,0x31,0x0B,0x30,0x09,0x06,0x03,0x55,
-       0x04,0x06,0x13,0x02,0x55,0x53,0x30,0x1E,0x17,0x0D,0x31,0x36,0x30,0x36,0x30,0x37,
-       0x32,0x31,0x35,0x33,0x30,0x38,0x5A,0x17,0x0D,0x31,0x37,0x30,0x36,0x30,0x37,0x32,
-       0x31,0x35,0x33,0x30,0x38,0x5A,0x30,0x35,0x31,0x26,0x30,0x24,0x06,0x03,0x55,0x04,
-       0x03,0x0C,0x1D,0x73,0x65,0x6C,0x66,0x2D,0x73,0x69,0x67,0x6E,0x65,0x64,0x2E,0x73,
-       0x73,0x6C,0x74,0x65,0x73,0x74,0x2E,0x61,0x70,0x70,0x6C,0x65,0x2E,0x63,0x6F,0x6D,
-       0x31,0x0B,0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x55,0x53,0x30,0x82,0x01,
-       0x22,0x30,0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x01,0x05,0x00,
-       0x03,0x82,0x01,0x0F,0x00,0x30,0x82,0x01,0x0A,0x02,0x82,0x01,0x01,0x00,0xCC,0x72,
-       0x7D,0x09,0x36,0x5A,0x6A,0xED,0xC1,0x7A,0x2C,0xF4,0x7C,0x58,0x63,0x05,0x3E,0x91,
-       0x68,0x55,0xB1,0x2A,0x5D,0x57,0xF3,0xA4,0xA7,0x80,0x05,0x41,0x74,0xB2,0xAD,0x5A,
-       0x7F,0x38,0xF6,0xF7,0xFD,0xF9,0x64,0x4D,0xDE,0xF9,0x7A,0xD3,0x8C,0x78,0xE9,0x71,
-       0xCF,0x1D,0x3E,0xF0,0xDB,0x12,0x48,0x74,0x22,0xA8,0x1F,0x3F,0xB9,0xDD,0xB0,0xAD,
-       0x8C,0x10,0x64,0x05,0x0E,0xE2,0x59,0x9A,0xEB,0x3F,0xBF,0xA9,0x48,0x07,0xD9,0x2C,
-       0x07,0x44,0x70,0x14,0x16,0x56,0x9C,0x73,0x01,0x2E,0x0B,0xF1,0x2A,0x9F,0x1C,0xC6,
-       0x78,0x56,0xB7,0x0B,0xDA,0xA6,0xE6,0x99,0x87,0x2D,0x49,0xFB,0xF0,0x47,0x22,0xA6,
-       0x8B,0xF0,0x02,0x37,0x31,0xD0,0x34,0x9F,0x43,0xD1,0x24,0x49,0x94,0x7F,0xFD,0x48,
-       0x9C,0xBA,0x5D,0x6B,0xD4,0xF9,0x9E,0xB5,0x18,0xE4,0xB2,0x06,0x46,0xC3,0xD9,0xE7,
-       0x80,0xD8,0x61,0xA9,0x09,0x5E,0xBA,0x2E,0x58,0x56,0xAE,0x37,0x31,0x6E,0x87,0x98,
-       0xD5,0xC9,0x2B,0x31,0x5C,0x40,0x01,0xDF,0xD5,0x63,0x9E,0x05,0x18,0x21,0x53,0x70,
-       0x62,0x36,0x44,0xCD,0x02,0xC0,0xCC,0x6A,0x58,0xC6,0xF6,0xA4,0xDC,0x89,0x94,0xBD,
-       0x4E,0xC4,0xEE,0xEE,0x40,0x31,0x59,0xC3,0x43,0xAD,0x34,0x30,0xDE,0xA9,0xA7,0x0D,
-       0x85,0xF7,0x96,0x8C,0x45,0xC1,0x6E,0x85,0x39,0x97,0xA6,0x4F,0xEA,0xE8,0x2F,0x01,
-       0x3D,0xC0,0x3B,0x34,0x9F,0x8F,0xCB,0xD6,0x22,0x79,0x2C,0x8C,0x8C,0xE6,0xBB,0x1F,
-       0x89,0x87,0x93,0x3B,0x39,0x4E,0x64,0x7D,0xDA,0x4D,0x52,0x4C,0x97,0xE5,0x02,0x03,
-       0x01,0x00,0x01,0xA3,0x2A,0x30,0x28,0x30,0x0E,0x06,0x03,0x55,0x1D,0x0F,0x01,0x01,
-       0xFF,0x04,0x04,0x03,0x02,0x07,0x80,0x30,0x16,0x06,0x03,0x55,0x1D,0x25,0x01,0x01,
-       0xFF,0x04,0x0C,0x30,0x0A,0x06,0x08,0x2B,0x06,0x01,0x05,0x05,0x07,0x03,0x01,0x30,
-       0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x0B,0x05,0x00,0x03,0x82,
-       0x01,0x01,0x00,0x36,0x06,0xC9,0xE6,0x98,0xC2,0x84,0x1D,0x13,0x1E,0x54,0x35,0x6D,
-       0xE5,0xCB,0xC5,0xFD,0xD9,0x54,0x45,0x83,0x53,0xB3,0x3B,0xE7,0x30,0x6F,0xAE,0xEA,
-       0x63,0x3F,0xA8,0xFA,0xD9,0x6D,0x0F,0x7D,0xD4,0xB6,0x28,0x66,0xF9,0x57,0x87,0x3E,
-       0x57,0x27,0xB6,0x9A,0x56,0xAE,0xD7,0xE0,0x11,0x20,0x71,0xC1,0xEA,0xF6,0xED,0x74,
-       0x1A,0x5A,0xB1,0x74,0x6C,0xBE,0xAC,0x0E,0x3C,0xD9,0x3E,0xEC,0x17,0x6E,0xF0,0x69,
-       0xC9,0x4D,0xD2,0x7E,0xAE,0x8B,0x01,0xCC,0x1A,0x23,0x7C,0x58,0x07,0x30,0xE4,0x2A,
-       0x12,0xE8,0xA0,0x25,0x65,0x66,0xB5,0xC7,0x5D,0xD8,0x47,0xDF,0xD7,0x51,0xBC,0xA2,
-       0xAA,0xF0,0x2F,0xB5,0x9E,0x20,0x6D,0x1F,0x84,0x00,0xF0,0xD0,0xB8,0x42,0x6A,0x9A,
-       0xE7,0xCA,0x7B,0xE5,0x39,0x09,0x91,0xBF,0xCB,0x4D,0x7A,0x32,0x1E,0x00,0x6E,0xE5,
-       0xF7,0x44,0x80,0x82,0x38,0x53,0x64,0xB7,0x26,0x81,0xCB,0xCE,0xA1,0xAF,0x0C,0x67,
-       0x32,0xC6,0xE4,0x5D,0x09,0x7B,0x37,0xD7,0xC8,0x43,0x44,0xEF,0xC6,0xF8,0x72,0xFF,
-       0x65,0xD4,0x39,0x3D,0xEC,0x72,0xA5,0x28,0xFF,0x70,0x47,0x38,0xA3,0xC7,0xCC,0x5E,
-       0x0F,0xFF,0x43,0x83,0x78,0x49,0x68,0x90,0x48,0x89,0xAD,0xE1,0x2E,0xFA,0x8F,0x59,
-       0xB6,0x08,0x2A,0x72,0x2F,0x52,0x3F,0x73,0x84,0xCA,0xD8,0x18,0x6C,0xDA,0xA3,0x2E,
-       0xF2,0xD7,0x4C,0x21,0xD9,0xF8,0xB1,0x86,0xE9,0x35,0x78,0xE4,0x4F,0xD0,0x93,0x11,
-       0x8F,0xF4,0xB1,0x17,0x4F,0xDE,0xAC,0xBD,0xA9,0xBC,0x94,0xFC,0x2E,0x7D,0xF9,0x05,
-       0x26,0x90,0xF1,
-};
-
-#define CFReleaseSafe(CF) { CFTypeRef _cf = (CF); if (_cf) CFRelease(_cf); }
-#define CFReleaseNull(CF) { CFTypeRef _cf = (CF); if (_cf) { (CF) = NULL; CFRelease(_cf); } }
-
-/* Test basic add delete update copy matching stuff. */
-static void tests(void)
-{
-       SecTrustRef trust;
-       SecCertificateRef cert0, cert1, sscert0;
-       CFArrayRef anchors = NULL;
-       isnt(cert0 = SecCertificateCreateWithBytes(NULL, _c0, sizeof(_c0)),
-                       NULL, "create cert0");
-       isnt(cert1 = SecCertificateCreateWithBytes(NULL, _c1, sizeof(_c1)),
-                       NULL, "create cert1");
-       isnt(sscert0 = SecCertificateCreateWithBytes(NULL, _ss0, sizeof(_ss0)),
-                       NULL, "create sscert0");
-       const void *v_certs[] = {
-               cert0,
-               cert1
-       };
-       SecPolicyRef policy = SecPolicyCreateSSL(true, CFSTR("store.apple.com"));
-       CFArrayRef certs = CFArrayCreate(NULL, v_certs,
-                       array_size(v_certs), NULL);
-       ok_status(SecTrustCreateWithCertificates(certs, policy, &trust), "create trust");
-       CFDateRef date = CFDateCreate(NULL, 545000000.0); /* April 9, 2018 at 1:53:20 PM PDT */
-       ok_status(SecTrustSetVerifyDate(trust, date), "set date");
-
-       SecTrustResultType trustResult;
-       ok_status(SecTrustEvaluate(trust, &trustResult), "evaluate trust");
-       is_status(trustResult, kSecTrustResultUnspecified,
-                       "trust is kSecTrustResultUnspecified");
-       CFDataRef exceptions;
-       ok(exceptions = SecTrustCopyExceptions(trust), "create exceptions");
-       if (!exceptions) { goto errOut; }
-       ok(SecTrustSetExceptions(trust, exceptions), "set exceptions");
-       ok_status(SecTrustEvaluate(trust, &trustResult), "evaluate trust");
-       is_status(trustResult, kSecTrustResultProceed, "trust is kSecTrustResultProceed");
-
-       CFReleaseNull(trust);
-       CFReleaseNull(policy);
-       policy = SecPolicyCreateSSL(true, CFSTR("badstore.apple.com"));
-       ok_status(SecTrustCreateWithCertificates(certs, policy, &trust), "create trust with hostname mismatch");
-       ok_status(SecTrustSetVerifyDate(trust, date), "set date");
-       ok(SecTrustSetExceptions(trust, exceptions), "set old exceptions");
-       ok_status(SecTrustEvaluate(trust, &trustResult), "evaluate trust");
-       is_status(trustResult, kSecTrustResultRecoverableTrustFailure, "trust is kSecTrustResultRecoverableTrustFailure");
-       CFReleaseNull(exceptions);
-       ok(exceptions = SecTrustCopyExceptions(trust), "create exceptions");
-       if (!exceptions) { goto errOut; }
-       ok(SecTrustSetExceptions(trust, exceptions), "set exceptions");
-       ok_status(SecTrustEvaluate(trust, &trustResult), "evaluate trust");
-       is_status(trustResult, kSecTrustResultProceed, "trust is kSecTrustResultProceed");
-
-       CFReleaseNull(trust);
-       ok_status(SecTrustCreateWithCertificates(certs, policy, &trust), "create trust");
-       ok_status(SecTrustSetVerifyDate(trust, date), "set date");
-       ok(SecTrustSetExceptions(trust, exceptions), "set exceptions");
-       ok_status(SecTrustEvaluate(trust, &trustResult), "evaluate trust");
-       is_status(trustResult, kSecTrustResultProceed, "trust is kSecTrustResultProceed");
-       anchors = CFArrayCreate(kCFAllocatorDefault, NULL, 0, &kCFTypeArrayCallBacks);
-       ok_status(SecTrustSetAnchorCertificates(trust, anchors), "set empty anchor list");
-       ok_status(SecTrustEvaluate(trust, &trustResult), "evaluate trust");
-       is_status(trustResult, kSecTrustResultRecoverableTrustFailure, "trust is kSecTrustResultRecoverableTrustFailure");
-
-       ok_status(SecTrustSetAnchorCertificatesOnly(trust, false), "trust passed in anchors and system anchors");
-       ok_status(SecTrustEvaluate(trust, &trustResult), "evaluate trust");
-       is_status(trustResult, kSecTrustResultProceed, "trust is now kSecTrustResultProceed");
-
-       ok_status(SecTrustSetAnchorCertificatesOnly(trust, true), "only trust passed in anchors (default)");
-       ok_status(SecTrustEvaluate(trust, &trustResult), "evaluate trust");
-       is_status(trustResult, kSecTrustResultRecoverableTrustFailure, "trust is kSecTrustResultRecoverableTrustFailure again");
-
-       CFReleaseNull(exceptions);
-       ok(exceptions = SecTrustCopyExceptions(trust), "create exceptions");
-       if (!exceptions) { goto errOut; }
-       ok(SecTrustSetExceptions(trust, exceptions), "set exceptions");
-       ok_status(SecTrustEvaluate(trust, &trustResult), "evaluate trust");
-       is_status(trustResult, kSecTrustResultProceed, "trust is kSecTrustResultProceed");
-       CFReleaseNull(date);
-       date = CFDateCreate(NULL, 667680000.0);
-       ok_status(SecTrustSetVerifyDate(trust, date), "set date to far future so certs are expired");
-       ok_status(SecTrustEvaluate(trust, &trustResult), "evaluate trust");
-       is_status(trustResult, kSecTrustResultRecoverableTrustFailure, "trust is kSecTrustResultRecoverableTrustFailure");
-
-       CFReleaseNull(trust);
-       CFReleaseNull(policy);
-       policy = SecPolicyCreateSSL(true, CFSTR("self-signed.ssltest.apple.com"));
-       ok_status(SecTrustCreateWithCertificates(sscert0, policy, &trust), "create trust");
-       ok_status(SecTrustEvaluate(trust, &trustResult), "evaluate trust");
-       is_status(trustResult, kSecTrustResultRecoverableTrustFailure, "trust is kSecTrustResultRecoverableTrustFailure");
-       CFReleaseNull(exceptions);
-       ok(exceptions = SecTrustCopyExceptions(trust), "create exceptions");
-       if (!exceptions) { goto errOut; }
-       ok(SecTrustSetExceptions(trust, exceptions), "set exceptions");
-       ok_status(SecTrustEvaluate(trust, &trustResult), "evaluate trust");
-       is_status(trustResult, kSecTrustResultProceed, "trust is kSecTrustResultProceed");
-    CFReleaseNull(exceptions);
-       ok(!SecTrustSetExceptions(trust, NULL), "clear exceptions");
-       ok_status(SecTrustEvaluate(trust, &trustResult), "evaluate trust");
-       is_status(trustResult, kSecTrustResultRecoverableTrustFailure, "trust is kSecTrustResultRecoverableTrustFailure");
-
-#if TARGET_OS_IPHONE
-    /* Test the extenions epoch feature. */
-    CFReleaseNull(exceptions);
-    CFReleaseNull(trust);
-    CFReleaseNull(policy);
-    policy = SecPolicyCreateSSL(false, CFSTR("self-signed.ssltest.apple.com"));
-    ok_status(SecTrustCreateWithCertificates(sscert0, policy, &trust), "create trust");
-    ok(exceptions = SecTrustCopyExceptions(trust), "create exceptions");
-    if (!exceptions) { goto errOut; }
-
-    /* Test the uninitialized extensions epoch. */
-    CFErrorRef exceptionResetCountError = NULL;
-    uint64_t exceptionResetCount = SecTrustGetExceptionResetCount(&exceptionResetCountError);
-    ok(exceptionResetCount == 0, "exception reset count is not set yet");
-    ok(SecTrustSetExceptions(trust, exceptions), "set exceptions");
-    ok_status(SecTrustEvaluate(trust, &trustResult), "evaluate trust");
-    is_status(trustResult, kSecTrustResultProceed, "trust is kSecTrustResultProceed");
-
-    /* Test increasing the extensions epoch. */
-    exceptionResetCountError = NULL;
-    ok_status(SecTrustIncrementExceptionResetCount(&exceptionResetCountError), "increase exception reset count");
-    exceptionResetCountError = NULL;
-    is(SecTrustGetExceptionResetCount(&exceptionResetCountError), 1 + exceptionResetCount, "exception reset count is 1 + previous count");
-
-    /* Test trust evaluation under a future extensions epoch. */
-    ok(!SecTrustSetExceptions(trust, exceptions), "set exceptions");
-    ok_status(SecTrustEvaluate(trust, &trustResult), "evaluate trust");
-    is_status(trustResult, kSecTrustResultRecoverableTrustFailure, "trust is kSecTrustResultRecoverableTrustFailure");
-#endif
-
-errOut:
-       CFReleaseSafe(anchors);
-       CFReleaseSafe(exceptions);
-       CFReleaseSafe(trust);
-       CFReleaseSafe(policy);
-       CFReleaseSafe(certs);
-       CFReleaseSafe(cert0);
-       CFReleaseSafe(cert1);
-       CFReleaseSafe(sscert0);
-       CFReleaseSafe(date);
-}
-
-int si_27_sectrust_exceptions(int argc, char *const *argv)
-{
-#if TARGET_OS_IPHONE
-       plan_tests(62);
-#else
-       plan_tests(51);
-#endif
-
-       tests();
-
-       return 0;
-}
index 849426be771072b38e538a42f1a95924513656d9..652e72e833702736069d5260c074dc113516dcba 100644 (file)
@@ -52,11 +52,23 @@ static void dumpValue(id value, NSMutableString *target, NSString *separator) {
     if (value == nil) {
         // Do nothing.
     } else if (CFGetTypeID((__bridge CFTypeRef)value) == CFBooleanGetTypeID()) {
     if (value == nil) {
         // Do nothing.
     } else if (CFGetTypeID((__bridge CFTypeRef)value) == CFBooleanGetTypeID()) {
-        [target appendString:[value boolValue] ? @"true" : @"false"];
+        NSNumber *boolValue = value;
+        [target appendString:boolValue.boolValue ? @"true" : @"false"];
     } else if ([value isKindOfClass:NSNumber.class]) {
     } else if ([value isKindOfClass:NSNumber.class]) {
-        [target appendString:[value string]];
+        NSNumber *numberValue = value;
+        [target appendString:numberValue.stringValue];
     } else if ([value isKindOfClass:NSString.class]) {
         [target appendString:value];
     } else if ([value isKindOfClass:NSString.class]) {
         [target appendString:value];
+    } else if ([value isKindOfClass:NSData.class]) {
+        NSData *dataValue = value;
+        const uint8_t *dataBuffer  = dataValue.bytes;
+        NSUInteger dumpLength = dataValue.length > 64 ? 64 : dataValue.length;
+        for (NSUInteger i = 0; i < dumpLength; i++) {
+            [target appendFormat:@"%02X", (unsigned)dataBuffer[i]];
+        }
+        if (dumpLength < dataValue.length) {
+            [target appendFormat:@"...(%db)", (int)dataValue.length];
+        }
     } else if ([value isKindOfClass:NSDictionary.class]) {
         [value enumerateKeysAndObjectsUsingBlock:^(id  _Nonnull key, id  _Nonnull obj, BOOL * _Nonnull stop) {
             [target appendString:separator];
     } else if ([value isKindOfClass:NSDictionary.class]) {
         [value enumerateKeysAndObjectsUsingBlock:^(id  _Nonnull key, id  _Nonnull obj, BOOL * _Nonnull stop) {
             [target appendString:separator];
index 5ebebb0655730efd6486c6c78423d92b38d9e12f..9d43e27a6d67e866ce29340223c0b0c489ce4b4a 100644 (file)
@@ -36,6 +36,7 @@
 #include <utilities/array_size.h>
 #include <ctkclient/ctkclient.h>
 #include <libaks_acl_cf_keys.h>
 #include <utilities/array_size.h>
 #include <ctkclient/ctkclient.h>
 #include <libaks_acl_cf_keys.h>
+#include <coreauthd_spi.h>
 #include "OSX/sec/Security/SecItemShim.h"
 
 #include "SecECKey.h"
 #include "OSX/sec/Security/SecItemShim.h"
 
 #include "SecECKey.h"
@@ -321,7 +322,7 @@ static Boolean SecCTKKeySetParameter(SecKeyRef key, CFStringRef name, CFProperty
     if (CFEqual(name, kSecUseAuthenticationContext)) {
         // Preprocess LAContext to ACMRef value.
         if (value != NULL) {
     if (CFEqual(name, kSecUseAuthenticationContext)) {
         // Preprocess LAContext to ACMRef value.
         if (value != NULL) {
-            require_quiet(acm_reference = SecItemAttributesCopyPreparedAuthContext(value, error), out);
+            require_quiet(acm_reference = LACopyACMContext(value, error), out);
             value = acm_reference;
         }
         name = kSecUseCredentialReference;
             value = acm_reference;
         }
         name = kSecUseCredentialReference;
index d9b3f39d992a107c1f9ef02155637d71a8d43757..204ec41a73f349199dfa8dda5bb1167fa4c21830 100644 (file)
@@ -1359,7 +1359,7 @@ static bool isAppleExtensionOID(const DERItem *extnID)
     static const uint8_t appleComponentExtensionArc[8] = { 0x2a,0x86,0x48,0x86,0xf7,0x63,0x64,0x0b };
     static const uint8_t appleSigningExtensionArc[8] = { 0x2a,0x86,0x48,0x86,0xf7,0x63,0x64,0x0c };
     static const uint8_t appleEncryptionExtensionArc[8] = { 0x2a,0x86,0x48,0x86,0xf7,0x63,0x64,0x0d };
     static const uint8_t appleComponentExtensionArc[8] = { 0x2a,0x86,0x48,0x86,0xf7,0x63,0x64,0x0b };
     static const uint8_t appleSigningExtensionArc[8] = { 0x2a,0x86,0x48,0x86,0xf7,0x63,0x64,0x0c };
     static const uint8_t appleEncryptionExtensionArc[8] = { 0x2a,0x86,0x48,0x86,0xf7,0x63,0x64,0x0d };
-    if (!extnID && !extnID->data && extnID->length <= sizeof(appleExtensionArc)) {
+    if (!extnID || !extnID->data || (extnID->length <= sizeof(appleExtensionArc))) {
         return false;
     }
     return (!memcmp(extnID->data, appleExtensionArc, sizeof(appleExtensionArc)) ||
         return false;
     }
     return (!memcmp(extnID->data, appleExtensionArc, sizeof(appleExtensionArc)) ||
@@ -1368,6 +1368,19 @@ static bool isAppleExtensionOID(const DERItem *extnID)
             !memcmp(extnID->data, appleEncryptionExtensionArc, sizeof(appleEncryptionExtensionArc)));
 }
 
             !memcmp(extnID->data, appleEncryptionExtensionArc, sizeof(appleEncryptionExtensionArc)));
 }
 
+static bool isCCCExtensionOID(const DERItem *extnID)
+{
+    static const uint8_t cccVehicleCA[] = { 0x2B,0x06,0x01,0x04,0x01,0x82,0xC4,0x69,0x05,0x09 };
+    static const uint8_t cccIntermediateCA[] = { 0x2B,0x06,0x01,0x04,0x01,0x82,0xC4,0x69,0x05,0x08 };
+    static const uint8_t cccVehicle[] = { 0x2B,0x06,0x01,0x04,0x01,0x82,0xC4,0x69,0x05,0x01 };
+    if (!extnID || !extnID->data || (extnID->length != sizeof(cccVehicleCA))) {
+        return false;
+    }
+    return (!memcmp(extnID->data, cccVehicleCA, sizeof(cccVehicleCA)) ||
+            !memcmp(extnID->data, cccIntermediateCA, sizeof(cccIntermediateCA)) ||
+            !memcmp(extnID->data, cccVehicle, sizeof(cccVehicle)));
+}
+
 /* Given the contents of an X.501 Name return the contents of a normalized
    X.501 name. */
 CFDataRef createNormalizedX501Name(CFAllocatorRef allocator,
 /* Given the contents of an X.501 Name return the contents of a normalized
    X.501 name. */
 CFDataRef createNormalizedX501Name(CFAllocatorRef allocator,
@@ -1841,7 +1854,7 @@ static bool SecCertificateParse(SecCertificateRef certificate)
                 }
                 require_quiet(parseResult || !certificate->_extensions[ix].critical, badCert);
             } else if (certificate->_extensions[ix].critical) {
                 }
                 require_quiet(parseResult || !certificate->_extensions[ix].critical, badCert);
             } else if (certificate->_extensions[ix].critical) {
-                if (isAppleExtensionOID(&extn.extnID)) {
+                if (isAppleExtensionOID(&extn.extnID) || isCCCExtensionOID(&extn.extnID)) {
                     continue;
                 }
                 secdebug("cert", "Found unknown critical extension");
                     continue;
                 }
                 secdebug("cert", "Found unknown critical extension");
index 7067b56d2c2392641c3f8818b867c7b693d020d9..d5f821526ff8e006318b8bde7fafad43d28a093d 100644 (file)
@@ -90,6 +90,7 @@ _kSecPolicyNameAppleParsecService
 _kSecPolicyNameApplePPQService
 _kSecPolicyNameApplePushService
 _kSecPolicyNameAppleSiriService
 _kSecPolicyNameApplePPQService
 _kSecPolicyNameApplePushService
 _kSecPolicyNameAppleSiriService
+_kSecPolicyNameAppleUpdatesService
 _kSecPolicyNameEAPClient
 _kSecPolicyNameEAPServer
 _kSecPolicyNameIPSecClient
 _kSecPolicyNameEAPClient
 _kSecPolicyNameEAPServer
 _kSecPolicyNameIPSecClient
index b526d63292249ec136d96672c1002a165fc5b078..98fb9085770d6a5d540494df6247ef794a2cc999 100644 (file)
@@ -674,16 +674,17 @@ static void infer_cert_label(SecCFDictionaryCOW *attributes)
 static CFDataRef CreateTokenPersistentRefData(CFTypeRef class, CFDictionaryRef attributes)
 {
     CFDataRef tokenPersistentRef = NULL;
 static CFDataRef CreateTokenPersistentRefData(CFTypeRef class, CFDictionaryRef attributes)
 {
     CFDataRef tokenPersistentRef = NULL;
-    CFStringRef tokenId = CFDictionaryGetValue(attributes, kSecAttrTokenID);
+    CFStringRef tokenId;
     CFDictionaryRef itemValue = NULL;
     CFDictionaryRef itemValue = NULL;
+    require_quiet(tokenId = CFCast(CFString, CFDictionaryGetValue(attributes, kSecAttrTokenID)), out);
     if (CFEqual(class, kSecClassIdentity)) {
         itemValue = SecTokenItemValueCopy(CFDictionaryGetValue(attributes, kSecAttrIdentityCertificateData), NULL);
     } else {
         itemValue = SecTokenItemValueCopy(CFDictionaryGetValue(attributes, kSecValueData), NULL);
     }
     if (CFEqual(class, kSecClassIdentity)) {
         itemValue = SecTokenItemValueCopy(CFDictionaryGetValue(attributes, kSecAttrIdentityCertificateData), NULL);
     } else {
         itemValue = SecTokenItemValueCopy(CFDictionaryGetValue(attributes, kSecValueData), NULL);
     }
-    require(itemValue, out);
+    require_quiet(itemValue, out);
     CFDataRef oid = CFDictionaryGetValue(itemValue, kSecTokenValueObjectIDKey);
     CFDataRef oid = CFDictionaryGetValue(itemValue, kSecTokenValueObjectIDKey);
-    require(oid, out);
+    require_quiet(oid, out);
     CFArrayRef array = CFArrayCreateForCFTypes(kCFAllocatorDefault, class, tokenId, oid, NULL);
     tokenPersistentRef = CFPropertyListCreateDERData(kCFAllocatorDefault, array, NULL);
     CFRelease(array);
     CFArrayRef array = CFArrayCreateForCFTypes(kCFAllocatorDefault, class, tokenId, oid, NULL);
     tokenPersistentRef = CFPropertyListCreateDERData(kCFAllocatorDefault, array, NULL);
     CFRelease(array);
@@ -825,12 +826,18 @@ static CFDataRef SecTokenItemValueCreate(CFDataRef oid, CFDataRef access_control
 
 CFDictionaryRef SecTokenItemValueCopy(CFDataRef db_value, CFErrorRef *error) {
     CFPropertyListRef plist = NULL;
 
 CFDictionaryRef SecTokenItemValueCopy(CFDataRef db_value, CFErrorRef *error) {
     CFPropertyListRef plist = NULL;
+    require_quiet(CFCastWithError(CFData, db_value, error), out);
     const uint8_t *der = CFDataGetBytePtr(db_value);
     const uint8_t *der_end = der + CFDataGetLength(db_value);
     require_quiet(der = der_decode_plist(0, kCFPropertyListImmutable, &plist, error, der, der_end), out);
     require_action_quiet(der == der_end, out, SecError(errSecDecode, error, CFSTR("trailing garbage at end of token data field")));
     const uint8_t *der = CFDataGetBytePtr(db_value);
     const uint8_t *der_end = der + CFDataGetLength(db_value);
     require_quiet(der = der_decode_plist(0, kCFPropertyListImmutable, &plist, error, der, der_end), out);
     require_action_quiet(der == der_end, out, SecError(errSecDecode, error, CFSTR("trailing garbage at end of token data field")));
-    require_action_quiet(CFDictionaryGetValue(plist, kSecTokenValueObjectIDKey) != NULL, out,
+    CFTypeRef value = CFDictionaryGetValue(plist, kSecTokenValueObjectIDKey);
+    require_action_quiet(CFCast(CFData, value) != NULL, out,
                          SecError(errSecInternal, error, CFSTR("token based item data does not have OID")));
                          SecError(errSecInternal, error, CFSTR("token based item data does not have OID")));
+    value = CFDictionaryGetValue(plist, kSecTokenValueAccessControlKey);
+    require_quiet(value == NULL || CFCastWithError(CFData, value, error), out);
+    value = CFDictionaryGetValue(plist, kSecTokenValueDataKey);
+    require_quiet(value == NULL || CFCastWithError(CFData, value, error), out);
 
 out:
     return plist;
 
 out:
     return plist;
@@ -894,6 +901,7 @@ static bool SecTokenItemCreateFromAttributes(CFDictionaryRef attributes, CFDicti
     CFMutableDictionaryRef attrs = CFDictionaryCreateMutableCopy(NULL, 0, attributes);
     CFTypeRef token_id = CFDictionaryGetValue(attributes, kSecAttrTokenID);
     if (token_id != NULL && object_id != NULL) {
     CFMutableDictionaryRef attrs = CFDictionaryCreateMutableCopy(NULL, 0, attributes);
     CFTypeRef token_id = CFDictionaryGetValue(attributes, kSecAttrTokenID);
     if (token_id != NULL && object_id != NULL) {
+        require_quiet(CFCastWithError(CFString, token_id, error), out);
         if (CFRetainSafe(token) == NULL) {
             require_quiet(token = SecTokenCreate(token_id, &auth_params, error), out);
         }
         if (CFRetainSafe(token) == NULL) {
             require_quiet(token = SecTokenCreate(token_id, &auth_params, error), out);
         }
@@ -946,9 +954,11 @@ static bool SecItemResultCopyPrepared(CFTypeRef raw_result, TKTokenRef token,
     if (token == NULL) {
         if (CFGetTypeID(raw_result) == CFDictionaryGetTypeID()) {
             token_id = CFDictionaryGetValue(raw_result, kSecAttrTokenID);
     if (token == NULL) {
         if (CFGetTypeID(raw_result) == CFDictionaryGetTypeID()) {
             token_id = CFDictionaryGetValue(raw_result, kSecAttrTokenID);
+            require_quiet(token_id == NULL || CFCastWithError(CFString, token_id, error) != NULL, out);
             token_item = (token_id != NULL);
 
             cert_token_id = CFDictionaryGetValue(raw_result, kSecAttrIdentityCertificateTokenID);
             token_item = (token_id != NULL);
 
             cert_token_id = CFDictionaryGetValue(raw_result, kSecAttrIdentityCertificateTokenID);
+            require_quiet(cert_token_id == NULL || CFCastWithError(CFString, cert_token_id, error) != NULL, out);
             cert_token_item = (cert_token_id != NULL);
         }
     } else {
             cert_token_item = (cert_token_id != NULL);
         }
     } else {
@@ -1137,23 +1147,6 @@ out:
     return ok;
 }
 
     return ok;
 }
 
-CFDataRef SecItemAttributesCopyPreparedAuthContext(CFTypeRef la_context, CFErrorRef *error) {
-    void *la_lib = NULL;
-    CFDataRef acm_context = NULL;
-    require_action_quiet(la_lib = dlopen("/System/Library/Frameworks/LocalAuthentication.framework/LocalAuthentication", RTLD_LAZY), out,
-                         SecError(errSecInternal, error, CFSTR("failed to open LocalAuthentication.framework")));
-    LAFunctionCopyExternalizedContext fnCopyExternalizedContext = NULL;
-    require_action_quiet(fnCopyExternalizedContext = dlsym(la_lib, "LACopyExternalizedContext"), out,
-                         SecError(errSecInternal, error, CFSTR("failed to obtain LACopyExternalizedContext")));
-    require_action_quiet(acm_context = fnCopyExternalizedContext(la_context), out,
-                         SecError(errSecInternal, error, CFSTR("failed to get ACM handle from LAContext")));
-out:
-    if (la_lib != NULL) {
-        dlclose(la_lib);
-    }
-    return acm_context;
-}
-
 static bool SecItemAttributesPrepare(SecCFDictionaryCOW *attrs, bool forQuery, CFErrorRef *error) {
     bool ok = false;
     CFDataRef ac_data = NULL, acm_context = NULL;
 static bool SecItemAttributesPrepare(SecCFDictionaryCOW *attrs, bool forQuery, CFErrorRef *error) {
     bool ok = false;
     CFDataRef ac_data = NULL, acm_context = NULL;
@@ -1199,7 +1192,7 @@ static bool SecItemAttributesPrepare(SecCFDictionaryCOW *attrs, bool forQuery, C
     if (la_context) {
         require_action_quiet(!CFDictionaryContainsKey(attrs->dictionary, kSecUseCredentialReference), out,
                              SecError(errSecParam, error, CFSTR("kSecUseAuthenticationContext cannot be used together with kSecUseCredentialReference")));
     if (la_context) {
         require_action_quiet(!CFDictionaryContainsKey(attrs->dictionary, kSecUseCredentialReference), out,
                              SecError(errSecParam, error, CFSTR("kSecUseAuthenticationContext cannot be used together with kSecUseCredentialReference")));
-        require_quiet(acm_context = SecItemAttributesCopyPreparedAuthContext(la_context, error), out);
+        require_quiet(acm_context = LACopyACMContext(la_context, error), out);
         CFDictionaryRemoveValue(SecCFDictionaryCOWGetMutable(attrs), kSecUseAuthenticationContext);
         CFDictionarySetValue(SecCFDictionaryCOWGetMutable(attrs), kSecUseCredentialReference, acm_context);
     }
         CFDictionaryRemoveValue(SecCFDictionaryCOWGetMutable(attrs), kSecUseAuthenticationContext);
         CFDictionarySetValue(SecCFDictionaryCOWGetMutable(attrs), kSecUseCredentialReference, acm_context);
     }
@@ -1227,7 +1220,9 @@ static bool SecItemAttributesPrepare(SecCFDictionaryCOW *attrs, bool forQuery, C
     value = CFDictionaryGetValue(attrs->dictionary, kSecAttrIssuer);
     if (value) {
         /* convert DN to canonical issuer, if value is DN (top level sequence) */
     value = CFDictionaryGetValue(attrs->dictionary, kSecAttrIssuer);
     if (value) {
         /* convert DN to canonical issuer, if value is DN (top level sequence) */
-        const DERItem name = { (unsigned char *)CFDataGetBytePtr(value), CFDataGetLength(value) };
+        CFDataRef issuer;
+        require_quiet(issuer = CFCastWithError(CFData, value, error), out);
+        const DERItem name = { (unsigned char *)CFDataGetBytePtr(issuer), CFDataGetLength(issuer) };
         DERDecodedInfo content;
         if (DERDecodeItem(&name, &content) == DR_Success && content.tag == ASN1_CONSTR_SEQUENCE) {
             CFDataRef canonical_issuer = createNormalizedX501Name(kCFAllocatorDefault, &content.content);
         DERDecodedInfo content;
         if (DERDecodeItem(&name, &content) == DR_Success && content.tag == ASN1_CONSTR_SEQUENCE) {
             CFDataRef canonical_issuer = createNormalizedX501Name(kCFAllocatorDefault, &content.content);
@@ -1475,6 +1470,7 @@ bool SecItemAuthDoQuery(SecCFDictionaryCOW *query, SecCFDictionaryCOW *attribute
 
         // Prepare connection to target token if it is present.
         CFStringRef token_id = CFDictionaryGetValue(query->dictionary, kSecAttrTokenID);
 
         // Prepare connection to target token if it is present.
         CFStringRef token_id = CFDictionaryGetValue(query->dictionary, kSecAttrTokenID);
+        require_quiet(token_id == NULL || CFCastWithError(CFString, token_id, error) != NULL, out);
         if (secItemOperation != SecItemCopyMatching && token_id != NULL) {
             require_quiet(CFAssignRetained(token, SecTokenCreate(token_id, &auth_params, error)), out);
         }
         if (secItemOperation != SecItemCopyMatching && token_id != NULL) {
             require_quiet(CFAssignRetained(token, SecTokenCreate(token_id, &auth_params, error)), out);
         }
index c3b022791509afa6baacca2f157885addf209d56..50cc4c6aeac94b9568d1f005bf6f3fca2140903f 100644 (file)
@@ -101,8 +101,6 @@ TKTokenRef SecTokenCreate(CFStringRef token_id, SecCFDictionaryCOW *auth_params,
 
 CFDictionaryRef SecTokenItemValueCopy(CFDataRef db_value, CFErrorRef *error);
 
 
 CFDictionaryRef SecTokenItemValueCopy(CFDataRef db_value, CFErrorRef *error);
 
-CFDataRef SecItemAttributesCopyPreparedAuthContext(CFTypeRef la_context, CFErrorRef *error);
-
 CFArrayRef SecItemCopyParentCertificates_ios(CFDataRef normalizedIssuer, CFArrayRef accessGroups, CFErrorRef *error);
 
 bool SecItemCertificateExists(CFDataRef normalizedIssuer, CFDataRef serialNumber, CFArrayRef accessGroups, CFErrorRef *error);
 CFArrayRef SecItemCopyParentCertificates_ios(CFDataRef normalizedIssuer, CFArrayRef accessGroups, CFErrorRef *error);
 
 bool SecItemCertificateExists(CFDataRef normalizedIssuer, CFDataRef serialNumber, CFArrayRef accessGroups, CFErrorRef *error);
index 81dc4409fe71653deeffc0c877ede5ecf50cdee6..3fe809942e1b7c33661350486b4430d7b0ed8db4 100644 (file)
@@ -85,7 +85,7 @@ OSStatus ReadDATA(const uint8_t**bytesPtr, size_t*sizePtr, size_t* dataSize, uin
 static CC_NONNULL((1,2,3))
 OSStatus CreatePublicKey(const uint8_t**bytesPtr, size_t*sizePtr, SecOTRPublicIdentityRef* publicId);
 
 static CC_NONNULL((1,2,3))
 OSStatus CreatePublicKey(const uint8_t**bytesPtr, size_t*sizePtr, SecOTRPublicIdentityRef* publicId);
 
-static CC_NONNULL((1,2,3))
+static CC_NONNULL((2,3))
 CFMutableDataRef CFDataCreateMutableFromOTRDATA(CFAllocatorRef allocator, const uint8_t**bytesPtr, size_t*sizePtr);
 
 static CC_NONNULL((1))
 CFMutableDataRef CFDataCreateMutableFromOTRDATA(CFAllocatorRef allocator, const uint8_t**bytesPtr, size_t*sizePtr);
 
 static CC_NONNULL((1))
index 98db0bf9a785f7b6b63bc3142e7371596bf88946..bc7bb235554a34b96afcc6d8dd6a351361f31203 100644 (file)
@@ -132,6 +132,7 @@ SEC_CONST_DECL (kSecPolicyNameAppleParsecService, "Parsec");
 SEC_CONST_DECL (kSecPolicyNameAppleAMPService, "AMP");
 SEC_CONST_DECL (kSecPolicyNameAppleSiriService, "Siri");
 SEC_CONST_DECL (kSecPolicyNameAppleHomeAppClipUploadService, "HomeAppClipUploadService");
 SEC_CONST_DECL (kSecPolicyNameAppleAMPService, "AMP");
 SEC_CONST_DECL (kSecPolicyNameAppleSiriService, "Siri");
 SEC_CONST_DECL (kSecPolicyNameAppleHomeAppClipUploadService, "HomeAppClipUploadService");
+SEC_CONST_DECL (kSecPolicyNameAppleUpdatesService, "Updates");
 
 #define kSecPolicySHA1Size 20
 #define kSecPolicySHA256Size 32
 
 #define kSecPolicySHA1Size 20
 #define kSecPolicySHA256Size 32
@@ -390,9 +391,10 @@ SecPolicyRef SecPolicyCreateWithProperties(CFTypeRef policyIdentifier,
         policy = SecPolicyCreateAppleComponentCertificate(rootDigest);
     }
     /* For a couple of common patterns we use the macro, but some of the
         policy = SecPolicyCreateAppleComponentCertificate(rootDigest);
     }
     /* For a couple of common patterns we use the macro, but some of the
-     * policies are deprecated, so we need to ignore the warning. */
+     * policies are deprecated (or not yet available), so we need to ignore the warning. */
 #pragma clang diagnostic push
 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
 #pragma clang diagnostic push
 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
+#pragma clang diagnostic ignored "-Wunguarded-availability"
 #define _P_OPTION_
 #define _P_OPTION_N name
 #define _P_PROPERTIES_(NAME, IN_NAME, FUNCTION)
 #define _P_OPTION_
 #define _P_OPTION_N name
 #define _P_PROPERTIES_(NAME, IN_NAME, FUNCTION)
@@ -3561,8 +3563,6 @@ errOut:
 /* This one is special because the intermediate has no marker OID */
 SecPolicyRef SecPolicyCreateAppleSoftwareSigning(void) {
     CFMutableDictionaryRef options = NULL;
 /* This one is special because the intermediate has no marker OID */
 SecPolicyRef SecPolicyCreateAppleSoftwareSigning(void) {
     CFMutableDictionaryRef options = NULL;
-    CFDictionaryRef keySizes = NULL;
-    CFNumberRef rsaSize = NULL, ecSize = NULL;
     SecPolicyRef result = NULL;
 
     require(options = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
     SecPolicyRef result = NULL;
 
     require(options = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
@@ -3598,9 +3598,6 @@ SecPolicyRef SecPolicyCreateAppleSoftwareSigning(void) {
 
 errOut:
     CFReleaseSafe(options);
 
 errOut:
     CFReleaseSafe(options);
-    CFReleaseSafe(keySizes);
-    CFReleaseSafe(rsaSize);
-    CFReleaseSafe(ecSize);
     return result;
 }
 
     return result;
 }
 
@@ -4114,3 +4111,67 @@ errOut:
     CFReleaseNull(options);
     return result;
 }
     CFReleaseNull(options);
     return result;
 }
+
+SecPolicyRef SecPolicyCreateAlisha(void) {
+    CFMutableDictionaryRef options = NULL;
+    SecPolicyRef result = NULL;
+    CFDictionaryRef keySizes = NULL;
+    CFNumberRef ecSize = NULL;
+
+    require(options = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
+                                                &kCFTypeDictionaryKeyCallBacks,
+                                                &kCFTypeDictionaryValueCallBacks), errOut);
+
+    /* Alisha certs don't expire */
+    SecPolicyAddBasicCertOptions(options);
+
+    /* RSA key sizes are disallowed. EC key sizes are P-256 or larger. */
+    require(ecSize = CFNumberCreateWithCFIndex(NULL, 256), errOut);
+    require(keySizes = CFDictionaryCreate(NULL, (const void**)&kSecAttrKeyTypeEC,
+                                          (const void**)&ecSize, 1,
+                                          &kCFTypeDictionaryKeyCallBacks,
+                                          &kCFTypeDictionaryValueCallBacks), errOut);
+    add_element(options, kSecPolicyCheckKeySize, keySizes);
+
+    /* Check for weak hashes */
+    require(SecPolicyRemoveWeakHashOptions(options), errOut);
+
+    require(result = SecPolicyCreate(kSecPolicyAppleAlisha,
+                                     kSecPolicyNameAlisha, options), errOut);
+errOut:
+    CFReleaseNull(options);
+    CFReleaseNull(keySizes);
+    CFReleaseNull(ecSize);
+    return result;
+}
+
+SecPolicyRef SecPolicyCreateMeasuredBootPolicySigning(void) {
+    CFMutableDictionaryRef options = NULL;
+    SecPolicyRef result = NULL;
+
+    require(options = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
+                                                &kCFTypeDictionaryKeyCallBacks,
+                                                &kCFTypeDictionaryValueCallBacks), errOut);
+
+    /* No expiration check. */
+    SecPolicyAddBasicCertOptions(options);
+
+    /* Exactly 3 certs in the chain */
+    require(SecPolicyAddChainLengthOptions(options, 3), errOut);
+
+    /* Corporate Signing subCA */
+    add_element(options, kSecPolicyCheckIntermediateMarkerOid, CFSTR("1.2.840.113635.100.6.24.17"));
+
+    /* Measured Boot Policy Signing Leaf OID */
+    add_leaf_marker_string(options, CFSTR("1.2.840.113635.100.6.26.6.1"));
+
+    /* RSA key sizes are 2048-bit or larger. EC key sizes are P-256 or larger. */
+    require(SecPolicyAddStrongKeySizeOptions(options), errOut);
+
+    require(result = SecPolicyCreate(kSecPolicyAppleMeasuredBootPolicySigning,
+                                     kSecPolicyNameMeasuredBootPolicySigning, options), errOut);
+
+errOut:
+    CFReleaseSafe(options);
+    return result;
+}
index 6dfb4452416e78c3c82f3c771184ee152380e6c9..c3ee5011e08dba75c7a3a5bfa3f2805dbfe28375 100644 (file)
@@ -99,3 +99,5 @@ POLICYMACRO(FDRProvisioning,                        91,  , FDRProvisioning,
 POLICYMACRO(ComponentCertificate,                   92, E, Component,                             ,  , AppleComponentCertificate)
 POLICYMACRO(KeyTransparency,                        93, E, KT,                                   N, Y, AppleKeyTransparency)
 POLICYMACRO(LegacySSL,                              94, E, legacySSL,                             ,  , LegacySSL)
 POLICYMACRO(ComponentCertificate,                   92, E, Component,                             ,  , AppleComponentCertificate)
 POLICYMACRO(KeyTransparency,                        93, E, KT,                                   N, Y, AppleKeyTransparency)
 POLICYMACRO(LegacySSL,                              94, E, legacySSL,                             ,  , LegacySSL)
+POLICYMACRO(Alisha,                                 95, E, Alisha,                                , Y, Alisha)
+POLICYMACRO(MeasuredBootPolicySigning,              96, E, MeasuredBootPolicySigning,             , Y, MeasuredBootPolicySigning)
index 696d1f2c729e7fc32e42632f8a7a9e3eb6877464..698ae145def6ccec094cbe6ed2ef2c26a7cc5eee 100644 (file)
@@ -101,8 +101,6 @@ CFStringRef SOSCCGetOperationDescription(enum SecXPCOperation op)
             return CFSTR("OTASecExperimentGetAsset");
         case kSecXPCOpAcceptApplicants:
             return CFSTR("AcceptApplicants");
             return CFSTR("OTASecExperimentGetAsset");
         case kSecXPCOpAcceptApplicants:
             return CFSTR("AcceptApplicants");
-        case kSecXPCOpApplyToARing:
-            return CFSTR("ApplyToARing");
         case kSecXPCOpBailFromCircle:
             return CFSTR("BailFromCircle");
         case kSecXPCOpCanAuthenticate:
         case kSecXPCOpBailFromCircle:
             return CFSTR("BailFromCircle");
         case kSecXPCOpCanAuthenticate:
@@ -115,8 +113,6 @@ CFStringRef SOSCCGetOperationDescription(enum SecXPCOperation op)
             return CFSTR("CopyEngineState");
         case kSecXPCOpCopyGenerationPeerInfo:
             return CFSTR("CopyGenerationPeerInfo");
             return CFSTR("CopyEngineState");
         case kSecXPCOpCopyGenerationPeerInfo:
             return CFSTR("CopyGenerationPeerInfo");
-        case kSecXPCOpCopyIncompatibilityInfo:
-            return CFSTR("CopyIncompatibilityInfo");
         case kSecXPCOpCopyMyPeerInfo:
             return CFSTR("CopyMyPeerInfo");
         case kSecXPCOpCopyNotValidPeerPeerInfo:
         case kSecXPCOpCopyMyPeerInfo:
             return CFSTR("CopyMyPeerInfo");
         case kSecXPCOpCopyNotValidPeerPeerInfo:
@@ -131,10 +127,6 @@ CFStringRef SOSCCGetOperationDescription(enum SecXPCOperation op)
             return CFSTR("CopyViewUnawarePeerInfo");
         case kSecXPCOpDeviceInCircle:
             return CFSTR("DeviceInCircle");
             return CFSTR("CopyViewUnawarePeerInfo");
         case kSecXPCOpDeviceInCircle:
             return CFSTR("DeviceInCircle");
-        case kSecXPCOpEnableRing:
-            return CFSTR("EnableRing");
-        case kSecXPCOpGetAllTheRings:
-            return CFSTR("GetAllTheRings");
         case kSecXPCOpGetLastDepartureReason:
             return CFSTR("GetLastDepartureReason");
         case kSecXPCOpLoggedOutOfAccount:
         case kSecXPCOpGetLastDepartureReason:
             return CFSTR("GetLastDepartureReason");
         case kSecXPCOpLoggedOutOfAccount:
@@ -157,8 +149,6 @@ CFStringRef SOSCCGetOperationDescription(enum SecXPCOperation op)
             return CFSTR("RemovePeersFromCircle");
         case kSecXPCOpRemovePeersFromCircleWithAnalytics:
             return CFSTR("RemovePeersFromCircleWithAnalytics");
             return CFSTR("RemovePeersFromCircle");
         case kSecXPCOpRemovePeersFromCircleWithAnalytics:
             return CFSTR("RemovePeersFromCircleWithAnalytics");
-        case kSecXPCOpRequestEnsureFreshParameters:
-            return CFSTR("RequestEnsureFreshParameters");
         case kSecXPCOpRequestToJoin:
             return CFSTR("RequestToJoin");
         case kSecXPCOpRequestToJoinWithAnalytics:
         case kSecXPCOpRequestToJoin:
             return CFSTR("RequestToJoin");
         case kSecXPCOpRequestToJoinWithAnalytics:
@@ -173,8 +163,6 @@ CFStringRef SOSCCGetOperationDescription(enum SecXPCOperation op)
             return CFSTR("ResetToEmptyWithAnalytics");
         case kSecXPCOpResetToOffering:
             return CFSTR("ResetToOffering");
             return CFSTR("ResetToEmptyWithAnalytics");
         case kSecXPCOpResetToOffering:
             return CFSTR("ResetToOffering");
-        case kSecXPCOpRingStatus:
-            return CFSTR("RingStatus");
         case kSecXPCOpRollKeys:
             return CFSTR("RollKeys");
         case kSecXPCOpSetBagForAllSlices:
         case kSecXPCOpRollKeys:
             return CFSTR("RollKeys");
         case kSecXPCOpSetBagForAllSlices:
@@ -195,8 +183,6 @@ CFStringRef SOSCCGetOperationDescription(enum SecXPCOperation op)
             return CFSTR("ValidateUserPublic");
         case kSecXPCOpView:
             return CFSTR("View");
             return CFSTR("ValidateUserPublic");
         case kSecXPCOpView:
             return CFSTR("View");
-        case kSecXPCOpWithdrawlFromARing:
-            return CFSTR("WithdrawlFromARing");
         case sec_add_shared_web_credential_id:
             return CFSTR("add_shared_web_credential");
         case sec_copy_shared_web_credential_id:
         case sec_add_shared_web_credential_id:
             return CFSTR("add_shared_web_credential");
         case sec_copy_shared_web_credential_id:
@@ -259,24 +245,10 @@ CFStringRef SOSCCGetOperationDescription(enum SecXPCOperation op)
             return CFSTR("ocsp_cache_flush");
         case soscc_EnsurePeerRegistration_id:
             return CFSTR("EnsurePeerRegistration");
             return CFSTR("ocsp_cache_flush");
         case soscc_EnsurePeerRegistration_id:
             return CFSTR("EnsurePeerRegistration");
-        case kSecXPCOpSetEscrowRecord:
-            return CFSTR("SetEscrowRecord");
-        case kSecXPCOpGetEscrowRecord:
-            return CFSTR("GetEscrowRecord");
         case kSecXPCOpWhoAmI:
             return CFSTR("WhoAmI");
         case kSecXPCOpTransmogrifyToSyncBubble:
             return CFSTR("TransmogrifyToSyncBubble");
         case kSecXPCOpWhoAmI:
             return CFSTR("WhoAmI");
         case kSecXPCOpTransmogrifyToSyncBubble:
             return CFSTR("TransmogrifyToSyncBubble");
-        case kSecXPCOpWrapToBackupSliceKeyBagForView:
-            return CFSTR("WrapToBackupSliceKeyBagForView");
-        case kSecXPCOpCopyAccountData:
-            return CFSTR("CopyAccountDataFromKeychain");
-        case kSecXPCOpDeleteAccountData:
-            return CFSTR("DeleteAccountDataFromKeychain");
-        case kSecXPCOpCopyEngineData:
-            return CFSTR("CopyEngineDataFromKeychain");
-        case kSecXPCOpDeleteEngineData:
-            return CFSTR("DeleteEngineDataFromKeychain");
         case sec_item_update_token_items_id:
             return CFSTR("UpdateTokenItems");
         case sec_delete_items_with_access_groups_id:
         case sec_item_update_token_items_id:
             return CFSTR("UpdateTokenItems");
         case sec_delete_items_with_access_groups_id:
@@ -287,8 +259,6 @@ CFStringRef SOSCCGetOperationDescription(enum SecXPCOperation op)
             return CFSTR("RegisterRecoveryPublicKey");
         case kSecXPCOpGetRecoveryPublicKey:
             return CFSTR("GetRecoveryPublicKey");
             return CFSTR("RegisterRecoveryPublicKey");
         case kSecXPCOpGetRecoveryPublicKey:
             return CFSTR("GetRecoveryPublicKey");
-        case kSecXPCOpCopyBackupInformation:
-            return CFSTR("CopyBackupInformation");
         case kSecXPCOpMessageFromPeerIsPending:
             return CFSTR("MessageFromPeerIsPending");
         case kSecXPCOpSendToPeerIsPending:
         case kSecXPCOpMessageFromPeerIsPending:
             return CFSTR("MessageFromPeerIsPending");
         case kSecXPCOpSendToPeerIsPending:
index afbd42d6cfcf1566190f247c7dfcd74c54b5760f..ed945f01c12e189e372072f26c6bb864a7dfbcdd 100644 (file)
@@ -203,12 +203,6 @@ enum SecXPCOperation {
     sec_set_xpc_log_settings_id,
     sec_set_circle_log_settings_id,
     soscc_EnsurePeerRegistration_id,
     sec_set_xpc_log_settings_id,
     sec_set_circle_log_settings_id,
     soscc_EnsurePeerRegistration_id,
-    kSecXPCOpRequestEnsureFreshParameters,
-    kSecXPCOpGetAllTheRings,
-    kSecXPCOpApplyToARing,
-    kSecXPCOpWithdrawlFromARing,
-    kSecXPCOpEnableRing,
-    kSecXPCOpRingStatus,
     kSecXPCOpRequestDeviceID,
     kSecXPCOpSetDeviceID,
     kSecXPCOpHandleIDSMessage,
     kSecXPCOpRequestDeviceID,
     kSecXPCOpSetDeviceID,
     kSecXPCOpHandleIDSMessage,
@@ -253,7 +247,6 @@ enum SecXPCOperation {
     kSecXPCOpCopyGenerationPeerInfo,
     kSecXPCOpGetLastDepartureReason,
     kSecXPCOpSetLastDepartureReason,
     kSecXPCOpCopyGenerationPeerInfo,
     kSecXPCOpGetLastDepartureReason,
     kSecXPCOpSetLastDepartureReason,
-    kSecXPCOpCopyIncompatibilityInfo,
     kSecXPCOpCopyRetirementPeerInfo,
     kSecXPCOpCopyViewUnawarePeerInfo,
     kSecXPCOpCopyEngineState,
     kSecXPCOpCopyRetirementPeerInfo,
     kSecXPCOpCopyViewUnawarePeerInfo,
     kSecXPCOpCopyEngineState,
@@ -263,38 +256,26 @@ enum SecXPCOperation {
     kSecXPCOpSetBagForAllSlices,
     kSecXPCOpWaitForInitialSync,
     kSecXPCOpWaitForInitialSyncWithAnalytics,
     kSecXPCOpSetBagForAllSlices,
     kSecXPCOpWaitForInitialSync,
     kSecXPCOpWaitForInitialSyncWithAnalytics,
-    kSecXPCOpCopyYetToSyncViews,
-    kSecXPCOpSetEscrowRecord,
-    kSecXPCOpGetEscrowRecord,
     kSecXPCOpCheckPeerAvailability,
     kSecXPCOpCheckPeerAvailability,
-    kSecXPCOpCopyAccountData,
-    kSecXPCOpDeleteAccountData,
-    kSecXPCOpCopyEngineData,
-    kSecXPCOpDeleteEngineData,
     kSecXPCOpCopyApplication,
     kSecXPCOpCopyCircleJoiningBlob,
     kSecXPCOpJoinWithCircleJoiningBlob,
     kSecXPCOpKVSKeyCleanup,
     kSecXPCOpCopyApplication,
     kSecXPCOpCopyCircleJoiningBlob,
     kSecXPCOpJoinWithCircleJoiningBlob,
     kSecXPCOpKVSKeyCleanup,
-    kSecXPCOpPopulateKVS,
     kSecXPCOpAccountHasPublicKey,
     kSecXPCOpAccountHasPublicKey,
-    kSecXPCOpAccountIsNew,
     kSecXPCOpClearKVSPeerMessage,
     kSecXPCOpRegisterRecoveryPublicKey,
     kSecXPCOpGetRecoveryPublicKey,
     kSecXPCOpClearKVSPeerMessage,
     kSecXPCOpRegisterRecoveryPublicKey,
     kSecXPCOpGetRecoveryPublicKey,
-    kSecXPCOpCopyBackupInformation,
     kSecXPCOpCopyInitialSyncBlob,
     /* after this is free for all */
     kSecXPCOpWhoAmI,
     kSecXPCOpTransmogrifyToSyncBubble,
     kSecXPCOpTransmogrifyToSystemKeychain,
     kSecXPCOpCopyInitialSyncBlob,
     /* after this is free for all */
     kSecXPCOpWhoAmI,
     kSecXPCOpTransmogrifyToSyncBubble,
     kSecXPCOpTransmogrifyToSystemKeychain,
-    kSecXPCOpWrapToBackupSliceKeyBagForView,
     sec_item_update_token_items_id,
     kSecXPCOpDeleteUserView,
     sec_trust_store_copy_all_id,
     sec_trust_store_copy_usage_constraints_id,
     sec_ocsp_cache_flush_id,
     sec_delete_items_with_access_groups_id,
     sec_item_update_token_items_id,
     kSecXPCOpDeleteUserView,
     sec_trust_store_copy_all_id,
     sec_trust_store_copy_usage_constraints_id,
     sec_ocsp_cache_flush_id,
     sec_delete_items_with_access_groups_id,
-    kSecXPCOpIsThisDeviceLastBackup,
     sec_keychain_backup_keybag_uuid_id,
     kSecXPCOpPeersHaveViewsEnabled,
     kSecXPCOpProcessSyncWithPeers,
     sec_keychain_backup_keybag_uuid_id,
     kSecXPCOpPeersHaveViewsEnabled,
     kSecXPCOpProcessSyncWithPeers,
@@ -378,12 +359,6 @@ struct securityd {
     bool (*soscc_RequestToJoinCircleWithAnalytics)(CFDataRef parentEvent, CFErrorRef* error);
     bool (*soscc_RequestToJoinCircleAfterRestore)(CFErrorRef* error);
     bool (*soscc_RequestToJoinCircleAfterRestoreWithAnalytics)(CFDataRef parentEvent, CFErrorRef* error);
     bool (*soscc_RequestToJoinCircleWithAnalytics)(CFDataRef parentEvent, CFErrorRef* error);
     bool (*soscc_RequestToJoinCircleAfterRestore)(CFErrorRef* error);
     bool (*soscc_RequestToJoinCircleAfterRestoreWithAnalytics)(CFDataRef parentEvent, CFErrorRef* error);
-    bool (*soscc_RequestEnsureFreshParameters)(CFErrorRef* error);
-    CFStringRef (*soscc_GetAllTheRings)(CFErrorRef *error);
-    bool (*soscc_ApplyToARing)(CFStringRef ringName, CFErrorRef* error);
-    bool (*soscc_WithdrawlFromARing)(CFStringRef ringName, CFErrorRef* error);
-    bool (*soscc_EnableRing)(CFStringRef ringName, CFErrorRef* error);
-    SOSRingStatus (*soscc_RingStatus)(CFStringRef ringName, CFErrorRef* error);
     bool (*soscc_SetToNew)(CFErrorRef *error);
     bool (*soscc_ResetToOffering)(CFErrorRef* error);
     bool (*soscc_ResetToEmpty)(CFErrorRef* error);
     bool (*soscc_SetToNew)(CFErrorRef *error);
     bool (*soscc_ResetToOffering)(CFErrorRef* error);
     bool (*soscc_ResetToEmpty)(CFErrorRef* error);
@@ -414,7 +389,6 @@ struct securityd {
     // Not sure why these are below the last entry in the enum order above, but they are:
     CFArrayRef (*soscc_CopyPeerInfo)(CFErrorRef* error);
     CFArrayRef (*soscc_CopyConcurringPeerInfo)(CFErrorRef* error);
     // Not sure why these are below the last entry in the enum order above, but they are:
     CFArrayRef (*soscc_CopyPeerInfo)(CFErrorRef* error);
     CFArrayRef (*soscc_CopyConcurringPeerInfo)(CFErrorRef* error);
-    CFStringRef (*soscc_CopyIncompatibilityInfo)(CFErrorRef* error);
     enum DepartureReason (*soscc_GetLastDepartureReason)(CFErrorRef* error);
     bool (*soscc_SetLastDepartureReason)(enum DepartureReason, CFErrorRef* error);
     CFSetRef (*soscc_ProcessSyncWithPeers)(CFSetRef peerIDs, CFSetRef backupPeerIDs, CFErrorRef* error);
     enum DepartureReason (*soscc_GetLastDepartureReason)(CFErrorRef* error);
     bool (*soscc_SetLastDepartureReason)(enum DepartureReason, CFErrorRef* error);
     CFSetRef (*soscc_ProcessSyncWithPeers)(CFSetRef peerIDs, CFSetRef backupPeerIDs, CFErrorRef* error);
@@ -427,25 +401,13 @@ struct securityd {
     SOSPeerInfoRef (*soscc_CopyMyPeerInfo)(CFErrorRef*);
     bool (*soscc_WaitForInitialSync)(CFErrorRef*);
     bool (*soscc_WaitForInitialSyncWithAnalytics)(CFDataRef parentEvent, CFErrorRef *error);
     SOSPeerInfoRef (*soscc_CopyMyPeerInfo)(CFErrorRef*);
     bool (*soscc_WaitForInitialSync)(CFErrorRef*);
     bool (*soscc_WaitForInitialSyncWithAnalytics)(CFDataRef parentEvent, CFErrorRef *error);
-    CFArrayRef (*soscc_CopyYetToSyncViewsList)(CFErrorRef*);
-    bool (*soscc_SetEscrowRecords)(CFStringRef escrow_label, uint64_t tries, CFErrorRef *error);
-    CFDictionaryRef (*soscc_CopyEscrowRecords)(CFErrorRef *error);
-    CFDictionaryRef (*soscc_CopyBackupInformation)(CFErrorRef *error);
     bool (*soscc_PeerAvailability)(CFErrorRef *error);
     bool (*soscc_PeerAvailability)(CFErrorRef *error);
-    bool (*sosbskb_WrapToBackupSliceKeyBagForView)(CFStringRef viewName, CFDataRef input, CFDataRef* output, CFDataRef* bskbEncoded, CFErrorRef* error);
-    CFDataRef (*soscc_CopyAccountState)(CFErrorRef *error);
-    bool (*soscc_DeleteAccountState)(CFErrorRef *error);
-    CFDataRef (*soscc_CopyEngineData)(CFErrorRef *error);
-    bool (*soscc_DeleteEngineState)(CFErrorRef *error);
     SOSPeerInfoRef (*soscc_CopyApplicant)(CFErrorRef *error);
     CFDataRef (*soscc_CopyCircleJoiningBlob)(SOSPeerInfoRef applicant, CFErrorRef *error);
     CFDataRef (*soscc_CopyInitialSyncData)(SOSInitialSyncFlags flags, CFErrorRef *error);
     bool (*soscc_JoinWithCircleJoiningBlob)(CFDataRef joiningBlob, PiggyBackProtocolVersion version, CFErrorRef *error);
     bool (*soscc_SOSCCCleanupKVSKeys)(CFErrorRef *error);
     SOSPeerInfoRef (*soscc_CopyApplicant)(CFErrorRef *error);
     CFDataRef (*soscc_CopyCircleJoiningBlob)(SOSPeerInfoRef applicant, CFErrorRef *error);
     CFDataRef (*soscc_CopyInitialSyncData)(SOSInitialSyncFlags flags, CFErrorRef *error);
     bool (*soscc_JoinWithCircleJoiningBlob)(CFDataRef joiningBlob, PiggyBackProtocolVersion version, CFErrorRef *error);
     bool (*soscc_SOSCCCleanupKVSKeys)(CFErrorRef *error);
-    bool (*soscc_SOSCCTestPopulateKVSWithBadKeys)(CFErrorRef *error);
     bool (*soscc_AccountHasPublicKey)(CFErrorRef *error);
     bool (*soscc_AccountHasPublicKey)(CFErrorRef *error);
-    bool (*soscc_AccountIsNew)(CFErrorRef *error);
-    bool (*soscc_IsThisDeviceLastBackup)(CFErrorRef *error);
     bool (*soscc_requestSyncWithPeerOverKVS)(CFStringRef peerID, CFDataRef message, CFErrorRef *error);
     CFBooleanRef (*soscc_SOSCCPeersHaveViewsEnabled)(CFArrayRef views, CFErrorRef *error);
     bool (*socc_clearPeerMessageKeyInKVS)(CFStringRef peerID, CFErrorRef *error);
     bool (*soscc_requestSyncWithPeerOverKVS)(CFStringRef peerID, CFDataRef message, CFErrorRef *error);
     CFBooleanRef (*soscc_SOSCCPeersHaveViewsEnabled)(CFArrayRef views, CFErrorRef *error);
     bool (*socc_clearPeerMessageKeyInKVS)(CFStringRef peerID, CFErrorRef *error);
index 917ac3e623fb0f6238c635bfb0d29a405486e11d..df744076dced74b6bff6c67132cf4a98ff158434 100644 (file)
@@ -909,12 +909,7 @@ static void securityd_xpc_dictionary_handler(const xpc_connection_t connection,
                                             SOSCCAccountHasPublicKey_Server(&error));
                 }
                 break;
                                             SOSCCAccountHasPublicKey_Server(&error));
                 }
                 break;
-            case kSecXPCOpAccountIsNew:
-                if (EntitlementPresentAndTrue(operation, client.task, kSecEntitlementKeychainCloudCircle, &error)) {
-                    xpc_dictionary_set_bool(replyMessage, kSecXPCKeyResult,
-                                            SOSCCAccountIsNew_Server(&error));
-                }
-                break;
+
             case kSecXPCOpRequestToJoinAfterRestore:
                 if (EntitlementPresentAndTrue(operation, client.task, kSecEntitlementKeychainCloudCircle, &error)) {
                     xpc_dictionary_set_bool(replyMessage, kSecXPCKeyResult,
             case kSecXPCOpRequestToJoinAfterRestore:
                 if (EntitlementPresentAndTrue(operation, client.task, kSecEntitlementKeychainCloudCircle, &error)) {
                     xpc_dictionary_set_bool(replyMessage, kSecXPCKeyResult,
@@ -932,49 +927,6 @@ static void securityd_xpc_dictionary_handler(const xpc_connection_t connection,
                     CFReleaseNull(parentEvent);
                 }
                 break;
                     CFReleaseNull(parentEvent);
                 }
                 break;
-            case kSecXPCOpRequestEnsureFreshParameters:
-                if (EntitlementPresentAndTrue(operation, client.task, kSecEntitlementKeychainCloudCircle, &error)) {
-                    xpc_dictionary_set_bool(replyMessage, kSecXPCKeyResult,
-                                            SOSCCRequestEnsureFreshParameters_Server(&error));
-                }
-                break;
-            case kSecXPCOpGetAllTheRings:
-                if (EntitlementPresentAndTrue(operation, client.task, kSecEntitlementKeychainCloudCircle, &error)) {
-                    CFStringRef ringDescriptions = SOSCCGetAllTheRings_Server(&error);
-                    xpc_object_t xpc_dictionary = _CFXPCCreateXPCObjectFromCFObject(ringDescriptions);
-                    xpc_dictionary_set_value(replyMessage, kSecXPCKeyResult, xpc_dictionary);
-                    xpc_release(xpc_dictionary);
-                    CFReleaseNull(ringDescriptions);
-                }
-                break;
-            case kSecXPCOpApplyToARing:
-                if (EntitlementPresentAndTrue(operation, client.task, kSecEntitlementKeychainCloudCircle, &error)) {
-                    CFStringRef ringName = SecXPCDictionaryCopyString(event, kSecXPCKeyString, &error);
-                    xpc_dictionary_set_bool(replyMessage, kSecXPCKeyResult, SOSCCApplyToARing_Server(ringName, &error));
-                    CFReleaseNull(ringName);
-                }
-                break;
-            case kSecXPCOpWithdrawlFromARing:
-                if (EntitlementPresentAndTrue(operation, client.task, kSecEntitlementKeychainCloudCircle, &error)) {
-                    CFStringRef ringName = SecXPCDictionaryCopyString(event, kSecXPCKeyString, &error);
-                    xpc_dictionary_set_bool(replyMessage, kSecXPCKeyResult, SOSCCWithdrawlFromARing_Server(ringName, &error));
-                    CFReleaseNull(ringName);
-                }
-                break;
-            case kSecXPCOpRingStatus:
-                if (EntitlementPresentAndTrue(operation, client.task, kSecEntitlementKeychainCloudCircle, &error)) {
-                    CFStringRef ringName = SecXPCDictionaryCopyString(event, kSecXPCKeyString, &error);
-                    xpc_dictionary_set_bool(replyMessage, kSecXPCKeyResult, SOSCCRingStatus_Server(ringName, &error));
-                    CFReleaseNull(ringName);
-                }
-                break;
-            case kSecXPCOpEnableRing:
-                if (EntitlementPresentAndTrue(operation, client.task, kSecEntitlementKeychainCloudCircle, &error)) {
-                    CFStringRef ringName = SecXPCDictionaryCopyString(event, kSecXPCKeyString, &error);
-                    xpc_dictionary_set_bool(replyMessage, kSecXPCKeyResult, SOSCCEnableRing_Server(ringName, &error));
-                    CFReleaseNull(ringName);
-                }
-                break;
             case kSecXPCOpRequestDeviceID:
             case kSecXPCOpSetDeviceID:
             case kSecXPCOpHandleIDSMessage:
             case kSecXPCOpRequestDeviceID:
             case kSecXPCOpSetDeviceID:
             case kSecXPCOpHandleIDSMessage:
@@ -1057,14 +1009,6 @@ static void securityd_xpc_dictionary_handler(const xpc_connection_t connection,
                                             SOSCCLoggedOutOfAccount_Server(&error));
                 }
                 break;
                                             SOSCCLoggedOutOfAccount_Server(&error));
                 }
                 break;
-            case kSecXPCOpBailFromCircle:
-                if (EntitlementPresentAndTrue(operation, client.task, kSecEntitlementKeychainCloudCircle, &error)) {
-                    // 0 is valid; ok for this parameter to be unset or incorrect type. Note: kSecXPCLimitInMinutes is actually seconds, not minutes
-                    uint64_t limit_in_seconds = xpc_dictionary_get_uint64(event, kSecXPCLimitInMinutes);
-                    xpc_dictionary_set_bool(replyMessage, kSecXPCKeyResult,
-                                            SOSCCBailFromCircle_Server(limit_in_seconds, &error));
-                }
-                break;
             case kSecXPCOpAcceptApplicants:
                 if (EntitlementPresentAndTrue(operation, client.task, kSecEntitlementKeychainCloudCircle, &error)) {
                     xpc_object_t xapplicants = xpc_dictionary_get_value(event, kSecXPCKeyPeerInfoArray);
             case kSecXPCOpAcceptApplicants:
                 if (EntitlementPresentAndTrue(operation, client.task, kSecEntitlementKeychainCloudCircle, &error)) {
                     xpc_object_t xapplicants = xpc_dictionary_get_value(event, kSecXPCKeyPeerInfoArray);
@@ -1190,73 +1134,29 @@ static void securityd_xpc_dictionary_handler(const xpc_connection_t connection,
                                                                  SOSCCCopyViewUnawarePeerInfo_Server(&error),
                                                                  &error);
                 }
                                                                  SOSCCCopyViewUnawarePeerInfo_Server(&error),
                                                                  &error);
                 }
-                break;
-            case kSecXPCOpCopyAccountData:
-                {
-                    if (EntitlementPresentAndTrue(operation, client.task, kSecEntitlementKeychainCloudCircle, &error)) {
-                        xpc_object_t xpc_account_object = NULL;
-                        CFDataRef accountData = SOSCCCopyAccountState_Server(&error);
-                        if(accountData)
-                            xpc_account_object = _CFXPCCreateXPCObjectFromCFObject(accountData);
-                        
-                        xpc_dictionary_set_value(replyMessage, kSecXPCKeyResult, xpc_account_object);
-                        CFReleaseNull(accountData);
-                    }
-                    break;
-                }
-            case kSecXPCOpDeleteAccountData:
-                {
-                    if (EntitlementPresentAndTrue(operation, client.task, kSecEntitlementKeychainCloudCircle, &error)) {
-                        bool status = SOSCCDeleteAccountState_Server(&error);
-                        xpc_dictionary_set_bool(replyMessage, kSecXPCKeyResult, status);
-                    }
-                    break;
-                }
-            case kSecXPCOpCopyEngineData:
-                {
-                    if (EntitlementPresentAndTrue(operation, client.task, kSecEntitlementKeychainCloudCircle, &error)) {
-
-                        xpc_object_t xpc_engine_object = NULL;
-                        CFDataRef engineData = SOSCCCopyEngineData_Server(&error);
-                        if(engineData)
-                            xpc_engine_object = _CFXPCCreateXPCObjectFromCFObject(engineData);
-
-                        xpc_dictionary_set_value(replyMessage, kSecXPCKeyResult, xpc_engine_object);
-                        CFReleaseNull(engineData);
-
-                    }
                     break;
                     break;
-                }
-            case kSecXPCOpDeleteEngineData:
-                {
-                    if (EntitlementPresentAndTrue(operation, client.task, kSecEntitlementKeychainCloudCircle, &error)) {
-                        bool status = SOSCCDeleteEngineState_Server(&error);
-                        xpc_dictionary_set_bool(replyMessage, kSecXPCKeyResult, status);
-                    }
-                    break;
-                }
             case kSecXPCOpCopyEngineState:
                 {
             case kSecXPCOpCopyEngineState:
                 {
-                    if (EntitlementPresentAndTrue(operation, client.task, kSecEntitlementKeychainCloudCircle, &error)) {
-                        CFArrayRef array = SOSCCCopyEngineState_Server(&error);
-                        CFDataRef derData = NULL;
+                if (EntitlementPresentAndTrue(operation, client.task, kSecEntitlementKeychainCloudCircle, &error)) {
+                    CFArrayRef array = SOSCCCopyEngineState_Server(&error);
+                    CFDataRef derData = NULL;
 
 
-                        require_quiet(array, done);
-                        derData = CFPropertyListCreateDERData(kCFAllocatorDefault, array, &error);
+                    require_quiet(array, done);
+                    derData = CFPropertyListCreateDERData(kCFAllocatorDefault, array, &error);
 
 
-                        require_quiet(derData, done);
-                        xpc_dictionary_set_data(replyMessage, kSecXPCKeyResult, CFDataGetBytePtr(derData), CFDataGetLength(derData));
+                    require_quiet(derData, done);
+                    xpc_dictionary_set_data(replyMessage, kSecXPCKeyResult, CFDataGetBytePtr(derData),CFDataGetLength(derData));
                     done:
                         CFReleaseNull(derData);
                         CFReleaseNull(array);
                     }
                 }
                 break;
                     done:
                         CFReleaseNull(derData);
                         CFReleaseNull(array);
                     }
                 }
                 break;
-            case kSecXPCOpCopyPeerPeerInfo:
-                if (EntitlementPresentAndTrue(operation, client.task, kSecEntitlementKeychainCloudCircle, &error)) {
-                    xpc_dictionary_set_and_consume_PeerInfoArray(replyMessage, kSecXPCKeyResult,
-                                                                 SOSCCCopyPeerPeerInfo_Server(&error),
-                                                                 &error);
+                case kSecXPCOpCopyPeerPeerInfo:
+                    if (EntitlementPresentAndTrue(operation, client.task, kSecEntitlementKeychainCloudCircle, &error)) {
+                        xpc_dictionary_set_and_consume_PeerInfoArray(replyMessage, kSecXPCKeyResult,
+                                                                     SOSCCCopyPeerPeerInfo_Server(&error),
+                                                                     &error);
                 }
                 break;
             case kSecXPCOpCopyConcurringPeerPeerInfo:
                 }
                 break;
             case kSecXPCOpCopyConcurringPeerPeerInfo:
@@ -1320,13 +1220,6 @@ static void securityd_xpc_dictionary_handler(const xpc_connection_t connection,
                                             SOSCCProcessEnsurePeerRegistration_Server(&error));
                 }
                 break;
                                             SOSCCProcessEnsurePeerRegistration_Server(&error));
                 }
                 break;
-            case kSecXPCOpCopyIncompatibilityInfo:
-                if (EntitlementPresentAndTrue(operation, client.task, kSecEntitlementKeychainCloudCircle, &error)) {
-                    CFStringRef iis = SOSCCCopyIncompatibilityInfo_Server(&error);
-                    SecXPCDictionarySetString(replyMessage, kSecXPCKeyResult, iis, &error);
-                    CFReleaseSafe(iis);
-                }
-                break;
             case kSecXPCOpRollKeys:
                 {
                     // false is valid, so it's safe for this parameter to be unset or incorrect type
             case kSecXPCOpRollKeys:
                 {
                     // false is valid, so it's safe for this parameter to be unset or incorrect type
@@ -1353,59 +1246,6 @@ static void securityd_xpc_dictionary_handler(const xpc_connection_t connection,
                     CFReleaseNull(parentEvent);
                 }
                 break;
                     CFReleaseNull(parentEvent);
                 }
                 break;
-            case kSecXPCOpCopyYetToSyncViews:
-                if (EntitlementPresentAndTrue(operation, client.task, kSecEntitlementKeychainCloudCircle, &error)) {
-                    CFArrayRef array = SOSCCCopyYetToSyncViewsList_Server(&error);
-                    if (array) {
-                        xpc_object_t xpc_array = _CFXPCCreateXPCObjectFromCFObject(array);
-                        xpc_dictionary_set_value(replyMessage, kSecXPCKeyResult, xpc_array);
-                        xpc_release(xpc_array);
-                    }
-                    CFReleaseNull(array);
-                }
-                break;
-            case kSecXPCOpSetEscrowRecord:
-                if (EntitlementPresentAndTrue(operation, client.task, kSecEntitlementKeychainCloudCircle, &error)) {
-                    CFStringRef escrow_label = SecXPCDictionaryCopyString(event, kSecXPCKeyEscrowLabel, &error); // NULL checked below
-                    uint64_t tries = xpc_dictionary_get_int64(event, kSecXPCKeyTriesLabel); // 0 is acceptable; safe for this parameter to be unset or incorrect type
-
-                    if (escrow_label != NULL) {
-                        bool result = SOSCCSetEscrowRecord_Server(escrow_label, tries, &error);
-                        if (result) {
-                            xpc_dictionary_set_bool(replyMessage, kSecXPCKeyResult, result);
-                        }
-                        CFReleaseNull(escrow_label);
-                    }
-                }
-                break;
-            case kSecXPCOpGetEscrowRecord:
-                if (EntitlementPresentAndTrue(operation, client.task, kSecEntitlementKeychainCloudCircle, &error)) {
-                    CFDictionaryRef record =  SOSCCCopyEscrowRecord_Server(&error);
-                    if (record) {
-                        xpc_object_t xpc_dictionary = _CFXPCCreateXPCObjectFromCFObject(record);
-                        xpc_dictionary_set_value(replyMessage, kSecXPCKeyResult, xpc_dictionary);
-                        xpc_release(xpc_dictionary);
-                    }
-                    CFReleaseNull(record);
-                }
-                break;
-            case kSecXPCOpCopyBackupInformation:
-                if (EntitlementPresentAndTrue(operation, client.task, kSecEntitlementKeychainCloudCircle, &error)) {
-                    CFDictionaryRef record =  SOSCCCopyBackupInformation_Server(&error);
-                    if (record) {
-                        xpc_object_t xpc_dictionary = _CFXPCCreateXPCObjectFromCFObject(record);
-                        xpc_dictionary_set_value(replyMessage, kSecXPCKeyResult, xpc_dictionary);
-                        xpc_release(xpc_dictionary);
-                    }
-                    CFReleaseNull(record);
-                }
-                break;
-                    
-            case kSecXPCOpIsThisDeviceLastBackup:
-                if (EntitlementPresentAndTrue(operation, client.task, kSecEntitlementKeychainCloudCircle, &error)) {
-                    xpc_dictionary_set_bool(replyMessage, kSecXPCKeyResult, SOSCCkSecXPCOpIsThisDeviceLastBackup_Server(&error));
-                }
-                break;
             case kSecXPCOpPeersHaveViewsEnabled:
                 {
                     CFArrayRef viewSet = SecXPCDictionaryCopyArray(event, kSecXPCKeyArray, &error);
             case kSecXPCOpPeersHaveViewsEnabled:
                 {
                     CFArrayRef viewSet = SecXPCDictionaryCopyArray(event, kSecXPCKeyArray, &error);
@@ -1475,34 +1315,6 @@ static void securityd_xpc_dictionary_handler(const xpc_connection_t connection,
                     }
                 }
                 break;
                     }
                 }
                 break;
-            case kSecXPCOpWrapToBackupSliceKeyBagForView:
-                {
-                    CFStringRef viewname = SecXPCDictionaryCopyString(event, kSecXPCKeyViewName, &error);
-                    if(viewname) {
-                        CFDataRef plaintext = SecXPCDictionaryCopyData(event, kSecXPCData, &error);
-                        if (plaintext) {
-                            CFDataRef ciphertext = NULL;
-                            CFDataRef bskbEncoded = NULL;
-
-                            bool result = SOSWrapToBackupSliceKeyBagForView_Server(viewname, plaintext, &ciphertext, &bskbEncoded, &error);
-                            xpc_dictionary_set_bool(replyMessage, kSecXPCKeyResult, result);
-
-                            if(!error && result) {
-                                if(ciphertext) {
-                                    xpc_dictionary_set_data(replyMessage, kSecXPCData, CFDataGetBytePtr(ciphertext), CFDataGetLength(ciphertext));
-                                }
-                                if(bskbEncoded) {
-                                    xpc_dictionary_set_data(replyMessage, kSecXPCKeyKeybag, CFDataGetBytePtr(bskbEncoded), CFDataGetLength(bskbEncoded));
-                                }
-                            }
-                            CFReleaseSafe(ciphertext);
-                            CFReleaseSafe(bskbEncoded);
-                        }
-                        CFReleaseSafe(plaintext);
-                    }
-                    CFReleaseNull(viewname);
-                }
-                break;
             case kSecXPCOpCopyApplication:
                     if (EntitlementPresentAndTrue(operation, client.task, kSecEntitlementCircleJoin, &error)) {
                         SOSPeerInfoRef peerInfo = SOSCCCopyApplication_Server(&error);
             case kSecXPCOpCopyApplication:
                     if (EntitlementPresentAndTrue(operation, client.task, kSecEntitlementCircleJoin, &error)) {
                         SOSPeerInfoRef peerInfo = SOSCCCopyApplication_Server(&error);
@@ -1565,12 +1377,7 @@ static void securityd_xpc_dictionary_handler(const xpc_connection_t connection,
                         xpc_dictionary_set_bool(replyMessage, kSecXPCKeyResult, retval);
                     }
                     break;
                         xpc_dictionary_set_bool(replyMessage, kSecXPCKeyResult, retval);
                     }
                     break;
-            case kSecXPCOpPopulateKVS:
-                    if (EntitlementPresentAndTrue(operation, client.task, kSecEntitlementKeychainCloudCircle, &error)) {
-                        bool retval = SOSCCTestPopulateKVSWithBadKeys_Server(&error);
-                        xpc_dictionary_set_bool(replyMessage, kSecXPCKeyResult, retval);
-                    }
-                    break;
+        
             case kSecXPCOpMessageFromPeerIsPending:
                 {
                     SOSPeerInfoRef peer = SecXPCDictionaryCopyPeerInfo(event, kSecXPCKeyPeerInfo, &error);
             case kSecXPCOpMessageFromPeerIsPending:
                 {
                     SOSPeerInfoRef peer = SecXPCDictionaryCopyPeerInfo(event, kSecXPCKeyPeerInfo, &error);
index 534f8cd58feec6228632bef5a905e94b8e8847f6..8378610461289e718f91f80c4c94a0722b322bf9 100644 (file)
@@ -21,7 +21,6 @@ ONE_TEST(si_24_sectrust_digicert_malaysia)
 ONE_TEST(si_24_sectrust_passbook)
 ONE_TEST(si_25_cms_skid)
 ONE_TEST(si_26_sectrust_copyproperties)
 ONE_TEST(si_24_sectrust_passbook)
 ONE_TEST(si_25_cms_skid)
 ONE_TEST(si_26_sectrust_copyproperties)
-ONE_TEST(si_27_sectrust_exceptions)
 ONE_TEST(si_28_sectrustsettings)
 ONE_TEST(si_29_cms_chain_mode)
 ONE_TEST(si_32_sectrust_pinning_required)
 ONE_TEST(si_28_sectrustsettings)
 ONE_TEST(si_29_cms_chain_mode)
 ONE_TEST(si_32_sectrust_pinning_required)
index 048929d5e55dfbf6796dad844107f11e680fa20a..e276aeb779d35f25f020881b321e192423a0f254 100644 (file)
@@ -410,6 +410,88 @@ static void rewrapTest(void) {
     ok([decrypted isEqualToData:message], "Decrypted data differs: %@ vs %@", decrypted, message);
 }
 
     ok([decrypted isEqualToData:message], "Decrypted data differs: %@ vs %@", decrypted, message);
 }
 
+static void keychainTest(void) {
+    id accessControl = CFBridgingRelease(SecAccessControlCreateWithFlags(NULL, kSecAttrAccessibleWhenUnlocked, kSecAccessControlPrivateKeyUsage, NULL));
+    NSDictionary *keyAttributes = @{ (id)kSecAttrTokenID : (id)kSecAttrTokenIDAppleKeyStore,
+                                     (id)kSecAttrKeyType : (id)kSecAttrKeyTypeECSECPrimeRandom,
+                                     (id)kSecPrivateKeyAttrs : @{
+                                             (id)kSecAttrAccessControl : accessControl,
+                                             (id)kSecAttrIsPermanent : @YES,
+                                             (id)kSecAttrLabel : @"si_44_seckey_aks_1",
+                                     },
+    };
+    NSError *error;
+    id key = (__bridge_transfer id)SecKeyCreateRandomKey((CFDictionaryRef)keyAttributes, (void *)&error);
+    ok(key, "failed to create random key %@", error);
+    id pubKey = CFBridgingRelease(SecKeyCopyPublicKey((SecKeyRef)key));
+    ok(pubKey, "failed to get public key from SEP key");
+    key = nil;
+
+    NSDictionary *query = @{
+        (id)kSecReturnRef: @YES,
+        (id)kSecClass: (id)kSecClassKey,
+        (id)kSecAttrLabel: @"si_44_seckey_aks_1",
+    };
+    OSStatus status = SecItemCopyMatching((CFDictionaryRef)query, (void *)&key);
+    is(status, errSecSuccess, "getting SEP key from keychain failed");
+
+    NSError *err;
+    NSData *data = [@"message" dataUsingEncoding:NSUTF8StringEncoding];
+    NSData *sig = CFBridgingRelease(SecKeyCreateSignature((SecKeyRef)key, kSecKeyAlgorithmECDSASignatureMessageX962SHA256, (CFDataRef)data, (void *)&err));
+    ok(sig, "failed to create signature: %@", err);
+    ok(SecKeyVerifySignature((SecKeyRef)pubKey, kSecKeyAlgorithmECDSASignatureMessageX962SHA256, (CFDataRef)data, (CFDataRef)sig, (void *)&err), "failed to verify signature: %@", err);
+
+    status = SecItemDelete((CFDictionaryRef)query);
+    is(status, errSecSuccess, "deleting SEP key from keychain failed");
+
+    status = SecItemCopyMatching((CFDictionaryRef)query, (void *)&key);
+    is(status, errSecItemNotFound, "SEP key was not deleted from keychain");
+}
+
+static void secAccessControlDescriptionTest(void) {
+    NSError *error;
+    NSObject *ac = CFBridgingRelease(SecAccessControlCreate(kCFAllocatorDefault, (void *)&error));
+    ok(ac, "failed to create ac: %@", error);
+    ok(SecAccessControlSetProtection((__bridge SecAccessControlRef)ac, kSecAttrAccessibleWhenUnlocked, (void *)&error), "failed to set protection: %@", error);
+
+    NSString *desc = ac.description;
+    ok([desc isEqualToString:@"<SecAccessControlRef: ak>"], "unexpected desc: %@", desc);
+
+    SecAccessControlSetConstraints((__bridge SecAccessControlRef)ac, (__bridge CFDictionaryRef)@{});
+    desc = ac.description;
+    ok([desc isEqualToString:@"<SecAccessControlRef: ak>"], "unexpected desc: %@", desc);
+
+    SecAccessControlSetConstraints((__bridge SecAccessControlRef)ac, (__bridge CFDictionaryRef)@{@"od": (__bridge id)kCFBooleanTrue});
+    desc = ac.description;
+    ok([desc isEqualToString:@"<SecAccessControlRef: ak;od(true)>"], "unexpected desc: %@", desc);
+
+    SecAccessControlSetConstraints((__bridge SecAccessControlRef)ac, (__bridge CFDictionaryRef)@{@"od": (__bridge id)kCFBooleanTrue, @"oe": (__bridge id)kCFBooleanFalse});
+    desc = ac.description;
+    ok([desc isEqualToString:@"<SecAccessControlRef: ak;od(true);oe(false)>"], "unexpected desc: %@", desc);
+
+    SecAccessControlSetConstraints((__bridge SecAccessControlRef)ac, (__bridge CFDictionaryRef)@{@"od": @"huh"});
+    desc = ac.description;
+    ok([desc isEqualToString:@"<SecAccessControlRef: ak;od(huh)>"], "unexpected desc: %@", desc);
+
+    SecAccessControlSetConstraints((__bridge SecAccessControlRef)ac, (__bridge CFDictionaryRef)@{@"od": @2});
+    desc = ac.description;
+    ok([desc isEqualToString:@"<SecAccessControlRef: ak;od(2)>"], "unexpected desc: %@", desc);
+
+    NSData *shortData = [NSData dataWithBytes:"\x01\x02\x03" length:3];
+    SecAccessControlSetConstraints((__bridge SecAccessControlRef)ac, (__bridge CFDictionaryRef)@{@"od": shortData});
+    desc = ac.description;
+    ok([desc isEqualToString:@"<SecAccessControlRef: ak;od(010203)>"], "unexpected desc: %@", desc);
+
+    NSData *longData = [NSMutableData dataWithLength:128];
+    SecAccessControlSetConstraints((__bridge SecAccessControlRef)ac, (__bridge CFDictionaryRef)@{@"od": longData});
+    desc = ac.description;
+    ok([desc isEqualToString:@"<SecAccessControlRef: ak;od(00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000...(128b))>"], "unexpected desc: %@", desc);
+
+    SecAccessControlSetConstraints((__bridge SecAccessControlRef)ac, (__bridge CFDictionaryRef)@{@"od": @{@"kofn": @2}});
+    desc = ac.description;
+    ok([desc isEqualToString:@"<SecAccessControlRef: ak;od(kofn(2))>"], "unexpected desc: %@", desc);
+}
+
 int si_44_seckey_aks(int argc, char *const *argv) {
     @autoreleasepool {
         BOOL testPKA = YES;
 int si_44_seckey_aks(int argc, char *const *argv) {
     @autoreleasepool {
         BOOL testPKA = YES;
@@ -428,12 +510,14 @@ int si_44_seckey_aks(int argc, char *const *argv) {
 
         testPKA = NO;
 #endif
 
         testPKA = NO;
 #endif
-        plan_tests(testPKA ? 102 : 87);
+        plan_tests(testPKA ? 119 : 104);
 
 
+        secAccessControlDescriptionTest();
         secKeySepTest(testPKA);
         attestationTest(kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly, NO);
         attestationTest(kSecAttrAccessibleUntilReboot, YES);
         keyFromBlobTest();
         secKeySepTest(testPKA);
         attestationTest(kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly, NO);
         attestationTest(kSecAttrAccessibleUntilReboot, YES);
         keyFromBlobTest();
+        keychainTest();
 
         // Put SEP keys into test-keybag mode. Available only when running in direct-mode, not with extension.
         SecKeySetParameter(NULL, kSecAttrTokenIDAppleKeyStore, kCFBooleanTrue, NULL);
 
         // Put SEP keys into test-keybag mode. Available only when running in direct-mode, not with extension.
         SecKeySetParameter(NULL, kSecAttrTokenIDAppleKeyStore, kCFBooleanTrue, NULL);
index f1efbf3f366a3efa27b2232da3f743bc953e72cb..1cfb109bccf3bcb6761d7085cbc2c2211feb2dd8 100644 (file)
@@ -223,6 +223,15 @@ static inline bool isNull(CFTypeRef cfType) {
     return cfType && CFGetTypeID(cfType) == CFNullGetTypeID();
 }
 
     return cfType && CFGetTypeID(cfType) == CFNullGetTypeID();
 }
 
+// Usage: void foo(CFTypeRef value) { CFDataRef data = CFCast(CFData, value); }
+#define CFCast(type, value)                                               \
+    ((value != NULL) && CFGetTypeID(value) == type ## GetTypeID() ? (type ## Ref)(value) : NULL)
+
+#define CFCastWithError(type, value, error)                               \
+    ((value != NULL) && CFGetTypeID(value) == type ## GetTypeID() ?       \
+        (type ## Ref)(value) :                                            \
+        (SecError(errSecParam, error, CFSTR("Unexpected type")), NULL))
+
 //
 // MARK CFEqual Helpers
 //
 //
 // MARK CFEqual Helpers
 //
index 729520a4e4f69db5e29a652101a1c755be38c56d..f09ef95f7031915a577446cbe06f8574a049b558 100644 (file)
@@ -1241,6 +1241,26 @@ void SecDbReleaseAllConnections(SecDbRef db) {
     });
 }
 
     });
 }
 
+// Please make sure you want to do this. Any use of the outstanding connections to this DB will cause a crash.
+void SecDbForceClose(SecDbRef db) {
+    dispatch_sync(db->queue, ^{
+        CFArrayForEach(db->connections, ^(const void* p) {
+            SecDbConnectionRef connection = (SecDbConnectionRef)p;
+
+            // this pointer is claimed to be nonretained
+            connection->db = NULL;
+
+            if(connection->handle) {
+                sqlite3_close(connection->handle);
+                connection->handle = NULL;
+            }
+        });
+        CFArrayRemoveAllValues(db->connections);
+        dispatch_semaphore_signal(db->write_semaphore);
+        dispatch_semaphore_signal(db->read_semaphore);
+    });
+}
+
 bool SecDbPerformRead(SecDbRef db, CFErrorRef *error, void (^perform)(SecDbConnectionRef dbconn)) {
     SecDbConnectionRef dbconn = SecDbConnectionAcquire(db, true, error);
     bool success = false;
 bool SecDbPerformRead(SecDbRef db, CFErrorRef *error, void (^perform)(SecDbConnectionRef dbconn)) {
     SecDbConnectionRef dbconn = SecDbConnectionAcquire(db, true, error);
     bool success = false;
index b3c90402b2d6bec6e68f1ddad4a1b2e3bec0e425..06ea04e4fea3ae8a5a0fe1edbab82c29483c4802 100644 (file)
@@ -125,6 +125,8 @@ bool SecDbPerformWrite(SecDbRef db, CFErrorRef *error, void (^perform)(SecDbConn
 CFIndex SecDbIdleConnectionCount(SecDbRef db);
 void SecDbReleaseAllConnections(SecDbRef db);
 
 CFIndex SecDbIdleConnectionCount(SecDbRef db);
 void SecDbReleaseAllConnections(SecDbRef db);
 
+void SecDbForceClose(SecDbRef db);
+
 CFStringRef SecDbGetPath(SecDbRef db);
 
 // MARK: -
 CFStringRef SecDbGetPath(SecDbRef db);
 
 // MARK: -
index 905eca6d11498a6867f151ff3d1d7bcadaaab56d..4b926795a624cf347bf11f72ae11af1fbf769598 100644 (file)
@@ -12,7 +12,7 @@ NS_ASSUME_NONNULL_BEGIN
 
 @interface SecXPCHelper : NSObject
 + (NSSet<Class> *)safeErrorClasses;
 
 @interface SecXPCHelper : NSObject
 + (NSSet<Class> *)safeErrorClasses;
-+ (NSError *)cleanseErrorForXPC:(NSError * _Nullable)error;
++ (NSError * _Nullable)cleanseErrorForXPC:(NSError * _Nullable)error;
 
 /*
  * Some NSError objects contain non-NSSecureCoding-compliant userInfo.
 
 /*
  * Some NSError objects contain non-NSSecureCoding-compliant userInfo.
index d9d80e096783be5ff9262e87a3ff74fea8fffa7f..5980d1822292de9948dc21c7d0ae52e176da2d5e 100644 (file)
     return NSStringFromClass([object class]);
 }
 
     return NSStringFromClass([object class]);
 }
 
-+ (NSError *)cleanseErrorForXPC:(NSError * _Nullable)error
++ (NSError * _Nullable)cleanseErrorForXPC:(NSError * _Nullable)error
 {
     if (!error) {
         return nil;
 {
     if (!error) {
         return nil;
index a027eee76deae4ad53aa81801fc719fdb43e09d3..a3efacd01c583602fe021a28a8bb7bb5541ec757 100644 (file)
@@ -347,6 +347,7 @@ __OctagonSignpostLogSystem
 
 _OTCliqueStatusToString
 _OTCliqueStatusFromString
 
 _OTCliqueStatusToString
 _OTCliqueStatusFromString
+_OTCDPStatusToString
 
 _OTCliqueCDPContextTypeNone
 _OTCliqueCDPContextTypeSignIn
 
 _OTCliqueCDPContextTypeNone
 _OTCliqueCDPContextTypeSignIn
@@ -2100,6 +2101,7 @@ _SFAnalyticsColumnHardFailureCount
 _SFAnalyticsColumnSoftFailureCount
 _SFAnalyticsColumnSampleValue
 _SFAnalyticsColumnSampleName
 _SFAnalyticsColumnSoftFailureCount
 _SFAnalyticsColumnSampleValue
 _SFAnalyticsColumnSampleName
+_SFAnalyticsPostTime
 _SFAnalyticsEventTime
 _SFAnalyticsEventType
 _SFAnalyticsEventTypeErrorEvent
 _SFAnalyticsEventTime
 _SFAnalyticsEventType
 _SFAnalyticsEventTypeErrorEvent
index fbfb182caf5d83c71c2e9be3162da2127621858e..d469fbf5f1a05a23ebe341bc7d128e7f310c6750 100644 (file)
                        name = Security_frameworks_ios;
                        productName = kernel;
                };
                        name = Security_frameworks_ios;
                        productName = kernel;
                };
+               0CA378E123876DD100090B7E /* reset_account */ = {
+                       isa = PBXAggregateTarget;
+                       buildConfigurationList = 0CA378E323876DD100090B7E /* Build configuration list for PBXAggregateTarget "reset_account" */;
+                       buildPhases = (
+                               0CA378E623876DEC00090B7E /* CopyFiles */,
+                       );
+                       dependencies = (
+                       );
+                       name = reset_account;
+                       productName = codesigning_DTrace;
+               };
                4C541F840F250BF500E508AE /* Security_executables_ios */ = {
                        isa = PBXAggregateTarget;
                        buildConfigurationList = 4C541FA30F250C8C00E508AE /* Build configuration list for PBXAggregateTarget "Security_executables_ios" */;
                4C541F840F250BF500E508AE /* Security_executables_ios */ = {
                        isa = PBXAggregateTarget;
                        buildConfigurationList = 4C541FA30F250C8C00E508AE /* Build configuration list for PBXAggregateTarget "Security_executables_ios" */;
                        buildPhases = (
                        );
                        dependencies = (
                        buildPhases = (
                        );
                        dependencies = (
+                               0CA378EB23876E1000090B7E /* PBXTargetDependency */,
                                D4E0E9BC2224E15500A802E0 /* PBXTargetDependency */,
                                D45D8F5C2224D9F100D6C124 /* PBXTargetDependency */,
                                D45D8F5A2224D8A100D6C124 /* PBXTargetDependency */,
                                D4E0E9BC2224E15500A802E0 /* PBXTargetDependency */,
                                D45D8F5C2224D9F100D6C124 /* PBXTargetDependency */,
                                D45D8F5A2224D8A100D6C124 /* PBXTargetDependency */,
                        buildPhases = (
                        );
                        dependencies = (
                        buildPhases = (
                        );
                        dependencies = (
+                               0CA378E923876E0900090B7E /* PBXTargetDependency */,
                                D4EB53C9223C4AB5009101F8 /* PBXTargetDependency */,
                                D4E0E9BE2224E15E00A802E0 /* PBXTargetDependency */,
                                D45D8F902224DC9900D6C124 /* PBXTargetDependency */,
                                D4EB53C9223C4AB5009101F8 /* PBXTargetDependency */,
                                D4E0E9BE2224E15E00A802E0 /* PBXTargetDependency */,
                                D45D8F902224DC9900D6C124 /* PBXTargetDependency */,
                0C9FB40720D872A600864612 /* CoreCDP.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0C9FB40120D8729A00864612 /* CoreCDP.framework */; };
                0C9FB40920D8735500864612 /* CoreCDP.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0C9FB40120D8729A00864612 /* CoreCDP.framework */; };
                0CA2282F2187A5CA00A1C56C /* BottledPeer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C0C4F84216FB56B00C14C61 /* BottledPeer.swift */; };
                0C9FB40720D872A600864612 /* CoreCDP.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0C9FB40120D8729A00864612 /* CoreCDP.framework */; };
                0C9FB40920D8735500864612 /* CoreCDP.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0C9FB40120D8729A00864612 /* CoreCDP.framework */; };
                0CA2282F2187A5CA00A1C56C /* BottledPeer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C0C4F84216FB56B00C14C61 /* BottledPeer.swift */; };
+               0CA378E723876DFC00090B7E /* reset_ick_account in CopyFiles */ = {isa = PBXBuildFile; fileRef = 0C7382F023863AD5004F98CB /* reset_ick_account */; };
                0CA4B4722171410200B17169 /* EscrowKeys.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C0C4F83216FB55600C14C61 /* EscrowKeys.swift */; };
                0CA4EBF3202B8D9C002B1D96 /* CloudKitKeychainSyncingTestsBase.m in Sources */ = {isa = PBXBuildFile; fileRef = 0CA4EBF2202B8D1D002B1D96 /* CloudKitKeychainSyncingTestsBase.m */; };
                0CA4EBF4202B8DBE002B1D96 /* CloudKitKeychainSyncingTestsBase.m in Sources */ = {isa = PBXBuildFile; fileRef = 0CA4EBF2202B8D1D002B1D96 /* CloudKitKeychainSyncingTestsBase.m */; };
                0CA4B4722171410200B17169 /* EscrowKeys.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C0C4F83216FB55600C14C61 /* EscrowKeys.swift */; };
                0CA4EBF3202B8D9C002B1D96 /* CloudKitKeychainSyncingTestsBase.m in Sources */ = {isa = PBXBuildFile; fileRef = 0CA4EBF2202B8D1D002B1D96 /* CloudKitKeychainSyncingTestsBase.m */; };
                0CA4EBF4202B8DBE002B1D96 /* CloudKitKeychainSyncingTestsBase.m in Sources */ = {isa = PBXBuildFile; fileRef = 0CA4EBF2202B8D1D002B1D96 /* CloudKitKeychainSyncingTestsBase.m */; };
                0CAD1E591E1C5CBD00537693 /* secd-52-offering-gencount-reset.m in Sources */ = {isa = PBXBuildFile; fileRef = DCC78C4F1D8085D800865A7C /* secd-52-offering-gencount-reset.m */; };
                0CAD1E5A1E1C5CD100537693 /* secd-71-engine-save.m in Sources */ = {isa = PBXBuildFile; fileRef = DCC78C641D8085D800865A7C /* secd-71-engine-save.m */; };
                0CAD1E5D1E1C5CF900537693 /* secd-80-views-alwayson.m in Sources */ = {isa = PBXBuildFile; fileRef = 7281E08B1DFD0A380021E1B7 /* secd-80-views-alwayson.m */; };
                0CAD1E591E1C5CBD00537693 /* secd-52-offering-gencount-reset.m in Sources */ = {isa = PBXBuildFile; fileRef = DCC78C4F1D8085D800865A7C /* secd-52-offering-gencount-reset.m */; };
                0CAD1E5A1E1C5CD100537693 /* secd-71-engine-save.m in Sources */ = {isa = PBXBuildFile; fileRef = DCC78C641D8085D800865A7C /* secd-71-engine-save.m */; };
                0CAD1E5D1E1C5CF900537693 /* secd-80-views-alwayson.m in Sources */ = {isa = PBXBuildFile; fileRef = 7281E08B1DFD0A380021E1B7 /* secd-80-views-alwayson.m */; };
-               0CAD1E5E1E1C5D0600537693 /* secd-95-escrow-persistence.m in Sources */ = {isa = PBXBuildFile; fileRef = DCC78C741D8085D800865A7C /* secd-95-escrow-persistence.m */; };
                0CADDF0721545CF100DF8B06 /* OctagonPairingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CADDF0421545C8E00DF8B06 /* OctagonPairingTests.swift */; };
                0CB50A0D20AA486800FE4675 /* SOSAccountTrustClassic+Expansion.m in Sources */ = {isa = PBXBuildFile; fileRef = 0CE760471E12F2F200B4381E /* SOSAccountTrustClassic+Expansion.m */; };
                0CB50A0E20AA4C2F00FE4675 /* SOSAccountTrustClassic+Circle.m in Sources */ = {isa = PBXBuildFile; fileRef = 0CE760491E12F30200B4381E /* SOSAccountTrustClassic+Circle.m */; };
                0CADDF0721545CF100DF8B06 /* OctagonPairingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CADDF0421545C8E00DF8B06 /* OctagonPairingTests.swift */; };
                0CB50A0D20AA486800FE4675 /* SOSAccountTrustClassic+Expansion.m in Sources */ = {isa = PBXBuildFile; fileRef = 0CE760471E12F2F200B4381E /* SOSAccountTrustClassic+Expansion.m */; };
                0CB50A0E20AA4C2F00FE4675 /* SOSAccountTrustClassic+Circle.m in Sources */ = {isa = PBXBuildFile; fileRef = 0CE760491E12F30200B4381E /* SOSAccountTrustClassic+Circle.m */; };
                0CB5C678218B803C0044F730 /* OTPrivateKey+SF.m in Sources */ = {isa = PBXBuildFile; fileRef = 0C3BB3522188E18A0018FC14 /* OTPrivateKey+SF.m */; };
                0CB72D9D21E42FCF00D8BC9B /* OTApplicantToSponsorRound2M1.m in Sources */ = {isa = PBXBuildFile; fileRef = 0C9AE28E214054F6003BFDB5 /* OTApplicantToSponsorRound2M1.m */; };
                0CB72D9E21E42FCF00D8BC9B /* OTPairingMessage.m in Sources */ = {isa = PBXBuildFile; fileRef = 0C9AE2A2214055CF003BFDB5 /* OTPairingMessage.m */; };
                0CB5C678218B803C0044F730 /* OTPrivateKey+SF.m in Sources */ = {isa = PBXBuildFile; fileRef = 0C3BB3522188E18A0018FC14 /* OTPrivateKey+SF.m */; };
                0CB72D9D21E42FCF00D8BC9B /* OTApplicantToSponsorRound2M1.m in Sources */ = {isa = PBXBuildFile; fileRef = 0C9AE28E214054F6003BFDB5 /* OTApplicantToSponsorRound2M1.m */; };
                0CB72D9E21E42FCF00D8BC9B /* OTPairingMessage.m in Sources */ = {isa = PBXBuildFile; fileRef = 0C9AE2A2214055CF003BFDB5 /* OTPairingMessage.m */; };
-               0CB72D9F21E42FCF00D8BC9B /* OTSOSMessage.m in Sources */ = {isa = PBXBuildFile; fileRef = 0CE9C98A21B8891A006BDD80 /* OTSOSMessage.m */; };
                0CB72DA021E42FCF00D8BC9B /* OTSponsorToApplicantRound1M2.m in Sources */ = {isa = PBXBuildFile; fileRef = 0C9AE28D214054F6003BFDB5 /* OTSponsorToApplicantRound1M2.m */; };
                0CB72DA121E42FCF00D8BC9B /* OTSponsorToApplicantRound2M2.m in Sources */ = {isa = PBXBuildFile; fileRef = 0C9AE290214054F7003BFDB5 /* OTSponsorToApplicantRound2M2.m */; };
                0CB8DC9A2194B14C0021A7C8 /* OTVouchWithBottleOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = 0CB8DC992194B1440021A7C8 /* OTVouchWithBottleOperation.m */; };
                0CB72DA021E42FCF00D8BC9B /* OTSponsorToApplicantRound1M2.m in Sources */ = {isa = PBXBuildFile; fileRef = 0C9AE28D214054F6003BFDB5 /* OTSponsorToApplicantRound1M2.m */; };
                0CB72DA121E42FCF00D8BC9B /* OTSponsorToApplicantRound2M2.m in Sources */ = {isa = PBXBuildFile; fileRef = 0C9AE290214054F7003BFDB5 /* OTSponsorToApplicantRound2M2.m */; };
                0CB8DC9A2194B14C0021A7C8 /* OTVouchWithBottleOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = 0CB8DC992194B1440021A7C8 /* OTVouchWithBottleOperation.m */; };
                0CE887D22299A8CF0082D120 /* libMobileGestalt.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = BE53602C209BBF2F0027E25A /* libMobileGestalt.tbd */; };
                0CE887D32299A9090082D120 /* libutilities.a in Frameworks */ = {isa = PBXBuildFile; fileRef = DC0BCC361D8C684F00070CB0 /* libutilities.a */; };
                0CE887D52299A9C70082D120 /* SystemConfiguration.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E71F3E3016EA69A900FAF9B4 /* SystemConfiguration.framework */; };
                0CE887D22299A8CF0082D120 /* libMobileGestalt.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = BE53602C209BBF2F0027E25A /* libMobileGestalt.tbd */; };
                0CE887D32299A9090082D120 /* libutilities.a in Frameworks */ = {isa = PBXBuildFile; fileRef = DC0BCC361D8C684F00070CB0 /* libutilities.a */; };
                0CE887D52299A9C70082D120 /* SystemConfiguration.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E71F3E3016EA69A900FAF9B4 /* SystemConfiguration.framework */; };
+               0CE902352395D0A3005E3F8C /* AuthKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D4A7DD7320A26CF900F51F3F /* AuthKit.framework */; };
                0CF406522072E422003D6A7F /* SFSignInAnalyticsTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 0CF405F42072E2BF003D6A7F /* SFSignInAnalyticsTests.m */; };
                0CF70BD9218BED1000EC3515 /* CuttlefishExtensionWorkaround.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CF70BD6218BECF500EC3515 /* CuttlefishExtensionWorkaround.swift */; };
                0CF70BDA218BEFAE00EC3515 /* CuttlefishExtensionWorkaround.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CF70BD6218BECF500EC3515 /* CuttlefishExtensionWorkaround.swift */; };
                0CF406522072E422003D6A7F /* SFSignInAnalyticsTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 0CF405F42072E2BF003D6A7F /* SFSignInAnalyticsTests.m */; };
                0CF70BD9218BED1000EC3515 /* CuttlefishExtensionWorkaround.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CF70BD6218BECF500EC3515 /* CuttlefishExtensionWorkaround.swift */; };
                0CF70BDA218BEFAE00EC3515 /* CuttlefishExtensionWorkaround.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CF70BD6218BECF500EC3515 /* CuttlefishExtensionWorkaround.swift */; };
                1B5EAADD2252ABCD008D27E7 /* OTFetchViewsOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = 1B5EAADB2252ABCC008D27E7 /* OTFetchViewsOperation.m */; };
                1B8341B92239AD3A002BF18A /* TPPBPolicyKeyViewMapping.proto in Sources */ = {isa = PBXBuildFile; fileRef = 1B8341B72239AD39002BF18A /* TPPBPolicyKeyViewMapping.proto */; };
                1B8D2D96226E1FA500C94238 /* SetValueTransformer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CE15E2A222DF63500B7EAA5 /* SetValueTransformer.swift */; };
                1B5EAADD2252ABCD008D27E7 /* OTFetchViewsOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = 1B5EAADB2252ABCC008D27E7 /* OTFetchViewsOperation.m */; };
                1B8341B92239AD3A002BF18A /* TPPBPolicyKeyViewMapping.proto in Sources */ = {isa = PBXBuildFile; fileRef = 1B8341B72239AD39002BF18A /* TPPBPolicyKeyViewMapping.proto */; };
                1B8D2D96226E1FA500C94238 /* SetValueTransformer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CE15E2A222DF63500B7EAA5 /* SetValueTransformer.swift */; };
-               1B916CD0223FFF25006657FD /* ProtocolBuffer.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6C0B0C441E2537CC007F95E5 /* ProtocolBuffer.framework */; };
                1B995259226681FA00A2D6CD /* PolicyReporter.h in Sources */ = {isa = PBXBuildFile; fileRef = 1B995256226681ED00A2D6CD /* PolicyReporter.h */; };
                1B99525A226681FA00A2D6CD /* PolicyReporter.m in Sources */ = {isa = PBXBuildFile; fileRef = 1B995258226681EE00A2D6CD /* PolicyReporter.m */; };
                1BB1CAB7232C05BD001D0C71 /* CuttlefishXPCWrapper.m in Sources */ = {isa = PBXBuildFile; fileRef = 1BB1CAB4232C05BB001D0C71 /* CuttlefishXPCWrapper.m */; };
                1B995259226681FA00A2D6CD /* PolicyReporter.h in Sources */ = {isa = PBXBuildFile; fileRef = 1B995256226681ED00A2D6CD /* PolicyReporter.h */; };
                1B99525A226681FA00A2D6CD /* PolicyReporter.m in Sources */ = {isa = PBXBuildFile; fileRef = 1B995258226681EE00A2D6CD /* PolicyReporter.m */; };
                1BB1CAB7232C05BD001D0C71 /* CuttlefishXPCWrapper.m in Sources */ = {isa = PBXBuildFile; fileRef = 1BB1CAB4232C05BB001D0C71 /* CuttlefishXPCWrapper.m */; };
                483E798F1DC87605005C0008 /* secd-67-prefixedKeyIDs.m in Sources */ = {isa = PBXBuildFile; fileRef = 483E79891DC875F2005C0008 /* secd-67-prefixedKeyIDs.m */; };
                48AC7B73232B1DA600F02B6F /* SOSIntervalEvent.m in Sources */ = {isa = PBXBuildFile; fileRef = 48AC7B71232B1A7000F02B6F /* SOSIntervalEvent.m */; };
                48CC589F1DA5FF2700EBD9DB /* secd-66-account-recovery.m in Sources */ = {isa = PBXBuildFile; fileRef = 48CC58971DA5FF0B00EBD9DB /* secd-66-account-recovery.m */; };
                483E798F1DC87605005C0008 /* secd-67-prefixedKeyIDs.m in Sources */ = {isa = PBXBuildFile; fileRef = 483E79891DC875F2005C0008 /* secd-67-prefixedKeyIDs.m */; };
                48AC7B73232B1DA600F02B6F /* SOSIntervalEvent.m in Sources */ = {isa = PBXBuildFile; fileRef = 48AC7B71232B1A7000F02B6F /* SOSIntervalEvent.m */; };
                48CC589F1DA5FF2700EBD9DB /* secd-66-account-recovery.m in Sources */ = {isa = PBXBuildFile; fileRef = 48CC58971DA5FF0B00EBD9DB /* secd-66-account-recovery.m */; };
-               48E617211DBEC6BA0098EAAD /* SOSBackupInformation.m in Sources */ = {isa = PBXBuildFile; fileRef = 48E6171A1DBEC40D0098EAAD /* SOSBackupInformation.m */; };
-               48E617221DBEC6C60098EAAD /* SOSBackupInformation.h in Headers */ = {isa = PBXBuildFile; fileRef = 48E6171B1DBEC40D0098EAAD /* SOSBackupInformation.h */; };
                48FE669620E6E69D00FAEF17 /* SOSAuthKitHelpers.m in Sources */ = {isa = PBXBuildFile; fileRef = 48FE668F20E6E69B00FAEF17 /* SOSAuthKitHelpers.m */; };
                48FE669720E6E69D00FAEF17 /* SOSAuthKitHelpers.h in Headers */ = {isa = PBXBuildFile; fileRef = 48FE669520E6E69C00FAEF17 /* SOSAuthKitHelpers.h */; };
                4AF7000115AFB73800B9D400 /* SecOTRMath.h in Headers */ = {isa = PBXBuildFile; fileRef = 4AF7FFF715AFB73800B9D400 /* SecOTRMath.h */; settings = {ATTRIBUTES = (Private, ); }; };
                48FE669620E6E69D00FAEF17 /* SOSAuthKitHelpers.m in Sources */ = {isa = PBXBuildFile; fileRef = 48FE668F20E6E69B00FAEF17 /* SOSAuthKitHelpers.m */; };
                48FE669720E6E69D00FAEF17 /* SOSAuthKitHelpers.h in Headers */ = {isa = PBXBuildFile; fileRef = 48FE669520E6E69C00FAEF17 /* SOSAuthKitHelpers.h */; };
                4AF7000115AFB73800B9D400 /* SecOTRMath.h in Headers */ = {isa = PBXBuildFile; fileRef = 4AF7FFF715AFB73800B9D400 /* SecOTRMath.h */; settings = {ATTRIBUTES = (Private, ); }; };
                4C7391790B01745000C4CBFA /* vmdh.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C7391770B01745000C4CBFA /* vmdh.h */; settings = {ATTRIBUTES = (Private, ); }; };
                4C7416040F1D71A2008E0E4D /* SecSCEP.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C7416020F1D71A2008E0E4D /* SecSCEP.h */; settings = {ATTRIBUTES = (Private, ); }; };
                4C7608B30AC34A8100980096 /* SecCertificatePriv.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C7608B10AC34A8100980096 /* SecCertificatePriv.h */; settings = {ATTRIBUTES = (Private, ); }; };
                4C7391790B01745000C4CBFA /* vmdh.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C7391770B01745000C4CBFA /* vmdh.h */; settings = {ATTRIBUTES = (Private, ); }; };
                4C7416040F1D71A2008E0E4D /* SecSCEP.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C7416020F1D71A2008E0E4D /* SecSCEP.h */; settings = {ATTRIBUTES = (Private, ); }; };
                4C7608B30AC34A8100980096 /* SecCertificatePriv.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C7608B10AC34A8100980096 /* SecCertificatePriv.h */; settings = {ATTRIBUTES = (Private, ); }; };
-               4C7913251799A5CC00A9633E /* MobileCoreServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4C7913241799A5CB00A9633E /* MobileCoreServices.framework */; };
                4C7CE5700DC7DC6600AE53FC /* SecEntitlements.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C7CE56E0DC7DB0A00AE53FC /* SecEntitlements.h */; settings = {ATTRIBUTES = (Private, ); }; };
                4C84DA551720698900AEE225 /* AppleAccount.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4C84DA541720698900AEE225 /* AppleAccount.framework */; };
                4C87F3A80D611C26000E7104 /* SecTrustPriv.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C87F3A70D611C26000E7104 /* SecTrustPriv.h */; settings = {ATTRIBUTES = (Private, ); }; };
                4C7CE5700DC7DC6600AE53FC /* SecEntitlements.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C7CE56E0DC7DB0A00AE53FC /* SecEntitlements.h */; settings = {ATTRIBUTES = (Private, ); }; };
                4C84DA551720698900AEE225 /* AppleAccount.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4C84DA541720698900AEE225 /* AppleAccount.framework */; };
                4C87F3A80D611C26000E7104 /* SecTrustPriv.h in Headers */ = {isa = PBXBuildFile; fileRef = 4C87F3A70D611C26000E7104 /* SecTrustPriv.h */; settings = {ATTRIBUTES = (Private, ); }; };
                52A23EDD161DEC3F00E271E0 /* Default-568h@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 52A23EDB161DEC3700E271E0 /* Default-568h@2x.png */; };
                52D82BDF16A621F70078DFE5 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E7FCBE431314471B000DE34E /* Foundation.framework */; };
                52D82BEE16A622370078DFE5 /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 52D82BD316A5EADA0078DFE5 /* Security.framework */; };
                52A23EDD161DEC3F00E271E0 /* Default-568h@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 52A23EDB161DEC3700E271E0 /* Default-568h@2x.png */; };
                52D82BDF16A621F70078DFE5 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E7FCBE431314471B000DE34E /* Foundation.framework */; };
                52D82BEE16A622370078DFE5 /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 52D82BD316A5EADA0078DFE5 /* Security.framework */; };
+               52DA3C7123C7E63600FEEDFF /* KCTLKRequestTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 52DA3C6F23C7E63500FEEDFF /* KCTLKRequestTest.m */; };
                5328C0521738903F00708984 /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4C32C0AF0A4975F6002891BD /* Security.framework */; };
                533B5D4F177CD63100995334 /* SpringBoardServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 52222CC0167BDAE100EDD09C /* SpringBoardServices.framework */; };
                5346480217331E1200FE9172 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E7FCBE431314471B000DE34E /* Foundation.framework */; };
                5328C0521738903F00708984 /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4C32C0AF0A4975F6002891BD /* Security.framework */; };
                533B5D4F177CD63100995334 /* SpringBoardServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 52222CC0167BDAE100EDD09C /* SpringBoardServices.framework */; };
                5346480217331E1200FE9172 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E7FCBE431314471B000DE34E /* Foundation.framework */; };
                D4707A2D2114C1E8005BCFDA /* SecCmsContentInfo.h in Headers */ = {isa = PBXBuildFile; fileRef = D4707A2B2114B31A005BCFDA /* SecCmsContentInfo.h */; settings = {ATTRIBUTES = (Private, ); }; };
                D4707A2F2114C315005BCFDA /* SecCmsDigestContext.h in Headers */ = {isa = PBXBuildFile; fileRef = D4707A2E2114C30A005BCFDA /* SecCmsDigestContext.h */; settings = {ATTRIBUTES = (Private, ); }; };
                D4707A302114C316005BCFDA /* SecCmsDigestContext.h in Headers */ = {isa = PBXBuildFile; fileRef = D4707A2E2114C30A005BCFDA /* SecCmsDigestContext.h */; settings = {ATTRIBUTES = (Private, ); }; };
                D4707A2D2114C1E8005BCFDA /* SecCmsContentInfo.h in Headers */ = {isa = PBXBuildFile; fileRef = D4707A2B2114B31A005BCFDA /* SecCmsContentInfo.h */; settings = {ATTRIBUTES = (Private, ); }; };
                D4707A2F2114C315005BCFDA /* SecCmsDigestContext.h in Headers */ = {isa = PBXBuildFile; fileRef = D4707A2E2114C30A005BCFDA /* SecCmsDigestContext.h */; settings = {ATTRIBUTES = (Private, ); }; };
                D4707A302114C316005BCFDA /* SecCmsDigestContext.h in Headers */ = {isa = PBXBuildFile; fileRef = D4707A2E2114C30A005BCFDA /* SecCmsDigestContext.h */; settings = {ATTRIBUTES = (Private, ); }; };
+               D477CB78237E482800C02355 /* si-88-sectrust-valid-data in Resources */ = {isa = PBXBuildFile; fileRef = D477CB76237E453C00C02355 /* si-88-sectrust-valid-data */; };
+               D477CB79237E484300C02355 /* si-88-sectrust-valid-data in Resources */ = {isa = PBXBuildFile; fileRef = D477CB76237E453C00C02355 /* si-88-sectrust-valid-data */; };
+               D477CB7B237E4BD700C02355 /* ExceptionTests.m in Sources */ = {isa = PBXBuildFile; fileRef = D477CB7A237E4BD700C02355 /* ExceptionTests.m */; };
+               D477CB7C237E4BD700C02355 /* ExceptionTests.m in Sources */ = {isa = PBXBuildFile; fileRef = D477CB7A237E4BD700C02355 /* ExceptionTests.m */; };
                D479F6E21F980FAB00388D28 /* Trust.strings in Resources */ = {isa = PBXBuildFile; fileRef = D479F6DF1F980F8F00388D28 /* Trust.strings */; };
                D479F6E31F981FD600388D28 /* OID.strings in Resources */ = {isa = PBXBuildFile; fileRef = 4C198F1F0ACDB4BF00AAB142 /* OID.strings */; };
                D479F6E41F981FD600388D28 /* Certificate.strings in Resources */ = {isa = PBXBuildFile; fileRef = 4C198F1D0ACDB4BF00AAB142 /* Certificate.strings */; };
                D479F6E21F980FAB00388D28 /* Trust.strings in Resources */ = {isa = PBXBuildFile; fileRef = D479F6DF1F980F8F00388D28 /* Trust.strings */; };
                D479F6E31F981FD600388D28 /* OID.strings in Resources */ = {isa = PBXBuildFile; fileRef = 4C198F1F0ACDB4BF00AAB142 /* OID.strings */; };
                D479F6E41F981FD600388D28 /* Certificate.strings in Resources */ = {isa = PBXBuildFile; fileRef = 4C198F1D0ACDB4BF00AAB142 /* Certificate.strings */; };
                D4B68C66211A8186009FED69 /* trustd_spi.h in Headers */ = {isa = PBXBuildFile; fileRef = D4B68C64211A8186009FED69 /* trustd_spi.h */; };
                D4B68C68211A827C009FED69 /* trustd_spi.c in Sources */ = {isa = PBXBuildFile; fileRef = D4B68C65211A8186009FED69 /* trustd_spi.c */; };
                D4B6D57C2069D8450099FBEF /* si-34-cms-timestamp.m in Sources */ = {isa = PBXBuildFile; fileRef = D4B6D57B2069D8450099FBEF /* si-34-cms-timestamp.m */; };
                D4B68C66211A8186009FED69 /* trustd_spi.h in Headers */ = {isa = PBXBuildFile; fileRef = D4B68C64211A8186009FED69 /* trustd_spi.h */; };
                D4B68C68211A827C009FED69 /* trustd_spi.c in Sources */ = {isa = PBXBuildFile; fileRef = D4B68C65211A8186009FED69 /* trustd_spi.c */; };
                D4B6D57C2069D8450099FBEF /* si-34-cms-timestamp.m in Sources */ = {isa = PBXBuildFile; fileRef = D4B6D57B2069D8450099FBEF /* si-34-cms-timestamp.m */; };
-               D4B858671D370D9A003B2D95 /* MobileCoreServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D4B858661D370D9A003B2D95 /* MobileCoreServices.framework */; };
                D4BD5E83228A6823001650A7 /* util.m in Sources */ = {isa = PBXBuildFile; fileRef = 1BC6F79621C9955D005ED67A /* util.m */; };
                D4BD5E85228A6854001650A7 /* util.m in Sources */ = {isa = PBXBuildFile; fileRef = 1BC6F79621C9955D005ED67A /* util.m */; };
                D4BD5E86228A6855001650A7 /* util.m in Sources */ = {isa = PBXBuildFile; fileRef = 1BC6F79621C9955D005ED67A /* util.m */; };
                D4BD5E83228A6823001650A7 /* util.m in Sources */ = {isa = PBXBuildFile; fileRef = 1BC6F79621C9955D005ED67A /* util.m */; };
                D4BD5E85228A6854001650A7 /* util.m in Sources */ = {isa = PBXBuildFile; fileRef = 1BC6F79621C9955D005ED67A /* util.m */; };
                D4BD5E86228A6855001650A7 /* util.m in Sources */ = {isa = PBXBuildFile; fileRef = 1BC6F79621C9955D005ED67A /* util.m */; };
                DC00C92420B3B82600628BEB /* libz.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = D4119E72202BDF2B0048587B /* libz.tbd */; };
                DC00C93420B48B4100628BEB /* SystemConfiguration.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E71F3E3016EA69A900FAF9B4 /* SystemConfiguration.framework */; };
                DC00C93520B48BA800628BEB /* IOKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4CBCE5A90BE7F69100FF81F5 /* IOKit.framework */; };
                DC00C92420B3B82600628BEB /* libz.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = D4119E72202BDF2B0048587B /* libz.tbd */; };
                DC00C93420B48B4100628BEB /* SystemConfiguration.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E71F3E3016EA69A900FAF9B4 /* SystemConfiguration.framework */; };
                DC00C93520B48BA800628BEB /* IOKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4CBCE5A90BE7F69100FF81F5 /* IOKit.framework */; };
+               DC03592D235FCCD500F14883 /* KCInitialMessageData.m in Sources */ = {isa = PBXBuildFile; fileRef = 5A47FFB5228F5E9000F781B8 /* KCInitialMessageData.m */; };
                DC047081218BB21E0078BDAA /* OTCuttlefishAccountStateHolder.h in Headers */ = {isa = PBXBuildFile; fileRef = DC04707F218BB21E0078BDAA /* OTCuttlefishAccountStateHolder.h */; };
                DC047082218BB21E0078BDAA /* OTCuttlefishAccountStateHolder.m in Sources */ = {isa = PBXBuildFile; fileRef = DC047080218BB21E0078BDAA /* OTCuttlefishAccountStateHolder.m */; };
                DC047087218BCEF20078BDAA /* OTOperationDependencies.h in Headers */ = {isa = PBXBuildFile; fileRef = DC047085218BCEF20078BDAA /* OTOperationDependencies.h */; };
                DC047081218BB21E0078BDAA /* OTCuttlefishAccountStateHolder.h in Headers */ = {isa = PBXBuildFile; fileRef = DC04707F218BB21E0078BDAA /* OTCuttlefishAccountStateHolder.h */; };
                DC047082218BB21E0078BDAA /* OTCuttlefishAccountStateHolder.m in Sources */ = {isa = PBXBuildFile; fileRef = DC047080218BB21E0078BDAA /* OTCuttlefishAccountStateHolder.m */; };
                DC047087218BCEF20078BDAA /* OTOperationDependencies.h in Headers */ = {isa = PBXBuildFile; fileRef = DC047085218BCEF20078BDAA /* OTOperationDependencies.h */; };
                DC0BD4F621BB0610006B9154 /* CKKSKeychainBackedKey.m in Sources */ = {isa = PBXBuildFile; fileRef = DC0BD4F021BB05F2006B9154 /* CKKSKeychainBackedKey.m */; };
                DC0C343821FA7DEB00417D04 /* SecEscrowRequest.h in Headers */ = {isa = PBXBuildFile; fileRef = DC90A4C021F27680001300EB /* SecEscrowRequest.h */; settings = {ATTRIBUTES = (Private, ); }; };
                DC0C343A21FA7DEB00417D04 /* SecEscrowRequest.h in Headers */ = {isa = PBXBuildFile; fileRef = DC90A4C021F27680001300EB /* SecEscrowRequest.h */; settings = {ATTRIBUTES = (Private, ); }; };
                DC0BD4F621BB0610006B9154 /* CKKSKeychainBackedKey.m in Sources */ = {isa = PBXBuildFile; fileRef = DC0BD4F021BB05F2006B9154 /* CKKSKeychainBackedKey.m */; };
                DC0C343821FA7DEB00417D04 /* SecEscrowRequest.h in Headers */ = {isa = PBXBuildFile; fileRef = DC90A4C021F27680001300EB /* SecEscrowRequest.h */; settings = {ATTRIBUTES = (Private, ); }; };
                DC0C343A21FA7DEB00417D04 /* SecEscrowRequest.h in Headers */ = {isa = PBXBuildFile; fileRef = DC90A4C021F27680001300EB /* SecEscrowRequest.h */; settings = {ATTRIBUTES = (Private, ); }; };
+               DC0D16012363A1D6007F0951 /* OTSetCDPBitOperation.h in Headers */ = {isa = PBXBuildFile; fileRef = DC0D15FF2363A1D6007F0951 /* OTSetCDPBitOperation.h */; };
+               DC0D16022363A1D6007F0951 /* OTSetCDPBitOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = DC0D16002363A1D6007F0951 /* OTSetCDPBitOperation.m */; };
+               DC0D16062363BAF4007F0951 /* OTDetermineCDPBitStatusOperation.h in Headers */ = {isa = PBXBuildFile; fileRef = DC0D16042363BAF4007F0951 /* OTDetermineCDPBitStatusOperation.h */; };
+               DC0D16072363BAF4007F0951 /* OTDetermineCDPBitStatusOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = DC0D16052363BAF4007F0951 /* OTDetermineCDPBitStatusOperation.m */; };
+               DC0DE87123750340006E2EAE /* OTPairingMessage.m in Sources */ = {isa = PBXBuildFile; fileRef = 0C9AE2A2214055CF003BFDB5 /* OTPairingMessage.m */; };
                DC0EF8F2208697C600AB9E95 /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC0EF8F1208697C600AB9E95 /* main.swift */; };
                DC0FA6B02291F63F00FE01C4 /* OctagonPendingFlag.h in Headers */ = {isa = PBXBuildFile; fileRef = DC0FA6AE2291F63F00FE01C4 /* OctagonPendingFlag.h */; };
                DC0FA6B12291F63F00FE01C4 /* OctagonPendingFlag.m in Sources */ = {isa = PBXBuildFile; fileRef = DC0FA6AF2291F63F00FE01C4 /* OctagonPendingFlag.m */; };
                DC0EF8F2208697C600AB9E95 /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC0EF8F1208697C600AB9E95 /* main.swift */; };
                DC0FA6B02291F63F00FE01C4 /* OctagonPendingFlag.h in Headers */ = {isa = PBXBuildFile; fileRef = DC0FA6AE2291F63F00FE01C4 /* OctagonPendingFlag.h */; };
                DC0FA6B12291F63F00FE01C4 /* OctagonPendingFlag.m in Sources */ = {isa = PBXBuildFile; fileRef = DC0FA6AF2291F63F00FE01C4 /* OctagonPendingFlag.m */; };
                DC14478A1F5764C600236DB4 /* CKKSResultOperation.h in Headers */ = {isa = PBXBuildFile; fileRef = DC1447881F5764C600236DB4 /* CKKSResultOperation.h */; };
                DC14478C1F5764C600236DB4 /* CKKSResultOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = DC1447891F5764C600236DB4 /* CKKSResultOperation.m */; };
                DC1447961F5766D200236DB4 /* NSOperationCategories.h in Headers */ = {isa = PBXBuildFile; fileRef = DC1447941F5766D200236DB4 /* NSOperationCategories.h */; };
                DC14478A1F5764C600236DB4 /* CKKSResultOperation.h in Headers */ = {isa = PBXBuildFile; fileRef = DC1447881F5764C600236DB4 /* CKKSResultOperation.h */; };
                DC14478C1F5764C600236DB4 /* CKKSResultOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = DC1447891F5764C600236DB4 /* CKKSResultOperation.m */; };
                DC1447961F5766D200236DB4 /* NSOperationCategories.h in Headers */ = {isa = PBXBuildFile; fileRef = DC1447941F5766D200236DB4 /* NSOperationCategories.h */; };
+               DC14C4C223AAACED007F673F /* Container_BottledPeers.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC3A9B2523A9D6120073ED06 /* Container_BottledPeers.swift */; };
                DC15F7661E67A6F6003B9A40 /* CKKSHealKeyHierarchyOperation.h in Headers */ = {isa = PBXBuildFile; fileRef = DC15F7641E67A6F6003B9A40 /* CKKSHealKeyHierarchyOperation.h */; };
                DC15F7681E67A6F6003B9A40 /* CKKSHealKeyHierarchyOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = DC15F7651E67A6F6003B9A40 /* CKKSHealKeyHierarchyOperation.m */; };
                DC15F79C1E68EAD5003B9A40 /* CKKSTests+API.m in Sources */ = {isa = PBXBuildFile; fileRef = DC15F79B1E68EAD5003B9A40 /* CKKSTests+API.m */; };
                DC15F7661E67A6F6003B9A40 /* CKKSHealKeyHierarchyOperation.h in Headers */ = {isa = PBXBuildFile; fileRef = DC15F7641E67A6F6003B9A40 /* CKKSHealKeyHierarchyOperation.h */; };
                DC15F7681E67A6F6003B9A40 /* CKKSHealKeyHierarchyOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = DC15F7651E67A6F6003B9A40 /* CKKSHealKeyHierarchyOperation.m */; };
                DC15F79C1E68EAD5003B9A40 /* CKKSTests+API.m in Sources */ = {isa = PBXBuildFile; fileRef = DC15F79B1E68EAD5003B9A40 /* CKKSTests+API.m */; };
                DC311E7A2124B8EF002F5EAE /* aks_real_witness.h in Headers */ = {isa = PBXBuildFile; fileRef = DC311E782124B8EF002F5EAE /* aks_real_witness.h */; };
                DC311E7B2124B8EF002F5EAE /* aks_real_witness.c in Sources */ = {isa = PBXBuildFile; fileRef = DC311E792124B8EF002F5EAE /* aks_real_witness.c */; };
                DC337B1F1EA04E2100B3A1F0 /* SecBase64.h in Headers */ = {isa = PBXBuildFile; fileRef = 18351B8F14CB65870097860E /* SecBase64.h */; settings = {ATTRIBUTES = (Private, ); }; };
                DC311E7A2124B8EF002F5EAE /* aks_real_witness.h in Headers */ = {isa = PBXBuildFile; fileRef = DC311E782124B8EF002F5EAE /* aks_real_witness.h */; };
                DC311E7B2124B8EF002F5EAE /* aks_real_witness.c in Sources */ = {isa = PBXBuildFile; fileRef = DC311E792124B8EF002F5EAE /* aks_real_witness.c */; };
                DC337B1F1EA04E2100B3A1F0 /* SecBase64.h in Headers */ = {isa = PBXBuildFile; fileRef = 18351B8F14CB65870097860E /* SecBase64.h */; settings = {ATTRIBUTES = (Private, ); }; };
+               DC33D7BD2374FD0500A68155 /* OTSponsorToApplicantRound1M2.m in Sources */ = {isa = PBXBuildFile; fileRef = 0C9AE28D214054F6003BFDB5 /* OTSponsorToApplicantRound1M2.m */; };
+               DC33D7BE2374FD0A00A68155 /* OTSponsorToApplicantRound2M2.m in Sources */ = {isa = PBXBuildFile; fileRef = 0C9AE290214054F7003BFDB5 /* OTSponsorToApplicantRound2M2.m */; };
                DC340C54208E828F004D7EEC /* TrustedPeers.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BEF88C281EAFFC3F00357577 /* TrustedPeers.framework */; };
                DC3502B81E0208BE00BC0587 /* CKKSTests.m in Sources */ = {isa = PBXBuildFile; fileRef = DC3502B71E0208BE00BC0587 /* CKKSTests.m */; };
                DC3502C51E020D5100BC0587 /* libASN1.a in Frameworks */ = {isa = PBXBuildFile; fileRef = DC8834081D8A218F00CE0ACA /* libASN1.a */; };
                DC340C54208E828F004D7EEC /* TrustedPeers.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BEF88C281EAFFC3F00357577 /* TrustedPeers.framework */; };
                DC3502B81E0208BE00BC0587 /* CKKSTests.m in Sources */ = {isa = PBXBuildFile; fileRef = DC3502B71E0208BE00BC0587 /* CKKSTests.m */; };
                DC3502C51E020D5100BC0587 /* libASN1.a in Frameworks */ = {isa = PBXBuildFile; fileRef = DC8834081D8A218F00CE0ACA /* libASN1.a */; };
                DC3A81D61D99D57F000C7419 /* libcoretls.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 0CFC029B1D41650700E6283B /* libcoretls.dylib */; };
                DC3A81D71D99D58A000C7419 /* libcoretls_cfhelpers.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = DC3A81D41D99D567000C7419 /* libcoretls_cfhelpers.dylib */; };
                DC3A81EC1D99F568000C7419 /* libcoretls.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 0CFC029B1D41650700E6283B /* libcoretls.dylib */; };
                DC3A81D61D99D57F000C7419 /* libcoretls.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 0CFC029B1D41650700E6283B /* libcoretls.dylib */; };
                DC3A81D71D99D58A000C7419 /* libcoretls_cfhelpers.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = DC3A81D41D99D567000C7419 /* libcoretls_cfhelpers.dylib */; };
                DC3A81EC1D99F568000C7419 /* libcoretls.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 0CFC029B1D41650700E6283B /* libcoretls.dylib */; };
+               DC3A9B2723A9D8BD0073ED06 /* Container_BottledPeers.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC3A9B2523A9D6120073ED06 /* Container_BottledPeers.swift */; };
+               DC3A9B2823A9D8C40073ED06 /* Container_BottledPeers.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC3A9B2523A9D6120073ED06 /* Container_BottledPeers.swift */; };
                DC3AA2782097DF70007CA68A /* readline.c in Sources */ = {isa = PBXBuildFile; fileRef = DC65E7BE1D8CBB1500152EF0 /* readline.c */; };
                DC3AA2792097DF71007CA68A /* readline.c in Sources */ = {isa = PBXBuildFile; fileRef = DC65E7BE1D8CBB1500152EF0 /* readline.c */; };
                DC3AA27A2097DF84007CA68A /* not_on_this_platorm.c in Sources */ = {isa = PBXBuildFile; fileRef = DC0BCDB41D8C6A5B00070CB0 /* not_on_this_platorm.c */; };
                DC3AA2782097DF70007CA68A /* readline.c in Sources */ = {isa = PBXBuildFile; fileRef = DC65E7BE1D8CBB1500152EF0 /* readline.c */; };
                DC3AA2792097DF71007CA68A /* readline.c in Sources */ = {isa = PBXBuildFile; fileRef = DC65E7BE1D8CBB1500152EF0 /* readline.c */; };
                DC3AA27A2097DF84007CA68A /* not_on_this_platorm.c in Sources */ = {isa = PBXBuildFile; fileRef = DC0BCDB41D8C6A5B00070CB0 /* not_on_this_platorm.c */; };
                DC4269101E82FD9F002B7110 /* server_security_helpers.m in Sources */ = {isa = PBXBuildFile; fileRef = DC4269061E82FBDF002B7110 /* server_security_helpers.m */; };
                DC4269111E82FDA0002B7110 /* server_security_helpers.m in Sources */ = {isa = PBXBuildFile; fileRef = DC4269061E82FBDF002B7110 /* server_security_helpers.m */; };
                DC4269121E82FDA1002B7110 /* server_security_helpers.m in Sources */ = {isa = PBXBuildFile; fileRef = DC4269061E82FBDF002B7110 /* server_security_helpers.m */; };
                DC4269101E82FD9F002B7110 /* server_security_helpers.m in Sources */ = {isa = PBXBuildFile; fileRef = DC4269061E82FBDF002B7110 /* server_security_helpers.m */; };
                DC4269111E82FDA0002B7110 /* server_security_helpers.m in Sources */ = {isa = PBXBuildFile; fileRef = DC4269061E82FBDF002B7110 /* server_security_helpers.m */; };
                DC4269121E82FDA1002B7110 /* server_security_helpers.m in Sources */ = {isa = PBXBuildFile; fileRef = DC4269061E82FBDF002B7110 /* server_security_helpers.m */; };
+               DC4415B423610BF40087981C /* OctagonTests+Account.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC4415B323610BF40087981C /* OctagonTests+Account.swift */; };
                DC45D43D22EB619D00CEB6B7 /* OctagonStateMachineObservers.h in Headers */ = {isa = PBXBuildFile; fileRef = DC45D43B22EB619D00CEB6B7 /* OctagonStateMachineObservers.h */; };
                DC45D43E22EB619D00CEB6B7 /* OctagonStateMachineObservers.m in Sources */ = {isa = PBXBuildFile; fileRef = DC45D43C22EB619D00CEB6B7 /* OctagonStateMachineObservers.m */; };
                DC45D43D22EB619D00CEB6B7 /* OctagonStateMachineObservers.h in Headers */ = {isa = PBXBuildFile; fileRef = DC45D43B22EB619D00CEB6B7 /* OctagonStateMachineObservers.h */; };
                DC45D43E22EB619D00CEB6B7 /* OctagonStateMachineObservers.m in Sources */ = {isa = PBXBuildFile; fileRef = DC45D43C22EB619D00CEB6B7 /* OctagonStateMachineObservers.m */; };
+               DC4A73C5235E69D800DB1E6E /* OTApplicantToSponsorRound2M1.m in Sources */ = {isa = PBXBuildFile; fileRef = 0C9AE28E214054F6003BFDB5 /* OTApplicantToSponsorRound2M1.m */; };
                DC4A76A02212676D006F2D8F /* CloudServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4C8A38C817B93DF10001B4C0 /* CloudServices.framework */; };
                DC4A76A3221267D4006F2D8F /* EscrowRequestServerHelpers.m in Sources */ = {isa = PBXBuildFile; fileRef = DC4A76A2221267D4006F2D8F /* EscrowRequestServerHelpers.m */; };
                DC4A76A5221268A6006F2D8F /* CloudServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4C8A38C817B93DF10001B4C0 /* CloudServices.framework */; };
                DC4A76A02212676D006F2D8F /* CloudServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4C8A38C817B93DF10001B4C0 /* CloudServices.framework */; };
                DC4A76A3221267D4006F2D8F /* EscrowRequestServerHelpers.m in Sources */ = {isa = PBXBuildFile; fileRef = DC4A76A2221267D4006F2D8F /* EscrowRequestServerHelpers.m */; };
                DC4A76A5221268A6006F2D8F /* CloudServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4C8A38C817B93DF10001B4C0 /* CloudServices.framework */; };
                DC4A76AC221269E4006F2D8F /* CloudServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4C8A38C817B93DF10001B4C0 /* CloudServices.framework */; };
                DC4A76AD22126A17006F2D8F /* CloudServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4C8A38C817B93DF10001B4C0 /* CloudServices.framework */; };
                DC4A76AE22126C49006F2D8F /* CloudServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4C8A38C817B93DF10001B4C0 /* CloudServices.framework */; };
                DC4A76AC221269E4006F2D8F /* CloudServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4C8A38C817B93DF10001B4C0 /* CloudServices.framework */; };
                DC4A76AD22126A17006F2D8F /* CloudServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4C8A38C817B93DF10001B4C0 /* CloudServices.framework */; };
                DC4A76AE22126C49006F2D8F /* CloudServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4C8A38C817B93DF10001B4C0 /* CloudServices.framework */; };
+               DC4CD9842372294E00EF55FC /* OctagonTests+Helpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC4CD9822372294D00EF55FC /* OctagonTests+Helpers.swift */; };
                DC4DB1501E24692100CD6769 /* CKKSKey.h in Headers */ = {isa = PBXBuildFile; fileRef = DC4DB14E1E24692100CD6769 /* CKKSKey.h */; };
                DC4DB1521E24692100CD6769 /* CKKSKey.m in Sources */ = {isa = PBXBuildFile; fileRef = DC4DB14F1E24692100CD6769 /* CKKSKey.m */; };
                DC4DB15F1E2590B100CD6769 /* CKKSAESSIVEncryptionTests.m in Sources */ = {isa = PBXBuildFile; fileRef = DC4DB15E1E2590B100CD6769 /* CKKSAESSIVEncryptionTests.m */; };
                DC4DB1501E24692100CD6769 /* CKKSKey.h in Headers */ = {isa = PBXBuildFile; fileRef = DC4DB14E1E24692100CD6769 /* CKKSKey.h */; };
                DC4DB1521E24692100CD6769 /* CKKSKey.m in Sources */ = {isa = PBXBuildFile; fileRef = DC4DB14F1E24692100CD6769 /* CKKSKey.m */; };
                DC4DB15F1E2590B100CD6769 /* CKKSAESSIVEncryptionTests.m in Sources */ = {isa = PBXBuildFile; fileRef = DC4DB15E1E2590B100CD6769 /* CKKSAESSIVEncryptionTests.m */; };
                DC52EC381D80CFDB00B0A59C /* secToolFileIO.c in Sources */ = {isa = PBXBuildFile; fileRef = DCC78D931D8085F200865A7C /* secToolFileIO.c */; };
                DC52EC391D80CFDF00B0A59C /* secViewDisplay.c in Sources */ = {isa = PBXBuildFile; fileRef = DCC78D9E1D8085F200865A7C /* secViewDisplay.c */; };
                DC52EC3A1D80CFE400B0A59C /* keychain_log.m in Sources */ = {isa = PBXBuildFile; fileRef = DCC78D9B1D8085F200865A7C /* keychain_log.m */; };
                DC52EC381D80CFDB00B0A59C /* secToolFileIO.c in Sources */ = {isa = PBXBuildFile; fileRef = DCC78D931D8085F200865A7C /* secToolFileIO.c */; };
                DC52EC391D80CFDF00B0A59C /* secViewDisplay.c in Sources */ = {isa = PBXBuildFile; fileRef = DCC78D9E1D8085F200865A7C /* secViewDisplay.c */; };
                DC52EC3A1D80CFE400B0A59C /* keychain_log.m in Sources */ = {isa = PBXBuildFile; fileRef = DCC78D9B1D8085F200865A7C /* keychain_log.m */; };
-               DC52EC3B1D80CFE900B0A59C /* syncbackup.m in Sources */ = {isa = PBXBuildFile; fileRef = DCC78D9D1D8085F200865A7C /* syncbackup.m */; };
                DC52EC4E1D80D01F00B0A59C /* swcagent_client.c in Sources */ = {isa = PBXBuildFile; fileRef = DCC78EA01D80860C00865A7C /* swcagent_client.c */; };
                DC52EC4F1D80D02400B0A59C /* SecuritydXPC.c in Sources */ = {isa = PBXBuildFile; fileRef = DCC78E9A1D8085FC00865A7C /* SecuritydXPC.c */; };
                DC52EC5D1D80D06300B0A59C /* SecLogging.c in Sources */ = {isa = PBXBuildFile; fileRef = DCC78E651D8085FC00865A7C /* SecLogging.c */; };
                DC52EC4E1D80D01F00B0A59C /* swcagent_client.c in Sources */ = {isa = PBXBuildFile; fileRef = DCC78EA01D80860C00865A7C /* swcagent_client.c */; };
                DC52EC4F1D80D02400B0A59C /* SecuritydXPC.c in Sources */ = {isa = PBXBuildFile; fileRef = DCC78E9A1D8085FC00865A7C /* SecuritydXPC.c */; };
                DC52EC5D1D80D06300B0A59C /* SecLogging.c in Sources */ = {isa = PBXBuildFile; fileRef = DCC78E651D8085FC00865A7C /* SecLogging.c */; };
                DC52EC731D80D12E00B0A59C /* sc-20-keynames.m in Sources */ = {isa = PBXBuildFile; fileRef = DCC78CFD1D8085F200865A7C /* sc-20-keynames.m */; };
                DC52EC741D80D13500B0A59C /* SOSTestDataSource.c in Sources */ = {isa = PBXBuildFile; fileRef = DCC78D0C1D8085F200865A7C /* SOSTestDataSource.c */; };
                DC52EC751D80D13B00B0A59C /* sc-42-circlegencount.c in Sources */ = {isa = PBXBuildFile; fileRef = DCC78D021D8085F200865A7C /* sc-42-circlegencount.c */; };
                DC52EC731D80D12E00B0A59C /* sc-20-keynames.m in Sources */ = {isa = PBXBuildFile; fileRef = DCC78CFD1D8085F200865A7C /* sc-20-keynames.m */; };
                DC52EC741D80D13500B0A59C /* SOSTestDataSource.c in Sources */ = {isa = PBXBuildFile; fileRef = DCC78D0C1D8085F200865A7C /* SOSTestDataSource.c */; };
                DC52EC751D80D13B00B0A59C /* sc-42-circlegencount.c in Sources */ = {isa = PBXBuildFile; fileRef = DCC78D021D8085F200865A7C /* sc-42-circlegencount.c */; };
-               DC52EC761D80D13F00B0A59C /* sc-150-ring.m in Sources */ = {isa = PBXBuildFile; fileRef = DCC78D061D8085F200865A7C /* sc-150-ring.m */; };
                DC52EC771D80D14400B0A59C /* sc-130-resignationticket.c in Sources */ = {isa = PBXBuildFile; fileRef = DCC78D041D8085F200865A7C /* sc-130-resignationticket.c */; };
                DC52EC781D80D14800B0A59C /* SOSRegressionUtilities.m in Sources */ = {isa = PBXBuildFile; fileRef = DCC78D0A1D8085F200865A7C /* SOSRegressionUtilities.m */; };
                DC52EC791D80D14D00B0A59C /* sc-45-digestvector.c in Sources */ = {isa = PBXBuildFile; fileRef = DCC78D031D8085F200865A7C /* sc-45-digestvector.c */; };
                DC52EC771D80D14400B0A59C /* sc-130-resignationticket.c in Sources */ = {isa = PBXBuildFile; fileRef = DCC78D041D8085F200865A7C /* sc-130-resignationticket.c */; };
                DC52EC781D80D14800B0A59C /* SOSRegressionUtilities.m in Sources */ = {isa = PBXBuildFile; fileRef = DCC78D0A1D8085F200865A7C /* SOSRegressionUtilities.m */; };
                DC52EC791D80D14D00B0A59C /* sc-45-digestvector.c in Sources */ = {isa = PBXBuildFile; fileRef = DCC78D031D8085F200865A7C /* sc-45-digestvector.c */; };
                DC52ECE81D80D2FA00B0A59C /* pbkdf2-00-hmac-sha1.c in Sources */ = {isa = PBXBuildFile; fileRef = DCC78DA41D8085FC00865A7C /* pbkdf2-00-hmac-sha1.c */; };
                DC52ECE91D80D2FA00B0A59C /* spbkdf-00-hmac-sha1.c in Sources */ = {isa = PBXBuildFile; fileRef = DCC78DA51D8085FC00865A7C /* spbkdf-00-hmac-sha1.c */; };
                DC52ECEA1D80D30900B0A59C /* so_01_serverencryption.c in Sources */ = {isa = PBXBuildFile; fileRef = DCC78E171D8085FC00865A7C /* so_01_serverencryption.c */; };
                DC52ECE81D80D2FA00B0A59C /* pbkdf2-00-hmac-sha1.c in Sources */ = {isa = PBXBuildFile; fileRef = DCC78DA41D8085FC00865A7C /* pbkdf2-00-hmac-sha1.c */; };
                DC52ECE91D80D2FA00B0A59C /* spbkdf-00-hmac-sha1.c in Sources */ = {isa = PBXBuildFile; fileRef = DCC78DA51D8085FC00865A7C /* spbkdf-00-hmac-sha1.c */; };
                DC52ECEA1D80D30900B0A59C /* so_01_serverencryption.c in Sources */ = {isa = PBXBuildFile; fileRef = DCC78E171D8085FC00865A7C /* so_01_serverencryption.c */; };
-               DC52ED9E1D80D4ED00B0A59C /* secd-95-escrow-persistence.m in Sources */ = {isa = PBXBuildFile; fileRef = DCC78C741D8085D800865A7C /* secd-95-escrow-persistence.m */; };
                DC52ED9F1D80D4F200B0A59C /* SOSTransportTestTransports.m in Sources */ = {isa = PBXBuildFile; fileRef = DCC78C7C1D8085D800865A7C /* SOSTransportTestTransports.m */; };
                DC52EDA01D80D4F700B0A59C /* sd-10-policytree.m in Sources */ = {isa = PBXBuildFile; fileRef = DCC78C3D1D8085D800865A7C /* sd-10-policytree.m */; };
                DC52EDB51D80D5C500B0A59C /* secd-03-corrupted-items.m in Sources */ = {isa = PBXBuildFile; fileRef = DCC78C391D8085D800865A7C /* secd-03-corrupted-items.m */; };
                DC52ED9F1D80D4F200B0A59C /* SOSTransportTestTransports.m in Sources */ = {isa = PBXBuildFile; fileRef = DCC78C7C1D8085D800865A7C /* SOSTransportTestTransports.m */; };
                DC52EDA01D80D4F700B0A59C /* sd-10-policytree.m in Sources */ = {isa = PBXBuildFile; fileRef = DCC78C3D1D8085D800865A7C /* sd-10-policytree.m */; };
                DC52EDB51D80D5C500B0A59C /* secd-03-corrupted-items.m in Sources */ = {isa = PBXBuildFile; fileRef = DCC78C391D8085D800865A7C /* secd-03-corrupted-items.m */; };
                DC52EE4A1D80D71900B0A59C /* si-24-sectrust-itms.c in Sources */ = {isa = PBXBuildFile; fileRef = DCC78DC31D8085FC00865A7C /* si-24-sectrust-itms.c */; };
                DC52EE4C1D80D71900B0A59C /* si-24-sectrust-passbook.c in Sources */ = {isa = PBXBuildFile; fileRef = DCC78DC51D8085FC00865A7C /* si-24-sectrust-passbook.c */; };
                DC52EE4D1D80D71900B0A59C /* si-26-sectrust-copyproperties.c in Sources */ = {isa = PBXBuildFile; fileRef = DCC78DC61D8085FC00865A7C /* si-26-sectrust-copyproperties.c */; };
                DC52EE4A1D80D71900B0A59C /* si-24-sectrust-itms.c in Sources */ = {isa = PBXBuildFile; fileRef = DCC78DC31D8085FC00865A7C /* si-24-sectrust-itms.c */; };
                DC52EE4C1D80D71900B0A59C /* si-24-sectrust-passbook.c in Sources */ = {isa = PBXBuildFile; fileRef = DCC78DC51D8085FC00865A7C /* si-24-sectrust-passbook.c */; };
                DC52EE4D1D80D71900B0A59C /* si-26-sectrust-copyproperties.c in Sources */ = {isa = PBXBuildFile; fileRef = DCC78DC61D8085FC00865A7C /* si-26-sectrust-copyproperties.c */; };
-               DC52EE4E1D80D71900B0A59C /* si-27-sectrust-exceptions.c in Sources */ = {isa = PBXBuildFile; fileRef = DCC78DC71D8085FC00865A7C /* si-27-sectrust-exceptions.c */; };
                DC52EE4F1D80D71900B0A59C /* si-28-sectrustsettings.m in Sources */ = {isa = PBXBuildFile; fileRef = DCC78DC81D8085FC00865A7C /* si-28-sectrustsettings.m */; };
                DC52EE531D80D73800B0A59C /* si-44-seckey-gen.m in Sources */ = {isa = PBXBuildFile; fileRef = DCC78DD31D8085FC00865A7C /* si-44-seckey-gen.m */; };
                DC52EE541D80D73800B0A59C /* si-44-seckey-rsa.m in Sources */ = {isa = PBXBuildFile; fileRef = DCC78DD41D8085FC00865A7C /* si-44-seckey-rsa.m */; };
                DC52EE4F1D80D71900B0A59C /* si-28-sectrustsettings.m in Sources */ = {isa = PBXBuildFile; fileRef = DCC78DC81D8085FC00865A7C /* si-28-sectrustsettings.m */; };
                DC52EE531D80D73800B0A59C /* si-44-seckey-gen.m in Sources */ = {isa = PBXBuildFile; fileRef = DCC78DD31D8085FC00865A7C /* si-44-seckey-gen.m */; };
                DC52EE541D80D73800B0A59C /* si-44-seckey-rsa.m in Sources */ = {isa = PBXBuildFile; fileRef = DCC78DD41D8085FC00865A7C /* si-44-seckey-rsa.m */; };
                DC797E1A1DD3F9A400CC9E42 /* CKKSSQLDatabaseObject.m in Sources */ = {isa = PBXBuildFile; fileRef = DC797E131DD3F88300CC9E42 /* CKKSSQLDatabaseObject.m */; };
                DC7A17ED1E36ABC200EF14CE /* CKKSProcessReceivedKeysOperation.h in Headers */ = {isa = PBXBuildFile; fileRef = DC7A17EB1E36ABC200EF14CE /* CKKSProcessReceivedKeysOperation.h */; };
                DC7A17EF1E36ABC200EF14CE /* CKKSProcessReceivedKeysOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = DC7A17EC1E36ABC200EF14CE /* CKKSProcessReceivedKeysOperation.m */; };
                DC797E1A1DD3F9A400CC9E42 /* CKKSSQLDatabaseObject.m in Sources */ = {isa = PBXBuildFile; fileRef = DC797E131DD3F88300CC9E42 /* CKKSSQLDatabaseObject.m */; };
                DC7A17ED1E36ABC200EF14CE /* CKKSProcessReceivedKeysOperation.h in Headers */ = {isa = PBXBuildFile; fileRef = DC7A17EB1E36ABC200EF14CE /* CKKSProcessReceivedKeysOperation.h */; };
                DC7A17EF1E36ABC200EF14CE /* CKKSProcessReceivedKeysOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = DC7A17EC1E36ABC200EF14CE /* CKKSProcessReceivedKeysOperation.m */; };
+               DC7F6A7D233D7FAC00DF5769 /* OctagonTests+ForwardCompatibility.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC7F6A7C233D7FAC00DF5769 /* OctagonTests+ForwardCompatibility.swift */; };
                DC7F79B622EA4ED4001FB69A /* OctagonTests+CKKS.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC7F79B522EA4ED4001FB69A /* OctagonTests+CKKS.swift */; };
                DC7F79BA22EA5C73001FB69A /* OTLocalCKKSResetOperation.h in Headers */ = {isa = PBXBuildFile; fileRef = DC7F79B822EA5C72001FB69A /* OTLocalCKKSResetOperation.h */; };
                DC7F79BB22EA5C73001FB69A /* OTLocalCKKSResetOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = DC7F79B922EA5C72001FB69A /* OTLocalCKKSResetOperation.m */; };
                DC7F79B622EA4ED4001FB69A /* OctagonTests+CKKS.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC7F79B522EA4ED4001FB69A /* OctagonTests+CKKS.swift */; };
                DC7F79BA22EA5C73001FB69A /* OTLocalCKKSResetOperation.h in Headers */ = {isa = PBXBuildFile; fileRef = DC7F79B822EA5C72001FB69A /* OTLocalCKKSResetOperation.h */; };
                DC7F79BB22EA5C73001FB69A /* OTLocalCKKSResetOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = DC7F79B922EA5C72001FB69A /* OTLocalCKKSResetOperation.m */; };
                DC8834931D8A21AB00CE0ACA /* oidsattr.c in Sources */ = {isa = PBXBuildFile; fileRef = DC88344B1D8A21AA00CE0ACA /* oidsattr.c */; };
                DC8834961D8A21AB00CE0ACA /* oidsocsp.c in Sources */ = {isa = PBXBuildFile; fileRef = DC88344E1D8A21AA00CE0ACA /* oidsocsp.c */; };
                DC88466B22373A5E00738068 /* TPPBDictionaryMatchingRule.proto in Sources */ = {isa = PBXBuildFile; fileRef = DC88466922373A4000738068 /* TPPBDictionaryMatchingRule.proto */; };
                DC8834931D8A21AB00CE0ACA /* oidsattr.c in Sources */ = {isa = PBXBuildFile; fileRef = DC88344B1D8A21AA00CE0ACA /* oidsattr.c */; };
                DC8834961D8A21AB00CE0ACA /* oidsocsp.c in Sources */ = {isa = PBXBuildFile; fileRef = DC88344E1D8A21AA00CE0ACA /* oidsocsp.c */; };
                DC88466B22373A5E00738068 /* TPPBDictionaryMatchingRule.proto in Sources */ = {isa = PBXBuildFile; fileRef = DC88466922373A4000738068 /* TPPBDictionaryMatchingRule.proto */; };
+               DC89608D2395C75600D339D9 /* CoreServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DC89608C2395C75500D339D9 /* CoreServices.framework */; };
+               DC89608E2395C76300D339D9 /* CoreServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DC89608C2395C75500D339D9 /* CoreServices.framework */; };
                DC8D238D2064649400E163C8 /* CKKSAPSHandlingTests.m in Sources */ = {isa = PBXBuildFile; fileRef = DC8D238C2064649400E163C8 /* CKKSAPSHandlingTests.m */; };
                DC8DF6DC212F8A7C007B3FE8 /* OTSOSAdapter.m in Sources */ = {isa = PBXBuildFile; fileRef = DCB9475C2127562100ED9272 /* OTSOSAdapter.m */; };
                DC8DF6DF212F8A7D007B3FE8 /* OTSOSAdapter.m in Sources */ = {isa = PBXBuildFile; fileRef = DCB9475C2127562100ED9272 /* OTSOSAdapter.m */; };
                DC8D238D2064649400E163C8 /* CKKSAPSHandlingTests.m in Sources */ = {isa = PBXBuildFile; fileRef = DC8D238C2064649400E163C8 /* CKKSAPSHandlingTests.m */; };
                DC8DF6DC212F8A7C007B3FE8 /* OTSOSAdapter.m in Sources */ = {isa = PBXBuildFile; fileRef = DCB9475C2127562100ED9272 /* OTSOSAdapter.m */; };
                DC8DF6DF212F8A7D007B3FE8 /* OTSOSAdapter.m in Sources */ = {isa = PBXBuildFile; fileRef = DCB9475C2127562100ED9272 /* OTSOSAdapter.m */; };
                DCA9D84821FFEE9800B27421 /* EscrowRequestXPCProtocol.m in Sources */ = {isa = PBXBuildFile; fileRef = DCA9D84521FFE7CF00B27421 /* EscrowRequestXPCProtocol.m */; };
                DCA9D84D21FFF04700B27421 /* EscrowRequestServer.h in Headers */ = {isa = PBXBuildFile; fileRef = DCA9D84B21FFF04600B27421 /* EscrowRequestServer.h */; };
                DCA9D84E21FFF04700B27421 /* EscrowRequestServer.m in Sources */ = {isa = PBXBuildFile; fileRef = DCA9D84C21FFF04700B27421 /* EscrowRequestServer.m */; };
                DCA9D84821FFEE9800B27421 /* EscrowRequestXPCProtocol.m in Sources */ = {isa = PBXBuildFile; fileRef = DCA9D84521FFE7CF00B27421 /* EscrowRequestXPCProtocol.m */; };
                DCA9D84D21FFF04700B27421 /* EscrowRequestServer.h in Headers */ = {isa = PBXBuildFile; fileRef = DCA9D84B21FFF04600B27421 /* EscrowRequestServer.h */; };
                DCA9D84E21FFF04700B27421 /* EscrowRequestServer.m in Sources */ = {isa = PBXBuildFile; fileRef = DCA9D84C21FFF04700B27421 /* EscrowRequestServer.m */; };
+               DCAA209A23AAF8F600DCB594 /* Container_RecoveryKey.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCAA209823AAF8BD00DCB594 /* Container_RecoveryKey.swift */; };
+               DCAA209B23AAF8FD00DCB594 /* Container_RecoveryKey.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCAA209823AAF8BD00DCB594 /* Container_RecoveryKey.swift */; };
+               DCAA209C23AAF93700DCB594 /* Container_RecoveryKey.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCAA209823AAF8BD00DCB594 /* Container_RecoveryKey.swift */; };
                DCAB14271E40039600C81511 /* libASN1.a in Frameworks */ = {isa = PBXBuildFile; fileRef = DC8834081D8A218F00CE0ACA /* libASN1.a */; };
                DCAB17CE21FFF75B00E1DFCF /* MockSynchronousEscrowServer.m in Sources */ = {isa = PBXBuildFile; fileRef = DCAB17CC21FFF6C400E1DFCF /* MockSynchronousEscrowServer.m */; };
                DCAB17D12200D26900E1DFCF /* SecEscrowPendingRecord.h in Headers */ = {isa = PBXBuildFile; fileRef = DCAB17CF2200D26700E1DFCF /* SecEscrowPendingRecord.h */; };
                DCAB14271E40039600C81511 /* libASN1.a in Frameworks */ = {isa = PBXBuildFile; fileRef = DC8834081D8A218F00CE0ACA /* libASN1.a */; };
                DCAB17CE21FFF75B00E1DFCF /* MockSynchronousEscrowServer.m in Sources */ = {isa = PBXBuildFile; fileRef = DCAB17CC21FFF6C400E1DFCF /* MockSynchronousEscrowServer.m */; };
                DCAB17D12200D26900E1DFCF /* SecEscrowPendingRecord.h in Headers */ = {isa = PBXBuildFile; fileRef = DCAB17CF2200D26700E1DFCF /* SecEscrowPendingRecord.h */; };
                DCC093801D80B0B700F984E4 /* SecCFAllocator.h in Headers */ = {isa = PBXBuildFile; fileRef = D47F514B1C3B812500A7CEFE /* SecCFAllocator.h */; };
                DCC19518214C53FD00C9E0B6 /* AuthKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D4A7DD7320A26CF900F51F3F /* AuthKit.framework */; };
                DCC1951C214C668A00C9E0B6 /* AppleAccount.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4C84DA541720698900AEE225 /* AppleAccount.framework */; settings = {ATTRIBUTES = (Weak, ); }; };
                DCC093801D80B0B700F984E4 /* SecCFAllocator.h in Headers */ = {isa = PBXBuildFile; fileRef = D47F514B1C3B812500A7CEFE /* SecCFAllocator.h */; };
                DCC19518214C53FD00C9E0B6 /* AuthKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D4A7DD7320A26CF900F51F3F /* AuthKit.framework */; };
                DCC1951C214C668A00C9E0B6 /* AppleAccount.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4C84DA541720698900AEE225 /* AppleAccount.framework */; settings = {ATTRIBUTES = (Weak, ); }; };
-               DCC19F711EB9151B00B7D70F /* KeychainCKKS.plist in Copy BATS Test Discovery Plist */ = {isa = PBXBuildFile; fileRef = 6CB5F4781E402E5700DBF3F0 /* KeychainCKKS.plist */; };
                DCC51C99209B7C1500A40387 /* print_cert.c in Sources */ = {isa = PBXBuildFile; fileRef = DC52EA951D80CC2A00B0A59C /* print_cert.c */; };
                DCC54181225C05180095D926 /* OTUploadNewCKKSTLKsOperation.h in Headers */ = {isa = PBXBuildFile; fileRef = DCC5417F225C05170095D926 /* OTUploadNewCKKSTLKsOperation.h */; };
                DCC54182225C05180095D926 /* OTUploadNewCKKSTLKsOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = DCC54180225C05180095D926 /* OTUploadNewCKKSTLKsOperation.m */; };
                DCC51C99209B7C1500A40387 /* print_cert.c in Sources */ = {isa = PBXBuildFile; fileRef = DC52EA951D80CC2A00B0A59C /* print_cert.c */; };
                DCC54181225C05180095D926 /* OTUploadNewCKKSTLKsOperation.h in Headers */ = {isa = PBXBuildFile; fileRef = DCC5417F225C05170095D926 /* OTUploadNewCKKSTLKsOperation.h */; };
                DCC54182225C05180095D926 /* OTUploadNewCKKSTLKsOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = DCC54180225C05180095D926 /* OTUploadNewCKKSTLKsOperation.m */; };
                DCE278DF1ED789EF0083B485 /* CKKSCurrentItemPointer.m in Sources */ = {isa = PBXBuildFile; fileRef = DCE278DC1ED789EF0083B485 /* CKKSCurrentItemPointer.m */; };
                DCE278E81ED7A5B40083B485 /* CKKSUpdateCurrentItemPointerOperation.h in Headers */ = {isa = PBXBuildFile; fileRef = DCE278E61ED7A5B40083B485 /* CKKSUpdateCurrentItemPointerOperation.h */; };
                DCE278EA1ED7A5B40083B485 /* CKKSUpdateCurrentItemPointerOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = DCE278E71ED7A5B40083B485 /* CKKSUpdateCurrentItemPointerOperation.m */; };
                DCE278DF1ED789EF0083B485 /* CKKSCurrentItemPointer.m in Sources */ = {isa = PBXBuildFile; fileRef = DCE278DC1ED789EF0083B485 /* CKKSCurrentItemPointer.m */; };
                DCE278E81ED7A5B40083B485 /* CKKSUpdateCurrentItemPointerOperation.h in Headers */ = {isa = PBXBuildFile; fileRef = DCE278E61ED7A5B40083B485 /* CKKSUpdateCurrentItemPointerOperation.h */; };
                DCE278EA1ED7A5B40083B485 /* CKKSUpdateCurrentItemPointerOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = DCE278E71ED7A5B40083B485 /* CKKSUpdateCurrentItemPointerOperation.m */; };
+               DCE405C523A04A7F00C4343B /* OctagonTests+CKKSConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCE405C423A04A7F00C4343B /* OctagonTests+CKKSConfiguration.swift */; };
                DCE4E6921D7A37FA00AFB96E /* security_tool_commands.c in Sources */ = {isa = PBXBuildFile; fileRef = E7104A0B169E171900DB0045 /* security_tool_commands.c */; };
                DCE4E6931D7A37FA00AFB96E /* NSFileHandle+Formatting.m in Sources */ = {isa = PBXBuildFile; fileRef = E78A9AD91D34959200006B5B /* NSFileHandle+Formatting.m */; };
                DCE4E6961D7A37FA00AFB96E /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5E43C48C1B00D07000E5ECB2 /* CoreFoundation.framework */; };
                DCE4E6921D7A37FA00AFB96E /* security_tool_commands.c in Sources */ = {isa = PBXBuildFile; fileRef = E7104A0B169E171900DB0045 /* security_tool_commands.c */; };
                DCE4E6931D7A37FA00AFB96E /* NSFileHandle+Formatting.m in Sources */ = {isa = PBXBuildFile; fileRef = E78A9AD91D34959200006B5B /* NSFileHandle+Formatting.m */; };
                DCE4E6961D7A37FA00AFB96E /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5E43C48C1B00D07000E5ECB2 /* CoreFoundation.framework */; };
                EB5E3BCC2003C67A00F1631B /* SecSignpost.h in Headers */ = {isa = PBXBuildFile; fileRef = EB5E3BC62003C66300F1631B /* SecSignpost.h */; settings = {ATTRIBUTES = (Private, ); }; };
                EB5E3BCD2003C67B00F1631B /* SecSignpost.h in Headers */ = {isa = PBXBuildFile; fileRef = EB5E3BC62003C66300F1631B /* SecSignpost.h */; settings = {ATTRIBUTES = (Private, ); }; };
                EB627A73233E339200F32437 /* MockAKSOptionalParameters.proto in Sources */ = {isa = PBXBuildFile; fileRef = EB627A6F233E323600F32437 /* MockAKSOptionalParameters.proto */; };
                EB5E3BCC2003C67A00F1631B /* SecSignpost.h in Headers */ = {isa = PBXBuildFile; fileRef = EB5E3BC62003C66300F1631B /* SecSignpost.h */; settings = {ATTRIBUTES = (Private, ); }; };
                EB5E3BCD2003C67B00F1631B /* SecSignpost.h in Headers */ = {isa = PBXBuildFile; fileRef = EB5E3BC62003C66300F1631B /* SecSignpost.h */; settings = {ATTRIBUTES = (Private, ); }; };
                EB627A73233E339200F32437 /* MockAKSOptionalParameters.proto in Sources */ = {isa = PBXBuildFile; fileRef = EB627A6F233E323600F32437 /* MockAKSOptionalParameters.proto */; };
-               EB627A79233E375A00F32437 /* MockAKSOptionalParameters.proto in Sources */ = {isa = PBXBuildFile; fileRef = EB627A6F233E323600F32437 /* MockAKSOptionalParameters.proto */; };
                EB627A7E233E3C1300F32437 /* MockAKSOptionalParameters.m in Sources */ = {isa = PBXBuildFile; fileRef = EB627A77233E342B00F32437 /* MockAKSOptionalParameters.m */; };
                EB627A7F233E3C1600F32437 /* MockAKSRefKey.m in Sources */ = {isa = PBXBuildFile; fileRef = EB627A75233E342800F32437 /* MockAKSRefKey.m */; };
                EB6667C7204CD69F000B404F /* testPlistDER.m in Sources */ = {isa = PBXBuildFile; fileRef = EB6667BE204CD65E000B404F /* testPlistDER.m */; };
                EB627A7E233E3C1300F32437 /* MockAKSOptionalParameters.m in Sources */ = {isa = PBXBuildFile; fileRef = EB627A77233E342B00F32437 /* MockAKSOptionalParameters.m */; };
                EB627A7F233E3C1600F32437 /* MockAKSRefKey.m in Sources */ = {isa = PBXBuildFile; fileRef = EB627A75233E342800F32437 /* MockAKSRefKey.m */; };
                EB6667C7204CD69F000B404F /* testPlistDER.m in Sources */ = {isa = PBXBuildFile; fileRef = EB6667BE204CD65E000B404F /* testPlistDER.m */; };
                EBD531772198AF19003A57E6 /* Accounts.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4CF4C19C171E0EA600877419 /* Accounts.framework */; };
                EBDAA7E920EC4838003EA6E5 /* SecurityLocalKeychain.plist in Install BATS plist */ = {isa = PBXBuildFile; fileRef = EBDAA7E320EC46CF003EA6E5 /* SecurityLocalKeychain.plist */; };
                EBDAF15D21C75FF200EAE89F /* NSXPCConnectionMock.h in Headers */ = {isa = PBXBuildFile; fileRef = EBDAF15B21C75FF200EAE89F /* NSXPCConnectionMock.h */; };
                EBD531772198AF19003A57E6 /* Accounts.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4CF4C19C171E0EA600877419 /* Accounts.framework */; };
                EBDAA7E920EC4838003EA6E5 /* SecurityLocalKeychain.plist in Install BATS plist */ = {isa = PBXBuildFile; fileRef = EBDAA7E320EC46CF003EA6E5 /* SecurityLocalKeychain.plist */; };
                EBDAF15D21C75FF200EAE89F /* NSXPCConnectionMock.h in Headers */ = {isa = PBXBuildFile; fileRef = EBDAF15B21C75FF200EAE89F /* NSXPCConnectionMock.h */; };
-               EBDCC001233DD3E000806566 /* MockAKSRefKey.proto in Sources */ = {isa = PBXBuildFile; fileRef = EBDCBFFE233DD31700806566 /* MockAKSRefKey.proto */; };
                EBDCC002233DD45700806566 /* MockAKSRefKey.proto in Sources */ = {isa = PBXBuildFile; fileRef = EBDCBFFE233DD31700806566 /* MockAKSRefKey.proto */; };
                EBDE5E0E22BA3DE900A229C8 /* CKKSMockOctagonAdapter.m in Sources */ = {isa = PBXBuildFile; fileRef = EBDE5DFA22BA3D5D00A229C8 /* CKKSMockOctagonAdapter.m */; };
                EBDE5E0F22BA3DEA00A229C8 /* CKKSMockOctagonAdapter.m in Sources */ = {isa = PBXBuildFile; fileRef = EBDE5DFA22BA3D5D00A229C8 /* CKKSMockOctagonAdapter.m */; };
                EBDCC002233DD45700806566 /* MockAKSRefKey.proto in Sources */ = {isa = PBXBuildFile; fileRef = EBDCBFFE233DD31700806566 /* MockAKSRefKey.proto */; };
                EBDE5E0E22BA3DE900A229C8 /* CKKSMockOctagonAdapter.m in Sources */ = {isa = PBXBuildFile; fileRef = EBDE5DFA22BA3D5D00A229C8 /* CKKSMockOctagonAdapter.m */; };
                EBDE5E0F22BA3DEA00A229C8 /* CKKSMockOctagonAdapter.m in Sources */ = {isa = PBXBuildFile; fileRef = EBDE5DFA22BA3D5D00A229C8 /* CKKSMockOctagonAdapter.m */; };
                        remoteGlobalIDString = DC1789031D77980500B50D50;
                        remoteInfo = Security_osx;
                };
                        remoteGlobalIDString = DC1789031D77980500B50D50;
                        remoteInfo = Security_osx;
                };
+               0CA378E823876E0900090B7E /* PBXContainerItemProxy */ = {
+                       isa = PBXContainerItemProxy;
+                       containerPortal = 4C35DB69094F906D002917C4 /* Project object */;
+                       proxyType = 1;
+                       remoteGlobalIDString = 0CA378E123876DD100090B7E;
+                       remoteInfo = reset_account;
+               };
+               0CA378EA23876E1000090B7E /* PBXContainerItemProxy */ = {
+                       isa = PBXContainerItemProxy;
+                       containerPortal = 4C35DB69094F906D002917C4 /* Project object */;
+                       proxyType = 1;
+                       remoteGlobalIDString = 0CA378E123876DD100090B7E;
+                       remoteInfo = reset_account;
+               };
                0CC593F72299EDFC006C34B5 /* PBXContainerItemProxy */ = {
                        isa = PBXContainerItemProxy;
                        containerPortal = 4C35DB69094F906D002917C4 /* Project object */;
                0CC593F72299EDFC006C34B5 /* PBXContainerItemProxy */ = {
                        isa = PBXContainerItemProxy;
                        containerPortal = 4C35DB69094F906D002917C4 /* Project object */;
                        name = "Embed OCMock";
                        runOnlyForDeploymentPostprocessing = 0;
                };
                        name = "Embed OCMock";
                        runOnlyForDeploymentPostprocessing = 0;
                };
+               0CA378E623876DEC00090B7E /* CopyFiles */ = {
+                       isa = PBXCopyFilesBuildPhase;
+                       buildActionMask = 8;
+                       dstPath = /usr/local/bin;
+                       dstSubfolderSpec = 0;
+                       files = (
+                               0CA378E723876DFC00090B7E /* reset_ick_account in CopyFiles */,
+                       );
+                       runOnlyForDeploymentPostprocessing = 1;
+               };
                0CF4064A2072E3E3003D6A7F /* Embed OCMock */ = {
                        isa = PBXCopyFilesBuildPhase;
                        buildActionMask = 2147483647;
                0CF4064A2072E3E3003D6A7F /* Embed OCMock */ = {
                        isa = PBXCopyFilesBuildPhase;
                        buildActionMask = 2147483647;
                        );
                        runOnlyForDeploymentPostprocessing = 1;
                };
                        );
                        runOnlyForDeploymentPostprocessing = 1;
                };
-               DC7162D41EB4154D000D2BB5 /* Copy BATS Test Discovery Plist */ = {
-                       isa = PBXCopyFilesBuildPhase;
-                       buildActionMask = 8;
-                       dstPath = /AppleInternal/CoreOS/BATS/unit_tests;
-                       dstSubfolderSpec = 0;
-                       files = (
-                               DCC19F711EB9151B00B7D70F /* KeychainCKKS.plist in Copy BATS Test Discovery Plist */,
-                       );
-                       name = "Copy BATS Test Discovery Plist";
-                       runOnlyForDeploymentPostprocessing = 1;
-               };
                DC7FC44F21EE9175003C39B8 /* Install Security FeatureFlags plist */ = {
                        isa = PBXCopyFilesBuildPhase;
                        buildActionMask = 8;
                DC7FC44F21EE9175003C39B8 /* Install Security FeatureFlags plist */ = {
                        isa = PBXCopyFilesBuildPhase;
                        buildActionMask = 8;
                0C664AB2175926B20092D3D9 /* secdtests-entitlements.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "secdtests-entitlements.plist"; sourceTree = "<group>"; };
                0C6C2B682258211800C53C96 /* AppleAccount.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppleAccount.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS13.0.Internal.sdk/System/Library/PrivateFrameworks/AppleAccount.framework; sourceTree = DEVELOPER_DIR; };
                0C6C2B6C2258295D00C53C96 /* UIKitCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKitCore.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS13.0.Internal.sdk/System/Library/PrivateFrameworks/UIKitCore.framework; sourceTree = DEVELOPER_DIR; };
                0C664AB2175926B20092D3D9 /* secdtests-entitlements.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "secdtests-entitlements.plist"; sourceTree = "<group>"; };
                0C6C2B682258211800C53C96 /* AppleAccount.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppleAccount.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS13.0.Internal.sdk/System/Library/PrivateFrameworks/AppleAccount.framework; sourceTree = DEVELOPER_DIR; };
                0C6C2B6C2258295D00C53C96 /* UIKitCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKitCore.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS13.0.Internal.sdk/System/Library/PrivateFrameworks/UIKitCore.framework; sourceTree = DEVELOPER_DIR; };
+               0C7382F023863AD5004F98CB /* reset_ick_account */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.python; path = reset_ick_account; sourceTree = "<group>"; };
                0C75AC642141F18D0073A2F9 /* KeychainCircle.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = KeychainCircle.framework; path = System/Library/PrivateFrameworks/KeychainCircle.framework; sourceTree = SDKROOT; };
                0C78F1C916A5E13400654E08 /* sectask_regressions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = sectask_regressions.h; sourceTree = "<group>"; };
                0C78F1CA16A5E1BF00654E08 /* sectask-10-sectask.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "sectask-10-sectask.c"; sourceTree = "<group>"; };
                0C75AC642141F18D0073A2F9 /* KeychainCircle.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = KeychainCircle.framework; path = System/Library/PrivateFrameworks/KeychainCircle.framework; sourceTree = SDKROOT; };
                0C78F1C916A5E13400654E08 /* sectask_regressions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = sectask_regressions.h; sourceTree = "<group>"; };
                0C78F1CA16A5E1BF00654E08 /* sectask-10-sectask.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "sectask-10-sectask.c"; sourceTree = "<group>"; };
                0CE760531E13155100B4381E /* SOSAccountTrustClassic+Circle.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "SOSAccountTrustClassic+Circle.h"; path = "SecureObjectSync/SOSAccountTrustClassic+Circle.h"; sourceTree = "<group>"; };
                0CE760551E1316E900B4381E /* SOSAccountTrustClassic+Retirement.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "SOSAccountTrustClassic+Retirement.h"; path = "SecureObjectSync/SOSAccountTrustClassic+Retirement.h"; sourceTree = "<group>"; };
                0CE98B5B1FA9360700CF1D54 /* libprequelite.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libprequelite.tbd; path = usr/lib/libprequelite.tbd; sourceTree = SDKROOT; };
                0CE760531E13155100B4381E /* SOSAccountTrustClassic+Circle.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "SOSAccountTrustClassic+Circle.h"; path = "SecureObjectSync/SOSAccountTrustClassic+Circle.h"; sourceTree = "<group>"; };
                0CE760551E1316E900B4381E /* SOSAccountTrustClassic+Retirement.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "SOSAccountTrustClassic+Retirement.h"; path = "SecureObjectSync/SOSAccountTrustClassic+Retirement.h"; sourceTree = "<group>"; };
                0CE98B5B1FA9360700CF1D54 /* libprequelite.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libprequelite.tbd; path = usr/lib/libprequelite.tbd; sourceTree = SDKROOT; };
-               0CE9C98921B88919006BDD80 /* OTSOSMessage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OTSOSMessage.h; sourceTree = "<group>"; };
-               0CE9C98A21B8891A006BDD80 /* OTSOSMessage.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OTSOSMessage.m; sourceTree = "<group>"; };
                0CF0E2E31F8EE3B000BD18E4 /* SFSignInAnalytics.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SFSignInAnalytics.m; sourceTree = "<group>"; };
                0CF0E2E71F8EE40700BD18E4 /* SFSignInAnalytics.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SFSignInAnalytics.h; sourceTree = "<group>"; };
                0CF405F42072E2BF003D6A7F /* SFSignInAnalyticsTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SFSignInAnalyticsTests.m; sourceTree = "<group>"; };
                0CF0E2E31F8EE3B000BD18E4 /* SFSignInAnalytics.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SFSignInAnalytics.m; sourceTree = "<group>"; };
                0CF0E2E71F8EE40700BD18E4 /* SFSignInAnalytics.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SFSignInAnalytics.h; sourceTree = "<group>"; };
                0CF405F42072E2BF003D6A7F /* SFSignInAnalyticsTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SFSignInAnalyticsTests.m; sourceTree = "<group>"; };
                48C2F9321E4BCFC30093D70C /* accountCirclesViewsPrint.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = accountCirclesViewsPrint.m; sourceTree = "<group>"; };
                48C2F9331E4BCFC30093D70C /* accountCirclesViewsPrint.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = accountCirclesViewsPrint.h; sourceTree = "<group>"; };
                48CC58971DA5FF0B00EBD9DB /* secd-66-account-recovery.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "secd-66-account-recovery.m"; sourceTree = "<group>"; };
                48C2F9321E4BCFC30093D70C /* accountCirclesViewsPrint.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = accountCirclesViewsPrint.m; sourceTree = "<group>"; };
                48C2F9331E4BCFC30093D70C /* accountCirclesViewsPrint.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = accountCirclesViewsPrint.h; sourceTree = "<group>"; };
                48CC58971DA5FF0B00EBD9DB /* secd-66-account-recovery.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "secd-66-account-recovery.m"; sourceTree = "<group>"; };
-               48E6171A1DBEC40D0098EAAD /* SOSBackupInformation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SOSBackupInformation.m; sourceTree = "<group>"; };
-               48E6171B1DBEC40D0098EAAD /* SOSBackupInformation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SOSBackupInformation.h; sourceTree = "<group>"; };
                48FE668F20E6E69B00FAEF17 /* SOSAuthKitHelpers.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SOSAuthKitHelpers.m; sourceTree = "<group>"; };
                48FE669520E6E69C00FAEF17 /* SOSAuthKitHelpers.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SOSAuthKitHelpers.h; sourceTree = "<group>"; };
                4AF7FFF315AFB73800B9D400 /* SecOTR.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SecOTR.h; sourceTree = "<group>"; };
                48FE668F20E6E69B00FAEF17 /* SOSAuthKitHelpers.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SOSAuthKitHelpers.m; sourceTree = "<group>"; };
                48FE669520E6E69C00FAEF17 /* SOSAuthKitHelpers.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SOSAuthKitHelpers.h; sourceTree = "<group>"; };
                4AF7FFF315AFB73800B9D400 /* SecOTR.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SecOTR.h; sourceTree = "<group>"; };
                52AA92881E662A4A004301A6 /* SecBackupKeybagEntry.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SecBackupKeybagEntry.m; sourceTree = "<group>"; };
                52D82BD316A5EADA0078DFE5 /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = System/Library/Frameworks/Security.framework; sourceTree = SDKROOT; };
                52D82BDE16A621F70078DFE5 /* CloudKeychainProxy.bundle */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = CloudKeychainProxy.bundle; sourceTree = BUILT_PRODUCTS_DIR; };
                52AA92881E662A4A004301A6 /* SecBackupKeybagEntry.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SecBackupKeybagEntry.m; sourceTree = "<group>"; };
                52D82BD316A5EADA0078DFE5 /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = System/Library/Frameworks/Security.framework; sourceTree = SDKROOT; };
                52D82BDE16A621F70078DFE5 /* CloudKeychainProxy.bundle */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = CloudKeychainProxy.bundle; sourceTree = BUILT_PRODUCTS_DIR; };
+               52DA3C6F23C7E63500FEEDFF /* KCTLKRequestTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = KCTLKRequestTest.m; path = Tests/KCTLKRequestTest.m; sourceTree = "<group>"; };
                5346480117331E1200FE9172 /* KeychainSyncAccountNotification.bundle */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = KeychainSyncAccountNotification.bundle; sourceTree = BUILT_PRODUCTS_DIR; };
                5346480517331E1200FE9172 /* KeychainSyncAccountNotification-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "KeychainSyncAccountNotification-Info.plist"; sourceTree = "<group>"; };
                5346480717331E1200FE9172 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = "<group>"; };
                5346480117331E1200FE9172 /* KeychainSyncAccountNotification.bundle */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = KeychainSyncAccountNotification.bundle; sourceTree = BUILT_PRODUCTS_DIR; };
                5346480517331E1200FE9172 /* KeychainSyncAccountNotification-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "KeychainSyncAccountNotification-Info.plist"; sourceTree = "<group>"; };
                5346480717331E1200FE9172 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = "<group>"; };
                6CA837612210C5E7002770F1 /* kc-45-change-password.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = "kc-45-change-password.c"; path = "regressions/kc-45-change-password.c"; sourceTree = "<group>"; };
                6CAA8D201F842FB3007B6E03 /* securityuploadd */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = securityuploadd; sourceTree = BUILT_PRODUCTS_DIR; };
                6CB5F4751E4025AB00DBF3F0 /* CKKSCloudKitTestsInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = CKKSCloudKitTestsInfo.plist; sourceTree = "<group>"; };
                6CA837612210C5E7002770F1 /* kc-45-change-password.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = "kc-45-change-password.c"; path = "regressions/kc-45-change-password.c"; sourceTree = "<group>"; };
                6CAA8D201F842FB3007B6E03 /* securityuploadd */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = securityuploadd; sourceTree = BUILT_PRODUCTS_DIR; };
                6CB5F4751E4025AB00DBF3F0 /* CKKSCloudKitTestsInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = CKKSCloudKitTestsInfo.plist; sourceTree = "<group>"; };
-               6CB5F4781E402E5700DBF3F0 /* KeychainCKKS.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = KeychainCKKS.plist; path = testrunner/KeychainCKKS.plist; sourceTree = "<group>"; };
                6CB5F4791E402E5700DBF3F0 /* KeychainEntitledTestRunner-Entitlements.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "KeychainEntitledTestRunner-Entitlements.plist"; sourceTree = "<group>"; };
                6CB5F47A1E402E5700DBF3F0 /* KeychainEntitledTestRunner.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = KeychainEntitledTestRunner.m; sourceTree = "<group>"; };
                6CB6CC022198D4BC0080AD6F /* SecDbBackupRecoverySet.proto */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.protobuf; path = SecDbBackupRecoverySet.proto; sourceTree = "<group>"; };
                6CB5F4791E402E5700DBF3F0 /* KeychainEntitledTestRunner-Entitlements.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "KeychainEntitledTestRunner-Entitlements.plist"; sourceTree = "<group>"; };
                6CB5F47A1E402E5700DBF3F0 /* KeychainEntitledTestRunner.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = KeychainEntitledTestRunner.m; sourceTree = "<group>"; };
                6CB6CC022198D4BC0080AD6F /* SecDbBackupRecoverySet.proto */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.protobuf; path = SecDbBackupRecoverySet.proto; sourceTree = "<group>"; };
                D4707A282113ECA0005BCFDA /* SecCmsMessage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SecCmsMessage.h; path = CMS/SecCmsMessage.h; sourceTree = "<group>"; };
                D4707A2B2114B31A005BCFDA /* SecCmsContentInfo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SecCmsContentInfo.h; path = CMS/SecCmsContentInfo.h; sourceTree = "<group>"; };
                D4707A2E2114C30A005BCFDA /* SecCmsDigestContext.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SecCmsDigestContext.h; path = CMS/SecCmsDigestContext.h; sourceTree = "<group>"; };
                D4707A282113ECA0005BCFDA /* SecCmsMessage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SecCmsMessage.h; path = CMS/SecCmsMessage.h; sourceTree = "<group>"; };
                D4707A2B2114B31A005BCFDA /* SecCmsContentInfo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SecCmsContentInfo.h; path = CMS/SecCmsContentInfo.h; sourceTree = "<group>"; };
                D4707A2E2114C30A005BCFDA /* SecCmsDigestContext.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SecCmsDigestContext.h; path = CMS/SecCmsDigestContext.h; sourceTree = "<group>"; };
+               D477CB76237E453C00C02355 /* si-88-sectrust-valid-data */ = {isa = PBXFileReference; lastKnownFileType = folder; name = "si-88-sectrust-valid-data"; path = "SecurityTests/si-88-sectrust-valid-data"; sourceTree = "<group>"; };
+               D477CB7A237E4BD700C02355 /* ExceptionTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = ExceptionTests.m; path = tests/TrustTests/EvaluationTests/ExceptionTests.m; sourceTree = "<group>"; };
+               D477CB7D237F321400C02355 /* ExceptionTests_data.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = ExceptionTests_data.h; path = tests/TrustTests/EvaluationTests/ExceptionTests_data.h; sourceTree = "<group>"; };
                D479F6E01F980F8F00388D28 /* English */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = English; path = en.lproj/Trust.strings; sourceTree = "<group>"; };
                D47AB2CA2356AD72005A3801 /* Network.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Network.framework; path = System/Library/Frameworks/Network.framework; sourceTree = SDKROOT; };
                D47C56AB1DCA831C00E18518 /* lib_ios_x64.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = lib_ios_x64.xcconfig; path = xcconfig/lib_ios_x64.xcconfig; sourceTree = "<group>"; };
                D479F6E01F980F8F00388D28 /* English */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = English; path = en.lproj/Trust.strings; sourceTree = "<group>"; };
                D47AB2CA2356AD72005A3801 /* Network.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Network.framework; path = System/Library/Frameworks/Network.framework; sourceTree = SDKROOT; };
                D47C56AB1DCA831C00E18518 /* lib_ios_x64.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = lib_ios_x64.xcconfig; path = xcconfig/lib_ios_x64.xcconfig; sourceTree = "<group>"; };
                DC0BCDB41D8C6A5B00070CB0 /* not_on_this_platorm.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = not_on_this_platorm.c; sourceTree = "<group>"; };
                DC0BD4EF21BB05F2006B9154 /* CKKSKeychainBackedKey.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CKKSKeychainBackedKey.h; sourceTree = "<group>"; };
                DC0BD4F021BB05F2006B9154 /* CKKSKeychainBackedKey.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CKKSKeychainBackedKey.m; sourceTree = "<group>"; };
                DC0BCDB41D8C6A5B00070CB0 /* not_on_this_platorm.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = not_on_this_platorm.c; sourceTree = "<group>"; };
                DC0BD4EF21BB05F2006B9154 /* CKKSKeychainBackedKey.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CKKSKeychainBackedKey.h; sourceTree = "<group>"; };
                DC0BD4F021BB05F2006B9154 /* CKKSKeychainBackedKey.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CKKSKeychainBackedKey.m; sourceTree = "<group>"; };
+               DC0D15FF2363A1D6007F0951 /* OTSetCDPBitOperation.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = OTSetCDPBitOperation.h; sourceTree = "<group>"; };
+               DC0D16002363A1D6007F0951 /* OTSetCDPBitOperation.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = OTSetCDPBitOperation.m; sourceTree = "<group>"; };
+               DC0D16042363BAF4007F0951 /* OTDetermineCDPBitStatusOperation.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = OTDetermineCDPBitStatusOperation.h; sourceTree = "<group>"; };
+               DC0D16052363BAF4007F0951 /* OTDetermineCDPBitStatusOperation.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = OTDetermineCDPBitStatusOperation.m; sourceTree = "<group>"; };
                DC0EF8EF208697C600AB9E95 /* tpctl */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = tpctl; sourceTree = BUILT_PRODUCTS_DIR; };
                DC0EF8F1208697C600AB9E95 /* main.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = main.swift; sourceTree = "<group>"; };
                DC0FA6AE2291F63F00FE01C4 /* OctagonPendingFlag.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = OctagonPendingFlag.h; sourceTree = "<group>"; };
                DC0EF8EF208697C600AB9E95 /* tpctl */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = tpctl; sourceTree = BUILT_PRODUCTS_DIR; };
                DC0EF8F1208697C600AB9E95 /* main.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = main.swift; sourceTree = "<group>"; };
                DC0FA6AE2291F63F00FE01C4 /* OctagonPendingFlag.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = OctagonPendingFlag.h; sourceTree = "<group>"; };
                DC3A4B601D91EAC500E46D4A /* com.apple.CodeSigningHelper.sb */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = com.apple.CodeSigningHelper.sb; sourceTree = "<group>"; };
                DC3A4B621D91EAC500E46D4A /* main.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = main.cpp; sourceTree = "<group>"; usesTabs = 1; };
                DC3A81D41D99D567000C7419 /* libcoretls_cfhelpers.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libcoretls_cfhelpers.dylib; path = usr/lib/libcoretls_cfhelpers.dylib; sourceTree = SDKROOT; };
                DC3A4B601D91EAC500E46D4A /* com.apple.CodeSigningHelper.sb */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = com.apple.CodeSigningHelper.sb; sourceTree = "<group>"; };
                DC3A4B621D91EAC500E46D4A /* main.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = main.cpp; sourceTree = "<group>"; usesTabs = 1; };
                DC3A81D41D99D567000C7419 /* libcoretls_cfhelpers.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libcoretls_cfhelpers.dylib; path = usr/lib/libcoretls_cfhelpers.dylib; sourceTree = SDKROOT; };
+               DC3A9B2523A9D6120073ED06 /* Container_BottledPeers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Container_BottledPeers.swift; sourceTree = "<group>"; };
                DC3AA27C2097DF94007CA68A /* security_tool_commands_table.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = security_tool_commands_table.h; sourceTree = "<group>"; };
                DC3AF52A2229E6C0006577E8 /* CKKSListenerCollection.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CKKSListenerCollection.h; sourceTree = "<group>"; };
                DC3AF52B2229E6C0006577E8 /* CKKSListenerCollection.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CKKSListenerCollection.m; sourceTree = "<group>"; };
                DC3AA27C2097DF94007CA68A /* security_tool_commands_table.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = security_tool_commands_table.h; sourceTree = "<group>"; };
                DC3AF52A2229E6C0006577E8 /* CKKSListenerCollection.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CKKSListenerCollection.h; sourceTree = "<group>"; };
                DC3AF52B2229E6C0006577E8 /* CKKSListenerCollection.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CKKSListenerCollection.m; sourceTree = "<group>"; };
                DC4269031E82EDAC002B7110 /* SecItem.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SecItem.m; sourceTree = "<group>"; };
                DC4269061E82FBDF002B7110 /* server_security_helpers.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = server_security_helpers.m; sourceTree = "<group>"; };
                DC4269071E82FBDF002B7110 /* server_security_helpers.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = server_security_helpers.h; sourceTree = "<group>"; };
                DC4269031E82EDAC002B7110 /* SecItem.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SecItem.m; sourceTree = "<group>"; };
                DC4269061E82FBDF002B7110 /* server_security_helpers.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = server_security_helpers.m; sourceTree = "<group>"; };
                DC4269071E82FBDF002B7110 /* server_security_helpers.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = server_security_helpers.h; sourceTree = "<group>"; };
+               DC4415B323610BF40087981C /* OctagonTests+Account.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "OctagonTests+Account.swift"; sourceTree = "<group>"; };
                DC45D43B22EB619D00CEB6B7 /* OctagonStateMachineObservers.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = OctagonStateMachineObservers.h; sourceTree = "<group>"; };
                DC45D43C22EB619D00CEB6B7 /* OctagonStateMachineObservers.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = OctagonStateMachineObservers.m; sourceTree = "<group>"; };
                DC4A76A2221267D4006F2D8F /* EscrowRequestServerHelpers.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = EscrowRequestServerHelpers.m; sourceTree = "<group>"; };
                DC4A76A4221267FB006F2D8F /* EscrowRequestServerHelpers.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = EscrowRequestServerHelpers.h; sourceTree = "<group>"; };
                DC4A76A92212698B006F2D8F /* CloudServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CloudServices.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS13.0.Internal.sdk/System/Library/PrivateFrameworks/CloudServices.framework; sourceTree = DEVELOPER_DIR; };
                DC45D43B22EB619D00CEB6B7 /* OctagonStateMachineObservers.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = OctagonStateMachineObservers.h; sourceTree = "<group>"; };
                DC45D43C22EB619D00CEB6B7 /* OctagonStateMachineObservers.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = OctagonStateMachineObservers.m; sourceTree = "<group>"; };
                DC4A76A2221267D4006F2D8F /* EscrowRequestServerHelpers.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = EscrowRequestServerHelpers.m; sourceTree = "<group>"; };
                DC4A76A4221267FB006F2D8F /* EscrowRequestServerHelpers.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = EscrowRequestServerHelpers.h; sourceTree = "<group>"; };
                DC4A76A92212698B006F2D8F /* CloudServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CloudServices.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS13.0.Internal.sdk/System/Library/PrivateFrameworks/CloudServices.framework; sourceTree = DEVELOPER_DIR; };
+               DC4CD9822372294D00EF55FC /* OctagonTests+Helpers.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "OctagonTests+Helpers.swift"; sourceTree = "<group>"; };
                DC4D49D81F857728007AF2B8 /* CKKSSerializedKey.proto */ = {isa = PBXFileReference; lastKnownFileType = text; path = CKKSSerializedKey.proto; sourceTree = "<group>"; };
                DC4DB14E1E24692100CD6769 /* CKKSKey.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CKKSKey.h; sourceTree = "<group>"; };
                DC4DB14F1E24692100CD6769 /* CKKSKey.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CKKSKey.m; sourceTree = "<group>"; };
                DC4D49D81F857728007AF2B8 /* CKKSSerializedKey.proto */ = {isa = PBXFileReference; lastKnownFileType = text; path = CKKSSerializedKey.proto; sourceTree = "<group>"; };
                DC4DB14E1E24692100CD6769 /* CKKSKey.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CKKSKey.h; sourceTree = "<group>"; };
                DC4DB14F1E24692100CD6769 /* CKKSKey.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CKKSKey.m; sourceTree = "<group>"; };
                DC7EB920211E17E500516452 /* OTAccountMetadataClassC+KeychainSupport.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "OTAccountMetadataClassC+KeychainSupport.h"; sourceTree = "<group>"; };
                DC7EB921211E17E500516452 /* OTAccountMetadataClassC+KeychainSupport.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "OTAccountMetadataClassC+KeychainSupport.m"; sourceTree = "<group>"; };
                DC7EB928211E20DF00516452 /* OctagonDataPersistenceTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OctagonDataPersistenceTests.swift; sourceTree = "<group>"; };
                DC7EB920211E17E500516452 /* OTAccountMetadataClassC+KeychainSupport.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "OTAccountMetadataClassC+KeychainSupport.h"; sourceTree = "<group>"; };
                DC7EB921211E17E500516452 /* OTAccountMetadataClassC+KeychainSupport.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "OTAccountMetadataClassC+KeychainSupport.m"; sourceTree = "<group>"; };
                DC7EB928211E20DF00516452 /* OctagonDataPersistenceTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OctagonDataPersistenceTests.swift; sourceTree = "<group>"; };
+               DC7F6A7C233D7FAC00DF5769 /* OctagonTests+ForwardCompatibility.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "OctagonTests+ForwardCompatibility.swift"; sourceTree = "<group>"; };
                DC7F79B522EA4ED4001FB69A /* OctagonTests+CKKS.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "OctagonTests+CKKS.swift"; sourceTree = "<group>"; };
                DC7F79B822EA5C72001FB69A /* OTLocalCKKSResetOperation.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = OTLocalCKKSResetOperation.h; sourceTree = "<group>"; };
                DC7F79B922EA5C72001FB69A /* OTLocalCKKSResetOperation.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = OTLocalCKKSResetOperation.m; sourceTree = "<group>"; };
                DC7F79B522EA4ED4001FB69A /* OctagonTests+CKKS.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "OctagonTests+CKKS.swift"; sourceTree = "<group>"; };
                DC7F79B822EA5C72001FB69A /* OTLocalCKKSResetOperation.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = OTLocalCKKSResetOperation.h; sourceTree = "<group>"; };
                DC7F79B922EA5C72001FB69A /* OTLocalCKKSResetOperation.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = OTLocalCKKSResetOperation.m; sourceTree = "<group>"; };
                DC88466C2237407500738068 /* TPDictionaryMatchingRuleTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = TPDictionaryMatchingRuleTests.m; sourceTree = "<group>"; };
                DC88467F2237431400738068 /* TPDictionaryMatchingRules.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TPDictionaryMatchingRules.h; sourceTree = "<group>"; };
                DC8846802237431400738068 /* TPDictionaryMatchingRules.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = TPDictionaryMatchingRules.m; sourceTree = "<group>"; };
                DC88466C2237407500738068 /* TPDictionaryMatchingRuleTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = TPDictionaryMatchingRuleTests.m; sourceTree = "<group>"; };
                DC88467F2237431400738068 /* TPDictionaryMatchingRules.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TPDictionaryMatchingRules.h; sourceTree = "<group>"; };
                DC8846802237431400738068 /* TPDictionaryMatchingRules.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = TPDictionaryMatchingRules.m; sourceTree = "<group>"; };
+               DC89608C2395C75500D339D9 /* CoreServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreServices.framework; path = System/Library/Frameworks/CoreServices.framework; sourceTree = SDKROOT; };
                DC8D238C2064649400E163C8 /* CKKSAPSHandlingTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CKKSAPSHandlingTests.m; sourceTree = "<group>"; };
                DC8E04901D7F6780006D80EB /* lib_ios.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = lib_ios.xcconfig; path = xcconfig/lib_ios.xcconfig; sourceTree = "<group>"; };
                DC9061B822B02BA30071474D /* TPTypes.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = TPTypes.m; sourceTree = "<group>"; };
                DC8D238C2064649400E163C8 /* CKKSAPSHandlingTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CKKSAPSHandlingTests.m; sourceTree = "<group>"; };
                DC8E04901D7F6780006D80EB /* lib_ios.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = lib_ios.xcconfig; path = xcconfig/lib_ios.xcconfig; sourceTree = "<group>"; };
                DC9061B822B02BA30071474D /* TPTypes.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = TPTypes.m; sourceTree = "<group>"; };
                DCA9D84521FFE7CF00B27421 /* EscrowRequestXPCProtocol.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = EscrowRequestXPCProtocol.m; sourceTree = "<group>"; };
                DCA9D84B21FFF04600B27421 /* EscrowRequestServer.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = EscrowRequestServer.h; sourceTree = "<group>"; };
                DCA9D84C21FFF04700B27421 /* EscrowRequestServer.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = EscrowRequestServer.m; sourceTree = "<group>"; };
                DCA9D84521FFE7CF00B27421 /* EscrowRequestXPCProtocol.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = EscrowRequestXPCProtocol.m; sourceTree = "<group>"; };
                DCA9D84B21FFF04600B27421 /* EscrowRequestServer.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = EscrowRequestServer.h; sourceTree = "<group>"; };
                DCA9D84C21FFF04700B27421 /* EscrowRequestServer.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = EscrowRequestServer.m; sourceTree = "<group>"; };
+               DCAA209823AAF8BD00DCB594 /* Container_RecoveryKey.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Container_RecoveryKey.swift; sourceTree = "<group>"; };
                DCAB17CB21FFF6C400E1DFCF /* MockSynchronousEscrowServer.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MockSynchronousEscrowServer.h; sourceTree = "<group>"; };
                DCAB17CC21FFF6C400E1DFCF /* MockSynchronousEscrowServer.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MockSynchronousEscrowServer.m; sourceTree = "<group>"; };
                DCAB17CF2200D26700E1DFCF /* SecEscrowPendingRecord.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SecEscrowPendingRecord.h; sourceTree = "<group>"; };
                DCAB17CB21FFF6C400E1DFCF /* MockSynchronousEscrowServer.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MockSynchronousEscrowServer.h; sourceTree = "<group>"; };
                DCAB17CC21FFF6C400E1DFCF /* MockSynchronousEscrowServer.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MockSynchronousEscrowServer.m; sourceTree = "<group>"; };
                DCAB17CF2200D26700E1DFCF /* SecEscrowPendingRecord.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SecEscrowPendingRecord.h; sourceTree = "<group>"; };
                DCC78C701D8085D800865A7C /* secd-83-item-match-valid-on-date.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "secd-83-item-match-valid-on-date.m"; sourceTree = "<group>"; };
                DCC78C711D8085D800865A7C /* secd-83-item-match-trusted.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "secd-83-item-match-trusted.m"; sourceTree = "<group>"; };
                DCC78C721D8085D800865A7C /* secd-83-item-match.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "secd-83-item-match.h"; sourceTree = "<group>"; };
                DCC78C701D8085D800865A7C /* secd-83-item-match-valid-on-date.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "secd-83-item-match-valid-on-date.m"; sourceTree = "<group>"; };
                DCC78C711D8085D800865A7C /* secd-83-item-match-trusted.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "secd-83-item-match-trusted.m"; sourceTree = "<group>"; };
                DCC78C721D8085D800865A7C /* secd-83-item-match.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "secd-83-item-match.h"; sourceTree = "<group>"; };
-               DCC78C741D8085D800865A7C /* secd-95-escrow-persistence.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "secd-95-escrow-persistence.m"; sourceTree = "<group>"; };
                DCC78C751D8085D800865A7C /* secd-100-initialsync.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "secd-100-initialsync.m"; sourceTree = "<group>"; };
                DCC78C761D8085D800865A7C /* secd-130-other-peer-views.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "secd-130-other-peer-views.m"; sourceTree = "<group>"; };
                DCC78C771D8085D800865A7C /* secd-154-engine-backoff.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "secd-154-engine-backoff.m"; sourceTree = "<group>"; };
                DCC78C751D8085D800865A7C /* secd-100-initialsync.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "secd-100-initialsync.m"; sourceTree = "<group>"; };
                DCC78C761D8085D800865A7C /* secd-130-other-peer-views.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "secd-130-other-peer-views.m"; sourceTree = "<group>"; };
                DCC78C771D8085D800865A7C /* secd-154-engine-backoff.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "secd-154-engine-backoff.m"; sourceTree = "<group>"; };
                DCC78D021D8085F200865A7C /* sc-42-circlegencount.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "sc-42-circlegencount.c"; sourceTree = "<group>"; };
                DCC78D031D8085F200865A7C /* sc-45-digestvector.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "sc-45-digestvector.c"; sourceTree = "<group>"; };
                DCC78D041D8085F200865A7C /* sc-130-resignationticket.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "sc-130-resignationticket.c"; sourceTree = "<group>"; };
                DCC78D021D8085F200865A7C /* sc-42-circlegencount.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "sc-42-circlegencount.c"; sourceTree = "<group>"; };
                DCC78D031D8085F200865A7C /* sc-45-digestvector.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "sc-45-digestvector.c"; sourceTree = "<group>"; };
                DCC78D041D8085F200865A7C /* sc-130-resignationticket.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "sc-130-resignationticket.c"; sourceTree = "<group>"; };
-               DCC78D061D8085F200865A7C /* sc-150-ring.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "sc-150-ring.m"; sourceTree = "<group>"; };
                DCC78D071D8085F200865A7C /* sc-150-backupkeyderivation.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "sc-150-backupkeyderivation.c"; sourceTree = "<group>"; };
                DCC78D081D8085F200865A7C /* sc-153-backupslicekeybag.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "sc-153-backupslicekeybag.c"; sourceTree = "<group>"; };
                DCC78D091D8085F200865A7C /* SOSCircle_regressions.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SOSCircle_regressions.h; sourceTree = "<group>"; };
                DCC78D071D8085F200865A7C /* sc-150-backupkeyderivation.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "sc-150-backupkeyderivation.c"; sourceTree = "<group>"; };
                DCC78D081D8085F200865A7C /* sc-153-backupslicekeybag.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "sc-153-backupslicekeybag.c"; sourceTree = "<group>"; };
                DCC78D091D8085F200865A7C /* SOSCircle_regressions.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SOSCircle_regressions.h; sourceTree = "<group>"; };
                DCC78D991D8085F200865A7C /* keychain_sync_test.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = keychain_sync_test.m; sourceTree = "<group>"; };
                DCC78D9A1D8085F200865A7C /* keychain_log.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = keychain_log.h; sourceTree = "<group>"; };
                DCC78D9B1D8085F200865A7C /* keychain_log.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = keychain_log.m; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objc; };
                DCC78D991D8085F200865A7C /* keychain_sync_test.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = keychain_sync_test.m; sourceTree = "<group>"; };
                DCC78D9A1D8085F200865A7C /* keychain_log.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = keychain_log.h; sourceTree = "<group>"; };
                DCC78D9B1D8085F200865A7C /* keychain_log.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = keychain_log.m; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objc; };
-               DCC78D9C1D8085F200865A7C /* syncbackup.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = syncbackup.h; sourceTree = "<group>"; };
-               DCC78D9D1D8085F200865A7C /* syncbackup.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = syncbackup.m; sourceTree = "<group>"; };
                DCC78D9E1D8085F200865A7C /* secViewDisplay.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = secViewDisplay.c; sourceTree = "<group>"; };
                DCC78D9F1D8085F200865A7C /* secViewDisplay.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = secViewDisplay.h; sourceTree = "<group>"; };
                DCC78DA21D8085FC00865A7C /* Security_regressions.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = Security_regressions.h; path = Regressions/Security_regressions.h; sourceTree = "<group>"; };
                DCC78D9E1D8085F200865A7C /* secViewDisplay.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = secViewDisplay.c; sourceTree = "<group>"; };
                DCC78D9F1D8085F200865A7C /* secViewDisplay.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = secViewDisplay.h; sourceTree = "<group>"; };
                DCC78DA21D8085FC00865A7C /* Security_regressions.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = Security_regressions.h; path = Regressions/Security_regressions.h; sourceTree = "<group>"; };
                DCC78DC31D8085FC00865A7C /* si-24-sectrust-itms.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "si-24-sectrust-itms.c"; sourceTree = "<group>"; };
                DCC78DC51D8085FC00865A7C /* si-24-sectrust-passbook.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "si-24-sectrust-passbook.c"; sourceTree = "<group>"; };
                DCC78DC61D8085FC00865A7C /* si-26-sectrust-copyproperties.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "si-26-sectrust-copyproperties.c"; sourceTree = "<group>"; };
                DCC78DC31D8085FC00865A7C /* si-24-sectrust-itms.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "si-24-sectrust-itms.c"; sourceTree = "<group>"; };
                DCC78DC51D8085FC00865A7C /* si-24-sectrust-passbook.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "si-24-sectrust-passbook.c"; sourceTree = "<group>"; };
                DCC78DC61D8085FC00865A7C /* si-26-sectrust-copyproperties.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "si-26-sectrust-copyproperties.c"; sourceTree = "<group>"; };
-               DCC78DC71D8085FC00865A7C /* si-27-sectrust-exceptions.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "si-27-sectrust-exceptions.c"; sourceTree = "<group>"; };
                DCC78DC81D8085FC00865A7C /* si-28-sectrustsettings.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "si-28-sectrustsettings.m"; sourceTree = "<group>"; };
                DCC78DC91D8085FC00865A7C /* si-28-sectrustsettings.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "si-28-sectrustsettings.h"; sourceTree = "<group>"; };
                DCC78DCA1D8085FC00865A7C /* si-30-keychain-upgrade.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "si-30-keychain-upgrade.c"; sourceTree = "<group>"; };
                DCC78DC81D8085FC00865A7C /* si-28-sectrustsettings.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "si-28-sectrustsettings.m"; sourceTree = "<group>"; };
                DCC78DC91D8085FC00865A7C /* si-28-sectrustsettings.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "si-28-sectrustsettings.h"; sourceTree = "<group>"; };
                DCC78DCA1D8085FC00865A7C /* si-30-keychain-upgrade.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "si-30-keychain-upgrade.c"; sourceTree = "<group>"; };
                DCE278DC1ED789EF0083B485 /* CKKSCurrentItemPointer.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CKKSCurrentItemPointer.m; sourceTree = "<group>"; };
                DCE278E61ED7A5B40083B485 /* CKKSUpdateCurrentItemPointerOperation.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CKKSUpdateCurrentItemPointerOperation.h; sourceTree = "<group>"; };
                DCE278E71ED7A5B40083B485 /* CKKSUpdateCurrentItemPointerOperation.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CKKSUpdateCurrentItemPointerOperation.m; sourceTree = "<group>"; };
                DCE278DC1ED789EF0083B485 /* CKKSCurrentItemPointer.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CKKSCurrentItemPointer.m; sourceTree = "<group>"; };
                DCE278E61ED7A5B40083B485 /* CKKSUpdateCurrentItemPointerOperation.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CKKSUpdateCurrentItemPointerOperation.h; sourceTree = "<group>"; };
                DCE278E71ED7A5B40083B485 /* CKKSUpdateCurrentItemPointerOperation.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CKKSUpdateCurrentItemPointerOperation.m; sourceTree = "<group>"; };
+               DCE405C423A04A7F00C4343B /* OctagonTests+CKKSConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "OctagonTests+CKKSConfiguration.swift"; sourceTree = "<group>"; };
                DCE4E6A41D7A37FA00AFB96E /* security2 */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = security2; sourceTree = BUILT_PRODUCTS_DIR; };
                DCE4E6A71D7A38C000AFB96E /* security2.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = security2.1; sourceTree = "<group>"; };
                DCE4E6D41D7A41E400AFB96E /* bc-10-knife-on-bread.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "bc-10-knife-on-bread.m"; path = "OSX/Breadcrumb/bc-10-knife-on-bread.m"; sourceTree = "<group>"; };
                DCE4E6A41D7A37FA00AFB96E /* security2 */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = security2; sourceTree = BUILT_PRODUCTS_DIR; };
                DCE4E6A71D7A38C000AFB96E /* security2.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = security2.1; sourceTree = "<group>"; };
                DCE4E6D41D7A41E400AFB96E /* bc-10-knife-on-bread.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "bc-10-knife-on-bread.m"; path = "OSX/Breadcrumb/bc-10-knife-on-bread.m"; sourceTree = "<group>"; };
                        buildActionMask = 2147483647;
                        files = (
                                0C84D83D1FCF449700B822E3 /* Security.framework in Frameworks */,
                        buildActionMask = 2147483647;
                        files = (
                                0C84D83D1FCF449700B822E3 /* Security.framework in Frameworks */,
+                               0CE902352395D0A3005E3F8C /* AuthKit.framework in Frameworks */,
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
                                0C9FB40720D872A600864612 /* CoreCDP.framework in Frameworks */,
                                43DB54551BB1F8920083C3F1 /* ProtectedCloudStorage.framework in Frameworks */,
                                4C8A38C917B93DF10001B4C0 /* CloudServices.framework in Frameworks */,
                                0C9FB40720D872A600864612 /* CoreCDP.framework in Frameworks */,
                                43DB54551BB1F8920083C3F1 /* ProtectedCloudStorage.framework in Frameworks */,
                                4C8A38C917B93DF10001B4C0 /* CloudServices.framework in Frameworks */,
-                               4C7913251799A5CC00A9633E /* MobileCoreServices.framework in Frameworks */,
                                4381603B1B4DCEFF00C54D58 /* AggregateDictionary.framework in Frameworks */,
                                4C3DD6BD179760280093F9D8 /* libMobileGestalt.dylib in Frameworks */,
                                533B5D4F177CD63100995334 /* SpringBoardServices.framework in Frameworks */,
                                7200D76F177B9999009BB396 /* ManagedConfiguration.framework in Frameworks */,
                                433E519E1B66D5F600482618 /* AppSupport.framework in Frameworks */,
                                4381603B1B4DCEFF00C54D58 /* AggregateDictionary.framework in Frameworks */,
                                4C3DD6BD179760280093F9D8 /* libMobileGestalt.dylib in Frameworks */,
                                533B5D4F177CD63100995334 /* SpringBoardServices.framework in Frameworks */,
                                7200D76F177B9999009BB396 /* ManagedConfiguration.framework in Frameworks */,
                                433E519E1B66D5F600482618 /* AppSupport.framework in Frameworks */,
+                               DC89608D2395C75600D339D9 /* CoreServices.framework in Frameworks */,
                                4C84DA551720698900AEE225 /* AppleAccount.framework in Frameworks */,
                                4CF4C19D171E0EA600877419 /* Accounts.framework in Frameworks */,
                                438168C41B4ED43800C54D58 /* CoreFoundation.framework in Frameworks */,
                                4C84DA551720698900AEE225 /* AppleAccount.framework in Frameworks */,
                                4CF4C19D171E0EA600877419 /* Accounts.framework in Frameworks */,
                                438168C41B4ED43800C54D58 /* CoreFoundation.framework in Frameworks */,
                                DC00AB9A1D821D8800513D74 /* libSWCAgent.a in Frameworks */,
                                DCD22D981D8CCF78001C9B81 /* libutilities.a in Frameworks */,
                                BE442BB318B7FDB800F24DAE /* Security.framework in Frameworks */,
                                DC00AB9A1D821D8800513D74 /* libSWCAgent.a in Frameworks */,
                                DCD22D981D8CCF78001C9B81 /* libutilities.a in Frameworks */,
                                BE442BB318B7FDB800F24DAE /* Security.framework in Frameworks */,
+                               DC89608E2395C76300D339D9 /* CoreServices.framework in Frameworks */,
                                BE442BB418B7FDB800F24DAE /* SystemConfiguration.framework in Frameworks */,
                                BE442BB618B7FDB800F24DAE /* CFNetwork.framework in Frameworks */,
                                BE25C41618B83491003320E0 /* Foundation.framework in Frameworks */,
                                BE442BB718B7FDB800F24DAE /* IOKit.framework in Frameworks */,
                                438168C61B4ED43F00C54D58 /* CoreFoundation.framework in Frameworks */,
                                BE442BB418B7FDB800F24DAE /* SystemConfiguration.framework in Frameworks */,
                                BE442BB618B7FDB800F24DAE /* CFNetwork.framework in Frameworks */,
                                BE25C41618B83491003320E0 /* Foundation.framework in Frameworks */,
                                BE442BB718B7FDB800F24DAE /* IOKit.framework in Frameworks */,
                                438168C61B4ED43F00C54D58 /* CoreFoundation.framework in Frameworks */,
-                               D4B858671D370D9A003B2D95 /* MobileCoreServices.framework in Frameworks */,
                                BE442BB818B7FDB800F24DAE /* libsqlite3.dylib in Frameworks */,
                                BE442BB918B7FDB800F24DAE /* libbsm.dylib in Frameworks */,
                        );
                                BE442BB818B7FDB800F24DAE /* libsqlite3.dylib in Frameworks */,
                                BE442BB918B7FDB800F24DAE /* libbsm.dylib in Frameworks */,
                        );
                        files = (
                                DC730E2922401F5E0051DD48 /* ProtocolBuffer.framework in Frameworks */,
                                DC730E2522401E310051DD48 /* TrustedPeers.framework in Frameworks */,
                        files = (
                                DC730E2922401F5E0051DD48 /* ProtocolBuffer.framework in Frameworks */,
                                DC730E2522401E310051DD48 /* TrustedPeers.framework in Frameworks */,
-                               1B916CD0223FFF25006657FD /* ProtocolBuffer.framework in Frameworks */,
                                BE72782C209D2C1400F0DA77 /* SecurityFoundation.framework in Frameworks */,
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                                BE72782C209D2C1400F0DA77 /* SecurityFoundation.framework in Frameworks */,
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                        isa = PBXGroup;
                        children = (
                                0C9FB40120D8729A00864612 /* CoreCDP.framework */,
                        isa = PBXGroup;
                        children = (
                                0C9FB40120D8729A00864612 /* CoreCDP.framework */,
-                               DCD067E71D8CDF7E007602F1 /* SecCodeHostLib.h */,
-                               DCD067E81D8CDF7E007602F1 /* SecCodeHostLib.c */,
                        );
                        name = "Recovered References";
                        sourceTree = "<group>";
                };
                        );
                        name = "Recovered References";
                        sourceTree = "<group>";
                };
+               0C7382E52386379E004F98CB /* ResetCloudKeychainAccount */ = {
+                       isa = PBXGroup;
+                       children = (
+                               0C7382F023863AD5004F98CB /* reset_ick_account */,
+                       );
+                       path = ResetCloudKeychainAccount;
+                       sourceTree = "<group>";
+               };
                0C78F1C816A5E13400654E08 /* regressions */ = {
                        isa = PBXGroup;
                        children = (
                0C78F1C816A5E13400654E08 /* regressions */ = {
                        isa = PBXGroup;
                        children = (
                                0C9AE28E214054F6003BFDB5 /* OTApplicantToSponsorRound2M1.m */,
                                0C9AE2A1214055CE003BFDB5 /* OTPairingMessage.h */,
                                0C9AE2A2214055CF003BFDB5 /* OTPairingMessage.m */,
                                0C9AE28E214054F6003BFDB5 /* OTApplicantToSponsorRound2M1.m */,
                                0C9AE2A1214055CE003BFDB5 /* OTPairingMessage.h */,
                                0C9AE2A2214055CF003BFDB5 /* OTPairingMessage.m */,
-                               0CE9C98921B88919006BDD80 /* OTSOSMessage.h */,
-                               0CE9C98A21B8891A006BDD80 /* OTSOSMessage.m */,
                                0C9AE289214054F4003BFDB5 /* OTSponsorToApplicantRound1M2.h */,
                                0C9AE28D214054F6003BFDB5 /* OTSponsorToApplicantRound1M2.m */,
                                0C9AE28A214054F5003BFDB5 /* OTSponsorToApplicantRound2M2.h */,
                                0C9AE289214054F4003BFDB5 /* OTSponsorToApplicantRound1M2.h */,
                                0C9AE28D214054F6003BFDB5 /* OTSponsorToApplicantRound1M2.m */,
                                0C9AE28A214054F5003BFDB5 /* OTSponsorToApplicantRound2M2.h */,
                                DC754C712228B57B00A39C8E /* TrustedPeersHelperProtocol.m */,
                                BE55C77B2044D0C90045863D /* Client.swift */,
                                BE9F8D0F206C099800B53D16 /* Container.swift */,
                                DC754C712228B57B00A39C8E /* TrustedPeersHelperProtocol.m */,
                                BE55C77B2044D0C90045863D /* Client.swift */,
                                BE9F8D0F206C099800B53D16 /* Container.swift */,
+                               DC3A9B2523A9D6120073ED06 /* Container_BottledPeers.swift */,
+                               DCAA209823AAF8BD00DCB594 /* Container_RecoveryKey.swift */,
                                DCAD8F8422C43EAD007C3872 /* Container_MachineIDs.swift */,
                                BE9F8D18206C4AD300B53D16 /* ContainerMap.swift */,
                                BE9F8D11206C121400B53D16 /* Decrypter.swift */,
                                DCAD8F8422C43EAD007C3872 /* Container_MachineIDs.swift */,
                                BE9F8D18206C4AD300B53D16 /* ContainerMap.swift */,
                                BE9F8D11206C121400B53D16 /* Decrypter.swift */,
                                D4AC5767214E195300A32C01 /* ECTests_data.h */,
                                D458C4AE214E198E0043D982 /* EvaluationBasicTests_data.h */,
                                D458C4AF214E198E0043D982 /* EvaluationBasicTests.m */,
                                D4AC5767214E195300A32C01 /* ECTests_data.h */,
                                D458C4AE214E198E0043D982 /* EvaluationBasicTests_data.h */,
                                D458C4AF214E198E0043D982 /* EvaluationBasicTests.m */,
+                               D477CB7A237E4BD700C02355 /* ExceptionTests.m */,
+                               D477CB7D237F321400C02355 /* ExceptionTests_data.h */,
                                D458C4B5214E19AE0043D982 /* iAPTests.m */,
                                D458C4AD214E198E0043D982 /* iAPTests_data.h */,
                                D4AC5768214E195400A32C01 /* KeySizeTests.m */,
                                D458C4B5214E19AE0043D982 /* iAPTests.m */,
                                D458C4AD214E198E0043D982 /* iAPTests_data.h */,
                                D4AC5768214E195400A32C01 /* KeySizeTests.m */,
                                D458C51B214E2CFF0043D982 /* si-20-sectrust-policies-data */,
                                D4A0F8C1211E6A2F00443CA1 /* si-82-sectrust-ct-data */,
                                D4056A1E22712D750026E24E /* si-87-sectrust-name-constraints */,
                                D458C51B214E2CFF0043D982 /* si-20-sectrust-policies-data */,
                                D4A0F8C1211E6A2F00443CA1 /* si-82-sectrust-ct-data */,
                                D4056A1E22712D750026E24E /* si-87-sectrust-name-constraints */,
+                               D477CB76237E453C00C02355 /* si-88-sectrust-valid-data */,
                                D4056A1D22712D740026E24E /* ssl-policy-certs */,
                        );
                        name = TestData;
                                D4056A1D22712D740026E24E /* ssl-policy-certs */,
                        );
                        name = TestData;
                                BEF88C451EAFFFED00357577 /* TrustedPeers */,
                                BEAA002C202A832500E51F45 /* TrustedPeersHelper */,
                                BED987D42099145300607A5F /* TrustedPeersHelperUnitTests */,
                                BEF88C451EAFFFED00357577 /* TrustedPeers */,
                                BEAA002C202A832500E51F45 /* TrustedPeersHelper */,
                                BED987D42099145300607A5F /* TrustedPeersHelperUnitTests */,
+                               0C7382E52386379E004F98CB /* ResetCloudKeychainAccount */,
                        );
                        path = keychain;
                        sourceTree = "<group>";
                        );
                        path = keychain;
                        sourceTree = "<group>";
                                DCC0A4C52152C4AB000AF654 /* Pairing */,
                                DC27C3C820EADD8200F7839C /* OctagonTests-BridgingHeader.h */,
                                DC85687C2284E7850088D3EF /* OctagonTestMocks.swift */,
                                DCC0A4C52152C4AB000AF654 /* Pairing */,
                                DC27C3C820EADD8200F7839C /* OctagonTests-BridgingHeader.h */,
                                DC85687C2284E7850088D3EF /* OctagonTestMocks.swift */,
+                               DC4CD9822372294D00EF55FC /* OctagonTests+Helpers.swift */,
                                DC27C3C020EAD9C300F7839C /* OctagonTests.swift */,
                                DC27C3C020EAD9C300F7839C /* OctagonTests.swift */,
+                               DC4415B323610BF40087981C /* OctagonTests+Account.swift */,
                                DC7F79B522EA4ED4001FB69A /* OctagonTests+CKKS.swift */,
                                DC7F79B522EA4ED4001FB69A /* OctagonTests+CKKS.swift */,
+                               DCE405C423A04A7F00C4343B /* OctagonTests+CKKSConfiguration.swift */,
+                               DC5BEACC2217509A001681F0 /* OctagonTests+CloudKitAccount.swift */,
                                DC5F2BBD2310B941001ADA5D /* OctagonTests+CoreFollowUp.swift */,
                                DC2819B822F8F6FE007829F5 /* OctagonTests+DeviceList.swift */,
                                DC5F2BBD2310B941001ADA5D /* OctagonTests+CoreFollowUp.swift */,
                                DC2819B822F8F6FE007829F5 /* OctagonTests+DeviceList.swift */,
+                               DC7F6A7C233D7FAC00DF5769 /* OctagonTests+ForwardCompatibility.swift */,
                                0C4CDE6D22922E360050C499 /* OctagonTests+RecoveryKey.swift */,
                                DC07090222936BCC002711B9 /* OctagonTests+ErrorHandling.swift */,
                                DCDF03112284E34B008055BA /* OctagonTests+EscrowRecovery.swift */,
                                0C4CDE6D22922E360050C499 /* OctagonTests+RecoveryKey.swift */,
                                DC07090222936BCC002711B9 /* OctagonTests+ErrorHandling.swift */,
                                DCDF03112284E34B008055BA /* OctagonTests+EscrowRecovery.swift */,
-                               DC5BEACC2217509A001681F0 /* OctagonTests+CloudKitAccount.swift */,
                                0C5824A322860001009E8C15 /* OctagonTests+HealthCheck.swift */,
                                DC72502D229600A800493D88 /* OctagonTests+Reset.swift */,
                                DCB947592127534C00ED9272 /* OctagonTests+SOSUpgrade.swift */,
                                0C5824A322860001009E8C15 /* OctagonTests+HealthCheck.swift */,
                                DC72502D229600A800493D88 /* OctagonTests+Reset.swift */,
                                DCB947592127534C00ED9272 /* OctagonTests+SOSUpgrade.swift */,
                DCA4D2121E5651950056214F /* Tests (Live CloudKit) */ = {
                        isa = PBXGroup;
                        children = (
                DCA4D2121E5651950056214F /* Tests (Live CloudKit) */ = {
                        isa = PBXGroup;
                        children = (
-                               6CB5F4781E402E5700DBF3F0 /* KeychainCKKS.plist */,
                                6CF4A0B51E45488B00ECD7B5 /* KeychainEntitledTestApp_mac */,
                                6CF4A0E11E4549F200ECD7B5 /* KeychainEntitledTestApp_ios */,
                                6CB5F4771E402D6D00DBF3F0 /* testrunner */,
                                6CF4A0B51E45488B00ECD7B5 /* KeychainEntitledTestApp_mac */,
                                6CF4A0E11E4549F200ECD7B5 /* KeychainEntitledTestApp_ios */,
                                6CB5F4771E402D6D00DBF3F0 /* testrunner */,
                                0CC8A9012123AEF7005D7F6A /* OTJoinWithVoucherOperation.m */,
                                0C66046E2134985100BFBBB8 /* OTEstablishOperation.h */,
                                0C6604692134983900BFBBB8 /* OTEstablishOperation.m */,
                                0CC8A9012123AEF7005D7F6A /* OTJoinWithVoucherOperation.m */,
                                0C66046E2134985100BFBBB8 /* OTEstablishOperation.h */,
                                0C6604692134983900BFBBB8 /* OTEstablishOperation.m */,
+                               DC0D15FF2363A1D6007F0951 /* OTSetCDPBitOperation.h */,
+                               DC0D16002363A1D6007F0951 /* OTSetCDPBitOperation.m */,
+                               DC0D16042363BAF4007F0951 /* OTDetermineCDPBitStatusOperation.h */,
+                               DC0D16052363BAF4007F0951 /* OTDetermineCDPBitStatusOperation.m */,
                                DCFF82722162876400D54B02 /* OTResetOperation.h */,
                                DCFF82732162876400D54B02 /* OTResetOperation.m */,
                                0C00FC85217A972E00C8BF00 /* OTLocalCuttlefishReset.h */,
                                DCFF82722162876400D54B02 /* OTResetOperation.h */,
                                DCFF82732162876400D54B02 /* OTResetOperation.m */,
                                0C00FC85217A972E00C8BF00 /* OTLocalCuttlefishReset.h */,
                                DCC78C701D8085D800865A7C /* secd-83-item-match-valid-on-date.m */,
                                DCC78C711D8085D800865A7C /* secd-83-item-match-trusted.m */,
                                DCC78C721D8085D800865A7C /* secd-83-item-match.h */,
                                DCC78C701D8085D800865A7C /* secd-83-item-match-valid-on-date.m */,
                                DCC78C711D8085D800865A7C /* secd-83-item-match-trusted.m */,
                                DCC78C721D8085D800865A7C /* secd-83-item-match.h */,
-                               DCC78C741D8085D800865A7C /* secd-95-escrow-persistence.m */,
                                DCC78C751D8085D800865A7C /* secd-100-initialsync.m */,
                                DCC78C761D8085D800865A7C /* secd-130-other-peer-views.m */,
                                DCC78C771D8085D800865A7C /* secd-154-engine-backoff.m */,
                                DCC78C751D8085D800865A7C /* secd-100-initialsync.m */,
                                DCC78C761D8085D800865A7C /* secd-130-other-peer-views.m */,
                                DCC78C771D8085D800865A7C /* secd-154-engine-backoff.m */,
                                DCC78D021D8085F200865A7C /* sc-42-circlegencount.c */,
                                DCC78D031D8085F200865A7C /* sc-45-digestvector.c */,
                                DCC78D041D8085F200865A7C /* sc-130-resignationticket.c */,
                                DCC78D021D8085F200865A7C /* sc-42-circlegencount.c */,
                                DCC78D031D8085F200865A7C /* sc-45-digestvector.c */,
                                DCC78D041D8085F200865A7C /* sc-130-resignationticket.c */,
-                               DCC78D061D8085F200865A7C /* sc-150-ring.m */,
                                DCC78D071D8085F200865A7C /* sc-150-backupkeyderivation.c */,
                                DCC78D081D8085F200865A7C /* sc-153-backupslicekeybag.c */,
                                DCC78D091D8085F200865A7C /* SOSCircle_regressions.h */,
                                DCC78D071D8085F200865A7C /* sc-150-backupkeyderivation.c */,
                                DCC78D081D8085F200865A7C /* sc-153-backupslicekeybag.c */,
                                DCC78D091D8085F200865A7C /* SOSCircle_regressions.h */,
                                DCC78D2A1D8085F200865A7C /* SOSBackupSliceKeyBag.h */,
                                48776C731DA5BB4200CC09B9 /* SOSRecoveryKeyBag.m */,
                                48776C741DA5BB4200CC09B9 /* SOSRecoveryKeyBag.h */,
                                DCC78D2A1D8085F200865A7C /* SOSBackupSliceKeyBag.h */,
                                48776C731DA5BB4200CC09B9 /* SOSRecoveryKeyBag.m */,
                                48776C741DA5BB4200CC09B9 /* SOSRecoveryKeyBag.h */,
-                               48E6171A1DBEC40D0098EAAD /* SOSBackupInformation.m */,
-                               48E6171B1DBEC40D0098EAAD /* SOSBackupInformation.h */,
                                DCC78D2B1D8085F200865A7C /* SOSUserKeygen.m */,
                                DCC78D2C1D8085F200865A7C /* SOSUserKeygen.h */,
                                485B64081DC16E8300B771B9 /* SOSKeyedPubKeyIdentifier.c */,
                                DCC78D2B1D8085F200865A7C /* SOSUserKeygen.m */,
                                DCC78D2C1D8085F200865A7C /* SOSUserKeygen.h */,
                                485B64081DC16E8300B771B9 /* SOSKeyedPubKeyIdentifier.c */,
                                DCC78D9B1D8085F200865A7C /* keychain_log.m */,
                                0C0CEC9D1DA45EA200C22FBC /* recovery_key.h */,
                                0C0CEC9E1DA45EA200C22FBC /* recovery_key.m */,
                                DCC78D9B1D8085F200865A7C /* keychain_log.m */,
                                0C0CEC9D1DA45EA200C22FBC /* recovery_key.h */,
                                0C0CEC9E1DA45EA200C22FBC /* recovery_key.m */,
-                               DCC78D9D1D8085F200865A7C /* syncbackup.m */,
-                               DCC78D9C1D8085F200865A7C /* syncbackup.h */,
                                DCC78D9E1D8085F200865A7C /* secViewDisplay.c */,
                                DCC78D9F1D8085F200865A7C /* secViewDisplay.h */,
                                48C2F9321E4BCFC30093D70C /* accountCirclesViewsPrint.m */,
                                DCC78D9E1D8085F200865A7C /* secViewDisplay.c */,
                                DCC78D9F1D8085F200865A7C /* secViewDisplay.h */,
                                48C2F9321E4BCFC30093D70C /* accountCirclesViewsPrint.m */,
                                DC0B62261D90973900D43BCB /* si-25-cms-skid.h */,
                                DC0B62271D90973900D43BCB /* si-25-cms-skid.m */,
                                DCC78DC61D8085FC00865A7C /* si-26-sectrust-copyproperties.c */,
                                DC0B62261D90973900D43BCB /* si-25-cms-skid.h */,
                                DC0B62271D90973900D43BCB /* si-25-cms-skid.m */,
                                DCC78DC61D8085FC00865A7C /* si-26-sectrust-copyproperties.c */,
-                               DCC78DC71D8085FC00865A7C /* si-27-sectrust-exceptions.c */,
                                DCC78DC81D8085FC00865A7C /* si-28-sectrustsettings.m */,
                                DCC78DC91D8085FC00865A7C /* si-28-sectrustsettings.h */,
                                D4AA0D9922FB959600D77FA4 /* si-29-cms-chain-mode.m */,
                                DCC78DC81D8085FC00865A7C /* si-28-sectrustsettings.m */,
                                DCC78DC91D8085FC00865A7C /* si-28-sectrustsettings.h */,
                                D4AA0D9922FB959600D77FA4 /* si-29-cms-chain-mode.m */,
                E7D848031C6BEFAB0025BB44 /* Tests */ = {
                        isa = PBXGroup;
                        children = (
                E7D848031C6BEFAB0025BB44 /* Tests */ = {
                        isa = PBXGroup;
                        children = (
+                               52DA3C6F23C7E63500FEEDFF /* KCTLKRequestTest.m */,
                                E7CFF7221C8660A000E3484E /* KeychainCircle.plist */,
                                E7D848061C6BEFFA0025BB44 /* Info.plist */,
                                E7D848041C6BEFC10025BB44 /* KCSRPTests.m */,
                                E7CFF7221C8660A000E3484E /* KeychainCircle.plist */,
                                E7D848061C6BEFFA0025BB44 /* Info.plist */,
                                E7D848041C6BEFC10025BB44 /* KCSRPTests.m */,
                E7FCBE401314471B000DE34E /* Frameworks */ = {
                        isa = PBXGroup;
                        children = (
                E7FCBE401314471B000DE34E /* Frameworks */ = {
                        isa = PBXGroup;
                        children = (
+                               DC89608C2395C75500D339D9 /* CoreServices.framework */,
                                BEC6A9142331992800080069 /* Network.framework */,
                                D47AB2CA2356AD72005A3801 /* Network.framework */,
                                0C6C2B6C2258295D00C53C96 /* UIKitCore.framework */,
                                BEC6A9142331992800080069 /* Network.framework */,
                                D47AB2CA2356AD72005A3801 /* Network.framework */,
                                0C6C2B6C2258295D00C53C96 /* UIKitCore.framework */,
                                DC9082C61EA027DB00D0C1C5 /* CKKSZoneChangeFetcher.h in Headers */,
                                DC614C5122A9BDB500E16ADA /* CKKSZoneModifier.h in Headers */,
                                DCA4D2151E5684220056214F /* CKKSReencryptOutgoingItemsOperation.h in Headers */,
                                DC9082C61EA027DB00D0C1C5 /* CKKSZoneChangeFetcher.h in Headers */,
                                DC614C5122A9BDB500E16ADA /* CKKSZoneModifier.h in Headers */,
                                DCA4D2151E5684220056214F /* CKKSReencryptOutgoingItemsOperation.h in Headers */,
+                               DC0D16012363A1D6007F0951 /* OTSetCDPBitOperation.h in Headers */,
                                0C38AA96212B2D1E00C90A1D /* OTClientVoucherOperation.h in Headers */,
                                DC378B3C1DF0CA7200A3DAFA /* CKKSIncomingQueueEntry.h in Headers */,
                                DC5F65AE2225C22C0051E9FA /* CKKSProvideKeySetOperation.h in Headers */,
                                0C38AA96212B2D1E00C90A1D /* OTClientVoucherOperation.h in Headers */,
                                DC378B3C1DF0CA7200A3DAFA /* CKKSIncomingQueueEntry.h in Headers */,
                                DC5F65AE2225C22C0051E9FA /* CKKSProvideKeySetOperation.h in Headers */,
                                DC52E7E61D80BE7B00B0A59C /* SecItemDb.h in Headers */,
                                DCF12673218A757A000124C6 /* OTLeaveCliqueOperation.h in Headers */,
                                DCAD9B441F8D939C00C5E2AE /* CKKSFixups.h in Headers */,
                                DC52E7E61D80BE7B00B0A59C /* SecItemDb.h in Headers */,
                                DCF12673218A757A000124C6 /* OTLeaveCliqueOperation.h in Headers */,
                                DCAD9B441F8D939C00C5E2AE /* CKKSFixups.h in Headers */,
+                               DC0D16062363BAF4007F0951 /* OTDetermineCDPBitStatusOperation.h in Headers */,
                                DC9C95B41F79CFD1000D19E5 /* CKKSControl.h in Headers */,
                                DC52E7EA1D80BE9500B0A59C /* SecItemSchema.h in Headers */,
                                DC7F79BA22EA5C73001FB69A /* OTLocalCKKSResetOperation.h in Headers */,
                                DC9C95B41F79CFD1000D19E5 /* CKKSControl.h in Headers */,
                                DC52E7EA1D80BE9500B0A59C /* SecItemSchema.h in Headers */,
                                DC7F79BA22EA5C73001FB69A /* OTLocalCKKSResetOperation.h in Headers */,
                                DC52E9161D80C41A00B0A59C /* SOSPeerInfoInternal.h in Headers */,
                                DC3C735A1D837C0000F6A832 /* SOSPeerInfoPriv.h in Headers */,
                                DC52E91A1D80C43500B0A59C /* SOSRing.h in Headers */,
                                DC52E9161D80C41A00B0A59C /* SOSPeerInfoInternal.h in Headers */,
                                DC3C735A1D837C0000F6A832 /* SOSPeerInfoPriv.h in Headers */,
                                DC52E91A1D80C43500B0A59C /* SOSRing.h in Headers */,
-                               48E617221DBEC6C60098EAAD /* SOSBackupInformation.h in Headers */,
                                CD198F971DE27B9E00F6FB83 /* SOSAccountPriv.h in Headers */,
                                DC52E9231D80C47100B0A59C /* SOSTransportCircleKVS.h in Headers */,
                                DC52E92C1D80C4AF00B0A59C /* SOSTransportKeyParameter.h in Headers */,
                                CD198F971DE27B9E00F6FB83 /* SOSAccountPriv.h in Headers */,
                                DC52E9231D80C47100B0A59C /* SOSTransportCircleKVS.h in Headers */,
                                DC52E92C1D80C4AF00B0A59C /* SOSTransportKeyParameter.h in Headers */,
                                DC3502B11E0208BE00BC0587 /* Sources */,
                                DC3502B21E0208BE00BC0587 /* Frameworks */,
                                DC9A2C791EB40A64008FAC27 /* Embed OCMock */,
                                DC3502B11E0208BE00BC0587 /* Sources */,
                                DC3502B21E0208BE00BC0587 /* Frameworks */,
                                DC9A2C791EB40A64008FAC27 /* Embed OCMock */,
-                               DC7162D41EB4154D000D2BB5 /* Copy BATS Test Discovery Plist */,
                                DC7162D61EB4157D000D2BB5 /* ShellScript */,
                        );
                        buildRules = (
                                DC7162D61EB4157D000D2BB5 /* ShellScript */,
                        );
                        buildRules = (
                        isa = PBXProject;
                        attributes = {
                                LastSwiftUpdateCheck = 1000;
                        isa = PBXProject;
                        attributes = {
                                LastSwiftUpdateCheck = 1000;
-                               LastUpgradeCheck = 1000;
+                               LastUpgradeCheck = 1120;
                                TargetAttributes = {
                                        4381690B1B4EDCBD00C54D58 = {
                                                CreatedOnToolsVersion = 7.0;
                                TargetAttributes = {
                                        4381690B1B4EDCBD00C54D58 = {
                                                CreatedOnToolsVersion = 7.0;
                                EB7E90F12193F90700B1FA21 /* Build C2 Metrics */,
                                3D58392D21890FFB000ACA44 /* SecExperimentTests */,
                                5A442F81233C330F00918373 /* experimentTool */,
                                EB7E90F12193F90700B1FA21 /* Build C2 Metrics */,
                                3D58392D21890FFB000ACA44 /* SecExperimentTests */,
                                5A442F81233C330F00918373 /* experimentTool */,
+                               0CA378E123876DD100090B7E /* reset_account */,
                        );
                };
 /* End PBXProject section */
                        );
                };
 /* End PBXProject section */
                                D458C51A214E2CC80043D982 /* si-20-sectrust-policies-data in Resources */,
                                D453A4BC2122236D00850A26 /* si-82-sectrust-ct-data in Resources */,
                                D4FD4226217D7C41002B7EE2 /* si-87-sectrust-name-constraints in Resources */,
                                D458C51A214E2CC80043D982 /* si-20-sectrust-policies-data in Resources */,
                                D453A4BC2122236D00850A26 /* si-82-sectrust-ct-data in Resources */,
                                D4FD4226217D7C41002B7EE2 /* si-87-sectrust-name-constraints in Resources */,
+                               D477CB79237E484300C02355 /* si-88-sectrust-valid-data in Resources */,
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
                                D458C517214E2C690043D982 /* si-20-sectrust-policies-data in Resources */,
                                D4A0F8C2211E6A2F00443CA1 /* si-82-sectrust-ct-data in Resources */,
                                D4FD4227217D7C4F002B7EE2 /* si-87-sectrust-name-constraints in Resources */,
                                D458C517214E2C690043D982 /* si-20-sectrust-policies-data in Resources */,
                                D4A0F8C2211E6A2F00443CA1 /* si-82-sectrust-ct-data in Resources */,
                                D4FD4227217D7C4F002B7EE2 /* si-87-sectrust-name-constraints in Resources */,
+                               D477CB78237E482800C02355 /* si-88-sectrust-valid-data in Resources */,
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
                        );
                        runOnlyForDeploymentPostprocessing = 1;
                        shellPath = /bin/sh;
                        );
                        runOnlyForDeploymentPostprocessing = 1;
                        shellPath = /bin/sh;
-                       shellScript = "[ \"$(whoami)\" == \"root\" ] || exit 0\nchown -f root:wheel ${DSTROOT}/AppleInternal/CoreOS/BATS/unit_tests/*.plist\n";
+                       shellScript = "CKKSTESTS_DIR=${SRCROOT}/keychain/ckks/tests\npython ${CKKSTESTS_DIR}/gen_test_plist.py ${CKKSTESTS_DIR} ${DSTROOT}/AppleInternal/CoreOS/BATS/unit_tests/KeychainCKKS.plist\n\n[ \"$(whoami)\" == \"root\" ] || exit 0\nchown -f root:wheel ${DSTROOT}/AppleInternal/CoreOS/BATS/unit_tests/*.plist\n";
                };
                DC82FFE61D90D3F60085674B /* security_utilities DTrace */ = {
                        isa = PBXShellScriptBuildPhase;
                };
                DC82FFE61D90D3F60085674B /* security_utilities DTrace */ = {
                        isa = PBXShellScriptBuildPhase;
                                0CB582D1218920090040C5F2 /* OTAuthenticatedCiphertext.m in Sources */,
                                0CF70BD9218BED1000EC3515 /* CuttlefishExtensionWorkaround.swift in Sources */,
                                DC754C742228B59000A39C8E /* TrustedPeersHelperProtocol.m in Sources */,
                                0CB582D1218920090040C5F2 /* OTAuthenticatedCiphertext.m in Sources */,
                                0CF70BD9218BED1000EC3515 /* CuttlefishExtensionWorkaround.swift in Sources */,
                                DC754C742228B59000A39C8E /* TrustedPeersHelperProtocol.m in Sources */,
+                               DC3A9B2823A9D8C40073ED06 /* Container_BottledPeers.swift in Sources */,
                                DC391FA621C04D1500772585 /* OctagonPeerKeys.swift in Sources */,
                                DCF6320521C074F30030CCC0 /* CuttlefishAPIHelpers.swift in Sources */,
                                DC391F8C21BF222B00772585 /* CKKSTLKShare.m in Sources */,
                                DC391FA621C04D1500772585 /* OctagonPeerKeys.swift in Sources */,
                                DCF6320521C074F30030CCC0 /* CuttlefishAPIHelpers.swift in Sources */,
                                DC391F8C21BF222B00772585 /* CKKSTLKShare.m in Sources */,
+                               DCAA209C23AAF93700DCB594 /* Container_RecoveryKey.swift in Sources */,
                                BE9F8D19206C4AD300B53D16 /* ContainerMap.swift in Sources */,
                                BE9F4F8C2072D881004A52C2 /* Cuttlefish.pb.swift in Sources */,
                                DC391F9D21BF2F8100772585 /* CKKSConstants.m in Sources */,
                                BE9F8D19206C4AD300B53D16 /* ContainerMap.swift in Sources */,
                                BE9F4F8C2072D881004A52C2 /* Cuttlefish.pb.swift in Sources */,
                                DC391F9D21BF2F8100772585 /* CKKSConstants.m in Sources */,
                                BED987E120991B9B00607A5F /* Decrypter.swift in Sources */,
                                DC391FA721C04D6800772585 /* OctagonPeerKeys.swift in Sources */,
                                BE536033209BC3B30027E25A /* server_entitlement_helpers.c in Sources */,
                                BED987E120991B9B00607A5F /* Decrypter.swift in Sources */,
                                DC391FA721C04D6800772585 /* OctagonPeerKeys.swift in Sources */,
                                BE536033209BC3B30027E25A /* server_entitlement_helpers.c in Sources */,
+                               DCAA209B23AAF8FD00DCB594 /* Container_RecoveryKey.swift in Sources */,
                                BED987E020991B1100607A5F /* TrustedPeersHelper.xcdatamodeld in Sources */,
                                BED987D62099145300607A5F /* TrustedPeersHelperUnitTests.swift in Sources */,
                                BE4C6AB820CAF4F700EAD6BE /* ContainerSync.swift in Sources */,
                                BED987E020991B1100607A5F /* TrustedPeersHelper.xcdatamodeld in Sources */,
                                BED987D62099145300607A5F /* TrustedPeersHelperUnitTests.swift in Sources */,
                                BE4C6AB820CAF4F700EAD6BE /* ContainerSync.swift in Sources */,
                                DCAD8F8922C43EDB007C3872 /* Container_MachineIDs.swift in Sources */,
                                0CF70BDA218BEFAE00EC3515 /* CuttlefishExtensionWorkaround.swift in Sources */,
                                DCD24DEC228C9A480052604C /* SetValueTransformer.swift in Sources */,
                                DCAD8F8922C43EDB007C3872 /* Container_MachineIDs.swift in Sources */,
                                0CF70BDA218BEFAE00EC3515 /* CuttlefishExtensionWorkaround.swift in Sources */,
                                DCD24DEC228C9A480052604C /* SetValueTransformer.swift in Sources */,
+                               DC14C4C223AAACED007F673F /* Container_BottledPeers.swift in Sources */,
                                BEC0A96520B362EC00DBD772 /* Utils.swift in Sources */,
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                                BEC0A96520B362EC00DBD772 /* Utils.swift in Sources */,
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                                D4FD4221217D7B2E002B7EE2 /* PathScoringTests.m in Sources */,
                                D40881F42175738C00180E81 /* SecPolicyLeafCallbacks.c in Sources */,
                                D40881F32175733F00180E81 /* SecPolicy.c in Sources */,
                                D4FD4221217D7B2E002B7EE2 /* PathScoringTests.m in Sources */,
                                D40881F42175738C00180E81 /* SecPolicyLeafCallbacks.c in Sources */,
                                D40881F32175733F00180E81 /* SecPolicy.c in Sources */,
+                               D477CB7C237E4BD700C02355 /* ExceptionTests.m in Sources */,
                                D40881F22175733500180E81 /* SecSignatureVerificationSupport.c in Sources */,
                                D40881F12175732E00180E81 /* SecCertificate.c in Sources */,
                                D40881F02175732600180E81 /* SecTrustStore.c in Sources */,
                                D40881F22175733500180E81 /* SecSignatureVerificationSupport.c in Sources */,
                                D40881F12175732E00180E81 /* SecCertificate.c in Sources */,
                                D40881F02175732600180E81 /* SecTrustStore.c in Sources */,
                                D458C525214E33440043D982 /* VerifyDateTests.m in Sources */,
                                D49A370623873BD30065719F /* TrustDaemonTestCase.m in Sources */,
                                D458C515214E286C0043D982 /* PolicyTests.m in Sources */,
                                D458C525214E33440043D982 /* VerifyDateTests.m in Sources */,
                                D49A370623873BD30065719F /* TrustDaemonTestCase.m in Sources */,
                                D458C515214E286C0043D982 /* PolicyTests.m in Sources */,
+                               D477CB7B237E4BD700C02355 /* ExceptionTests.m in Sources */,
                                D4EF32182156DDEB000A31A5 /* TrustSettingsInterfaceTests.m in Sources */,
                                D44282FF22D68564001746B3 /* TrustEvaluationTestHelpers.m in Sources */,
                        );
                                D4EF32182156DDEB000A31A5 /* TrustSettingsInterfaceTests.m in Sources */,
                                D44282FF22D68564001746B3 /* TrustEvaluationTestHelpers.m in Sources */,
                        );
                                DCDCCB901DF7B8D4006E840E /* CKKSItem.m in Sources */,
                                DC1ED8C11DD5197E002BDCFA /* CKKSItemEncrypter.m in Sources */,
                                0CDD6F79226E83F6009094C2 /* OTTriggerEscrowUpdateOperation.m in Sources */,
                                DCDCCB901DF7B8D4006E840E /* CKKSItem.m in Sources */,
                                DC1ED8C11DD5197E002BDCFA /* CKKSItemEncrypter.m in Sources */,
                                0CDD6F79226E83F6009094C2 /* OTTriggerEscrowUpdateOperation.m in Sources */,
+                               DC0D16072363BAF4007F0951 /* OTDetermineCDPBitStatusOperation.m in Sources */,
                                DC6D2C921DD2835A00BE372D /* CKKSOutgoingQueueEntry.m in Sources */,
                                DC378B3D1DF0CA7200A3DAFA /* CKKSIncomingQueueEntry.m in Sources */,
                                6C880FCB21C3351400D38D66 /* SecDbBackupMetadataClassKey.m in Sources */,
                                DC6D2C921DD2835A00BE372D /* CKKSOutgoingQueueEntry.m in Sources */,
                                DC378B3D1DF0CA7200A3DAFA /* CKKSIncomingQueueEntry.m in Sources */,
                                6C880FCB21C3351400D38D66 /* SecDbBackupMetadataClassKey.m in Sources */,
                                DCB41DFC216D5E5B00F219E0 /* OTAccountMetadataClassC+KeychainSupport.m in Sources */,
                                6C880FCC21C3351400D38D66 /* SecDbBackupRecoverySet.m in Sources */,
                                DCA4D1FF1E552DD50056214F /* CKKSCurrentKeyPointer.m in Sources */,
                                DCB41DFC216D5E5B00F219E0 /* OTAccountMetadataClassC+KeychainSupport.m in Sources */,
                                6C880FCC21C3351400D38D66 /* SecDbBackupRecoverySet.m in Sources */,
                                DCA4D1FF1E552DD50056214F /* CKKSCurrentKeyPointer.m in Sources */,
+                               DC0D16022363A1D6007F0951 /* OTSetCDPBitOperation.m in Sources */,
                                EB7E91212194849900B1FA21 /* SECC2MPMetric.m in Sources */,
                                DA6AA1651FE88AFB004565B0 /* CKKSControlServer.m in Sources */,
                                DCFE1C531F1825F7007640C8 /* CKKSUpdateDeviceStateOperation.m in Sources */,
                                EB7E91212194849900B1FA21 /* SECC2MPMetric.m in Sources */,
                                DA6AA1651FE88AFB004565B0 /* CKKSControlServer.m in Sources */,
                                DCFE1C531F1825F7007640C8 /* CKKSUpdateDeviceStateOperation.m in Sources */,
                        isa = PBXSourcesBuildPhase;
                        buildActionMask = 2147483647;
                        files = (
                        isa = PBXSourcesBuildPhase;
                        buildActionMask = 2147483647;
                        files = (
-                               48E617211DBEC6BA0098EAAD /* SOSBackupInformation.m in Sources */,
                                DC52E8F11D80C34000B0A59C /* SOSAccount.m in Sources */,
                                DC52E8F31D80C34000B0A59C /* SOSAccountBackup.m in Sources */,
                                DCB332591F478C3C00178C30 /* SOSUserKeygen.m in Sources */,
                                DC52E8F11D80C34000B0A59C /* SOSAccount.m in Sources */,
                                DC52E8F31D80C34000B0A59C /* SOSAccountBackup.m in Sources */,
                                DCB332591F478C3C00178C30 /* SOSUserKeygen.m in Sources */,
                        files = (
                                DCD7EE841F4E46F9007D9804 /* accountCirclesViewsPrint.m in Sources */,
                                0C0CECA41DA45ED700C22FBC /* recovery_key.m in Sources */,
                        files = (
                                DCD7EE841F4E46F9007D9804 /* accountCirclesViewsPrint.m in Sources */,
                                0C0CECA41DA45ED700C22FBC /* recovery_key.m in Sources */,
-                               DC52EC3B1D80CFE900B0A59C /* syncbackup.m in Sources */,
                                DC52EC3A1D80CFE400B0A59C /* keychain_log.m in Sources */,
                                DC52EC391D80CFDF00B0A59C /* secViewDisplay.c in Sources */,
                                DC52EC381D80CFDB00B0A59C /* secToolFileIO.c in Sources */,
                                DC52EC3A1D80CFE400B0A59C /* keychain_log.m in Sources */,
                                DC52EC391D80CFDF00B0A59C /* secViewDisplay.c in Sources */,
                                DC52EC381D80CFDB00B0A59C /* secToolFileIO.c in Sources */,
                                DC52EC791D80D14D00B0A59C /* sc-45-digestvector.c in Sources */,
                                DC52EC781D80D14800B0A59C /* SOSRegressionUtilities.m in Sources */,
                                DC52EC771D80D14400B0A59C /* sc-130-resignationticket.c in Sources */,
                                DC52EC791D80D14D00B0A59C /* sc-45-digestvector.c in Sources */,
                                DC52EC781D80D14800B0A59C /* SOSRegressionUtilities.m in Sources */,
                                DC52EC771D80D14400B0A59C /* sc-130-resignationticket.c in Sources */,
-                               DC52EC761D80D13F00B0A59C /* sc-150-ring.m in Sources */,
                                DC52EC751D80D13B00B0A59C /* sc-42-circlegencount.c in Sources */,
                                DC52EC741D80D13500B0A59C /* SOSTestDataSource.c in Sources */,
                                DC52EC731D80D12E00B0A59C /* sc-20-keynames.m in Sources */,
                                DC52EC751D80D13B00B0A59C /* sc-42-circlegencount.c in Sources */,
                                DC52EC741D80D13500B0A59C /* SOSTestDataSource.c in Sources */,
                                DC52EC731D80D12E00B0A59C /* sc-20-keynames.m in Sources */,
                        files = (
                                DC52EDA01D80D4F700B0A59C /* sd-10-policytree.m in Sources */,
                                DC52ED9F1D80D4F200B0A59C /* SOSTransportTestTransports.m in Sources */,
                        files = (
                                DC52EDA01D80D4F700B0A59C /* sd-10-policytree.m in Sources */,
                                DC52ED9F1D80D4F200B0A59C /* SOSTransportTestTransports.m in Sources */,
-                               DC52ED9E1D80D4ED00B0A59C /* secd-95-escrow-persistence.m in Sources */,
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
                                DC52EDF51D80D62E00B0A59C /* SecdTestKeychainUtilities.c in Sources */,
                                DC52EDF61D80D62E00B0A59C /* SOSTransportTestTransports.m in Sources */,
                                EB9C02481E8A15B40040D3C6 /* secd-37-pairing-initial-sync.m in Sources */,
                                DC52EDF51D80D62E00B0A59C /* SecdTestKeychainUtilities.c in Sources */,
                                DC52EDF61D80D62E00B0A59C /* SOSTransportTestTransports.m in Sources */,
                                EB9C02481E8A15B40040D3C6 /* secd-37-pairing-initial-sync.m in Sources */,
-                               0CAD1E5E1E1C5D0600537693 /* secd-95-escrow-persistence.m in Sources */,
                                DC52EDB51D80D5C500B0A59C /* secd-03-corrupted-items.m in Sources */,
                                0CAD1E5D1E1C5CF900537693 /* secd-80-views-alwayson.m in Sources */,
                                DC52EDB61D80D5C500B0A59C /* secd-04-corrupted-items.m in Sources */,
                                DC52EDB51D80D5C500B0A59C /* secd-03-corrupted-items.m in Sources */,
                                0CAD1E5D1E1C5CF900537693 /* secd-80-views-alwayson.m in Sources */,
                                DC52EDB61D80D5C500B0A59C /* secd-04-corrupted-items.m in Sources */,
                                DC52EE4C1D80D71900B0A59C /* si-24-sectrust-passbook.c in Sources */,
                                DC52EE4D1D80D71900B0A59C /* si-26-sectrust-copyproperties.c in Sources */,
                                5E7793751E5F025A0074A2D1 /* si-44-seckey-aks.m in Sources */,
                                DC52EE4C1D80D71900B0A59C /* si-24-sectrust-passbook.c in Sources */,
                                DC52EE4D1D80D71900B0A59C /* si-26-sectrust-copyproperties.c in Sources */,
                                5E7793751E5F025A0074A2D1 /* si-44-seckey-aks.m in Sources */,
-                               DC52EE4E1D80D71900B0A59C /* si-27-sectrust-exceptions.c in Sources */,
                                DC52EE4F1D80D71900B0A59C /* si-28-sectrustsettings.m in Sources */,
                                DC52EE531D80D73800B0A59C /* si-44-seckey-gen.m in Sources */,
                                DC52EE541D80D73800B0A59C /* si-44-seckey-rsa.m in Sources */,
                                DC52EE4F1D80D71900B0A59C /* si-28-sectrustsettings.m in Sources */,
                                DC52EE531D80D73800B0A59C /* si-44-seckey-gen.m in Sources */,
                                DC52EE541D80D73800B0A59C /* si-44-seckey-rsa.m in Sources */,
                        isa = PBXSourcesBuildPhase;
                        buildActionMask = 2147483647;
                        files = (
                        isa = PBXSourcesBuildPhase;
                        buildActionMask = 2147483647;
                        files = (
+                               DCE405C523A04A7F00C4343B /* OctagonTests+CKKSConfiguration.swift in Sources */,
                                1B8D2D96226E1FA500C94238 /* SetValueTransformer.swift in Sources */,
                                DC5F2BBE2310B941001ADA5D /* OctagonTests+CoreFollowUp.swift in Sources */,
                                DCB468E520EC262C00BA7E5B /* ContainerMap.swift in Sources */,
                                1B8D2D96226E1FA500C94238 /* SetValueTransformer.swift in Sources */,
                                DC5F2BBE2310B941001ADA5D /* OctagonTests+CoreFollowUp.swift in Sources */,
                                DCB468E520EC262C00BA7E5B /* ContainerMap.swift in Sources */,
                                DC27C3C920EADEE700F7839C /* MockCloudKit.m in Sources */,
                                0CF70BE0218CF26600EC3515 /* BottledPeer.swift in Sources */,
                                DC99B86B20EACA470065B73B /* spi.c in Sources */,
                                DC27C3C920EADEE700F7839C /* MockCloudKit.m in Sources */,
                                0CF70BE0218CF26600EC3515 /* BottledPeer.swift in Sources */,
                                DC99B86B20EACA470065B73B /* spi.c in Sources */,
+                               DC4CD9842372294E00EF55FC /* OctagonTests+Helpers.swift in Sources */,
+                               DCAA209A23AAF8F600DCB594 /* Container_RecoveryKey.swift in Sources */,
                                DCDF03122284E34B008055BA /* OctagonTests+EscrowRecovery.swift in Sources */,
                                DCFF82712162834D00D54B02 /* OctagonTestsXPCConnections.swift in Sources */,
                                DC5BEACD2217509A001681F0 /* OctagonTests+CloudKitAccount.swift in Sources */,
                                DCDF03122284E34B008055BA /* OctagonTests+EscrowRecovery.swift in Sources */,
                                DCFF82712162834D00D54B02 /* OctagonTestsXPCConnections.swift in Sources */,
                                DC5BEACD2217509A001681F0 /* OctagonTests+CloudKitAccount.swift in Sources */,
                                0CBEF3432242CA0600015691 /* TestsObjcTranslation.m in Sources */,
                                5A04BB0222982733001848A0 /* OTFollowupTests.m in Sources */,
                                DCB24B45221B901700BE73FE /* CKKSMockSOSPresentAdapter.m in Sources */,
                                0CBEF3432242CA0600015691 /* TestsObjcTranslation.m in Sources */,
                                5A04BB0222982733001848A0 /* OTFollowupTests.m in Sources */,
                                DCB24B45221B901700BE73FE /* CKKSMockSOSPresentAdapter.m in Sources */,
+                               DC33D7BE2374FD0A00A68155 /* OTSponsorToApplicantRound2M2.m in Sources */,
                                0CE15E31222DF63600B7EAA4 /* RecoverKeySet.swift in Sources */,
                                0CADDF0721545CF100DF8B06 /* OctagonPairingTests.swift in Sources */,
                                DC99B86D20EACA470065B73B /* SecdWatchdog.m in Sources */,
                                0CE15E31222DF63600B7EAA4 /* RecoverKeySet.swift in Sources */,
                                0CADDF0721545CF100DF8B06 /* OctagonPairingTests.swift in Sources */,
                                DC99B86D20EACA470065B73B /* SecdWatchdog.m in Sources */,
                                DCB0C291222F5E130083AECB /* CuttlefishErrors.swift in Sources */,
                                DC99B86F20EACA470065B73B /* SecFramework.c in Sources */,
                                0C5258BB21BB128000B32C96 /* FakeSOSControl.m in Sources */,
                                DCB0C291222F5E130083AECB /* CuttlefishErrors.swift in Sources */,
                                DC99B86F20EACA470065B73B /* SecFramework.c in Sources */,
                                0C5258BB21BB128000B32C96 /* FakeSOSControl.m in Sources */,
+                               DC3A9B2723A9D8BD0073ED06 /* Container_BottledPeers.swift in Sources */,
                                DC99B87020EACA470065B73B /* server_endpoint.m in Sources */,
                                0CD5797A21498F8200C43496 /* OctagonPairingTests+Piggybacking.swift in Sources */,
                                DC99B87020EACA470065B73B /* server_endpoint.m in Sources */,
                                0CD5797A21498F8200C43496 /* OctagonPairingTests+Piggybacking.swift in Sources */,
+                               DC4A73C5235E69D800DB1E6E /* OTApplicantToSponsorRound2M1.m in Sources */,
                                DCAD8F8722C43ECA007C3872 /* Container_MachineIDs.swift in Sources */,
                                DCAD8F8722C43ECA007C3872 /* Container_MachineIDs.swift in Sources */,
+                               DC03592D235FCCD500F14883 /* KCInitialMessageData.m in Sources */,
                                DC99B87120EACA470065B73B /* server_security_helpers.m in Sources */,
                                0CF70BE1218CF27600EC3515 /* EscrowKeys.swift in Sources */,
                                DC7F79B622EA4ED4001FB69A /* OctagonTests+CKKS.swift in Sources */,
                                0CBA047D214C4E4D005B3A2F /* OctagonPairingTests+ProxMultiClients.swift in Sources */,
                                DCF6320821C09DCE0030CCC0 /* CuttlefishAPIHelpers.swift in Sources */,
                                0CE15E44222DF6A800B7EAA4 /* Recovery.m in Sources */,
                                DC99B87120EACA470065B73B /* server_security_helpers.m in Sources */,
                                0CF70BE1218CF27600EC3515 /* EscrowKeys.swift in Sources */,
                                DC7F79B622EA4ED4001FB69A /* OctagonTests+CKKS.swift in Sources */,
                                0CBA047D214C4E4D005B3A2F /* OctagonPairingTests+ProxMultiClients.swift in Sources */,
                                DCF6320821C09DCE0030CCC0 /* CuttlefishAPIHelpers.swift in Sources */,
                                0CE15E44222DF6A800B7EAA4 /* Recovery.m in Sources */,
+                               DC4415B423610BF40087981C /* OctagonTests+Account.swift in Sources */,
                                DC725030229600C000493D88 /* OctagonTests+Reset.swift in Sources */,
                                0C3E316B21372FA50093C04B /* OctagonPairingTests+ProximitySetup.swift in Sources */,
                                DC2819B922F8F6FE007829F5 /* OctagonTests+DeviceList.swift in Sources */,
                                DC725030229600C000493D88 /* OctagonTests+Reset.swift in Sources */,
                                0C3E316B21372FA50093C04B /* OctagonPairingTests+ProximitySetup.swift in Sources */,
                                DC2819B922F8F6FE007829F5 /* OctagonTests+DeviceList.swift in Sources */,
                                0CF70BE2218CF2AA00EC3515 /* OTAuthenticatedCiphertext.m in Sources */,
                                DCB41E01216D5FE500F219E0 /* OctagonDataPersistenceTests.swift in Sources */,
                                DC85687E2284E7860088D3EF /* OctagonTestMocks.swift in Sources */,
                                0CF70BE2218CF2AA00EC3515 /* OTAuthenticatedCiphertext.m in Sources */,
                                DCB41E01216D5FE500F219E0 /* OctagonDataPersistenceTests.swift in Sources */,
                                DC85687E2284E7860088D3EF /* OctagonTestMocks.swift in Sources */,
+                               DC0DE87123750340006E2EAE /* OTPairingMessage.m in Sources */,
                                DC99B87320EACA470065B73B /* Decrypter.swift in Sources */,
                                DC99B87420EACA470065B73B /* server_entitlement_helpers.c in Sources */,
                                DC99B87520EACA470065B73B /* TrustedPeersHelper.xcdatamodeld in Sources */,
                                DC99B87320EACA470065B73B /* Decrypter.swift in Sources */,
                                DC99B87420EACA470065B73B /* server_entitlement_helpers.c in Sources */,
                                DC99B87520EACA470065B73B /* TrustedPeersHelper.xcdatamodeld in Sources */,
                                DC27C3C120EAD9C300F7839C /* OctagonTests.swift in Sources */,
                                0CE15E3F222DF6A800B7EAA4 /* OTRecovery.m in Sources */,
                                DCB9475A2127534C00ED9272 /* OctagonTests+SOSUpgrade.swift in Sources */,
                                DC27C3C120EAD9C300F7839C /* OctagonTests.swift in Sources */,
                                0CE15E3F222DF6A800B7EAA4 /* OTRecovery.m in Sources */,
                                DCB9475A2127534C00ED9272 /* OctagonTests+SOSUpgrade.swift in Sources */,
+                               DC7F6A7D233D7FAC00DF5769 /* OctagonTests+ForwardCompatibility.swift in Sources */,
                                DC99B87720EACA470065B73B /* ContainerSync.swift in Sources */,
                                DC99B87720EACA470065B73B /* ContainerSync.swift in Sources */,
+                               DC33D7BD2374FD0500A68155 /* OTSponsorToApplicantRound1M2.m in Sources */,
                                0C61F1F62194FC79009566D4 /* OTPrivateKey+SF.m in Sources */,
                                DC391FA821C04DAE00772585 /* OctagonPeerKeys.swift in Sources */,
                                DC99B87820EACA470065B73B /* Container.swift in Sources */,
                                0C61F1F62194FC79009566D4 /* OTPrivateKey+SF.m in Sources */,
                                DC391FA821C04DAE00772585 /* OctagonPeerKeys.swift in Sources */,
                                DC99B87820EACA470065B73B /* Container.swift in Sources */,
                                0CB72DA121E42FCF00D8BC9B /* OTSponsorToApplicantRound2M2.m in Sources */,
                                E7F480151C73980D00390FDB /* KCJoiningRequestSecretSession.m in Sources */,
                                E7F480331C73FC4C00390FDB /* KCAESGCMDuplexSession.m in Sources */,
                                0CB72DA121E42FCF00D8BC9B /* OTSponsorToApplicantRound2M2.m in Sources */,
                                E7F480151C73980D00390FDB /* KCJoiningRequestSecretSession.m in Sources */,
                                E7F480331C73FC4C00390FDB /* KCAESGCMDuplexSession.m in Sources */,
-                               0CB72D9F21E42FCF00D8BC9B /* OTSOSMessage.m in Sources */,
                                DC6063B221B09AB200069B82 /* KCJoiningRequestCircleSession.m in Sources */,
                                E794BB001C7598F900339A0F /* KCJoiningMessages.m in Sources */,
                                5A47FFB9228F5F2A00F781B8 /* KCInitialMessageData.m in Sources */,
                                DC6063B221B09AB200069B82 /* KCJoiningRequestCircleSession.m in Sources */,
                                E794BB001C7598F900339A0F /* KCJoiningMessages.m in Sources */,
                                5A47FFB9228F5F2A00F781B8 /* KCInitialMessageData.m in Sources */,
                                E7F4809C1C74E85200390FDB /* KCDerTest.m in Sources */,
                                E7D848051C6BEFCD0025BB44 /* KCSRPTests.m in Sources */,
                                E7F4809E1C74E86D00390FDB /* KCAESGCMTest.m in Sources */,
                                E7F4809C1C74E85200390FDB /* KCDerTest.m in Sources */,
                                E7D848051C6BEFCD0025BB44 /* KCSRPTests.m in Sources */,
                                E7F4809E1C74E86D00390FDB /* KCAESGCMTest.m in Sources */,
+                               52DA3C7123C7E63600FEEDFF /* KCTLKRequestTest.m in Sources */,
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
                                EB49B2D9202DF1F7003F34A0 /* server_security_helpers.m in Sources */,
                                EBC73F2B2099785900AE3350 /* SFObjCType.m in Sources */,
                                480ADDB22155A0CE00318FC6 /* SOSAnalytics.m in Sources */,
                                EB49B2D9202DF1F7003F34A0 /* server_security_helpers.m in Sources */,
                                EBC73F2B2099785900AE3350 /* SFObjCType.m in Sources */,
                                480ADDB22155A0CE00318FC6 /* SOSAnalytics.m in Sources */,
-                               EB627A79233E375A00F32437 /* MockAKSOptionalParameters.proto in Sources */,
                                EB49B2E0202DF5D7003F34A0 /* server_entitlement_helpers.c in Sources */,
                                5A061196229ED6E8006AF14A /* NSDate+SFAnalytics.m in Sources */,
                                EBC73F2A20996AD400AE3350 /* SFSQLiteStatement.m in Sources */,
                                EB49B2E0202DF5D7003F34A0 /* server_entitlement_helpers.c in Sources */,
                                5A061196229ED6E8006AF14A /* NSDate+SFAnalytics.m in Sources */,
                                EBC73F2A20996AD400AE3350 /* SFSQLiteStatement.m in Sources */,
                                EB49B2D1202DF15F003F34A0 /* SFAnalyticsActivityTracker.m in Sources */,
                                EB49B2D0202DF14D003F34A0 /* SFAnalytics.m in Sources */,
                                EBC73F2820993FDA00AE3350 /* SFAnalyticsSampler.m in Sources */,
                                EB49B2D1202DF15F003F34A0 /* SFAnalyticsActivityTracker.m in Sources */,
                                EB49B2D0202DF14D003F34A0 /* SFAnalytics.m in Sources */,
                                EBC73F2820993FDA00AE3350 /* SFAnalyticsSampler.m in Sources */,
-                               EBDCC001233DD3E000806566 /* MockAKSRefKey.proto in Sources */,
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
                        target = DC1789031D77980500B50D50 /* Security_osx */;
                        targetProxy = 0C9AEEB920783FE000BF6237 /* PBXContainerItemProxy */;
                };
                        target = DC1789031D77980500B50D50 /* Security_osx */;
                        targetProxy = 0C9AEEB920783FE000BF6237 /* PBXContainerItemProxy */;
                };
+               0CA378E923876E0900090B7E /* PBXTargetDependency */ = {
+                       isa = PBXTargetDependency;
+                       target = 0CA378E123876DD100090B7E /* reset_account */;
+                       targetProxy = 0CA378E823876E0900090B7E /* PBXContainerItemProxy */;
+               };
+               0CA378EB23876E1000090B7E /* PBXTargetDependency */ = {
+                       isa = PBXTargetDependency;
+                       target = 0CA378E123876DD100090B7E /* reset_account */;
+                       targetProxy = 0CA378EA23876E1000090B7E /* PBXContainerItemProxy */;
+               };
                0CC593F82299EDFC006C34B5 /* PBXTargetDependency */ = {
                        isa = PBXTargetDependency;
                        target = DC52E7731D80BC8000B0A59C /* libsecurityd_ios */;
                0CC593F82299EDFC006C34B5 /* PBXTargetDependency */ = {
                        isa = PBXTargetDependency;
                        target = DC52E7731D80BC8000B0A59C /* libsecurityd_ios */;
                                ALWAYS_SEARCH_USER_PATHS = NO;
                                CLANG_ANALYZER_NONNULL = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
                                ALWAYS_SEARCH_USER_PATHS = NO;
                                CLANG_ANALYZER_NONNULL = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
-                               CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
                                CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
                                CLANG_WARN_UNREACHABLE_CODE = YES;
                                CODE_SIGN_IDENTITY = "";
                                CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
                                CLANG_WARN_UNREACHABLE_CODE = YES;
                                CODE_SIGN_IDENTITY = "";
                                ALWAYS_SEARCH_USER_PATHS = NO;
                                CLANG_ANALYZER_NONNULL = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
                                ALWAYS_SEARCH_USER_PATHS = NO;
                                CLANG_ANALYZER_NONNULL = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
-                               CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
                                CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
                                CLANG_WARN_UNREACHABLE_CODE = YES;
                                CODE_SIGN_IDENTITY = "";
                                CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
                                CLANG_WARN_UNREACHABLE_CODE = YES;
                                CODE_SIGN_IDENTITY = "";
                        };
                        name = Release;
                };
                        };
                        name = Release;
                };
+               0CA378E423876DD100090B7E /* Debug */ = {
+                       isa = XCBuildConfiguration;
+                       buildSettings = {
+                               PRODUCT_NAME = "$(TARGET_NAME)";
+                       };
+                       name = Debug;
+               };
+               0CA378E523876DD100090B7E /* Release */ = {
+                       isa = XCBuildConfiguration;
+                       buildSettings = {
+                               PRODUCT_NAME = "$(TARGET_NAME)";
+                       };
+                       name = Release;
+               };
                0CF4064E2072E3E3003D6A7F /* Debug */ = {
                        isa = XCBuildConfiguration;
                        buildSettings = {
                0CF4064E2072E3E3003D6A7F /* Debug */ = {
                        isa = XCBuildConfiguration;
                        buildSettings = {
                                CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
                                CLANG_WARN_COMMA = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
                                CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
                                CLANG_WARN_COMMA = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
-                               CLANG_WARN_DOCUMENTATION_COMMENTS = NO;
                                CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
                                CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
                                CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
                                CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
                                CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
                                CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
                                CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
                                CLANG_WARN_COMMA = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
                                CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
                                CLANG_WARN_COMMA = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
-                               CLANG_WARN_DOCUMENTATION_COMMENTS = NO;
                                CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
                                CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
                                CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
                                CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
                                CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
                                CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
                                CLANG_WARN_COMMA = YES;
                                CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
                                CLANG_WARN_COMMA = YES;
                                CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
-                               CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
                                CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
                                CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
                                CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
                                CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
                                CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
                                CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
                                CLANG_WARN_COMMA = YES;
                                CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
                                CLANG_WARN_COMMA = YES;
                                CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
-                               CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
                                CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
                                CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
                                CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
                                CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
                                CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
                                CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
                                CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
                                CLANG_WARN_COMMA = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
                                CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
                                CLANG_WARN_COMMA = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
-                               CLANG_WARN_DOCUMENTATION_COMMENTS = NO;
                                CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
                                CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
                                CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
                                CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
                                CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
                                CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
                                CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
                                CLANG_WARN_COMMA = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
                                CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
                                CLANG_WARN_COMMA = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
-                               CLANG_WARN_DOCUMENTATION_COMMENTS = NO;
                                CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
                                CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
                                CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
                                CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
                                CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
                                CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
                                CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
                                CLANG_WARN_COMMA = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
                                CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
                                CLANG_WARN_COMMA = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
-                               CLANG_WARN_DOCUMENTATION_COMMENTS = NO;
                                CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
                                CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
                                CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
                                CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
                                CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
                                CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
                                CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
                                CLANG_WARN_COMMA = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
                                CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
                                CLANG_WARN_COMMA = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
-                               CLANG_WARN_DOCUMENTATION_COMMENTS = NO;
                                CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
                                CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
                                CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
                                CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
                                CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
                                CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
                                CLANG_WARN_COMMA = YES;
                                CLANG_WARN_CONSTANT_CONVERSION = YES;
                                CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
                                CLANG_WARN_COMMA = YES;
                                CLANG_WARN_CONSTANT_CONVERSION = YES;
                                CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
-                               CLANG_WARN_DOCUMENTATION_COMMENTS = NO;
+                               CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
                                CLANG_WARN_EMPTY_BODY = YES;
                                CLANG_WARN_ENUM_CONVERSION = YES;
                                CLANG_WARN_INFINITE_RECURSION = YES;
                                CLANG_WARN_EMPTY_BODY = YES;
                                CLANG_WARN_ENUM_CONVERSION = YES;
                                CLANG_WARN_INFINITE_RECURSION = YES;
                                CLANG_WARN_COMMA = YES;
                                CLANG_WARN_CONSTANT_CONVERSION = YES;
                                CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
                                CLANG_WARN_COMMA = YES;
                                CLANG_WARN_CONSTANT_CONVERSION = YES;
                                CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
-                               CLANG_WARN_DOCUMENTATION_COMMENTS = NO;
+                               CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
                                CLANG_WARN_EMPTY_BODY = YES;
                                CLANG_WARN_ENUM_CONVERSION = YES;
                                CLANG_WARN_INFINITE_RECURSION = YES;
                                CLANG_WARN_EMPTY_BODY = YES;
                                CLANG_WARN_ENUM_CONVERSION = YES;
                                CLANG_WARN_INFINITE_RECURSION = YES;
                        buildSettings = {
                                CLANG_ANALYZER_NONNULL = YES;
                                CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
                        buildSettings = {
                                CLANG_ANALYZER_NONNULL = YES;
                                CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
-                               CLANG_WARN_DOCUMENTATION_COMMENTS = NO;
                                CLANG_WARN_SUSPICIOUS_MOVES = YES;
                                GCC_C_LANGUAGE_STANDARD = gnu11;
                                GCC_PREPROCESSOR_DEFINITIONS = (
                                CLANG_WARN_SUSPICIOUS_MOVES = YES;
                                GCC_C_LANGUAGE_STANDARD = gnu11;
                                GCC_PREPROCESSOR_DEFINITIONS = (
                        buildSettings = {
                                CLANG_ANALYZER_NONNULL = YES;
                                CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
                        buildSettings = {
                                CLANG_ANALYZER_NONNULL = YES;
                                CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
-                               CLANG_WARN_DOCUMENTATION_COMMENTS = NO;
                                CLANG_WARN_SUSPICIOUS_MOVES = YES;
                                GCC_C_LANGUAGE_STANDARD = gnu11;
                                GCC_PREPROCESSOR_DEFINITIONS = (
                                CLANG_WARN_SUSPICIOUS_MOVES = YES;
                                GCC_C_LANGUAGE_STANDARD = gnu11;
                                GCC_PREPROCESSOR_DEFINITIONS = (
                        isa = XCBuildConfiguration;
                        buildSettings = {
                                ALWAYS_SEARCH_USER_PATHS = NO;
                        isa = XCBuildConfiguration;
                        buildSettings = {
                                ALWAYS_SEARCH_USER_PATHS = NO;
-                               CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
                                CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
                                GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
                                PRODUCT_NAME = "$(TARGET_NAME)";
                                CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
                                GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
                                PRODUCT_NAME = "$(TARGET_NAME)";
                        isa = XCBuildConfiguration;
                        buildSettings = {
                                ALWAYS_SEARCH_USER_PATHS = NO;
                        isa = XCBuildConfiguration;
                        buildSettings = {
                                ALWAYS_SEARCH_USER_PATHS = NO;
-                               CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
                                CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
                                GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
                                PRODUCT_NAME = "$(TARGET_NAME)";
                                CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
                                GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
                                PRODUCT_NAME = "$(TARGET_NAME)";
                                        "$(OTHER_LDFLAGS_AKS_LIBRARY)",
                                        "$(OTHER_LDFLAGS_UPWARD_PROTOCOLBUFFER)",
                                        "$(OTHER_LDFLAGS_UPWARD_FOUNDATION)",
                                        "$(OTHER_LDFLAGS_AKS_LIBRARY)",
                                        "$(OTHER_LDFLAGS_UPWARD_PROTOCOLBUFFER)",
                                        "$(OTHER_LDFLAGS_UPWARD_FOUNDATION)",
-                                       "$(OTHER_LDFLAGS_UPWARD_SECURITY)",
                                        "$(OTHER_LDFLAGS_UPWARD_SECURITYFOUNDATION)",
                                        "$(OTHER_LDFLAGS_APPLEIDAUTHSUPPORT)",
                                );
                                PRODUCT_BUNDLE_IDENTIFIER = com.apple.TrustedPeers;
                                PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
                                        "$(OTHER_LDFLAGS_UPWARD_SECURITYFOUNDATION)",
                                        "$(OTHER_LDFLAGS_APPLEIDAUTHSUPPORT)",
                                );
                                PRODUCT_BUNDLE_IDENTIFIER = com.apple.TrustedPeers;
                                PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
+                               TAPI_VERIFY_MODE = Pedantic;
                                VERSION_INFO_PREFIX = "";
                        };
                        name = Debug;
                                VERSION_INFO_PREFIX = "";
                        };
                        name = Debug;
                                        "$(OTHER_LDFLAGS_AKS_LIBRARY)",
                                        "$(OTHER_LDFLAGS_UPWARD_PROTOCOLBUFFER)",
                                        "$(OTHER_LDFLAGS_UPWARD_FOUNDATION)",
                                        "$(OTHER_LDFLAGS_AKS_LIBRARY)",
                                        "$(OTHER_LDFLAGS_UPWARD_PROTOCOLBUFFER)",
                                        "$(OTHER_LDFLAGS_UPWARD_FOUNDATION)",
-                                       "$(OTHER_LDFLAGS_UPWARD_SECURITY)",
                                        "$(OTHER_LDFLAGS_UPWARD_SECURITYFOUNDATION)",
                                        "$(OTHER_LDFLAGS_APPLEIDAUTHSUPPORT)",
                                );
                                PRODUCT_BUNDLE_IDENTIFIER = com.apple.TrustedPeers;
                                PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
                                        "$(OTHER_LDFLAGS_UPWARD_SECURITYFOUNDATION)",
                                        "$(OTHER_LDFLAGS_APPLEIDAUTHSUPPORT)",
                                );
                                PRODUCT_BUNDLE_IDENTIFIER = com.apple.TrustedPeers;
                                PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
+                               TAPI_VERIFY_MODE = Pedantic;
                                VERSION_INFO_PREFIX = "";
                        };
                        name = Release;
                                VERSION_INFO_PREFIX = "";
                        };
                        name = Release;
                                CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
                                CLANG_ENABLE_OBJC_WEAK = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
                                CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
                                CLANG_ENABLE_OBJC_WEAK = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
-                               CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
                                CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
                                CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
                                CLANG_WARN_UNREACHABLE_CODE = YES;
                                CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
                                CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
                                CLANG_WARN_UNREACHABLE_CODE = YES;
                                CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
                                CLANG_ENABLE_OBJC_WEAK = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
                                CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
                                CLANG_ENABLE_OBJC_WEAK = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
-                               CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
                                CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
                                CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
                                CLANG_WARN_UNREACHABLE_CODE = YES;
                                CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
                                CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
                                CLANG_WARN_UNREACHABLE_CODE = YES;
                                CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
                                CLANG_ENABLE_OBJC_WEAK = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
                                CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
                                CLANG_ENABLE_OBJC_WEAK = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
-                               CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
                                CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
                                CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
                                CLANG_WARN_UNREACHABLE_CODE = YES;
                                CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
                                CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
                                CLANG_WARN_UNREACHABLE_CODE = YES;
                                CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
                                CLANG_ENABLE_OBJC_WEAK = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
                                CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
                                CLANG_ENABLE_OBJC_WEAK = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
-                               CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
                                CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
                                CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
                                CLANG_WARN_UNREACHABLE_CODE = YES;
                                CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
                                CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
                                CLANG_WARN_UNREACHABLE_CODE = YES;
                                CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
                                CLANG_ENABLE_OBJC_WEAK = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
                                CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
                                CLANG_ENABLE_OBJC_WEAK = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
-                               CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
                                CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
                                CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
                                CLANG_WARN_UNREACHABLE_CODE = YES;
                                CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
                                CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
                                CLANG_WARN_UNREACHABLE_CODE = YES;
                                CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
                                CLANG_ENABLE_OBJC_WEAK = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
                                CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
                                CLANG_ENABLE_OBJC_WEAK = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
-                               CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
                                CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
                                CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
                                CLANG_WARN_UNREACHABLE_CODE = YES;
                                CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
                                CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
                                CLANG_WARN_UNREACHABLE_CODE = YES;
                                CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
                                CLANG_ENABLE_OBJC_WEAK = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
                                CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
                                CLANG_ENABLE_OBJC_WEAK = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
-                               CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
                                CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
                                CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
                                CLANG_WARN_UNREACHABLE_CODE = YES;
                                CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
                                CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
                                CLANG_WARN_UNREACHABLE_CODE = YES;
                                CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
                                CLANG_ENABLE_OBJC_WEAK = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
                                CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
                                CLANG_ENABLE_OBJC_WEAK = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
-                               CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
                                CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
                                CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
                                CLANG_WARN_UNREACHABLE_CODE = YES;
                                CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
                                CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
                                CLANG_WARN_UNREACHABLE_CODE = YES;
                        baseConfigurationReference = DC0067911D87816C005AF8DB /* macos_legacy_lib.xcconfig */;
                        buildSettings = {
                                CLANG_ANALYZER_NONNULL = YES;
                        baseConfigurationReference = DC0067911D87816C005AF8DB /* macos_legacy_lib.xcconfig */;
                        buildSettings = {
                                CLANG_ANALYZER_NONNULL = YES;
-                               CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
                                CLANG_WARN_SUSPICIOUS_MOVES = YES;
                                ENABLE_STRICT_OBJC_MSGSEND = YES;
                                GCC_SYMBOLS_PRIVATE_EXTERN = YES;
                                CLANG_WARN_SUSPICIOUS_MOVES = YES;
                                ENABLE_STRICT_OBJC_MSGSEND = YES;
                                GCC_SYMBOLS_PRIVATE_EXTERN = YES;
                        baseConfigurationReference = DC0067911D87816C005AF8DB /* macos_legacy_lib.xcconfig */;
                        buildSettings = {
                                CLANG_ANALYZER_NONNULL = YES;
                        baseConfigurationReference = DC0067911D87816C005AF8DB /* macos_legacy_lib.xcconfig */;
                        buildSettings = {
                                CLANG_ANALYZER_NONNULL = YES;
-                               CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
                                CLANG_WARN_SUSPICIOUS_MOVES = YES;
                                ENABLE_STRICT_OBJC_MSGSEND = YES;
                                GCC_SYMBOLS_PRIVATE_EXTERN = YES;
                                CLANG_WARN_SUSPICIOUS_MOVES = YES;
                                ENABLE_STRICT_OBJC_MSGSEND = YES;
                                GCC_SYMBOLS_PRIVATE_EXTERN = YES;
                        baseConfigurationReference = DC0067911D87816C005AF8DB /* macos_legacy_lib.xcconfig */;
                        buildSettings = {
                                CLANG_ANALYZER_NONNULL = YES;
                        baseConfigurationReference = DC0067911D87816C005AF8DB /* macos_legacy_lib.xcconfig */;
                        buildSettings = {
                                CLANG_ANALYZER_NONNULL = YES;
-                               CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
                                CLANG_WARN_SUSPICIOUS_MOVES = YES;
                                ENABLE_STRICT_OBJC_MSGSEND = YES;
                                GCC_SYMBOLS_PRIVATE_EXTERN = YES;
                                CLANG_WARN_SUSPICIOUS_MOVES = YES;
                                ENABLE_STRICT_OBJC_MSGSEND = YES;
                                GCC_SYMBOLS_PRIVATE_EXTERN = YES;
                        baseConfigurationReference = DC0067911D87816C005AF8DB /* macos_legacy_lib.xcconfig */;
                        buildSettings = {
                                CLANG_ANALYZER_NONNULL = YES;
                        baseConfigurationReference = DC0067911D87816C005AF8DB /* macos_legacy_lib.xcconfig */;
                        buildSettings = {
                                CLANG_ANALYZER_NONNULL = YES;
-                               CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
                                CLANG_WARN_SUSPICIOUS_MOVES = YES;
                                ENABLE_STRICT_OBJC_MSGSEND = YES;
                                GCC_SYMBOLS_PRIVATE_EXTERN = YES;
                                CLANG_WARN_SUSPICIOUS_MOVES = YES;
                                ENABLE_STRICT_OBJC_MSGSEND = YES;
                                GCC_SYMBOLS_PRIVATE_EXTERN = YES;
                                CLANG_ANALYZER_NONNULL = YES;
                                CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
                                CLANG_ANALYZER_NONNULL = YES;
                                CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
-                               CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
                                CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
                                CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
                                CLANG_WARN_UNREACHABLE_CODE = YES;
                                CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
                                CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
                                CLANG_WARN_UNREACHABLE_CODE = YES;
                                CLANG_ANALYZER_NONNULL = YES;
                                CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
                                CLANG_ANALYZER_NONNULL = YES;
                                CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
-                               CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
                                CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
                                CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
                                CLANG_WARN_UNREACHABLE_CODE = YES;
                                CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
                                CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
                                CLANG_WARN_UNREACHABLE_CODE = YES;
                                CLANG_ANALYZER_NONNULL = YES;
                                CLANG_ENABLE_OBJC_ARC = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
                                CLANG_ANALYZER_NONNULL = YES;
                                CLANG_ENABLE_OBJC_ARC = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
-                               CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
                                CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
                                CLANG_WARN_SUSPICIOUS_MOVES = YES;
                                CLANG_WARN_UNREACHABLE_CODE = YES;
                                CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
                                CLANG_WARN_SUSPICIOUS_MOVES = YES;
                                CLANG_WARN_UNREACHABLE_CODE = YES;
                                CLANG_ANALYZER_NONNULL = YES;
                                CLANG_ENABLE_OBJC_ARC = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
                                CLANG_ANALYZER_NONNULL = YES;
                                CLANG_ENABLE_OBJC_ARC = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
-                               CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
                                CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
                                CLANG_WARN_SUSPICIOUS_MOVES = YES;
                                CLANG_WARN_UNREACHABLE_CODE = YES;
                                CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
                                CLANG_WARN_SUSPICIOUS_MOVES = YES;
                                CLANG_WARN_UNREACHABLE_CODE = YES;
                                CLANG_ENABLE_OBJC_ARC = NO;
                                CLANG_ENABLE_OBJC_WEAK = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
                                CLANG_ENABLE_OBJC_ARC = NO;
                                CLANG_ENABLE_OBJC_WEAK = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
-                               CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
                                CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
                                CLANG_WARN_SUSPICIOUS_MOVES = YES;
                                CLANG_WARN_UNREACHABLE_CODE = YES;
                                CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
                                CLANG_WARN_SUSPICIOUS_MOVES = YES;
                                CLANG_WARN_UNREACHABLE_CODE = YES;
                                CLANG_ENABLE_OBJC_ARC = NO;
                                CLANG_ENABLE_OBJC_WEAK = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
                                CLANG_ENABLE_OBJC_ARC = NO;
                                CLANG_ENABLE_OBJC_WEAK = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
-                               CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
                                CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
                                CLANG_WARN_SUSPICIOUS_MOVES = YES;
                                CLANG_WARN_UNREACHABLE_CODE = YES;
                                CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
                                CLANG_WARN_SUSPICIOUS_MOVES = YES;
                                CLANG_WARN_UNREACHABLE_CODE = YES;
                        baseConfigurationReference = DC0067911D87816C005AF8DB /* macos_legacy_lib.xcconfig */;
                        buildSettings = {
                                CLANG_ANALYZER_NONNULL = YES;
                        baseConfigurationReference = DC0067911D87816C005AF8DB /* macos_legacy_lib.xcconfig */;
                        buildSettings = {
                                CLANG_ANALYZER_NONNULL = YES;
-                               CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
                                CLANG_WARN_SUSPICIOUS_MOVES = YES;
                                GCC_SYMBOLS_PRIVATE_EXTERN = YES;
                                MTL_ENABLE_DEBUG_INFO = YES;
                                CLANG_WARN_SUSPICIOUS_MOVES = YES;
                                GCC_SYMBOLS_PRIVATE_EXTERN = YES;
                                MTL_ENABLE_DEBUG_INFO = YES;
                        baseConfigurationReference = DC0067911D87816C005AF8DB /* macos_legacy_lib.xcconfig */;
                        buildSettings = {
                                CLANG_ANALYZER_NONNULL = YES;
                        baseConfigurationReference = DC0067911D87816C005AF8DB /* macos_legacy_lib.xcconfig */;
                        buildSettings = {
                                CLANG_ANALYZER_NONNULL = YES;
-                               CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
                                CLANG_WARN_SUSPICIOUS_MOVES = YES;
                                GCC_SYMBOLS_PRIVATE_EXTERN = YES;
                                MTL_ENABLE_DEBUG_INFO = NO;
                                CLANG_WARN_SUSPICIOUS_MOVES = YES;
                                GCC_SYMBOLS_PRIVATE_EXTERN = YES;
                                MTL_ENABLE_DEBUG_INFO = NO;
                        baseConfigurationReference = DC0067911D87816C005AF8DB /* macos_legacy_lib.xcconfig */;
                        buildSettings = {
                                CLANG_ANALYZER_NONNULL = YES;
                        baseConfigurationReference = DC0067911D87816C005AF8DB /* macos_legacy_lib.xcconfig */;
                        buildSettings = {
                                CLANG_ANALYZER_NONNULL = YES;
-                               CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
                                CLANG_WARN_SUSPICIOUS_MOVES = YES;
                                ENABLE_STRICT_OBJC_MSGSEND = YES;
                                MTL_ENABLE_DEBUG_INFO = YES;
                                CLANG_WARN_SUSPICIOUS_MOVES = YES;
                                ENABLE_STRICT_OBJC_MSGSEND = YES;
                                MTL_ENABLE_DEBUG_INFO = YES;
                        baseConfigurationReference = DC0067911D87816C005AF8DB /* macos_legacy_lib.xcconfig */;
                        buildSettings = {
                                CLANG_ANALYZER_NONNULL = YES;
                        baseConfigurationReference = DC0067911D87816C005AF8DB /* macos_legacy_lib.xcconfig */;
                        buildSettings = {
                                CLANG_ANALYZER_NONNULL = YES;
-                               CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
                                CLANG_WARN_SUSPICIOUS_MOVES = YES;
                                ENABLE_STRICT_OBJC_MSGSEND = YES;
                                MTL_ENABLE_DEBUG_INFO = NO;
                                CLANG_WARN_SUSPICIOUS_MOVES = YES;
                                ENABLE_STRICT_OBJC_MSGSEND = YES;
                                MTL_ENABLE_DEBUG_INFO = NO;
                                ALWAYS_SEARCH_USER_PATHS = NO;
                                CLANG_ANALYZER_NONNULL = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
                                ALWAYS_SEARCH_USER_PATHS = NO;
                                CLANG_ANALYZER_NONNULL = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
-                               CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
                                CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
                                CLANG_WARN_SUSPICIOUS_MOVES = YES;
                                CLANG_WARN_UNREACHABLE_CODE = YES;
                                CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
                                CLANG_WARN_SUSPICIOUS_MOVES = YES;
                                CLANG_WARN_UNREACHABLE_CODE = YES;
                                ALWAYS_SEARCH_USER_PATHS = NO;
                                CLANG_ANALYZER_NONNULL = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
                                ALWAYS_SEARCH_USER_PATHS = NO;
                                CLANG_ANALYZER_NONNULL = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
-                               CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
                                CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
                                CLANG_WARN_SUSPICIOUS_MOVES = YES;
                                CLANG_WARN_UNREACHABLE_CODE = YES;
                                CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
                                CLANG_WARN_SUSPICIOUS_MOVES = YES;
                                CLANG_WARN_UNREACHABLE_CODE = YES;
                                ALWAYS_SEARCH_USER_PATHS = NO;
                                CLANG_ANALYZER_NONNULL = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
                                ALWAYS_SEARCH_USER_PATHS = NO;
                                CLANG_ANALYZER_NONNULL = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
-                               CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
                                CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
                                CLANG_WARN_SUSPICIOUS_MOVES = YES;
                                CLANG_WARN_UNREACHABLE_CODE = YES;
                                CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
                                CLANG_WARN_SUSPICIOUS_MOVES = YES;
                                CLANG_WARN_UNREACHABLE_CODE = YES;
                                ALWAYS_SEARCH_USER_PATHS = NO;
                                CLANG_ANALYZER_NONNULL = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
                                ALWAYS_SEARCH_USER_PATHS = NO;
                                CLANG_ANALYZER_NONNULL = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
-                               CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
                                CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
                                CLANG_WARN_SUSPICIOUS_MOVES = YES;
                                CLANG_WARN_UNREACHABLE_CODE = YES;
                                CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
                                CLANG_WARN_SUSPICIOUS_MOVES = YES;
                                CLANG_WARN_UNREACHABLE_CODE = YES;
                        isa = XCBuildConfiguration;
                        baseConfigurationReference = DC0067911D87816C005AF8DB /* macos_legacy_lib.xcconfig */;
                        buildSettings = {
                        isa = XCBuildConfiguration;
                        baseConfigurationReference = DC0067911D87816C005AF8DB /* macos_legacy_lib.xcconfig */;
                        buildSettings = {
-                               CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
                                CLANG_WARN_SUSPICIOUS_MOVES = YES;
                                MTL_ENABLE_DEBUG_INFO = YES;
                                PRODUCT_NAME = "$(TARGET_NAME)";
                                CLANG_WARN_SUSPICIOUS_MOVES = YES;
                                MTL_ENABLE_DEBUG_INFO = YES;
                                PRODUCT_NAME = "$(TARGET_NAME)";
                        isa = XCBuildConfiguration;
                        baseConfigurationReference = DC0067911D87816C005AF8DB /* macos_legacy_lib.xcconfig */;
                        buildSettings = {
                        isa = XCBuildConfiguration;
                        baseConfigurationReference = DC0067911D87816C005AF8DB /* macos_legacy_lib.xcconfig */;
                        buildSettings = {
-                               CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
                                CLANG_WARN_SUSPICIOUS_MOVES = YES;
                                MTL_ENABLE_DEBUG_INFO = NO;
                                PRODUCT_NAME = "$(TARGET_NAME)";
                                CLANG_WARN_SUSPICIOUS_MOVES = YES;
                                MTL_ENABLE_DEBUG_INFO = NO;
                                PRODUCT_NAME = "$(TARGET_NAME)";
                        baseConfigurationReference = DC0067911D87816C005AF8DB /* macos_legacy_lib.xcconfig */;
                        buildSettings = {
                                CLANG_ANALYZER_NONNULL = YES;
                        baseConfigurationReference = DC0067911D87816C005AF8DB /* macos_legacy_lib.xcconfig */;
                        buildSettings = {
                                CLANG_ANALYZER_NONNULL = YES;
-                               CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
                                CLANG_WARN_SUSPICIOUS_MOVES = YES;
                                ENABLE_STRICT_OBJC_MSGSEND = YES;
                                GCC_PREPROCESSOR_DEFINITIONS = (
                                CLANG_WARN_SUSPICIOUS_MOVES = YES;
                                ENABLE_STRICT_OBJC_MSGSEND = YES;
                                GCC_PREPROCESSOR_DEFINITIONS = (
                        baseConfigurationReference = DC0067911D87816C005AF8DB /* macos_legacy_lib.xcconfig */;
                        buildSettings = {
                                CLANG_ANALYZER_NONNULL = YES;
                        baseConfigurationReference = DC0067911D87816C005AF8DB /* macos_legacy_lib.xcconfig */;
                        buildSettings = {
                                CLANG_ANALYZER_NONNULL = YES;
-                               CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
                                CLANG_WARN_SUSPICIOUS_MOVES = YES;
                                ENABLE_STRICT_OBJC_MSGSEND = YES;
                                GCC_PREPROCESSOR_DEFINITIONS = (
                                CLANG_WARN_SUSPICIOUS_MOVES = YES;
                                ENABLE_STRICT_OBJC_MSGSEND = YES;
                                GCC_PREPROCESSOR_DEFINITIONS = (
                        baseConfigurationReference = DC0067911D87816C005AF8DB /* macos_legacy_lib.xcconfig */;
                        buildSettings = {
                                CLANG_ANALYZER_NONNULL = YES;
                        baseConfigurationReference = DC0067911D87816C005AF8DB /* macos_legacy_lib.xcconfig */;
                        buildSettings = {
                                CLANG_ANALYZER_NONNULL = YES;
-                               CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
                                CLANG_WARN_SUSPICIOUS_MOVES = YES;
                                ENABLE_STRICT_OBJC_MSGSEND = YES;
                                HEADER_SEARCH_PATHS = (
                                CLANG_WARN_SUSPICIOUS_MOVES = YES;
                                ENABLE_STRICT_OBJC_MSGSEND = YES;
                                HEADER_SEARCH_PATHS = (
                        baseConfigurationReference = DC0067911D87816C005AF8DB /* macos_legacy_lib.xcconfig */;
                        buildSettings = {
                                CLANG_ANALYZER_NONNULL = YES;
                        baseConfigurationReference = DC0067911D87816C005AF8DB /* macos_legacy_lib.xcconfig */;
                        buildSettings = {
                                CLANG_ANALYZER_NONNULL = YES;
-                               CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
                                CLANG_WARN_SUSPICIOUS_MOVES = YES;
                                ENABLE_STRICT_OBJC_MSGSEND = YES;
                                HEADER_SEARCH_PATHS = (
                                CLANG_WARN_SUSPICIOUS_MOVES = YES;
                                ENABLE_STRICT_OBJC_MSGSEND = YES;
                                HEADER_SEARCH_PATHS = (
                        baseConfigurationReference = DC0067911D87816C005AF8DB /* macos_legacy_lib.xcconfig */;
                        buildSettings = {
                                CLANG_ANALYZER_NONNULL = YES;
                        baseConfigurationReference = DC0067911D87816C005AF8DB /* macos_legacy_lib.xcconfig */;
                        buildSettings = {
                                CLANG_ANALYZER_NONNULL = YES;
-                               CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
                                CLANG_WARN_SUSPICIOUS_MOVES = YES;
                                ENABLE_STRICT_OBJC_MSGSEND = YES;
                                MTL_ENABLE_DEBUG_INFO = YES;
                                CLANG_WARN_SUSPICIOUS_MOVES = YES;
                                ENABLE_STRICT_OBJC_MSGSEND = YES;
                                MTL_ENABLE_DEBUG_INFO = YES;
                        baseConfigurationReference = DC0067911D87816C005AF8DB /* macos_legacy_lib.xcconfig */;
                        buildSettings = {
                                CLANG_ANALYZER_NONNULL = YES;
                        baseConfigurationReference = DC0067911D87816C005AF8DB /* macos_legacy_lib.xcconfig */;
                        buildSettings = {
                                CLANG_ANALYZER_NONNULL = YES;
-                               CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
                                CLANG_WARN_SUSPICIOUS_MOVES = YES;
                                ENABLE_STRICT_OBJC_MSGSEND = YES;
                                MTL_ENABLE_DEBUG_INFO = NO;
                                CLANG_WARN_SUSPICIOUS_MOVES = YES;
                                ENABLE_STRICT_OBJC_MSGSEND = YES;
                                MTL_ENABLE_DEBUG_INFO = NO;
                        baseConfigurationReference = DC0067911D87816C005AF8DB /* macos_legacy_lib.xcconfig */;
                        buildSettings = {
                                CLANG_ANALYZER_NONNULL = YES;
                        baseConfigurationReference = DC0067911D87816C005AF8DB /* macos_legacy_lib.xcconfig */;
                        buildSettings = {
                                CLANG_ANALYZER_NONNULL = YES;
-                               CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
                                CLANG_WARN_SUSPICIOUS_MOVES = YES;
                                ENABLE_STRICT_OBJC_MSGSEND = YES;
                                GCC_WARN_64_TO_32_BIT_CONVERSION = NO;
                                CLANG_WARN_SUSPICIOUS_MOVES = YES;
                                ENABLE_STRICT_OBJC_MSGSEND = YES;
                                GCC_WARN_64_TO_32_BIT_CONVERSION = NO;
                        baseConfigurationReference = DC0067911D87816C005AF8DB /* macos_legacy_lib.xcconfig */;
                        buildSettings = {
                                CLANG_ANALYZER_NONNULL = YES;
                        baseConfigurationReference = DC0067911D87816C005AF8DB /* macos_legacy_lib.xcconfig */;
                        buildSettings = {
                                CLANG_ANALYZER_NONNULL = YES;
-                               CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
                                CLANG_WARN_SUSPICIOUS_MOVES = YES;
                                ENABLE_STRICT_OBJC_MSGSEND = YES;
                                GCC_WARN_64_TO_32_BIT_CONVERSION = NO;
                                CLANG_WARN_SUSPICIOUS_MOVES = YES;
                                ENABLE_STRICT_OBJC_MSGSEND = YES;
                                GCC_WARN_64_TO_32_BIT_CONVERSION = NO;
                        baseConfigurationReference = DC0067911D87816C005AF8DB /* macos_legacy_lib.xcconfig */;
                        buildSettings = {
                                CLANG_ANALYZER_NONNULL = YES;
                        baseConfigurationReference = DC0067911D87816C005AF8DB /* macos_legacy_lib.xcconfig */;
                        buildSettings = {
                                CLANG_ANALYZER_NONNULL = YES;
-                               CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
                                CLANG_WARN_SUSPICIOUS_MOVES = YES;
                                ENABLE_STRICT_OBJC_MSGSEND = YES;
                                MTL_ENABLE_DEBUG_INFO = YES;
                                CLANG_WARN_SUSPICIOUS_MOVES = YES;
                                ENABLE_STRICT_OBJC_MSGSEND = YES;
                                MTL_ENABLE_DEBUG_INFO = YES;
                        baseConfigurationReference = DC0067911D87816C005AF8DB /* macos_legacy_lib.xcconfig */;
                        buildSettings = {
                                CLANG_ANALYZER_NONNULL = YES;
                        baseConfigurationReference = DC0067911D87816C005AF8DB /* macos_legacy_lib.xcconfig */;
                        buildSettings = {
                                CLANG_ANALYZER_NONNULL = YES;
-                               CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
                                CLANG_WARN_SUSPICIOUS_MOVES = YES;
                                ENABLE_STRICT_OBJC_MSGSEND = YES;
                                MTL_ENABLE_DEBUG_INFO = NO;
                                CLANG_WARN_SUSPICIOUS_MOVES = YES;
                                ENABLE_STRICT_OBJC_MSGSEND = YES;
                                MTL_ENABLE_DEBUG_INFO = NO;
                        baseConfigurationReference = DC0067911D87816C005AF8DB /* macos_legacy_lib.xcconfig */;
                        buildSettings = {
                                CLANG_ANALYZER_NONNULL = YES;
                        baseConfigurationReference = DC0067911D87816C005AF8DB /* macos_legacy_lib.xcconfig */;
                        buildSettings = {
                                CLANG_ANALYZER_NONNULL = YES;
-                               CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
                                CLANG_WARN_SUSPICIOUS_MOVES = YES;
                                ENABLE_STRICT_OBJC_MSGSEND = YES;
                                GCC_SYMBOLS_PRIVATE_EXTERN = YES;
                                CLANG_WARN_SUSPICIOUS_MOVES = YES;
                                ENABLE_STRICT_OBJC_MSGSEND = YES;
                                GCC_SYMBOLS_PRIVATE_EXTERN = YES;
                        baseConfigurationReference = DC0067911D87816C005AF8DB /* macos_legacy_lib.xcconfig */;
                        buildSettings = {
                                CLANG_ANALYZER_NONNULL = YES;
                        baseConfigurationReference = DC0067911D87816C005AF8DB /* macos_legacy_lib.xcconfig */;
                        buildSettings = {
                                CLANG_ANALYZER_NONNULL = YES;
-                               CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
                                CLANG_WARN_SUSPICIOUS_MOVES = YES;
                                ENABLE_STRICT_OBJC_MSGSEND = YES;
                                GCC_SYMBOLS_PRIVATE_EXTERN = YES;
                                CLANG_WARN_SUSPICIOUS_MOVES = YES;
                                ENABLE_STRICT_OBJC_MSGSEND = YES;
                                GCC_SYMBOLS_PRIVATE_EXTERN = YES;
                        baseConfigurationReference = DC0067911D87816C005AF8DB /* macos_legacy_lib.xcconfig */;
                        buildSettings = {
                                CLANG_ANALYZER_NONNULL = YES;
                        baseConfigurationReference = DC0067911D87816C005AF8DB /* macos_legacy_lib.xcconfig */;
                        buildSettings = {
                                CLANG_ANALYZER_NONNULL = YES;
-                               CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
                                CLANG_WARN_SUSPICIOUS_MOVES = YES;
                                ENABLE_STRICT_OBJC_MSGSEND = YES;
                                MTL_ENABLE_DEBUG_INFO = YES;
                                CLANG_WARN_SUSPICIOUS_MOVES = YES;
                                ENABLE_STRICT_OBJC_MSGSEND = YES;
                                MTL_ENABLE_DEBUG_INFO = YES;
                        baseConfigurationReference = DC0067911D87816C005AF8DB /* macos_legacy_lib.xcconfig */;
                        buildSettings = {
                                CLANG_ANALYZER_NONNULL = YES;
                        baseConfigurationReference = DC0067911D87816C005AF8DB /* macos_legacy_lib.xcconfig */;
                        buildSettings = {
                                CLANG_ANALYZER_NONNULL = YES;
-                               CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
                                CLANG_WARN_SUSPICIOUS_MOVES = YES;
                                ENABLE_STRICT_OBJC_MSGSEND = YES;
                                MTL_ENABLE_DEBUG_INFO = NO;
                                CLANG_WARN_SUSPICIOUS_MOVES = YES;
                                ENABLE_STRICT_OBJC_MSGSEND = YES;
                                MTL_ENABLE_DEBUG_INFO = NO;
                        baseConfigurationReference = DC0067911D87816C005AF8DB /* macos_legacy_lib.xcconfig */;
                        buildSettings = {
                                CLANG_ANALYZER_NONNULL = YES;
                        baseConfigurationReference = DC0067911D87816C005AF8DB /* macos_legacy_lib.xcconfig */;
                        buildSettings = {
                                CLANG_ANALYZER_NONNULL = YES;
-                               CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
                                CLANG_WARN_SUSPICIOUS_MOVES = YES;
                                ENABLE_STRICT_OBJC_MSGSEND = YES;
                                MTL_ENABLE_DEBUG_INFO = YES;
                                CLANG_WARN_SUSPICIOUS_MOVES = YES;
                                ENABLE_STRICT_OBJC_MSGSEND = YES;
                                MTL_ENABLE_DEBUG_INFO = YES;
                        baseConfigurationReference = DC0067911D87816C005AF8DB /* macos_legacy_lib.xcconfig */;
                        buildSettings = {
                                CLANG_ANALYZER_NONNULL = YES;
                        baseConfigurationReference = DC0067911D87816C005AF8DB /* macos_legacy_lib.xcconfig */;
                        buildSettings = {
                                CLANG_ANALYZER_NONNULL = YES;
-                               CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
                                CLANG_WARN_SUSPICIOUS_MOVES = YES;
                                ENABLE_STRICT_OBJC_MSGSEND = YES;
                                MTL_ENABLE_DEBUG_INFO = NO;
                                CLANG_WARN_SUSPICIOUS_MOVES = YES;
                                ENABLE_STRICT_OBJC_MSGSEND = YES;
                                MTL_ENABLE_DEBUG_INFO = NO;
                        baseConfigurationReference = DC0067911D87816C005AF8DB /* macos_legacy_lib.xcconfig */;
                        buildSettings = {
                                CLANG_ANALYZER_NONNULL = YES;
                        baseConfigurationReference = DC0067911D87816C005AF8DB /* macos_legacy_lib.xcconfig */;
                        buildSettings = {
                                CLANG_ANALYZER_NONNULL = YES;
-                               CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
                                CLANG_WARN_SUSPICIOUS_MOVES = YES;
                                ENABLE_STRICT_OBJC_MSGSEND = YES;
                                MTL_ENABLE_DEBUG_INFO = YES;
                                CLANG_WARN_SUSPICIOUS_MOVES = YES;
                                ENABLE_STRICT_OBJC_MSGSEND = YES;
                                MTL_ENABLE_DEBUG_INFO = YES;
                        baseConfigurationReference = DC0067911D87816C005AF8DB /* macos_legacy_lib.xcconfig */;
                        buildSettings = {
                                CLANG_ANALYZER_NONNULL = YES;
                        baseConfigurationReference = DC0067911D87816C005AF8DB /* macos_legacy_lib.xcconfig */;
                        buildSettings = {
                                CLANG_ANALYZER_NONNULL = YES;
-                               CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
                                CLANG_WARN_SUSPICIOUS_MOVES = YES;
                                ENABLE_STRICT_OBJC_MSGSEND = YES;
                                MTL_ENABLE_DEBUG_INFO = NO;
                                CLANG_WARN_SUSPICIOUS_MOVES = YES;
                                ENABLE_STRICT_OBJC_MSGSEND = YES;
                                MTL_ENABLE_DEBUG_INFO = NO;
                                CLANG_WARN_COMMA = YES;
                                CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
                                CLANG_WARN_COMMA = YES;
                                CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
-                               CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
                                CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
                                CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
                                CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
                                CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
                                CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
                                CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
                                CLANG_WARN_COMMA = YES;
                                CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
                                CLANG_WARN_COMMA = YES;
                                CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
-                               CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
                                CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
                                CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
                                CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
                                CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
                                CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
                                CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
                                CLANG_WARN_BOOL_CONVERSION = YES;
                                CLANG_WARN_CONSTANT_CONVERSION = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
                                CLANG_WARN_BOOL_CONVERSION = YES;
                                CLANG_WARN_CONSTANT_CONVERSION = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
-                               CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
                                CLANG_WARN_EMPTY_BODY = YES;
                                CLANG_WARN_ENUM_CONVERSION = YES;
                                CLANG_WARN_INFINITE_RECURSION = YES;
                                CLANG_WARN_EMPTY_BODY = YES;
                                CLANG_WARN_ENUM_CONVERSION = YES;
                                CLANG_WARN_INFINITE_RECURSION = YES;
                                CLANG_WARN_BOOL_CONVERSION = YES;
                                CLANG_WARN_CONSTANT_CONVERSION = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
                                CLANG_WARN_BOOL_CONVERSION = YES;
                                CLANG_WARN_CONSTANT_CONVERSION = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
-                               CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
                                CLANG_WARN_EMPTY_BODY = YES;
                                CLANG_WARN_ENUM_CONVERSION = YES;
                                CLANG_WARN_INFINITE_RECURSION = YES;
                                CLANG_WARN_EMPTY_BODY = YES;
                                CLANG_WARN_ENUM_CONVERSION = YES;
                                CLANG_WARN_INFINITE_RECURSION = YES;
                                CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
                                CLANG_ENABLE_OBJC_WEAK = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
                                CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
                                CLANG_ENABLE_OBJC_WEAK = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
-                               CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
                                CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
                                CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
                                CLANG_WARN_UNREACHABLE_CODE = YES;
                                CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
                                CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
                                CLANG_WARN_UNREACHABLE_CODE = YES;
                                CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
                                CLANG_ENABLE_OBJC_WEAK = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
                                CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
                                CLANG_ENABLE_OBJC_WEAK = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
-                               CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
                                CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
                                CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
                                CLANG_WARN_UNREACHABLE_CODE = YES;
                                CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
                                CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
                                CLANG_WARN_UNREACHABLE_CODE = YES;
                                        "-framework",
                                        Security,
                                        "$(OTHER_LDFLAGS_FOR_SECURITYD)",
                                        "-framework",
                                        Security,
                                        "$(OTHER_LDFLAGS_FOR_SECURITYD)",
+                                       "-framework",
+                                       TrustedPeers,
                                );
                                PRODUCT_BUNDLE_IDENTIFIER = com.apple.security.CKKSTests;
                                PRODUCT_NAME = "$(TARGET_NAME)";
                                );
                                PRODUCT_BUNDLE_IDENTIFIER = com.apple.security.CKKSTests;
                                PRODUCT_NAME = "$(TARGET_NAME)";
                                        "-framework",
                                        Security,
                                        "$(OTHER_LDFLAGS_FOR_SECURITYD)",
                                        "-framework",
                                        Security,
                                        "$(OTHER_LDFLAGS_FOR_SECURITYD)",
+                                       "-framework",
+                                       TrustedPeers,
                                );
                                PRODUCT_BUNDLE_IDENTIFIER = com.apple.security.CKKSTests;
                                PRODUCT_NAME = "$(TARGET_NAME)";
                                );
                                PRODUCT_BUNDLE_IDENTIFIER = com.apple.security.CKKSTests;
                                PRODUCT_NAME = "$(TARGET_NAME)";
                                CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
                                CLANG_ENABLE_OBJC_WEAK = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
                                CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
                                CLANG_ENABLE_OBJC_WEAK = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
-                               CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
                                CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
                                CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
                                CLANG_WARN_UNREACHABLE_CODE = YES;
                                CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
                                CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
                                CLANG_WARN_UNREACHABLE_CODE = YES;
                                CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
                                CLANG_ENABLE_OBJC_WEAK = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
                                CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
                                CLANG_ENABLE_OBJC_WEAK = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
-                               CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
                                CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
                                CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
                                CLANG_WARN_UNREACHABLE_CODE = YES;
                                CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
                                CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
                                CLANG_WARN_UNREACHABLE_CODE = YES;
                                ALWAYS_SEARCH_USER_PATHS = NO;
                                CLANG_ANALYZER_NONNULL = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
                                ALWAYS_SEARCH_USER_PATHS = NO;
                                CLANG_ANALYZER_NONNULL = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
-                               CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
                                CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
                                CLANG_WARN_SUSPICIOUS_MOVES = YES;
                                CLANG_WARN_UNREACHABLE_CODE = YES;
                                CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
                                CLANG_WARN_SUSPICIOUS_MOVES = YES;
                                CLANG_WARN_UNREACHABLE_CODE = YES;
                                ALWAYS_SEARCH_USER_PATHS = NO;
                                CLANG_ANALYZER_NONNULL = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
                                ALWAYS_SEARCH_USER_PATHS = NO;
                                CLANG_ANALYZER_NONNULL = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
-                               CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
                                CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
                                CLANG_WARN_SUSPICIOUS_MOVES = YES;
                                CLANG_WARN_UNREACHABLE_CODE = YES;
                                CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
                                CLANG_WARN_SUSPICIOUS_MOVES = YES;
                                CLANG_WARN_UNREACHABLE_CODE = YES;
                                CLANG_WARN_BOOL_CONVERSION = YES;
                                CLANG_WARN_CONSTANT_CONVERSION = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
                                CLANG_WARN_BOOL_CONVERSION = YES;
                                CLANG_WARN_CONSTANT_CONVERSION = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
-                               CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
                                CLANG_WARN_EMPTY_BODY = YES;
                                CLANG_WARN_ENUM_CONVERSION = YES;
                                CLANG_WARN_INFINITE_RECURSION = YES;
                                CLANG_WARN_EMPTY_BODY = YES;
                                CLANG_WARN_ENUM_CONVERSION = YES;
                                CLANG_WARN_INFINITE_RECURSION = YES;
                                CLANG_WARN_BOOL_CONVERSION = YES;
                                CLANG_WARN_CONSTANT_CONVERSION = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
                                CLANG_WARN_BOOL_CONVERSION = YES;
                                CLANG_WARN_CONSTANT_CONVERSION = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
-                               CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
                                CLANG_WARN_EMPTY_BODY = YES;
                                CLANG_WARN_ENUM_CONVERSION = YES;
                                CLANG_WARN_INFINITE_RECURSION = YES;
                                CLANG_WARN_EMPTY_BODY = YES;
                                CLANG_WARN_ENUM_CONVERSION = YES;
                                CLANG_WARN_INFINITE_RECURSION = YES;
                                CLANG_WARN_BOOL_CONVERSION = YES;
                                CLANG_WARN_CONSTANT_CONVERSION = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
                                CLANG_WARN_BOOL_CONVERSION = YES;
                                CLANG_WARN_CONSTANT_CONVERSION = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
-                               CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
                                CLANG_WARN_EMPTY_BODY = YES;
                                CLANG_WARN_ENUM_CONVERSION = YES;
                                CLANG_WARN_INT_CONVERSION = YES;
                                CLANG_WARN_EMPTY_BODY = YES;
                                CLANG_WARN_ENUM_CONVERSION = YES;
                                CLANG_WARN_INT_CONVERSION = YES;
                                CLANG_WARN_BOOL_CONVERSION = YES;
                                CLANG_WARN_CONSTANT_CONVERSION = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
                                CLANG_WARN_BOOL_CONVERSION = YES;
                                CLANG_WARN_CONSTANT_CONVERSION = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
-                               CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
                                CLANG_WARN_EMPTY_BODY = YES;
                                CLANG_WARN_ENUM_CONVERSION = YES;
                                CLANG_WARN_INT_CONVERSION = YES;
                                CLANG_WARN_EMPTY_BODY = YES;
                                CLANG_WARN_ENUM_CONVERSION = YES;
                                CLANG_WARN_INT_CONVERSION = YES;
                                CLANG_WARN_BOOL_CONVERSION = YES;
                                CLANG_WARN_CONSTANT_CONVERSION = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
                                CLANG_WARN_BOOL_CONVERSION = YES;
                                CLANG_WARN_CONSTANT_CONVERSION = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
-                               CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
                                CLANG_WARN_EMPTY_BODY = YES;
                                CLANG_WARN_ENUM_CONVERSION = YES;
                                CLANG_WARN_INT_CONVERSION = YES;
                                CLANG_WARN_EMPTY_BODY = YES;
                                CLANG_WARN_ENUM_CONVERSION = YES;
                                CLANG_WARN_INT_CONVERSION = YES;
                                CLANG_WARN_BOOL_CONVERSION = YES;
                                CLANG_WARN_CONSTANT_CONVERSION = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
                                CLANG_WARN_BOOL_CONVERSION = YES;
                                CLANG_WARN_CONSTANT_CONVERSION = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
-                               CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
                                CLANG_WARN_EMPTY_BODY = YES;
                                CLANG_WARN_ENUM_CONVERSION = YES;
                                CLANG_WARN_INT_CONVERSION = YES;
                                CLANG_WARN_EMPTY_BODY = YES;
                                CLANG_WARN_ENUM_CONVERSION = YES;
                                CLANG_WARN_INT_CONVERSION = YES;
                                CLANG_WARN_BOOL_CONVERSION = YES;
                                CLANG_WARN_CONSTANT_CONVERSION = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
                                CLANG_WARN_BOOL_CONVERSION = YES;
                                CLANG_WARN_CONSTANT_CONVERSION = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
-                               CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
                                CLANG_WARN_EMPTY_BODY = YES;
                                CLANG_WARN_ENUM_CONVERSION = YES;
                                CLANG_WARN_INFINITE_RECURSION = YES;
                                CLANG_WARN_EMPTY_BODY = YES;
                                CLANG_WARN_ENUM_CONVERSION = YES;
                                CLANG_WARN_INFINITE_RECURSION = YES;
                                CLANG_WARN_BOOL_CONVERSION = YES;
                                CLANG_WARN_CONSTANT_CONVERSION = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
                                CLANG_WARN_BOOL_CONVERSION = YES;
                                CLANG_WARN_CONSTANT_CONVERSION = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
-                               CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
                                CLANG_WARN_EMPTY_BODY = YES;
                                CLANG_WARN_ENUM_CONVERSION = YES;
                                CLANG_WARN_INFINITE_RECURSION = YES;
                                CLANG_WARN_EMPTY_BODY = YES;
                                CLANG_WARN_ENUM_CONVERSION = YES;
                                CLANG_WARN_INFINITE_RECURSION = YES;
                                CLANG_WARN_BOOL_CONVERSION = YES;
                                CLANG_WARN_CONSTANT_CONVERSION = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
                                CLANG_WARN_BOOL_CONVERSION = YES;
                                CLANG_WARN_CONSTANT_CONVERSION = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
-                               CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
                                CLANG_WARN_EMPTY_BODY = YES;
                                CLANG_WARN_ENUM_CONVERSION = YES;
                                CLANG_WARN_INFINITE_RECURSION = YES;
                                CLANG_WARN_EMPTY_BODY = YES;
                                CLANG_WARN_ENUM_CONVERSION = YES;
                                CLANG_WARN_INFINITE_RECURSION = YES;
                                CLANG_WARN_BOOL_CONVERSION = YES;
                                CLANG_WARN_CONSTANT_CONVERSION = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
                                CLANG_WARN_BOOL_CONVERSION = YES;
                                CLANG_WARN_CONSTANT_CONVERSION = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
-                               CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
                                CLANG_WARN_EMPTY_BODY = YES;
                                CLANG_WARN_ENUM_CONVERSION = YES;
                                CLANG_WARN_INFINITE_RECURSION = YES;
                                CLANG_WARN_EMPTY_BODY = YES;
                                CLANG_WARN_ENUM_CONVERSION = YES;
                                CLANG_WARN_INFINITE_RECURSION = YES;
                                CLANG_WARN_BOOL_CONVERSION = YES;
                                CLANG_WARN_CONSTANT_CONVERSION = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
                                CLANG_WARN_BOOL_CONVERSION = YES;
                                CLANG_WARN_CONSTANT_CONVERSION = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
-                               CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
                                CLANG_WARN_EMPTY_BODY = YES;
                                CLANG_WARN_ENUM_CONVERSION = YES;
                                CLANG_WARN_INFINITE_RECURSION = YES;
                                CLANG_WARN_EMPTY_BODY = YES;
                                CLANG_WARN_ENUM_CONVERSION = YES;
                                CLANG_WARN_INFINITE_RECURSION = YES;
                                CLANG_WARN_BOOL_CONVERSION = YES;
                                CLANG_WARN_CONSTANT_CONVERSION = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
                                CLANG_WARN_BOOL_CONVERSION = YES;
                                CLANG_WARN_CONSTANT_CONVERSION = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
-                               CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
                                CLANG_WARN_EMPTY_BODY = YES;
                                CLANG_WARN_ENUM_CONVERSION = YES;
                                CLANG_WARN_INFINITE_RECURSION = YES;
                                CLANG_WARN_EMPTY_BODY = YES;
                                CLANG_WARN_ENUM_CONVERSION = YES;
                                CLANG_WARN_INFINITE_RECURSION = YES;
                                CLANG_WARN_BOOL_CONVERSION = YES;
                                CLANG_WARN_CONSTANT_CONVERSION = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
                                CLANG_WARN_BOOL_CONVERSION = YES;
                                CLANG_WARN_CONSTANT_CONVERSION = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
-                               CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
                                CLANG_WARN_EMPTY_BODY = YES;
                                CLANG_WARN_ENUM_CONVERSION = YES;
                                CLANG_WARN_INFINITE_RECURSION = YES;
                                CLANG_WARN_EMPTY_BODY = YES;
                                CLANG_WARN_ENUM_CONVERSION = YES;
                                CLANG_WARN_INFINITE_RECURSION = YES;
                                CLANG_WARN_BOOL_CONVERSION = YES;
                                CLANG_WARN_CONSTANT_CONVERSION = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
                                CLANG_WARN_BOOL_CONVERSION = YES;
                                CLANG_WARN_CONSTANT_CONVERSION = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
-                               CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
                                CLANG_WARN_EMPTY_BODY = YES;
                                CLANG_WARN_ENUM_CONVERSION = YES;
                                CLANG_WARN_INFINITE_RECURSION = YES;
                                CLANG_WARN_EMPTY_BODY = YES;
                                CLANG_WARN_ENUM_CONVERSION = YES;
                                CLANG_WARN_INFINITE_RECURSION = YES;
                        baseConfigurationReference = DC0067911D87816C005AF8DB /* macos_legacy_lib.xcconfig */;
                        buildSettings = {
                                CLANG_ANALYZER_NONNULL = YES;
                        baseConfigurationReference = DC0067911D87816C005AF8DB /* macos_legacy_lib.xcconfig */;
                        buildSettings = {
                                CLANG_ANALYZER_NONNULL = YES;
-                               CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
                                CLANG_WARN_SUSPICIOUS_MOVES = YES;
                                ENABLE_STRICT_OBJC_MSGSEND = YES;
                                GCC_SYMBOLS_PRIVATE_EXTERN = YES;
                                CLANG_WARN_SUSPICIOUS_MOVES = YES;
                                ENABLE_STRICT_OBJC_MSGSEND = YES;
                                GCC_SYMBOLS_PRIVATE_EXTERN = YES;
                        baseConfigurationReference = DC0067911D87816C005AF8DB /* macos_legacy_lib.xcconfig */;
                        buildSettings = {
                                CLANG_ANALYZER_NONNULL = YES;
                        baseConfigurationReference = DC0067911D87816C005AF8DB /* macos_legacy_lib.xcconfig */;
                        buildSettings = {
                                CLANG_ANALYZER_NONNULL = YES;
-                               CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
                                CLANG_WARN_SUSPICIOUS_MOVES = YES;
                                ENABLE_STRICT_OBJC_MSGSEND = YES;
                                GCC_SYMBOLS_PRIVATE_EXTERN = YES;
                                CLANG_WARN_SUSPICIOUS_MOVES = YES;
                                ENABLE_STRICT_OBJC_MSGSEND = YES;
                                GCC_SYMBOLS_PRIVATE_EXTERN = YES;
                                CLANG_ANALYZER_NONNULL = YES;
                                CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
                                CLANG_ANALYZER_NONNULL = YES;
                                CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
-                               CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
                                CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
                                CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
                                CLANG_WARN_UNREACHABLE_CODE = YES;
                                CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
                                CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
                                CLANG_WARN_UNREACHABLE_CODE = YES;
                                CLANG_ANALYZER_NONNULL = YES;
                                CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
                                CLANG_ANALYZER_NONNULL = YES;
                                CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
-                               CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
                                CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
                                CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
                                CLANG_WARN_UNREACHABLE_CODE = YES;
                                CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
                                CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
                                CLANG_WARN_UNREACHABLE_CODE = YES;
                                CLANG_WARN_BOOL_CONVERSION = YES;
                                CLANG_WARN_CONSTANT_CONVERSION = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
                                CLANG_WARN_BOOL_CONVERSION = YES;
                                CLANG_WARN_CONSTANT_CONVERSION = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
-                               CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
                                CLANG_WARN_EMPTY_BODY = YES;
                                CLANG_WARN_ENUM_CONVERSION = YES;
                                CLANG_WARN_INFINITE_RECURSION = YES;
                                CLANG_WARN_EMPTY_BODY = YES;
                                CLANG_WARN_ENUM_CONVERSION = YES;
                                CLANG_WARN_INFINITE_RECURSION = YES;
                                CLANG_WARN_BOOL_CONVERSION = YES;
                                CLANG_WARN_CONSTANT_CONVERSION = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
                                CLANG_WARN_BOOL_CONVERSION = YES;
                                CLANG_WARN_CONSTANT_CONVERSION = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
-                               CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
                                CLANG_WARN_EMPTY_BODY = YES;
                                CLANG_WARN_ENUM_CONVERSION = YES;
                                CLANG_WARN_INFINITE_RECURSION = YES;
                                CLANG_WARN_EMPTY_BODY = YES;
                                CLANG_WARN_ENUM_CONVERSION = YES;
                                CLANG_WARN_INFINITE_RECURSION = YES;
                                CLANG_WARN_BOOL_CONVERSION = YES;
                                CLANG_WARN_CONSTANT_CONVERSION = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
                                CLANG_WARN_BOOL_CONVERSION = YES;
                                CLANG_WARN_CONSTANT_CONVERSION = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
-                               CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
                                CLANG_WARN_EMPTY_BODY = YES;
                                CLANG_WARN_ENUM_CONVERSION = YES;
                                CLANG_WARN_INFINITE_RECURSION = YES;
                                CLANG_WARN_EMPTY_BODY = YES;
                                CLANG_WARN_ENUM_CONVERSION = YES;
                                CLANG_WARN_INFINITE_RECURSION = YES;
                                CLANG_WARN_BOOL_CONVERSION = YES;
                                CLANG_WARN_CONSTANT_CONVERSION = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
                                CLANG_WARN_BOOL_CONVERSION = YES;
                                CLANG_WARN_CONSTANT_CONVERSION = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
-                               CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
                                CLANG_WARN_EMPTY_BODY = YES;
                                CLANG_WARN_ENUM_CONVERSION = YES;
                                CLANG_WARN_INFINITE_RECURSION = YES;
                                CLANG_WARN_EMPTY_BODY = YES;
                                CLANG_WARN_ENUM_CONVERSION = YES;
                                CLANG_WARN_INFINITE_RECURSION = YES;
                                CLANG_WARN_BOOL_CONVERSION = YES;
                                CLANG_WARN_CONSTANT_CONVERSION = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
                                CLANG_WARN_BOOL_CONVERSION = YES;
                                CLANG_WARN_CONSTANT_CONVERSION = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
-                               CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
                                CLANG_WARN_EMPTY_BODY = YES;
                                CLANG_WARN_ENUM_CONVERSION = YES;
                                CLANG_WARN_INFINITE_RECURSION = YES;
                                CLANG_WARN_EMPTY_BODY = YES;
                                CLANG_WARN_ENUM_CONVERSION = YES;
                                CLANG_WARN_INFINITE_RECURSION = YES;
                                CLANG_WARN_BOOL_CONVERSION = YES;
                                CLANG_WARN_CONSTANT_CONVERSION = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
                                CLANG_WARN_BOOL_CONVERSION = YES;
                                CLANG_WARN_CONSTANT_CONVERSION = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
-                               CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
                                CLANG_WARN_EMPTY_BODY = YES;
                                CLANG_WARN_ENUM_CONVERSION = YES;
                                CLANG_WARN_INFINITE_RECURSION = YES;
                                CLANG_WARN_EMPTY_BODY = YES;
                                CLANG_WARN_ENUM_CONVERSION = YES;
                                CLANG_WARN_INFINITE_RECURSION = YES;
                                CLANG_WARN_BOOL_CONVERSION = YES;
                                CLANG_WARN_CONSTANT_CONVERSION = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
                                CLANG_WARN_BOOL_CONVERSION = YES;
                                CLANG_WARN_CONSTANT_CONVERSION = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
-                               CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
                                CLANG_WARN_EMPTY_BODY = YES;
                                CLANG_WARN_ENUM_CONVERSION = YES;
                                CLANG_WARN_INFINITE_RECURSION = YES;
                                CLANG_WARN_EMPTY_BODY = YES;
                                CLANG_WARN_ENUM_CONVERSION = YES;
                                CLANG_WARN_INFINITE_RECURSION = YES;
                                CLANG_WARN_BOOL_CONVERSION = YES;
                                CLANG_WARN_CONSTANT_CONVERSION = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
                                CLANG_WARN_BOOL_CONVERSION = YES;
                                CLANG_WARN_CONSTANT_CONVERSION = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
-                               CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
                                CLANG_WARN_EMPTY_BODY = YES;
                                CLANG_WARN_ENUM_CONVERSION = YES;
                                CLANG_WARN_INFINITE_RECURSION = YES;
                                CLANG_WARN_EMPTY_BODY = YES;
                                CLANG_WARN_ENUM_CONVERSION = YES;
                                CLANG_WARN_INFINITE_RECURSION = YES;
                                CLANG_WARN_BOOL_CONVERSION = YES;
                                CLANG_WARN_CONSTANT_CONVERSION = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
                                CLANG_WARN_BOOL_CONVERSION = YES;
                                CLANG_WARN_CONSTANT_CONVERSION = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
-                               CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
                                CLANG_WARN_EMPTY_BODY = YES;
                                CLANG_WARN_ENUM_CONVERSION = YES;
                                CLANG_WARN_INFINITE_RECURSION = YES;
                                CLANG_WARN_EMPTY_BODY = YES;
                                CLANG_WARN_ENUM_CONVERSION = YES;
                                CLANG_WARN_INFINITE_RECURSION = YES;
                                CLANG_WARN_BOOL_CONVERSION = YES;
                                CLANG_WARN_CONSTANT_CONVERSION = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
                                CLANG_WARN_BOOL_CONVERSION = YES;
                                CLANG_WARN_CONSTANT_CONVERSION = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
-                               CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
                                CLANG_WARN_EMPTY_BODY = YES;
                                CLANG_WARN_ENUM_CONVERSION = YES;
                                CLANG_WARN_INFINITE_RECURSION = YES;
                                CLANG_WARN_EMPTY_BODY = YES;
                                CLANG_WARN_ENUM_CONVERSION = YES;
                                CLANG_WARN_INFINITE_RECURSION = YES;
                                CLANG_WARN_BOOL_CONVERSION = YES;
                                CLANG_WARN_CONSTANT_CONVERSION = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
                                CLANG_WARN_BOOL_CONVERSION = YES;
                                CLANG_WARN_CONSTANT_CONVERSION = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
-                               CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
                                CLANG_WARN_EMPTY_BODY = YES;
                                CLANG_WARN_ENUM_CONVERSION = YES;
                                CLANG_WARN_INFINITE_RECURSION = YES;
                                CLANG_WARN_EMPTY_BODY = YES;
                                CLANG_WARN_ENUM_CONVERSION = YES;
                                CLANG_WARN_INFINITE_RECURSION = YES;
                                CLANG_WARN_BOOL_CONVERSION = YES;
                                CLANG_WARN_CONSTANT_CONVERSION = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
                                CLANG_WARN_BOOL_CONVERSION = YES;
                                CLANG_WARN_CONSTANT_CONVERSION = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
-                               CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
                                CLANG_WARN_EMPTY_BODY = YES;
                                CLANG_WARN_ENUM_CONVERSION = YES;
                                CLANG_WARN_INFINITE_RECURSION = YES;
                                CLANG_WARN_EMPTY_BODY = YES;
                                CLANG_WARN_ENUM_CONVERSION = YES;
                                CLANG_WARN_INFINITE_RECURSION = YES;
                                CLANG_WARN_BOOL_CONVERSION = YES;
                                CLANG_WARN_CONSTANT_CONVERSION = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
                                CLANG_WARN_BOOL_CONVERSION = YES;
                                CLANG_WARN_CONSTANT_CONVERSION = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
-                               CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
                                CLANG_WARN_EMPTY_BODY = YES;
                                CLANG_WARN_ENUM_CONVERSION = YES;
                                CLANG_WARN_INFINITE_RECURSION = YES;
                                CLANG_WARN_EMPTY_BODY = YES;
                                CLANG_WARN_ENUM_CONVERSION = YES;
                                CLANG_WARN_INFINITE_RECURSION = YES;
                                CLANG_WARN_BOOL_CONVERSION = YES;
                                CLANG_WARN_CONSTANT_CONVERSION = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
                                CLANG_WARN_BOOL_CONVERSION = YES;
                                CLANG_WARN_CONSTANT_CONVERSION = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
-                               CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
                                CLANG_WARN_EMPTY_BODY = YES;
                                CLANG_WARN_ENUM_CONVERSION = YES;
                                CLANG_WARN_INFINITE_RECURSION = YES;
                                CLANG_WARN_EMPTY_BODY = YES;
                                CLANG_WARN_ENUM_CONVERSION = YES;
                                CLANG_WARN_INFINITE_RECURSION = YES;
                                CLANG_WARN_BOOL_CONVERSION = YES;
                                CLANG_WARN_CONSTANT_CONVERSION = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
                                CLANG_WARN_BOOL_CONVERSION = YES;
                                CLANG_WARN_CONSTANT_CONVERSION = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
-                               CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
                                CLANG_WARN_EMPTY_BODY = YES;
                                CLANG_WARN_ENUM_CONVERSION = YES;
                                CLANG_WARN_INFINITE_RECURSION = YES;
                                CLANG_WARN_EMPTY_BODY = YES;
                                CLANG_WARN_ENUM_CONVERSION = YES;
                                CLANG_WARN_INFINITE_RECURSION = YES;
                                CLANG_WARN_BOOL_CONVERSION = YES;
                                CLANG_WARN_CONSTANT_CONVERSION = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
                                CLANG_WARN_BOOL_CONVERSION = YES;
                                CLANG_WARN_CONSTANT_CONVERSION = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
-                               CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
                                CLANG_WARN_EMPTY_BODY = YES;
                                CLANG_WARN_ENUM_CONVERSION = YES;
                                CLANG_WARN_INFINITE_RECURSION = YES;
                                CLANG_WARN_EMPTY_BODY = YES;
                                CLANG_WARN_ENUM_CONVERSION = YES;
                                CLANG_WARN_INFINITE_RECURSION = YES;
                                CLANG_WARN_BOOL_CONVERSION = YES;
                                CLANG_WARN_CONSTANT_CONVERSION = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
                                CLANG_WARN_BOOL_CONVERSION = YES;
                                CLANG_WARN_CONSTANT_CONVERSION = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
-                               CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
                                CLANG_WARN_EMPTY_BODY = YES;
                                CLANG_WARN_ENUM_CONVERSION = YES;
                                CLANG_WARN_INFINITE_RECURSION = YES;
                                CLANG_WARN_EMPTY_BODY = YES;
                                CLANG_WARN_ENUM_CONVERSION = YES;
                                CLANG_WARN_INFINITE_RECURSION = YES;
                                CLANG_WARN_BOOL_CONVERSION = YES;
                                CLANG_WARN_CONSTANT_CONVERSION = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
                                CLANG_WARN_BOOL_CONVERSION = YES;
                                CLANG_WARN_CONSTANT_CONVERSION = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
-                               CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
                                CLANG_WARN_EMPTY_BODY = YES;
                                CLANG_WARN_ENUM_CONVERSION = YES;
                                CLANG_WARN_INFINITE_RECURSION = YES;
                                CLANG_WARN_EMPTY_BODY = YES;
                                CLANG_WARN_ENUM_CONVERSION = YES;
                                CLANG_WARN_INFINITE_RECURSION = YES;
                        baseConfigurationReference = DC0067911D87816C005AF8DB /* macos_legacy_lib.xcconfig */;
                        buildSettings = {
                                CLANG_ANALYZER_NONNULL = YES;
                        baseConfigurationReference = DC0067911D87816C005AF8DB /* macos_legacy_lib.xcconfig */;
                        buildSettings = {
                                CLANG_ANALYZER_NONNULL = YES;
-                               CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
                                CLANG_WARN_SUSPICIOUS_MOVES = YES;
                                ENABLE_STRICT_OBJC_MSGSEND = YES;
                                MTL_ENABLE_DEBUG_INFO = YES;
                                CLANG_WARN_SUSPICIOUS_MOVES = YES;
                                ENABLE_STRICT_OBJC_MSGSEND = YES;
                                MTL_ENABLE_DEBUG_INFO = YES;
                        baseConfigurationReference = DC0067911D87816C005AF8DB /* macos_legacy_lib.xcconfig */;
                        buildSettings = {
                                CLANG_ANALYZER_NONNULL = YES;
                        baseConfigurationReference = DC0067911D87816C005AF8DB /* macos_legacy_lib.xcconfig */;
                        buildSettings = {
                                CLANG_ANALYZER_NONNULL = YES;
-                               CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
                                CLANG_WARN_SUSPICIOUS_MOVES = YES;
                                ENABLE_STRICT_OBJC_MSGSEND = YES;
                                MTL_ENABLE_DEBUG_INFO = NO;
                                CLANG_WARN_SUSPICIOUS_MOVES = YES;
                                ENABLE_STRICT_OBJC_MSGSEND = YES;
                                MTL_ENABLE_DEBUG_INFO = NO;
                        baseConfigurationReference = DC0067911D87816C005AF8DB /* macos_legacy_lib.xcconfig */;
                        buildSettings = {
                                CLANG_ANALYZER_NONNULL = YES;
                        baseConfigurationReference = DC0067911D87816C005AF8DB /* macos_legacy_lib.xcconfig */;
                        buildSettings = {
                                CLANG_ANALYZER_NONNULL = YES;
-                               CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
                                CLANG_WARN_SUSPICIOUS_MOVES = YES;
                                GCC_SYMBOLS_PRIVATE_EXTERN = YES;
                                MTL_ENABLE_DEBUG_INFO = YES;
                                CLANG_WARN_SUSPICIOUS_MOVES = YES;
                                GCC_SYMBOLS_PRIVATE_EXTERN = YES;
                                MTL_ENABLE_DEBUG_INFO = YES;
                        baseConfigurationReference = DC0067911D87816C005AF8DB /* macos_legacy_lib.xcconfig */;
                        buildSettings = {
                                CLANG_ANALYZER_NONNULL = YES;
                        baseConfigurationReference = DC0067911D87816C005AF8DB /* macos_legacy_lib.xcconfig */;
                        buildSettings = {
                                CLANG_ANALYZER_NONNULL = YES;
-                               CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
                                CLANG_WARN_SUSPICIOUS_MOVES = YES;
                                GCC_SYMBOLS_PRIVATE_EXTERN = YES;
                                MTL_ENABLE_DEBUG_INFO = NO;
                                CLANG_WARN_SUSPICIOUS_MOVES = YES;
                                GCC_SYMBOLS_PRIVATE_EXTERN = YES;
                                MTL_ENABLE_DEBUG_INFO = NO;
                        baseConfigurationReference = DC0067911D87816C005AF8DB /* macos_legacy_lib.xcconfig */;
                        buildSettings = {
                                CLANG_ANALYZER_NONNULL = YES;
                        baseConfigurationReference = DC0067911D87816C005AF8DB /* macos_legacy_lib.xcconfig */;
                        buildSettings = {
                                CLANG_ANALYZER_NONNULL = YES;
-                               CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
                                CLANG_WARN_SUSPICIOUS_MOVES = YES;
                                GCC_SYMBOLS_PRIVATE_EXTERN = YES;
                                MTL_ENABLE_DEBUG_INFO = YES;
                                CLANG_WARN_SUSPICIOUS_MOVES = YES;
                                GCC_SYMBOLS_PRIVATE_EXTERN = YES;
                                MTL_ENABLE_DEBUG_INFO = YES;
                        baseConfigurationReference = DC0067911D87816C005AF8DB /* macos_legacy_lib.xcconfig */;
                        buildSettings = {
                                CLANG_ANALYZER_NONNULL = YES;
                        baseConfigurationReference = DC0067911D87816C005AF8DB /* macos_legacy_lib.xcconfig */;
                        buildSettings = {
                                CLANG_ANALYZER_NONNULL = YES;
-                               CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
                                CLANG_WARN_SUSPICIOUS_MOVES = YES;
                                GCC_SYMBOLS_PRIVATE_EXTERN = YES;
                                MTL_ENABLE_DEBUG_INFO = NO;
                                CLANG_WARN_SUSPICIOUS_MOVES = YES;
                                GCC_SYMBOLS_PRIVATE_EXTERN = YES;
                                MTL_ENABLE_DEBUG_INFO = NO;
                        baseConfigurationReference = DC0067911D87816C005AF8DB /* macos_legacy_lib.xcconfig */;
                        buildSettings = {
                                CLANG_ANALYZER_NONNULL = YES;
                        baseConfigurationReference = DC0067911D87816C005AF8DB /* macos_legacy_lib.xcconfig */;
                        buildSettings = {
                                CLANG_ANALYZER_NONNULL = YES;
-                               CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
                                CLANG_WARN_SUSPICIOUS_MOVES = YES;
                                ENABLE_STRICT_OBJC_MSGSEND = YES;
                                MTL_ENABLE_DEBUG_INFO = YES;
                                CLANG_WARN_SUSPICIOUS_MOVES = YES;
                                ENABLE_STRICT_OBJC_MSGSEND = YES;
                                MTL_ENABLE_DEBUG_INFO = YES;
                        baseConfigurationReference = DC0067911D87816C005AF8DB /* macos_legacy_lib.xcconfig */;
                        buildSettings = {
                                CLANG_ANALYZER_NONNULL = YES;
                        baseConfigurationReference = DC0067911D87816C005AF8DB /* macos_legacy_lib.xcconfig */;
                        buildSettings = {
                                CLANG_ANALYZER_NONNULL = YES;
-                               CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
                                CLANG_WARN_SUSPICIOUS_MOVES = YES;
                                ENABLE_STRICT_OBJC_MSGSEND = YES;
                                MTL_ENABLE_DEBUG_INFO = NO;
                                CLANG_WARN_SUSPICIOUS_MOVES = YES;
                                ENABLE_STRICT_OBJC_MSGSEND = YES;
                                MTL_ENABLE_DEBUG_INFO = NO;
                        baseConfigurationReference = DC0067911D87816C005AF8DB /* macos_legacy_lib.xcconfig */;
                        buildSettings = {
                                CLANG_ANALYZER_NONNULL = YES;
                        baseConfigurationReference = DC0067911D87816C005AF8DB /* macos_legacy_lib.xcconfig */;
                        buildSettings = {
                                CLANG_ANALYZER_NONNULL = YES;
-                               CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
                                CLANG_WARN_SUSPICIOUS_MOVES = YES;
                                ENABLE_STRICT_OBJC_MSGSEND = YES;
                                GCC_PREPROCESSOR_DEFINITIONS = "$(inherited)";
                                CLANG_WARN_SUSPICIOUS_MOVES = YES;
                                ENABLE_STRICT_OBJC_MSGSEND = YES;
                                GCC_PREPROCESSOR_DEFINITIONS = "$(inherited)";
                        baseConfigurationReference = DC0067911D87816C005AF8DB /* macos_legacy_lib.xcconfig */;
                        buildSettings = {
                                CLANG_ANALYZER_NONNULL = YES;
                        baseConfigurationReference = DC0067911D87816C005AF8DB /* macos_legacy_lib.xcconfig */;
                        buildSettings = {
                                CLANG_ANALYZER_NONNULL = YES;
-                               CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
                                CLANG_WARN_SUSPICIOUS_MOVES = YES;
                                ENABLE_STRICT_OBJC_MSGSEND = YES;
                                GCC_PREPROCESSOR_DEFINITIONS = "$(inherited)";
                                CLANG_WARN_SUSPICIOUS_MOVES = YES;
                                ENABLE_STRICT_OBJC_MSGSEND = YES;
                                GCC_PREPROCESSOR_DEFINITIONS = "$(inherited)";
                        buildSettings = {
                                CLANG_ANALYZER_NONNULL = YES;
                                CLANG_ENABLE_OBJC_ARC = YES;
                        buildSettings = {
                                CLANG_ANALYZER_NONNULL = YES;
                                CLANG_ENABLE_OBJC_ARC = YES;
-                               CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
                                CLANG_WARN_SUSPICIOUS_MOVES = YES;
                                ENABLE_STRICT_OBJC_MSGSEND = YES;
                                GCC_PREPROCESSOR_DEFINITIONS = "$(inherited)";
                                CLANG_WARN_SUSPICIOUS_MOVES = YES;
                                ENABLE_STRICT_OBJC_MSGSEND = YES;
                                GCC_PREPROCESSOR_DEFINITIONS = "$(inherited)";
                        buildSettings = {
                                CLANG_ANALYZER_NONNULL = YES;
                                CLANG_ENABLE_OBJC_ARC = YES;
                        buildSettings = {
                                CLANG_ANALYZER_NONNULL = YES;
                                CLANG_ENABLE_OBJC_ARC = YES;
-                               CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
                                CLANG_WARN_SUSPICIOUS_MOVES = YES;
                                ENABLE_STRICT_OBJC_MSGSEND = YES;
                                GCC_PREPROCESSOR_DEFINITIONS = "$(inherited)";
                                CLANG_WARN_SUSPICIOUS_MOVES = YES;
                                ENABLE_STRICT_OBJC_MSGSEND = YES;
                                GCC_PREPROCESSOR_DEFINITIONS = "$(inherited)";
                                CLANG_WARN_BOOL_CONVERSION = YES;
                                CLANG_WARN_CONSTANT_CONVERSION = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
                                CLANG_WARN_BOOL_CONVERSION = YES;
                                CLANG_WARN_CONSTANT_CONVERSION = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
-                               CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
                                CLANG_WARN_EMPTY_BODY = YES;
                                CLANG_WARN_ENUM_CONVERSION = YES;
                                CLANG_WARN_INFINITE_RECURSION = YES;
                                CLANG_WARN_EMPTY_BODY = YES;
                                CLANG_WARN_ENUM_CONVERSION = YES;
                                CLANG_WARN_INFINITE_RECURSION = YES;
                                CLANG_WARN_BOOL_CONVERSION = YES;
                                CLANG_WARN_CONSTANT_CONVERSION = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
                                CLANG_WARN_BOOL_CONVERSION = YES;
                                CLANG_WARN_CONSTANT_CONVERSION = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
-                               CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
                                CLANG_WARN_EMPTY_BODY = YES;
                                CLANG_WARN_ENUM_CONVERSION = YES;
                                CLANG_WARN_INFINITE_RECURSION = YES;
                                CLANG_WARN_EMPTY_BODY = YES;
                                CLANG_WARN_ENUM_CONVERSION = YES;
                                CLANG_WARN_INFINITE_RECURSION = YES;
                        baseConfigurationReference = DC0067911D87816C005AF8DB /* macos_legacy_lib.xcconfig */;
                        buildSettings = {
                                CLANG_ANALYZER_NONNULL = YES;
                        baseConfigurationReference = DC0067911D87816C005AF8DB /* macos_legacy_lib.xcconfig */;
                        buildSettings = {
                                CLANG_ANALYZER_NONNULL = YES;
-                               CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
                                CLANG_WARN_SUSPICIOUS_MOVES = YES;
                                ENABLE_STRICT_OBJC_MSGSEND = YES;
                                MTL_ENABLE_DEBUG_INFO = YES;
                                CLANG_WARN_SUSPICIOUS_MOVES = YES;
                                ENABLE_STRICT_OBJC_MSGSEND = YES;
                                MTL_ENABLE_DEBUG_INFO = YES;
                        baseConfigurationReference = DC0067911D87816C005AF8DB /* macos_legacy_lib.xcconfig */;
                        buildSettings = {
                                CLANG_ANALYZER_NONNULL = YES;
                        baseConfigurationReference = DC0067911D87816C005AF8DB /* macos_legacy_lib.xcconfig */;
                        buildSettings = {
                                CLANG_ANALYZER_NONNULL = YES;
-                               CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
                                CLANG_WARN_SUSPICIOUS_MOVES = YES;
                                ENABLE_STRICT_OBJC_MSGSEND = YES;
                                MTL_ENABLE_DEBUG_INFO = NO;
                                CLANG_WARN_SUSPICIOUS_MOVES = YES;
                                ENABLE_STRICT_OBJC_MSGSEND = YES;
                                MTL_ENABLE_DEBUG_INFO = NO;
                        baseConfigurationReference = DC0067911D87816C005AF8DB /* macos_legacy_lib.xcconfig */;
                        buildSettings = {
                                CLANG_ANALYZER_NONNULL = YES;
                        baseConfigurationReference = DC0067911D87816C005AF8DB /* macos_legacy_lib.xcconfig */;
                        buildSettings = {
                                CLANG_ANALYZER_NONNULL = YES;
-                               CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
                                CLANG_WARN_SUSPICIOUS_MOVES = YES;
                                ENABLE_STRICT_OBJC_MSGSEND = YES;
                                MTL_ENABLE_DEBUG_INFO = YES;
                                CLANG_WARN_SUSPICIOUS_MOVES = YES;
                                ENABLE_STRICT_OBJC_MSGSEND = YES;
                                MTL_ENABLE_DEBUG_INFO = YES;
                        baseConfigurationReference = DC0067911D87816C005AF8DB /* macos_legacy_lib.xcconfig */;
                        buildSettings = {
                                CLANG_ANALYZER_NONNULL = YES;
                        baseConfigurationReference = DC0067911D87816C005AF8DB /* macos_legacy_lib.xcconfig */;
                        buildSettings = {
                                CLANG_ANALYZER_NONNULL = YES;
-                               CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
                                CLANG_WARN_SUSPICIOUS_MOVES = YES;
                                ENABLE_STRICT_OBJC_MSGSEND = YES;
                                MTL_ENABLE_DEBUG_INFO = NO;
                                CLANG_WARN_SUSPICIOUS_MOVES = YES;
                                ENABLE_STRICT_OBJC_MSGSEND = YES;
                                MTL_ENABLE_DEBUG_INFO = NO;
                                CLANG_WARN_BOOL_CONVERSION = YES;
                                CLANG_WARN_CONSTANT_CONVERSION = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
                                CLANG_WARN_BOOL_CONVERSION = YES;
                                CLANG_WARN_CONSTANT_CONVERSION = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
-                               CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
                                CLANG_WARN_EMPTY_BODY = YES;
                                CLANG_WARN_ENUM_CONVERSION = YES;
                                CLANG_WARN_INFINITE_RECURSION = YES;
                                CLANG_WARN_EMPTY_BODY = YES;
                                CLANG_WARN_ENUM_CONVERSION = YES;
                                CLANG_WARN_INFINITE_RECURSION = YES;
                                CLANG_WARN_BOOL_CONVERSION = YES;
                                CLANG_WARN_CONSTANT_CONVERSION = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
                                CLANG_WARN_BOOL_CONVERSION = YES;
                                CLANG_WARN_CONSTANT_CONVERSION = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
-                               CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
                                CLANG_WARN_EMPTY_BODY = YES;
                                CLANG_WARN_ENUM_CONVERSION = YES;
                                CLANG_WARN_INFINITE_RECURSION = YES;
                                CLANG_WARN_EMPTY_BODY = YES;
                                CLANG_WARN_ENUM_CONVERSION = YES;
                                CLANG_WARN_INFINITE_RECURSION = YES;
                                CLANG_WARN_BOOL_CONVERSION = YES;
                                CLANG_WARN_CONSTANT_CONVERSION = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
                                CLANG_WARN_BOOL_CONVERSION = YES;
                                CLANG_WARN_CONSTANT_CONVERSION = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
-                               CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
                                CLANG_WARN_EMPTY_BODY = YES;
                                CLANG_WARN_ENUM_CONVERSION = YES;
                                CLANG_WARN_INFINITE_RECURSION = YES;
                                CLANG_WARN_EMPTY_BODY = YES;
                                CLANG_WARN_ENUM_CONVERSION = YES;
                                CLANG_WARN_INFINITE_RECURSION = YES;
                                CLANG_WARN_BOOL_CONVERSION = YES;
                                CLANG_WARN_CONSTANT_CONVERSION = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
                                CLANG_WARN_BOOL_CONVERSION = YES;
                                CLANG_WARN_CONSTANT_CONVERSION = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
-                               CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
                                CLANG_WARN_EMPTY_BODY = YES;
                                CLANG_WARN_ENUM_CONVERSION = YES;
                                CLANG_WARN_INFINITE_RECURSION = YES;
                                CLANG_WARN_EMPTY_BODY = YES;
                                CLANG_WARN_ENUM_CONVERSION = YES;
                                CLANG_WARN_INFINITE_RECURSION = YES;
                                CLANG_WARN_BOOL_CONVERSION = YES;
                                CLANG_WARN_CONSTANT_CONVERSION = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
                                CLANG_WARN_BOOL_CONVERSION = YES;
                                CLANG_WARN_CONSTANT_CONVERSION = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
-                               CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
                                CLANG_WARN_EMPTY_BODY = YES;
                                CLANG_WARN_ENUM_CONVERSION = YES;
                                CLANG_WARN_INFINITE_RECURSION = YES;
                                CLANG_WARN_EMPTY_BODY = YES;
                                CLANG_WARN_ENUM_CONVERSION = YES;
                                CLANG_WARN_INFINITE_RECURSION = YES;
                                CLANG_WARN_BOOL_CONVERSION = YES;
                                CLANG_WARN_CONSTANT_CONVERSION = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
                                CLANG_WARN_BOOL_CONVERSION = YES;
                                CLANG_WARN_CONSTANT_CONVERSION = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
-                               CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
                                CLANG_WARN_EMPTY_BODY = YES;
                                CLANG_WARN_ENUM_CONVERSION = YES;
                                CLANG_WARN_INFINITE_RECURSION = YES;
                                CLANG_WARN_EMPTY_BODY = YES;
                                CLANG_WARN_ENUM_CONVERSION = YES;
                                CLANG_WARN_INFINITE_RECURSION = YES;
                                CLANG_ENABLE_OBJC_ARC = YES;
                                CLANG_WARN_BOOL_CONVERSION = YES;
                                CLANG_WARN_CONSTANT_CONVERSION = YES;
                                CLANG_ENABLE_OBJC_ARC = YES;
                                CLANG_WARN_BOOL_CONVERSION = YES;
                                CLANG_WARN_CONSTANT_CONVERSION = YES;
-                               CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
                                CLANG_WARN_EMPTY_BODY = YES;
                                CLANG_WARN_ENUM_CONVERSION = YES;
                                CLANG_WARN_INFINITE_RECURSION = YES;
                                CLANG_WARN_EMPTY_BODY = YES;
                                CLANG_WARN_ENUM_CONVERSION = YES;
                                CLANG_WARN_INFINITE_RECURSION = YES;
                                CLANG_ENABLE_OBJC_ARC = YES;
                                CLANG_WARN_BOOL_CONVERSION = YES;
                                CLANG_WARN_CONSTANT_CONVERSION = YES;
                                CLANG_ENABLE_OBJC_ARC = YES;
                                CLANG_WARN_BOOL_CONVERSION = YES;
                                CLANG_WARN_CONSTANT_CONVERSION = YES;
-                               CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
                                CLANG_WARN_EMPTY_BODY = YES;
                                CLANG_WARN_ENUM_CONVERSION = YES;
                                CLANG_WARN_INFINITE_RECURSION = YES;
                                CLANG_WARN_EMPTY_BODY = YES;
                                CLANG_WARN_ENUM_CONVERSION = YES;
                                CLANG_WARN_INFINITE_RECURSION = YES;
                                CLANG_WARN_BOOL_CONVERSION = YES;
                                CLANG_WARN_CONSTANT_CONVERSION = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
                                CLANG_WARN_BOOL_CONVERSION = YES;
                                CLANG_WARN_CONSTANT_CONVERSION = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
-                               CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
                                CLANG_WARN_EMPTY_BODY = YES;
                                CLANG_WARN_ENUM_CONVERSION = YES;
                                CLANG_WARN_INFINITE_RECURSION = YES;
                                CLANG_WARN_EMPTY_BODY = YES;
                                CLANG_WARN_ENUM_CONVERSION = YES;
                                CLANG_WARN_INFINITE_RECURSION = YES;
                                CLANG_WARN_BOOL_CONVERSION = YES;
                                CLANG_WARN_CONSTANT_CONVERSION = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
                                CLANG_WARN_BOOL_CONVERSION = YES;
                                CLANG_WARN_CONSTANT_CONVERSION = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
-                               CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
                                CLANG_WARN_EMPTY_BODY = YES;
                                CLANG_WARN_ENUM_CONVERSION = YES;
                                CLANG_WARN_INFINITE_RECURSION = YES;
                                CLANG_WARN_EMPTY_BODY = YES;
                                CLANG_WARN_ENUM_CONVERSION = YES;
                                CLANG_WARN_INFINITE_RECURSION = YES;
                                CLANG_WARN_BOOL_CONVERSION = YES;
                                CLANG_WARN_CONSTANT_CONVERSION = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
                                CLANG_WARN_BOOL_CONVERSION = YES;
                                CLANG_WARN_CONSTANT_CONVERSION = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
-                               CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
                                CLANG_WARN_EMPTY_BODY = YES;
                                CLANG_WARN_ENUM_CONVERSION = YES;
                                CLANG_WARN_INFINITE_RECURSION = YES;
                                CLANG_WARN_EMPTY_BODY = YES;
                                CLANG_WARN_ENUM_CONVERSION = YES;
                                CLANG_WARN_INFINITE_RECURSION = YES;
                                CLANG_WARN_BOOL_CONVERSION = YES;
                                CLANG_WARN_CONSTANT_CONVERSION = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
                                CLANG_WARN_BOOL_CONVERSION = YES;
                                CLANG_WARN_CONSTANT_CONVERSION = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
-                               CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
                                CLANG_WARN_EMPTY_BODY = YES;
                                CLANG_WARN_ENUM_CONVERSION = YES;
                                CLANG_WARN_INFINITE_RECURSION = YES;
                                CLANG_WARN_EMPTY_BODY = YES;
                                CLANG_WARN_ENUM_CONVERSION = YES;
                                CLANG_WARN_INFINITE_RECURSION = YES;
                                CLANG_WARN_BOOL_CONVERSION = YES;
                                CLANG_WARN_CONSTANT_CONVERSION = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
                                CLANG_WARN_BOOL_CONVERSION = YES;
                                CLANG_WARN_CONSTANT_CONVERSION = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
-                               CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
                                CLANG_WARN_EMPTY_BODY = YES;
                                CLANG_WARN_ENUM_CONVERSION = YES;
                                CLANG_WARN_INFINITE_RECURSION = YES;
                                CLANG_WARN_EMPTY_BODY = YES;
                                CLANG_WARN_ENUM_CONVERSION = YES;
                                CLANG_WARN_INFINITE_RECURSION = YES;
                                CLANG_WARN_BOOL_CONVERSION = YES;
                                CLANG_WARN_CONSTANT_CONVERSION = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
                                CLANG_WARN_BOOL_CONVERSION = YES;
                                CLANG_WARN_CONSTANT_CONVERSION = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
-                               CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
                                CLANG_WARN_EMPTY_BODY = YES;
                                CLANG_WARN_ENUM_CONVERSION = YES;
                                CLANG_WARN_INFINITE_RECURSION = YES;
                                CLANG_WARN_EMPTY_BODY = YES;
                                CLANG_WARN_ENUM_CONVERSION = YES;
                                CLANG_WARN_INFINITE_RECURSION = YES;
                        buildSettings = {
                                APPLY_RULES_IN_COPY_FILES = NO;
                                CLANG_ANALYZER_NONNULL = YES;
                        buildSettings = {
                                APPLY_RULES_IN_COPY_FILES = NO;
                                CLANG_ANALYZER_NONNULL = YES;
-                               CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
                                CLANG_WARN_STRICT_PROTOTYPES = NO;
                                ENABLE_STRICT_OBJC_MSGSEND = YES;
                                GCC_PREPROCESSOR_DEFINITIONS = (
                                CLANG_WARN_STRICT_PROTOTYPES = NO;
                                ENABLE_STRICT_OBJC_MSGSEND = YES;
                                GCC_PREPROCESSOR_DEFINITIONS = (
                        buildSettings = {
                                APPLY_RULES_IN_COPY_FILES = NO;
                                CLANG_ANALYZER_NONNULL = YES;
                        buildSettings = {
                                APPLY_RULES_IN_COPY_FILES = NO;
                                CLANG_ANALYZER_NONNULL = YES;
-                               CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
                                CLANG_WARN_STRICT_PROTOTYPES = NO;
                                ENABLE_STRICT_OBJC_MSGSEND = YES;
                                GCC_PREPROCESSOR_DEFINITIONS = (
                                CLANG_WARN_STRICT_PROTOTYPES = NO;
                                ENABLE_STRICT_OBJC_MSGSEND = YES;
                                GCC_PREPROCESSOR_DEFINITIONS = (
                        baseConfigurationReference = DC0067911D87816C005AF8DB /* macos_legacy_lib.xcconfig */;
                        buildSettings = {
                                CLANG_ANALYZER_NONNULL = YES;
                        baseConfigurationReference = DC0067911D87816C005AF8DB /* macos_legacy_lib.xcconfig */;
                        buildSettings = {
                                CLANG_ANALYZER_NONNULL = YES;
-                               CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
                                CLANG_WARN_SUSPICIOUS_MOVES = YES;
                                ENABLE_STRICT_OBJC_MSGSEND = YES;
                                GCC_PREPROCESSOR_DEFINITIONS = (
                                CLANG_WARN_SUSPICIOUS_MOVES = YES;
                                ENABLE_STRICT_OBJC_MSGSEND = YES;
                                GCC_PREPROCESSOR_DEFINITIONS = (
                        baseConfigurationReference = DC0067911D87816C005AF8DB /* macos_legacy_lib.xcconfig */;
                        buildSettings = {
                                CLANG_ANALYZER_NONNULL = YES;
                        baseConfigurationReference = DC0067911D87816C005AF8DB /* macos_legacy_lib.xcconfig */;
                        buildSettings = {
                                CLANG_ANALYZER_NONNULL = YES;
-                               CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
                                CLANG_WARN_SUSPICIOUS_MOVES = YES;
                                ENABLE_STRICT_OBJC_MSGSEND = YES;
                                GCC_PREPROCESSOR_DEFINITIONS = (
                                CLANG_WARN_SUSPICIOUS_MOVES = YES;
                                ENABLE_STRICT_OBJC_MSGSEND = YES;
                                GCC_PREPROCESSOR_DEFINITIONS = (
                        baseConfigurationReference = DC0067911D87816C005AF8DB /* macos_legacy_lib.xcconfig */;
                        buildSettings = {
                                CLANG_ANALYZER_NONNULL = YES;
                        baseConfigurationReference = DC0067911D87816C005AF8DB /* macos_legacy_lib.xcconfig */;
                        buildSettings = {
                                CLANG_ANALYZER_NONNULL = YES;
-                               CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
                                CLANG_WARN_SUSPICIOUS_MOVES = YES;
                                ENABLE_STRICT_OBJC_MSGSEND = YES;
                                MTL_ENABLE_DEBUG_INFO = YES;
                                CLANG_WARN_SUSPICIOUS_MOVES = YES;
                                ENABLE_STRICT_OBJC_MSGSEND = YES;
                                MTL_ENABLE_DEBUG_INFO = YES;
                        baseConfigurationReference = DC0067911D87816C005AF8DB /* macos_legacy_lib.xcconfig */;
                        buildSettings = {
                                CLANG_ANALYZER_NONNULL = YES;
                        baseConfigurationReference = DC0067911D87816C005AF8DB /* macos_legacy_lib.xcconfig */;
                        buildSettings = {
                                CLANG_ANALYZER_NONNULL = YES;
-                               CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
                                CLANG_WARN_SUSPICIOUS_MOVES = YES;
                                ENABLE_STRICT_OBJC_MSGSEND = YES;
                                MTL_ENABLE_DEBUG_INFO = NO;
                                CLANG_WARN_SUSPICIOUS_MOVES = YES;
                                ENABLE_STRICT_OBJC_MSGSEND = YES;
                                MTL_ENABLE_DEBUG_INFO = NO;
                        baseConfigurationReference = DC0067911D87816C005AF8DB /* macos_legacy_lib.xcconfig */;
                        buildSettings = {
                                CLANG_ANALYZER_NONNULL = YES;
                        baseConfigurationReference = DC0067911D87816C005AF8DB /* macos_legacy_lib.xcconfig */;
                        buildSettings = {
                                CLANG_ANALYZER_NONNULL = YES;
-                               CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
                                CLANG_WARN_SUSPICIOUS_MOVES = YES;
                                ENABLE_STRICT_OBJC_MSGSEND = YES;
                                MTL_ENABLE_DEBUG_INFO = YES;
                                CLANG_WARN_SUSPICIOUS_MOVES = YES;
                                ENABLE_STRICT_OBJC_MSGSEND = YES;
                                MTL_ENABLE_DEBUG_INFO = YES;
                        baseConfigurationReference = DC0067911D87816C005AF8DB /* macos_legacy_lib.xcconfig */;
                        buildSettings = {
                                CLANG_ANALYZER_NONNULL = YES;
                        baseConfigurationReference = DC0067911D87816C005AF8DB /* macos_legacy_lib.xcconfig */;
                        buildSettings = {
                                CLANG_ANALYZER_NONNULL = YES;
-                               CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
                                CLANG_WARN_SUSPICIOUS_MOVES = YES;
                                ENABLE_STRICT_OBJC_MSGSEND = YES;
                                MTL_ENABLE_DEBUG_INFO = NO;
                                CLANG_WARN_SUSPICIOUS_MOVES = YES;
                                ENABLE_STRICT_OBJC_MSGSEND = YES;
                                MTL_ENABLE_DEBUG_INFO = NO;
                        baseConfigurationReference = DC0067911D87816C005AF8DB /* macos_legacy_lib.xcconfig */;
                        buildSettings = {
                                CLANG_ANALYZER_NONNULL = YES;
                        baseConfigurationReference = DC0067911D87816C005AF8DB /* macos_legacy_lib.xcconfig */;
                        buildSettings = {
                                CLANG_ANALYZER_NONNULL = YES;
-                               CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
                                CLANG_WARN_SUSPICIOUS_MOVES = YES;
                                ENABLE_STRICT_OBJC_MSGSEND = YES;
                                MTL_ENABLE_DEBUG_INFO = YES;
                                CLANG_WARN_SUSPICIOUS_MOVES = YES;
                                ENABLE_STRICT_OBJC_MSGSEND = YES;
                                MTL_ENABLE_DEBUG_INFO = YES;
                        baseConfigurationReference = DC0067911D87816C005AF8DB /* macos_legacy_lib.xcconfig */;
                        buildSettings = {
                                CLANG_ANALYZER_NONNULL = YES;
                        baseConfigurationReference = DC0067911D87816C005AF8DB /* macos_legacy_lib.xcconfig */;
                        buildSettings = {
                                CLANG_ANALYZER_NONNULL = YES;
-                               CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
                                CLANG_WARN_SUSPICIOUS_MOVES = YES;
                                ENABLE_STRICT_OBJC_MSGSEND = YES;
                                MTL_ENABLE_DEBUG_INFO = NO;
                                CLANG_WARN_SUSPICIOUS_MOVES = YES;
                                ENABLE_STRICT_OBJC_MSGSEND = YES;
                                MTL_ENABLE_DEBUG_INFO = NO;
                                CLANG_ENABLE_MODULES = YES;
                                CLANG_ENABLE_OBJC_ARC = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
                                CLANG_ENABLE_MODULES = YES;
                                CLANG_ENABLE_OBJC_ARC = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
-                               CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
                                CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
                                CLANG_WARN_SUSPICIOUS_MOVES = YES;
                                CLANG_WARN_UNREACHABLE_CODE = YES;
                                CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
                                CLANG_WARN_SUSPICIOUS_MOVES = YES;
                                CLANG_WARN_UNREACHABLE_CODE = YES;
                                CLANG_ENABLE_MODULES = YES;
                                CLANG_ENABLE_OBJC_ARC = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
                                CLANG_ENABLE_MODULES = YES;
                                CLANG_ENABLE_OBJC_ARC = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
-                               CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
                                CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
                                CLANG_WARN_SUSPICIOUS_MOVES = YES;
                                CLANG_WARN_UNREACHABLE_CODE = YES;
                                CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
                                CLANG_WARN_SUSPICIOUS_MOVES = YES;
                                CLANG_WARN_UNREACHABLE_CODE = YES;
                        baseConfigurationReference = DC0067911D87816C005AF8DB /* macos_legacy_lib.xcconfig */;
                        buildSettings = {
                                CLANG_ANALYZER_NONNULL = YES;
                        baseConfigurationReference = DC0067911D87816C005AF8DB /* macos_legacy_lib.xcconfig */;
                        buildSettings = {
                                CLANG_ANALYZER_NONNULL = YES;
-                               CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
                                CLANG_WARN_SUSPICIOUS_MOVES = YES;
                                ENABLE_STRICT_OBJC_MSGSEND = YES;
                                MTL_ENABLE_DEBUG_INFO = YES;
                                CLANG_WARN_SUSPICIOUS_MOVES = YES;
                                ENABLE_STRICT_OBJC_MSGSEND = YES;
                                MTL_ENABLE_DEBUG_INFO = YES;
                        baseConfigurationReference = DC0067911D87816C005AF8DB /* macos_legacy_lib.xcconfig */;
                        buildSettings = {
                                CLANG_ANALYZER_NONNULL = YES;
                        baseConfigurationReference = DC0067911D87816C005AF8DB /* macos_legacy_lib.xcconfig */;
                        buildSettings = {
                                CLANG_ANALYZER_NONNULL = YES;
-                               CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
                                CLANG_WARN_SUSPICIOUS_MOVES = YES;
                                ENABLE_STRICT_OBJC_MSGSEND = YES;
                                MTL_ENABLE_DEBUG_INFO = NO;
                                CLANG_WARN_SUSPICIOUS_MOVES = YES;
                                ENABLE_STRICT_OBJC_MSGSEND = YES;
                                MTL_ENABLE_DEBUG_INFO = NO;
                                CLANG_ENABLE_MODULES = NO;
                                CLANG_ENABLE_OBJC_WEAK = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
                                CLANG_ENABLE_MODULES = NO;
                                CLANG_ENABLE_OBJC_WEAK = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
-                               CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
                                CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
                                CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
                                CLANG_WARN_UNREACHABLE_CODE = YES;
                                CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
                                CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
                                CLANG_WARN_UNREACHABLE_CODE = YES;
                                CLANG_ENABLE_MODULES = NO;
                                CLANG_ENABLE_OBJC_WEAK = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
                                CLANG_ENABLE_MODULES = NO;
                                CLANG_ENABLE_OBJC_WEAK = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
-                               CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
                                CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
                                CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
                                CLANG_WARN_UNREACHABLE_CODE = YES;
                                CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
                                CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
                                CLANG_WARN_UNREACHABLE_CODE = YES;
                                CLANG_ENABLE_MODULES = NO;
                                CLANG_ENABLE_OBJC_WEAK = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
                                CLANG_ENABLE_MODULES = NO;
                                CLANG_ENABLE_OBJC_WEAK = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
-                               CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
                                CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
                                CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
                                CLANG_WARN_UNREACHABLE_CODE = YES;
                                CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
                                CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
                                CLANG_WARN_UNREACHABLE_CODE = YES;
                                CLANG_ENABLE_MODULES = NO;
                                CLANG_ENABLE_OBJC_WEAK = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
                                CLANG_ENABLE_MODULES = NO;
                                CLANG_ENABLE_OBJC_WEAK = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
-                               CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
                                CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
                                CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
                                CLANG_WARN_UNREACHABLE_CODE = YES;
                                CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
                                CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
                                CLANG_WARN_UNREACHABLE_CODE = YES;
                                CLANG_ENABLE_MODULES = NO;
                                CLANG_ENABLE_OBJC_WEAK = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
                                CLANG_ENABLE_MODULES = NO;
                                CLANG_ENABLE_OBJC_WEAK = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
-                               CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
                                CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
                                CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
                                CLANG_WARN_UNREACHABLE_CODE = YES;
                                CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
                                CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
                                CLANG_WARN_UNREACHABLE_CODE = YES;
                                CLANG_ENABLE_MODULES = NO;
                                CLANG_ENABLE_OBJC_WEAK = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
                                CLANG_ENABLE_MODULES = NO;
                                CLANG_ENABLE_OBJC_WEAK = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
-                               CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
                                CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
                                CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
                                CLANG_WARN_UNREACHABLE_CODE = YES;
                                CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
                                CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
                                CLANG_WARN_UNREACHABLE_CODE = YES;
                                MTL_ENABLE_DEBUG_INFO = YES;
                                ONLY_ACTIVE_ARCH = YES;
                                OTHER_LDFLAGS = (
                                MTL_ENABLE_DEBUG_INFO = YES;
                                ONLY_ACTIVE_ARCH = YES;
                                OTHER_LDFLAGS = (
-                                       "$(OTHER_LDFLAGS_MOBILEGESTALT)",
                                        "$(OTHER_LDFLAGS_UPWARD_FOUNDATION)",
                                        "$(OTHER_LDFLAGS_UPWARD_PROTOCOLBUFFER)",
                                );
                                        "$(OTHER_LDFLAGS_UPWARD_FOUNDATION)",
                                        "$(OTHER_LDFLAGS_UPWARD_PROTOCOLBUFFER)",
                                );
                                MODULEMAP_FILE = Modules/KeychainCircle.modulemap;
                                MTL_ENABLE_DEBUG_INFO = NO;
                                OTHER_LDFLAGS = (
                                MODULEMAP_FILE = Modules/KeychainCircle.modulemap;
                                MTL_ENABLE_DEBUG_INFO = NO;
                                OTHER_LDFLAGS = (
-                                       "$(OTHER_LDFLAGS_MOBILEGESTALT)",
                                        "$(OTHER_LDFLAGS_UPWARD_FOUNDATION)",
                                        "$(OTHER_LDFLAGS_UPWARD_PROTOCOLBUFFER)",
                                );
                                        "$(OTHER_LDFLAGS_UPWARD_FOUNDATION)",
                                        "$(OTHER_LDFLAGS_UPWARD_PROTOCOLBUFFER)",
                                );
                                CLANG_WARN_BOOL_CONVERSION = YES;
                                CLANG_WARN_CONSTANT_CONVERSION = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
                                CLANG_WARN_BOOL_CONVERSION = YES;
                                CLANG_WARN_CONSTANT_CONVERSION = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
-                               CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
                                CLANG_WARN_EMPTY_BODY = YES;
                                CLANG_WARN_ENUM_CONVERSION = YES;
                                CLANG_WARN_INFINITE_RECURSION = YES;
                                CLANG_WARN_EMPTY_BODY = YES;
                                CLANG_WARN_ENUM_CONVERSION = YES;
                                CLANG_WARN_INFINITE_RECURSION = YES;
                                CLANG_WARN_BOOL_CONVERSION = YES;
                                CLANG_WARN_CONSTANT_CONVERSION = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
                                CLANG_WARN_BOOL_CONVERSION = YES;
                                CLANG_WARN_CONSTANT_CONVERSION = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
-                               CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
                                CLANG_WARN_EMPTY_BODY = YES;
                                CLANG_WARN_ENUM_CONVERSION = YES;
                                CLANG_WARN_INFINITE_RECURSION = YES;
                                CLANG_WARN_EMPTY_BODY = YES;
                                CLANG_WARN_ENUM_CONVERSION = YES;
                                CLANG_WARN_INFINITE_RECURSION = YES;
                                CLANG_ANALYZER_NONNULL = YES;
                                CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
                                CLANG_ANALYZER_NONNULL = YES;
                                CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
-                               CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
                                CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
                                CLANG_WARN_UNREACHABLE_CODE = YES;
                                COPY_PHASE_STRIP = NO;
                                CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
                                CLANG_WARN_UNREACHABLE_CODE = YES;
                                COPY_PHASE_STRIP = NO;
                                CLANG_ANALYZER_NONNULL = YES;
                                CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
                                CLANG_ANALYZER_NONNULL = YES;
                                CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
-                               CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
                                CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
                                CLANG_WARN_UNREACHABLE_CODE = YES;
                                COPY_PHASE_STRIP = NO;
                                CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
                                CLANG_WARN_UNREACHABLE_CODE = YES;
                                COPY_PHASE_STRIP = NO;
                                CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
                                CLANG_ENABLE_OBJC_WEAK = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
                                CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
                                CLANG_ENABLE_OBJC_WEAK = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
-                               CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
                                CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
                                CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
                                CLANG_WARN_UNREACHABLE_CODE = YES;
                                CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
                                CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
                                CLANG_WARN_UNREACHABLE_CODE = YES;
                                CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
                                CLANG_ENABLE_OBJC_WEAK = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
                                CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
                                CLANG_ENABLE_OBJC_WEAK = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
-                               CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
                                CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
                                CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
                                CLANG_WARN_UNREACHABLE_CODE = YES;
                                CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
                                CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
                                CLANG_WARN_UNREACHABLE_CODE = YES;
                                CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
                                CLANG_ENABLE_OBJC_ARC = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
                                CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
                                CLANG_ENABLE_OBJC_ARC = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
-                               CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
                                CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
                                CLANG_WARN_SUSPICIOUS_MOVES = YES;
                                CLANG_WARN_UNREACHABLE_CODE = YES;
                                CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
                                CLANG_WARN_SUSPICIOUS_MOVES = YES;
                                CLANG_WARN_UNREACHABLE_CODE = YES;
                                CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
                                CLANG_ENABLE_OBJC_ARC = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
                                CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
                                CLANG_ENABLE_OBJC_ARC = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
-                               CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
                                CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
                                CLANG_WARN_SUSPICIOUS_MOVES = YES;
                                CLANG_WARN_UNREACHABLE_CODE = YES;
                                CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
                                CLANG_WARN_SUSPICIOUS_MOVES = YES;
                                CLANG_WARN_UNREACHABLE_CODE = YES;
                                CLANG_WARN_BOOL_CONVERSION = YES;
                                CLANG_WARN_CONSTANT_CONVERSION = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
                                CLANG_WARN_BOOL_CONVERSION = YES;
                                CLANG_WARN_CONSTANT_CONVERSION = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
-                               CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
                                CLANG_WARN_EMPTY_BODY = YES;
                                CLANG_WARN_ENUM_CONVERSION = YES;
                                CLANG_WARN_INT_CONVERSION = YES;
                                CLANG_WARN_EMPTY_BODY = YES;
                                CLANG_WARN_ENUM_CONVERSION = YES;
                                CLANG_WARN_INT_CONVERSION = YES;
                                CLANG_WARN_BOOL_CONVERSION = YES;
                                CLANG_WARN_CONSTANT_CONVERSION = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
                                CLANG_WARN_BOOL_CONVERSION = YES;
                                CLANG_WARN_CONSTANT_CONVERSION = YES;
                                CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
-                               CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
                                CLANG_WARN_EMPTY_BODY = YES;
                                CLANG_WARN_ENUM_CONVERSION = YES;
                                CLANG_WARN_INT_CONVERSION = YES;
                                CLANG_WARN_EMPTY_BODY = YES;
                                CLANG_WARN_ENUM_CONVERSION = YES;
                                CLANG_WARN_INT_CONVERSION = YES;
                        defaultConfigurationIsVisible = 0;
                        defaultConfigurationName = Release;
                };
                        defaultConfigurationIsVisible = 0;
                        defaultConfigurationName = Release;
                };
+               0CA378E323876DD100090B7E /* Build configuration list for PBXAggregateTarget "reset_account" */ = {
+                       isa = XCConfigurationList;
+                       buildConfigurations = (
+                               0CA378E423876DD100090B7E /* Debug */,
+                               0CA378E523876DD100090B7E /* Release */,
+                       );
+                       defaultConfigurationIsVisible = 0;
+                       defaultConfigurationName = Release;
+               };
                0CF4064D2072E3E3003D6A7F /* Build configuration list for PBXNativeTarget "SignInAnalyticsTests_ios" */ = {
                        isa = XCConfigurationList;
                        buildConfigurations = (
                0CF4064D2072E3E3003D6A7F /* Build configuration list for PBXNativeTarget "SignInAnalyticsTests_ios" */ = {
                        isa = XCConfigurationList;
                        buildConfigurations = (
index c30a62b4dfab81ccdb6af86fd5d519802b3b2416..577b55c3ec2157a7abfbc876fc0f5f6dae384209 100644 (file)
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <Scheme
 <?xml version="1.0" encoding="UTF-8"?>
 <Scheme
-   LastUpgradeVersion = "1000"
+   LastUpgradeVersion = "1120"
    version = "1.3">
    <BuildAction
       parallelizeBuildables = "NO"
    version = "1.3">
    <BuildAction
       parallelizeBuildables = "NO"
@@ -41,6 +41,9 @@
                <Test
                   Identifier = "CloudKitKeychainSyncingMockXCTest">
                </Test>
                <Test
                   Identifier = "CloudKitKeychainSyncingMockXCTest">
                </Test>
+               <Test
+                  Identifier = "CloudKitKeychainSyncingMultiZoneTestsBase">
+               </Test>
                <Test
                   Identifier = "CloudKitKeychainSyncingTestsBase">
                </Test>
                <Test
                   Identifier = "CloudKitKeychainSyncingTestsBase">
                </Test>
@@ -50,8 +53,6 @@
             </SkippedTests>
          </TestableReference>
       </Testables>
             </SkippedTests>
          </TestableReference>
       </Testables>
-      <AdditionalOptions>
-      </AdditionalOptions>
    </TestAction>
    <LaunchAction
       buildConfiguration = "Debug"
    </TestAction>
    <LaunchAction
       buildConfiguration = "Debug"
@@ -66,8 +67,6 @@
       migratedStopOnEveryIssue = "YES"
       debugServiceExtension = "internal"
       allowLocationSimulation = "YES">
       migratedStopOnEveryIssue = "YES"
       debugServiceExtension = "internal"
       allowLocationSimulation = "YES">
-      <AdditionalOptions>
-      </AdditionalOptions>
    </LaunchAction>
    <ProfileAction
       buildConfiguration = "Release"
    </LaunchAction>
    <ProfileAction
       buildConfiguration = "Release"
index 34a200c9a9467201981690e89073a66a242f0e02..0ecee1d0459596f53538a366d836ff1da55f10c7 100644 (file)
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <Scheme
 <?xml version="1.0" encoding="UTF-8"?>
 <Scheme
-   LastUpgradeVersion = "1000"
+   LastUpgradeVersion = "1120"
    version = "1.3">
    <BuildAction
       parallelizeBuildables = "NO"
    version = "1.3">
    <BuildAction
       parallelizeBuildables = "NO"
@@ -39,8 +39,6 @@
             </BuildableReference>
          </TestableReference>
       </Testables>
             </BuildableReference>
          </TestableReference>
       </Testables>
-      <AdditionalOptions>
-      </AdditionalOptions>
    </TestAction>
    <LaunchAction
       buildConfiguration = "Debug"
    </TestAction>
    <LaunchAction
       buildConfiguration = "Debug"
@@ -52,8 +50,6 @@
       debugDocumentVersioning = "YES"
       debugServiceExtension = "internal"
       allowLocationSimulation = "YES">
       debugDocumentVersioning = "YES"
       debugServiceExtension = "internal"
       allowLocationSimulation = "YES">
-      <AdditionalOptions>
-      </AdditionalOptions>
    </LaunchAction>
    <ProfileAction
       buildConfiguration = "Release"
    </LaunchAction>
    <ProfileAction
       buildConfiguration = "Release"
index 3fbabf063001538d9af87c45d71938afdb77d4e5..51730e53983b9985a8c43ea2428c00671b8cdb83 100644 (file)
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <Scheme
 <?xml version="1.0" encoding="UTF-8"?>
 <Scheme
-   LastUpgradeVersion = "1000"
+   LastUpgradeVersion = "1120"
    version = "1.3">
    <BuildAction
       parallelizeBuildables = "NO"
    version = "1.3">
    <BuildAction
       parallelizeBuildables = "NO"
       buildConfiguration = "Debug"
       selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
       selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
       buildConfiguration = "Debug"
       selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
       selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      shouldUseLaunchSchemeArgsEnv = "YES"
       codeCoverageEnabled = "YES"
       codeCoverageEnabled = "YES"
-      onlyGenerateCoverageForSpecifiedTargets = "YES"
-      shouldUseLaunchSchemeArgsEnv = "YES">
+      onlyGenerateCoverageForSpecifiedTargets = "YES">
+      <MacroExpansion>
+         <BuildableReference
+            BuildableIdentifier = "primary"
+            BlueprintIdentifier = "D4707A0421136E69005BCFDA"
+            BuildableName = "TrustTests.xctest"
+            BlueprintName = "TrustTests_ios"
+            ReferencedContainer = "container:Security.xcodeproj">
+         </BuildableReference>
+      </MacroExpansion>
       <CodeCoverageTargets>
          <BuildableReference
             BuildableIdentifier = "primary"
       <CodeCoverageTargets>
          <BuildableReference
             BuildableIdentifier = "primary"
             </BuildableReference>
          </TestableReference>
       </Testables>
             </BuildableReference>
          </TestableReference>
       </Testables>
-      <MacroExpansion>
-         <BuildableReference
-            BuildableIdentifier = "primary"
-            BlueprintIdentifier = "D4707A0421136E69005BCFDA"
-            BuildableName = "TrustTests.xctest"
-            BlueprintName = "TrustTests_ios"
-            ReferencedContainer = "container:Security.xcodeproj">
-         </BuildableReference>
-      </MacroExpansion>
-      <AdditionalOptions>
-      </AdditionalOptions>
    </TestAction>
    <LaunchAction
       buildConfiguration = "Debug"
    </TestAction>
    <LaunchAction
       buildConfiguration = "Debug"
@@ -88,8 +86,6 @@
             ReferencedContainer = "container:Security.xcodeproj">
          </BuildableReference>
       </MacroExpansion>
             ReferencedContainer = "container:Security.xcodeproj">
          </BuildableReference>
       </MacroExpansion>
-      <AdditionalOptions>
-      </AdditionalOptions>
    </LaunchAction>
    <ProfileAction
       buildConfiguration = "Release"
    </LaunchAction>
    <ProfileAction
       buildConfiguration = "Release"
index 42212060feeb9e6dd6fee04fae99df3b3c45dfd5..f14558e0d3b23a307fb502020f108cc5019fd2a7 100644 (file)
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <Scheme
 <?xml version="1.0" encoding="UTF-8"?>
 <Scheme
-   LastUpgradeVersion = "1000"
+   LastUpgradeVersion = "1120"
    version = "1.3">
    <BuildAction
       parallelizeBuildables = "NO"
    version = "1.3">
    <BuildAction
       parallelizeBuildables = "NO"
       buildConfiguration = "Debug"
       selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
       selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
       buildConfiguration = "Debug"
       selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
       selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      shouldUseLaunchSchemeArgsEnv = "YES"
       codeCoverageEnabled = "YES"
       codeCoverageEnabled = "YES"
-      onlyGenerateCoverageForSpecifiedTargets = "YES"
-      shouldUseLaunchSchemeArgsEnv = "YES">
+      onlyGenerateCoverageForSpecifiedTargets = "YES">
+      <MacroExpansion>
+         <BuildableReference
+            BuildableIdentifier = "primary"
+            BlueprintIdentifier = "D453A4A42122236D00850A26"
+            BuildableName = "TrustTests.xctest"
+            BlueprintName = "TrustTests_macos"
+            ReferencedContainer = "container:Security.xcodeproj">
+         </BuildableReference>
+      </MacroExpansion>
       <CodeCoverageTargets>
          <BuildableReference
             BuildableIdentifier = "primary"
       <CodeCoverageTargets>
          <BuildableReference
             BuildableIdentifier = "primary"
             </BuildableReference>
          </TestableReference>
       </Testables>
             </BuildableReference>
          </TestableReference>
       </Testables>
-      <MacroExpansion>
-         <BuildableReference
-            BuildableIdentifier = "primary"
-            BlueprintIdentifier = "D453A4A42122236D00850A26"
-            BuildableName = "TrustTests.xctest"
-            BlueprintName = "TrustTests_macos"
-            ReferencedContainer = "container:Security.xcodeproj">
-         </BuildableReference>
-      </MacroExpansion>
-      <AdditionalOptions>
-      </AdditionalOptions>
    </TestAction>
    <LaunchAction
       buildConfiguration = "Debug"
    </TestAction>
    <LaunchAction
       buildConfiguration = "Debug"
@@ -88,8 +86,6 @@
             ReferencedContainer = "container:Security.xcodeproj">
          </BuildableReference>
       </MacroExpansion>
             ReferencedContainer = "container:Security.xcodeproj">
          </BuildableReference>
       </MacroExpansion>
-      <AdditionalOptions>
-      </AdditionalOptions>
    </LaunchAction>
    <ProfileAction
       buildConfiguration = "Release"
    </LaunchAction>
    <ProfileAction
       buildConfiguration = "Release"
index 2f0c83a9b6921a1bda0f5a6945ac40231a65e5c8..52c6d628822d5f1afbf46d8dd220771cf82c954a 100644 (file)
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <Scheme
 <?xml version="1.0" encoding="UTF-8"?>
 <Scheme
-   LastUpgradeVersion = "1000"
+   LastUpgradeVersion = "1120"
    version = "1.3">
    <BuildAction
       parallelizeBuildables = "NO"
    version = "1.3">
    <BuildAction
       parallelizeBuildables = "NO"
       buildConfiguration = "Debug"
       selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
       selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
       buildConfiguration = "Debug"
       selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
       selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
-      codeCoverageEnabled = "YES"
-      shouldUseLaunchSchemeArgsEnv = "YES">
+      shouldUseLaunchSchemeArgsEnv = "YES"
+      codeCoverageEnabled = "YES">
+      <MacroExpansion>
+         <BuildableReference
+            BuildableIdentifier = "primary"
+            BlueprintIdentifier = "BEF88C271EAFFC3F00357577"
+            BuildableName = "TrustedPeers.framework"
+            BlueprintName = "TrustedPeers"
+            ReferencedContainer = "container:Security.xcodeproj">
+         </BuildableReference>
+      </MacroExpansion>
       <Testables>
          <TestableReference
             skipped = "NO">
       <Testables>
          <TestableReference
             skipped = "NO">
             </BuildableReference>
          </TestableReference>
       </Testables>
             </BuildableReference>
          </TestableReference>
       </Testables>
-      <MacroExpansion>
-         <BuildableReference
-            BuildableIdentifier = "primary"
-            BlueprintIdentifier = "BEF88C271EAFFC3F00357577"
-            BuildableName = "TrustedPeers.framework"
-            BlueprintName = "TrustedPeers"
-            ReferencedContainer = "container:Security.xcodeproj">
-         </BuildableReference>
-      </MacroExpansion>
-      <AdditionalOptions>
-      </AdditionalOptions>
    </TestAction>
    <LaunchAction
       buildConfiguration = "Debug"
    </TestAction>
    <LaunchAction
       buildConfiguration = "Debug"
@@ -71,8 +69,6 @@
             ReferencedContainer = "container:Security.xcodeproj">
          </BuildableReference>
       </MacroExpansion>
             ReferencedContainer = "container:Security.xcodeproj">
          </BuildableReference>
       </MacroExpansion>
-      <AdditionalOptions>
-      </AdditionalOptions>
    </LaunchAction>
    <ProfileAction
       buildConfiguration = "Release"
    </LaunchAction>
    <ProfileAction
       buildConfiguration = "Release"
index 6a67afc5f3948f2eea82e0789847a5f163f17103..d5c67142667dbf17aa6c3eeab45273f9bbd2f50f 100644 (file)
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <Scheme
 <?xml version="1.0" encoding="UTF-8"?>
 <Scheme
-   LastUpgradeVersion = "1000"
+   LastUpgradeVersion = "1120"
    version = "1.8">
    <BuildAction
       parallelizeBuildables = "NO"
    version = "1.8">
    <BuildAction
       parallelizeBuildables = "NO"
             argument = "si_26_sectrust_copyproperties"
             isEnabled = "NO">
          </CommandLineArgument>
             argument = "si_26_sectrust_copyproperties"
             isEnabled = "NO">
          </CommandLineArgument>
-         <CommandLineArgument
-            argument = "si_27_sectrust_exceptions"
-            isEnabled = "NO">
-         </CommandLineArgument>
          <CommandLineArgument
             argument = "si_28_sectrustsettings"
             isEnabled = "NO">
          <CommandLineArgument
             argument = "si_28_sectrustsettings"
             isEnabled = "NO">
index 11eaea1e35841be59011ae2396ba1f26ef81c8e7..06344ef9ad7f0687967aa5345d6089ad92235139 100644 (file)
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <Scheme
 <?xml version="1.0" encoding="UTF-8"?>
 <Scheme
-   LastUpgradeVersion = "1000"
+   LastUpgradeVersion = "1120"
    version = "1.3">
    <BuildAction
       parallelizeBuildables = "NO"
    version = "1.3">
    <BuildAction
       parallelizeBuildables = "NO"
             argument = "si_26_sectrust_copyproperties"
             isEnabled = "NO">
          </CommandLineArgument>
             argument = "si_26_sectrust_copyproperties"
             isEnabled = "NO">
          </CommandLineArgument>
-         <CommandLineArgument
-            argument = "si_27_sectrust_exceptions"
-            isEnabled = "NO">
-         </CommandLineArgument>
          <CommandLineArgument
             argument = "si_28_sectrustsettings"
             isEnabled = "NO">
          <CommandLineArgument
             argument = "si_28_sectrustsettings"
             isEnabled = "NO">
index d1fcb4cf5e20e00bd56962a18f3301d8034dedf8..cf5cb28f9ffa2bd44de96c89a0e5177b82a3b81b 100644 (file)
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <Scheme
 <?xml version="1.0" encoding="UTF-8"?>
 <Scheme
-   LastUpgradeVersion = "1000"
+   LastUpgradeVersion = "1120"
    version = "2.0">
    <BuildAction
       parallelizeBuildables = "NO"
    version = "2.0">
    <BuildAction
       parallelizeBuildables = "NO"
       selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
       selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
       shouldUseLaunchSchemeArgsEnv = "YES">
       selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
       selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
       shouldUseLaunchSchemeArgsEnv = "YES">
+      <MacroExpansion>
+         <BuildableReference
+            BuildableIdentifier = "primary"
+            BlueprintIdentifier = "0C0BDB2E175685B000BC1A7E"
+            BuildableName = "secdtests"
+            BlueprintName = "secdtests_ios"
+            ReferencedContainer = "container:Security.xcodeproj">
+         </BuildableReference>
+      </MacroExpansion>
       <Testables>
          <TestableReference
             skipped = "NO">
       <Testables>
          <TestableReference
             skipped = "NO">
             </BuildableReference>
          </TestableReference>
       </Testables>
             </BuildableReference>
          </TestableReference>
       </Testables>
-      <MacroExpansion>
-         <BuildableReference
-            BuildableIdentifier = "primary"
-            BlueprintIdentifier = "0C0BDB2E175685B000BC1A7E"
-            BuildableName = "secdtests"
-            BlueprintName = "secdtests_ios"
-            ReferencedContainer = "container:Security.xcodeproj">
-         </BuildableReference>
-      </MacroExpansion>
-      <AdditionalOptions>
-      </AdditionalOptions>
    </TestAction>
    <LaunchAction
       buildConfiguration = "Debug"
    </TestAction>
    <LaunchAction
       buildConfiguration = "Debug"
             isEnabled = "NO">
          </EnvironmentVariable>
       </EnvironmentVariables>
             isEnabled = "NO">
          </EnvironmentVariable>
       </EnvironmentVariables>
-      <AdditionalOptions>
-      </AdditionalOptions>
    </LaunchAction>
    <ProfileAction
       buildConfiguration = "Debug"
    </LaunchAction>
    <ProfileAction
       buildConfiguration = "Debug"
index 09059a5058385ecfcfe208a985066e1a1c0b820e..cb70464e3ab66128eb1c69764ee8977dce6b496e 100644 (file)
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <Scheme
 <?xml version="1.0" encoding="UTF-8"?>
 <Scheme
-   LastUpgradeVersion = "1000"
+   LastUpgradeVersion = "1120"
    version = "1.3">
    <BuildAction
       parallelizeBuildables = "NO"
    version = "1.3">
    <BuildAction
       parallelizeBuildables = "NO"
             argument = "si_26_sectrust_copyproperties"
             isEnabled = "NO">
          </CommandLineArgument>
             argument = "si_26_sectrust_copyproperties"
             isEnabled = "NO">
          </CommandLineArgument>
-         <CommandLineArgument
-            argument = "si_27_sectrust_exceptions"
-            isEnabled = "NO">
-         </CommandLineArgument>
          <CommandLineArgument
             argument = "si_28_sectrustsettings"
             isEnabled = "NO">
          <CommandLineArgument
             argument = "si_28_sectrustsettings"
             isEnabled = "NO">
index 30a0789a8c80ba9d8cfc708596c3ef869ffcf4d3..034c0652363faa5987811bf6ca2881ea658b7d48 100644 (file)
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <Scheme
 <?xml version="1.0" encoding="UTF-8"?>
 <Scheme
-   LastUpgradeVersion = "1000"
+   LastUpgradeVersion = "1120"
    version = "1.3">
    <BuildAction
       parallelizeBuildables = "NO"
    version = "1.3">
    <BuildAction
       parallelizeBuildables = "NO"
index 67510aa41ad6fae33a9c999a5f53682398cbdccc..a64e3fd148f8c68adab0b9cead97a11e535b72f4 100644 (file)
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <Scheme
 <?xml version="1.0" encoding="UTF-8"?>
 <Scheme
-   LastUpgradeVersion = "1000"
+   LastUpgradeVersion = "1120"
    version = "1.3">
    <BuildAction
       parallelizeBuildables = "NO"
    version = "1.3">
    <BuildAction
       parallelizeBuildables = "NO"
@@ -41,8 +41,6 @@
       selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
       selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
       shouldUseLaunchSchemeArgsEnv = "YES">
       selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
       selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
       shouldUseLaunchSchemeArgsEnv = "YES">
-      <Testables>
-      </Testables>
       <MacroExpansion>
          <BuildableReference
             BuildableIdentifier = "primary"
       <MacroExpansion>
          <BuildableReference
             BuildableIdentifier = "primary"
@@ -52,8 +50,8 @@
             ReferencedContainer = "container:Security.xcodeproj">
          </BuildableReference>
       </MacroExpansion>
             ReferencedContainer = "container:Security.xcodeproj">
          </BuildableReference>
       </MacroExpansion>
-      <AdditionalOptions>
-      </AdditionalOptions>
+      <Testables>
+      </Testables>
    </TestAction>
    <LaunchAction
       buildConfiguration = "Debug"
    </TestAction>
    <LaunchAction
       buildConfiguration = "Debug"
@@ -75,8 +73,6 @@
             ReferencedContainer = "container:Security.xcodeproj">
          </BuildableReference>
       </BuildableProductRunnable>
             ReferencedContainer = "container:Security.xcodeproj">
          </BuildableReference>
       </BuildableProductRunnable>
-      <AdditionalOptions>
-      </AdditionalOptions>
    </LaunchAction>
    <ProfileAction
       buildConfiguration = "Release"
    </LaunchAction>
    <ProfileAction
       buildConfiguration = "Release"
index bca98ccebac2a150cee48b5e28af87d7abed0fd8..ac9beb1a97a88d2e0fa92ee2237780a234bba7c5 100644 (file)
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <Scheme
 <?xml version="1.0" encoding="UTF-8"?>
 <Scheme
-   LastUpgradeVersion = "1000"
+   LastUpgradeVersion = "1120"
    version = "1.3">
    <BuildAction
       parallelizeBuildables = "NO"
    version = "1.3">
    <BuildAction
       parallelizeBuildables = "NO"
@@ -23,8 +23,6 @@
             </BuildableReference>
          </TestableReference>
       </Testables>
             </BuildableReference>
          </TestableReference>
       </Testables>
-      <AdditionalOptions>
-      </AdditionalOptions>
    </TestAction>
    <LaunchAction
       buildConfiguration = "Debug"
    </TestAction>
    <LaunchAction
       buildConfiguration = "Debug"
@@ -36,8 +34,6 @@
       debugDocumentVersioning = "YES"
       debugServiceExtension = "internal"
       allowLocationSimulation = "YES">
       debugDocumentVersioning = "YES"
       debugServiceExtension = "internal"
       allowLocationSimulation = "YES">
-      <AdditionalOptions>
-      </AdditionalOptions>
    </LaunchAction>
    <ProfileAction
       buildConfiguration = "Release"
    </LaunchAction>
    <ProfileAction
       buildConfiguration = "Release"
index e52156b70b50712fd3ed1195be4ee7afc3197950..1fae8a91028717474093470da2301457d4c8da3d 100644 (file)
@@ -184,14 +184,12 @@ command_sos_control(__unused int argc, __unused char * const * argv)
         bool gbinfo = false;
         bool gbtriggered = false;
         bool circleHash = false;
         bool gbinfo = false;
         bool gbtriggered = false;
         bool circleHash = false;
-        bool triggerRingUpdate = false;
 
         static struct option long_options[] =
         {
             /* These options set a flag. */
             {"assertStashAccountKey",   no_argument, NULL, 'a'},
             {"trigger-backup",   optional_argument, NULL, 'B'},
 
         static struct option long_options[] =
         {
             /* These options set a flag. */
             {"assertStashAccountKey",   no_argument, NULL, 'a'},
             {"trigger-backup",   optional_argument, NULL, 'B'},
-            {"trigger-ring-update",   no_argument, NULL, 'R'},
             {"trigger-sync",   optional_argument, NULL, 's'},
             {"circle-hash", optional_argument, NULL, 'H'},
             {"ghostbustByMID",   optional_argument, NULL, 'M'},
             {"trigger-sync",   optional_argument, NULL, 's'},
             {"circle-hash", optional_argument, NULL, 'H'},
             {"ghostbustByMID",   optional_argument, NULL, 'M'},
@@ -203,7 +201,7 @@ command_sos_control(__unused int argc, __unused char * const * argv)
             {0, 0, 0, 0}
         };
 
             {0, 0, 0, 0}
         };
 
-        while ((ch = getopt_long(argc, argv, "as:AB:GHIMRST", long_options, &option_index)) != -1) {
+        while ((ch = getopt_long(argc, argv, "as:AB:GHMSIT", long_options, &option_index)) != -1) {
             switch  (ch) {
                 case 'a': {
                     assertStashAccountKey = true;
             switch  (ch) {
                 case 'a': {
                     assertStashAccountKey = true;
@@ -241,9 +239,6 @@ command_sos_control(__unused int argc, __unused char * const * argv)
                     gboptions |= SOSGhostBustByMID;
                     break;
                 }
                     gboptions |= SOSGhostBustByMID;
                     break;
                 }
-                case 'R':
-                    triggerRingUpdate = true;
-                    break;
                 case 'S': {
                     gboptions |= SOSGhostBustBySerialNumber;
                     break;
                 case 'S': {
                     gboptions |= SOSGhostBustBySerialNumber;
                     break;
@@ -305,7 +300,7 @@ command_sos_control(__unused int argc, __unused char * const * argv)
         } else if (triggerSync) {
             [[control.connection synchronousRemoteObjectProxyWithErrorHandler:^(NSError *error) {
                 printControlFailureMessage(error);
         } else if (triggerSync) {
             [[control.connection synchronousRemoteObjectProxyWithErrorHandler:^(NSError *error) {
                 printControlFailureMessage(error);
-            }] rpcTriggerSync:syncingPeers complete:^(bool res, NSError *error) {
+            }] triggerSync:syncingPeers complete:^(bool res, NSError *error) {
                 if (res) {
                     printf("starting to sync was successful\n");
                 } else {
                 if (res) {
                     printf("starting to sync was successful\n");
                 } else {
@@ -315,21 +310,11 @@ command_sos_control(__unused int argc, __unused char * const * argv)
         } else if (triggerBackup) {
             [[control.connection synchronousRemoteObjectProxyWithErrorHandler:^(NSError *error) {
                 printControlFailureMessage(error);
         } else if (triggerBackup) {
             [[control.connection synchronousRemoteObjectProxyWithErrorHandler:^(NSError *error) {
                 printControlFailureMessage(error);
-            }] rpcTriggerBackup:backupPeers complete:^(NSError *error) {
+            }] triggerBackup:backupPeers complete:^(NSError *error) {
                 if (error == NULL) {
                     printf("trigger backup was successful\n");
                 } else {
                 if (error == NULL) {
                     printf("trigger backup was successful\n");
                 } else {
-                    printf("%s", [[NSString stringWithFormat:@"Failed to start backup: %@\n", error] UTF8String]);
-                }
-            }];
-        } else if (triggerRingUpdate) {
-            [[control.connection synchronousRemoteObjectProxyWithErrorHandler:^(NSError *error) {
-                printControlFailureMessage(error);
-            }] rpcTriggerRingUpdate:^(NSError *error) {
-                if (error == NULL) {
-                    printf("trigger ring update was successful\n");
-                } else {
-                    printf("%s", [[NSString stringWithFormat:@"Failed to start ring update: %@\n", error] UTF8String]);
+                    printf("%s", [[NSString stringWithFormat:@"Failed to start sync: %@\n", error] UTF8String]);
                 }
             }];
 
                 }
             }];
 
index de949106a59330237126bf835548880b78d44fd1..61f5d813a2d66149385b33a0e006e4573c5e4649 100644 (file)
@@ -29,5 +29,4 @@
 #include "keychain/SecureObjectSync/Tool/keychain_sync.h"
 #include "keychain/SecureObjectSync/Tool/keychain_sync_test.h"
 #include "keychain/SecureObjectSync/Tool/keychain_log.h"
 #include "keychain/SecureObjectSync/Tool/keychain_sync.h"
 #include "keychain/SecureObjectSync/Tool/keychain_sync_test.h"
 #include "keychain/SecureObjectSync/Tool/keychain_log.h"
-#include "keychain/SecureObjectSync/Tool/syncbackup.h"
 #include "keychain/SecureObjectSync/Tool/recovery_key.h"
 #include "keychain/SecureObjectSync/Tool/recovery_key.h"
index e475dcdbd7ef22273e73a0bbcc739185e8400add..2e05a8139104f0c38ebb4645f5dd1f2ba46858ec 100644 (file)
@@ -50,7 +50,7 @@ extern const NSString *SecExperimentConfigurationKeyConfigurationData;
  * @abstract
  *      Create an ARC-able `sec_experiment_t` instance wrapping an internal `SecExperiment` object.
  *
  * @abstract
  *      Create an ARC-able `sec_experiment_t` instance wrapping an internal `SecExperiment` object.
  *
- * @param experiment_name
+ * @param experiment
  *      Name of the experiment.
  *
  * @return a `sec_experiment_t` instance.
  *      Name of the experiment.
  *
  * @return a `sec_experiment_t` instance.
diff --git a/keychain/ResetCloudKeychainAccount/reset_ick_account b/keychain/ResetCloudKeychainAccount/reset_ick_account
new file mode 100644 (file)
index 0000000..79e748b
--- /dev/null
@@ -0,0 +1,58 @@
+#!/usr/bin/python
+#
+
+import sys
+from glob import glob
+import subprocess
+import re
+import os
+import argparse
+
+parser = argparse.ArgumentParser(description='Reset your iCloud Keychain account')
+parser.add_argument('icloudpassword', help='iCloud password')
+parser.add_argument('passcode', help='passcode or password of the local device')
+args = vars(parser.parse_args())
+
+
+iCloudPassword = args['icloudpassword']
+passcode = args['passcode']
+
+def set_security_mac_cmd():
+        return 'security2'
+
+def set_security_ios_cmd():
+        return 'security'
+
+def security_cmd_by_platform():
+        swVers = subprocess.check_output(["sw_vers"])
+        deviceInformation = str(swVers, 'utf-8')
+        if "Mac OS X" in deviceInformation:
+                print("using security2 command on macosx")
+                return set_security_mac_cmd()
+        elif "iPhone OS" in deviceInformation:
+                print("using security command on ios")
+                return set_security_ios_cmd()
+        else:
+                print("unsupported platform")
+                sys.exit(1)
+
+security_cmd = security_cmd_by_platform()
+
+print("resetting octagon")
+subprocess.check_output(["otctl", "resetoctagon"])
+
+print("resetting ckks")
+subprocess.check_output(["ckksctl", "reset-cloudkit"])
+
+print("resetting SOS")
+subprocess.check_output([security_cmd, "sync", "-C"])
+subprocess.check_output([security_cmd, "sync", "-P", "$iCloudPassword"])
+subprocess.check_output([security_cmd, "sync", "-O"])
+
+print("deleting all escrow records")
+subprocess.check_output(["stingrayutil", "--deleteAll", "ReallyDeleteAll"])
+
+print("creating new escrow record")
+subprocess.check_output(["sbdtool", "passcode_request_trigger"])
+subprocess.check_output(["sbdtool", "passcode_request_provide_passcode", "$passcode"])
+
index fb048fd43c76147ca047aa77eeaff1e055377c8d..3bdeb8b15c91939138362e76dcfa5f20897db14d 100644 (file)
@@ -13,7 +13,6 @@ ONE_TEST(sc_42_circlegencount)
 ONE_TEST(sc_45_digestvector)
 
 ONE_TEST(sc_130_resignationticket)
 ONE_TEST(sc_45_digestvector)
 
 ONE_TEST(sc_130_resignationticket)
-ONE_TEST(sc_150_Ring)
 
 ONE_TEST(sc_150_backupkeyderivation)
 ONE_TEST(sc_153_backupslicekeybag)
 
 ONE_TEST(sc_150_backupkeyderivation)
 ONE_TEST(sc_153_backupslicekeybag)
diff --git a/keychain/SecureObjectSync/Regressions/sc-150-ring.m b/keychain/SecureObjectSync/Regressions/sc-150-ring.m
deleted file mode 100644 (file)
index c547a98..0000000
+++ /dev/null
@@ -1,181 +0,0 @@
-//
-//  sc-150-ring.c
-//  sec
-//
-//  Created by Richard Murphy on 3/3/15.
-//
-//
-
-#include <stdio.h>
-/*
- * Copyright (c) 2012-2014 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@
- */
-
-
-
-#include <Security/SecBase.h>
-#include <Security/SecItem.h>
-#include <Security/SecKeyPriv.h>
-
-#include "keychain/SecureObjectSync/SOSRing.h"
-#include "keychain/SecureObjectSync/SOSRingTypes.h"
-#include "keychain/SecureObjectSync/SOSRingUtils.h"
-#include <Security/SecureObjectSync/SOSPeerInfo.h>
-#include "keychain/SecureObjectSync/SOSInternal.h"
-#include "keychain/SecureObjectSync/SOSUserKeygen.h"
-
-#include <utilities/SecCFWrappers.h>
-
-#include <CoreFoundation/CoreFoundation.h>
-
-#include <stdlib.h>
-#include <unistd.h>
-
-#include "SOSCircle_regressions.h"
-#include "SOSRegressionUtilities.h"
-
-static SOSFullPeerInfoRef SOSCreateApplicantFullPeerInfoFromName(CFStringRef peerName,
-                                                                 SecKeyRef user_private_key,
-                                                                 SecKeyRef* outSigningKey,
-                                                                 SecKeyRef* outOctagonSigningKey,
-                                                                 SecKeyRef* outOctagonEncryptionKey,
-                                                                 CFErrorRef *error)
-{
-    SOSFullPeerInfoRef result = NULL;
-    SOSFullPeerInfoRef fullPeer = SOSCreateFullPeerInfoFromName(peerName, outSigningKey, outOctagonSigningKey, outOctagonEncryptionKey, error);
-
-    if (fullPeer && SOSFullPeerInfoPromoteToApplication(fullPeer, user_private_key, error))
-        CFTransferRetained(result, fullPeer);
-
-    CFReleaseNull(fullPeer);
-    return result;
-}
-
-static int kTestTestCount = 24;
-static void tests(void)
-{
-
-    //SecKeyRef publicKey = NULL;
-    SecKeyRef dev_a_key = NULL;
-    SecKeyRef dev_b_key = NULL;
-    SecKeyRef dev_c_key = NULL;
-    SecKeyRef oct_dev_as_key = NULL;
-    SecKeyRef oct_dev_aw_key = NULL;
-    SecKeyRef oct_dev_bs_key = NULL;
-    SecKeyRef oct_dev_bw_key = NULL;
-    SecKeyRef oct_dev_cs_key = NULL;
-    SecKeyRef oct_dev_cw_key = NULL;
-    CFErrorRef error = NULL;
-    CFDataRef cfpassword = CFDataCreate(NULL, (uint8_t *) "FooFooFoo", 10);
-
-    ok(cfpassword, "no password");
-
-    CFDataRef parameters = SOSUserKeyCreateGenerateParameters(&error);
-    ok(parameters, "No parameters!");
-    ok(error == NULL, "Error: (%@)", error);
-    CFReleaseNull(error);
-
-    SecKeyRef user_privkey = SOSUserKeygen(cfpassword, parameters, &error);
-    CFReleaseNull(parameters);
-
-    SecKeyRef user_pubkey = SecKeyCreatePublicFromPrivate(user_privkey);
-
-
-    SOSFullPeerInfoRef peer_a_full_info = SOSCreateApplicantFullPeerInfoFromName(CFSTR("Peer A"), user_privkey, &dev_a_key, &oct_dev_as_key, &oct_dev_aw_key, NULL);
-    SOSFullPeerInfoRef peer_b_full_info = SOSCreateApplicantFullPeerInfoFromName(CFSTR("Peer B"), user_privkey, &dev_b_key, &oct_dev_bs_key, &oct_dev_bw_key, NULL);
-    SOSFullPeerInfoRef peer_c_full_info = SOSCreateApplicantFullPeerInfoFromName(CFSTR("Peer C"), user_privkey, &dev_c_key, &oct_dev_cs_key, &oct_dev_cw_key, NULL);
-    CFStringRef peerID_a = SOSPeerInfoGetPeerID(SOSFullPeerInfoGetPeerInfo(peer_a_full_info));
-    CFStringRef peerID_b = SOSPeerInfoGetPeerID(SOSFullPeerInfoGetPeerInfo(peer_b_full_info));
-    SOSRingRef Ring = SOSRingCreate(CFSTR("TESTRING"), peerID_a, kSOSRingBase, NULL);
-
-    ok(Ring, "Ring creation");
-
-
-    ok(0 == SOSRingCountPeers(Ring), "Zero peers");
-
-    ok(SOSRingApply(Ring, user_pubkey, peer_a_full_info, NULL));
-    ok(SOSRingApply(Ring, user_pubkey, peer_b_full_info, NULL));
-
-    ok(2 == SOSRingCountPeers(Ring), "Two peers");
-
-    ok(SOSRingWithdraw(Ring, user_privkey, peer_b_full_info, NULL));
-
-    ok(1 == SOSRingCountPeers(Ring), "One peer");
-
-    ok(kSOSRingMember == SOSRingDeviceIsInRing(Ring, peerID_a), "peer_a is in Ring");
-    ok(kSOSRingNotInRing == SOSRingDeviceIsInRing(Ring, peerID_b), "peer_b is not in Ring");
-    CFStringRef lastmod = SOSRingGetLastModifier(Ring);
-    ok(CFEqual(lastmod, peerID_b), "peer_b_full_info did last mod");
-
-    ok(SOSRingResetToEmpty(Ring, peerID_a, NULL), "Reset the circle");
-    ok(kSOSRingNotInRing == SOSRingDeviceIsInRing(Ring, peerID_a), "peer_a is not in Ring");
-
-    ok(SOSRingResetToOffering(Ring, NULL, peer_a_full_info, NULL), "Reset Ring to Offering for PeerA");
-    ok(kSOSRingMember == SOSRingDeviceIsInRing(Ring, peerID_a), "peer_a is in Ring");
-    ok(kSOSRingNotInRing == SOSRingDeviceIsInRing(Ring, peerID_b), "peer_b is not in Ring");
-
-    CFDataRef ringDER = SOSRingCopyEncodedData(Ring, NULL);
-    ok(ringDER, "Successful encoding to DER of Ring");
-    SOSRingRef Ring2 = SOSRingCreateFromData(NULL, ringDER);
-    ok(Ring2, "Successful decoding of DER to Ring");
-
-    ok(CFEqualSafe(Ring, Ring2), "Compares");
-
-    ok(SOSRingApply(Ring, user_pubkey, peer_c_full_info, NULL));
-    ok(SOSRingApply(Ring, user_pubkey, peer_b_full_info, NULL));
-
-    CFReleaseNull(ringDER);
-    CFReleaseNull(Ring2);
-    ringDER = SOSRingCopyEncodedData(Ring, NULL);
-    Ring2 = SOSRingCreateFromData(NULL, ringDER);
-    ok(CFEqualSafe(Ring, Ring2), "Compares");
-
-    CFReleaseNull(ringDER);
-    CFReleaseNull(Ring2);
-    CFReleaseNull(dev_a_key);
-    CFReleaseNull(dev_b_key);
-    CFReleaseNull(dev_c_key);
-    CFReleaseNull(oct_dev_as_key);
-    CFReleaseNull(oct_dev_aw_key);
-    CFReleaseNull(oct_dev_bs_key);
-    CFReleaseNull(oct_dev_bw_key);
-    CFReleaseNull(oct_dev_cs_key);
-    CFReleaseNull(oct_dev_cw_key);
-    CFReleaseNull(cfpassword);
-
-    CFReleaseNull(user_privkey);
-    CFReleaseNull(user_pubkey);
-
-    CFReleaseNull(peer_a_full_info);
-    CFReleaseNull(peer_b_full_info);
-    CFReleaseNull(peer_c_full_info);
-    CFReleaseNull(Ring);
-}
-
-int sc_150_Ring(int argc, char *const *argv)
-{
-    plan_tests(kTestTestCount);
-
-    tests();
-
-    return 0;
-}
diff --git a/keychain/SecureObjectSync/Regressions/sc-kvstool.m b/keychain/SecureObjectSync/Regressions/sc-kvstool.m
deleted file mode 100644 (file)
index 63660de..0000000
+++ /dev/null
@@ -1,356 +0,0 @@
-/*
- * Copyright (c) 2012-2014 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@
- */
-
-
-
-// Run on a device:
-//      /AppleInternal/Applications/SecurityTests.app/SecurityTests sc_kvstool -v --  --dump
-//      /AppleInternal/Applications/SecurityTests.app/SecurityTests sc_kvstool -v --  --clear
-//      /AppleInternal/Applications/SecurityTests.app/SecurityTests sc_kvstool -v --  --putcircle
-//      /AppleInternal/Applications/SecurityTests.app/SecurityTests sc_kvstool -v -- --direct  --dump
-//      /AppleInternal/Applications/SecurityTests.app/SecurityTests sc_kvstool -v -- --direct --putcircle
-
-#include <Foundation/Foundation.h>
-#include <SecureObjectSync/SOSEngine.h>
-#include <SecureObjectSync/SOSPeer.h>
-#include <SecureObjectSync/SOSInternal.h>
-#include <SecureObjectSync/SOSCloudCircle.h>
-
-#include "SOSCircle_regressions.h"
-
-#include <corecrypto/ccsha2.h>
-
-#include <utilities/SecCFWrappers.h>
-#include <utilities/debugging.h>
-
-#include <stdint.h>
-
-#include <AssertMacros.h>
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <CoreFoundation/CFDate.h>
-#include <getopt.h>
-
-#include <notify.h>
-#include <dispatch/dispatch.h>
-
-#include "SOSCircle_regressions.h"
-#include "CKDLocalKeyValueStore.h"
-#include "SOSRegressionUtilities.h"
-#include "SOSTestDataSource.h"
-#include "SOSTestTransport.h"
-#include "SOSCloudKeychainClient.h"
-
-#import "SOSDirectCloudTransport.h"
-
-// MARK: ----- Constants -----
-
-static CFStringRef circleKey = CFSTR("Circle");
-
-// MARK: ----- start of all tests -----
-
-static void putCircleInCloud(SOSCircleRef circle, dispatch_queue_t work_queue, dispatch_group_t work_group)
-{
-    CFErrorRef error = NULL;
-    CFDataRef newCloudCircleEncoded = SOSCircleCopyEncodedData(circle, kCFAllocatorDefault, &error);
-    ok(newCloudCircleEncoded, "Encoded as: %@ [%@]", newCloudCircleEncoded, error);
-
-    // Send the circle with our application request back to cloud
-    testPutObjectInCloud(circleKey, newCloudCircleEncoded, &error, work_group, work_queue);
-}
-
-static void createAndPutInitialCircle(void)
-{
-    dispatch_queue_t work_queue = dispatch_queue_create("capic", DISPATCH_QUEUE_CONCURRENT);
-    dispatch_group_t work_group = dispatch_group_create();
-
-    CFErrorRef error = NULL;
-    CFStringRef cflabel = CFSTR("TEST_USERKEY");
-    CFDataRef cfpassword = CFDataCreate(NULL, (uint8_t *) "FooFooFoo", 10);
-    SOSCCRegisterUserCredentials(cflabel, cfpassword, &error);
-
-    CFErrorRef localError = NULL;
-    
-    SOSDataSourceFactoryRef our_data_source_factory = SOSTestDataSourceFactoryCreate();
-    SOSDataSourceRef our_data_source = SOSTestDataSourceCreate();
-    SOSTestDataSourceFactoryAddDataSource(our_data_source_factory, circleKey, our_data_source);
-
-    CFDictionaryRef gestalt = SOSCreatePeerGestaltFromName(CFSTR("Alice"));
-
-    SOSAccountRef our_account = SOSAccountCreate(kCFAllocatorDefault, gestalt, our_data_source_factory,
-        ^(CFArrayRef keys)
-        {
-            pass("SOSAccountKeyInterestBlock");
-        },
-        ^ bool (CFDictionaryRef keys, CFErrorRef *error)
-        {
-            pass("SOSAccountDataUpdateBlock");
-            return false;
-        },
-        NULL);
-    SOSAccountEnsureCircle(our_account, circleKey);
-
-    SOSFullPeerInfoRef our_full_peer_info = SOSAccountGetMyFullPeerInCircleNamed(our_account, circleKey, &error);
-    SOSPeerInfoRef our_peer_info = SOSFullPeerInfoGetPeerInfo(our_full_peer_info);
-    CFRetain(our_peer_info);
-
-    SOSCircleRef circle = SOSAccountFindCircle(our_account, circleKey);
-    CFRetain(circle);
-    
-//    SecKeyRef user_privkey = SOSUserGetPrivKey(&localError);
-    SecKeyRef user_privkey = NULL;  // TODO: this will not work
-    ok(SOSCircleRequestAdmission(circle, user_privkey, our_full_peer_info, &localError), "Requested admission (%@)", our_peer_info);
-    ok(SOSCircleAcceptRequests(circle, user_privkey, our_full_peer_info, &localError), "Accepted self");
-    
-    putCircleInCloud(circle, work_queue, work_group);
-    pass("Put circle in cloud: (%@)", circle);
-    
-    CFRelease(circle);
-}
-
-static void postCircleChangeNotification()
-{
-    NSArray *keys = [NSArray arrayWithObjects:(id)circleKey, nil];
-    NSDictionary* userInfo = [[NSDictionary alloc] initWithObjectsAndKeys:
-        keys, NSUbiquitousKeyValueStoreChangedKeysKey,
-        [NSNumber numberWithInt:NSUbiquitousKeyValueStoreServerChange], NSUbiquitousKeyValueStoreChangeReasonKey,
-        nil];
-    [[NSNotificationCenter defaultCenter] postNotificationName:NSUbiquitousKeyValueStoreDidChangeExternallyNotification object:NULL userInfo:userInfo];
-    [userInfo release];
-}
-
-static void requestSynchronization(dispatch_queue_t processQueue, dispatch_group_t dgroup)
-{
-    testSynchronize(processQueue, dgroup);
-}
-
-static void displayCircles(CFTypeRef objects)
-{
-    // SOSCCCopyApplicantPeerInfo doesn't display all info, e.g. in the case where we are not in circle
-    CFDictionaryForEach(objects, ^(const void *key, const void *value)
-    {
-        if (SOSKVSKeyGetKeyType(key) == kCircleKey)
-        {
-            CFErrorRef localError = NULL;
-            if (isData(value))
-            {
-                SOSCircleRef circle = SOSCircleCreateFromData(NULL, (CFDataRef) value, &localError);
-                pass("circle: %@ %@", key, circle);
-                CFReleaseSafe(circle);
-            }
-            else
-                pass("non-circle: %@ %@", key, value);
-        }
-    });
-}
-
-
-static void dumpCircleInfo()
-{
-    CFErrorRef error = NULL;
-    CFArrayRef applicantPeerInfos = NULL;
-    CFArrayRef peerInfos = NULL;
-    int idx;
-    
-    NSArray *ccmsgs = @[@"Error", @"InCircle", @"NotInCircle", @"RequestPending", @"CircleAbsent"];
-
-    SOSCCStatus ccstatus = SOSCCThisDeviceIsInCircle(&error);
-    pass("ccstatus: %d, error: %@", ccstatus, error);
-    idx = ccstatus-kSOSCCError;
-    if (0<=idx && idx<(int)[ccmsgs count])
-        pass("ccstatus: %d (%@)", ccstatus, ccmsgs[idx]);
-
-    // Now look at current applicants
-    applicantPeerInfos = SOSCCCopyApplicantPeerInfo(&error);
-    if (applicantPeerInfos)
-    {
-        pass("Applicants: %ld, error: %@", (long)CFArrayGetCount(applicantPeerInfos), error);
-        CFArrayForEach(applicantPeerInfos, ^(const void *value) {
-            SOSPeerInfoRef peer = (SOSPeerInfoRef)value;
-            CFStringRef peerName = SOSPeerInfoGetPeerName(peer);
-            pass("Applicant: %@", peerName);
-        });
-    }
-    else
-        pass("No applicants, error: %@", error);
-    
-    
-    peerInfos = SOSCCCopyPeerPeerInfo(&error);
-    if (peerInfos)
-    {
-        pass("Peers: %ld, error: %@", (long)CFArrayGetCount(applicantPeerInfos), error);
-        CFArrayForEach(peerInfos, ^(const void *value) {
-            SOSPeerInfoRef peer = (SOSPeerInfoRef)value;
-            CFStringRef peerName = SOSPeerInfoGetPeerName(peer);
-            pass("Peer: %@", peerName);
-        });
-    }
-    else
-        pass("No peers, error: %@", error);
-}
-
-// define the options table for the command line
-static const struct option options[] =
-{
-       { "verbose",            optional_argument,      NULL, 'v' },
-       { "dump",           optional_argument,  NULL, 'd' },
-       { "clear",          optional_argument,  NULL, 'C' },
-       { "putcircle",      optional_argument,  NULL, 'p' },
-       { "direct",         optional_argument,  NULL, 'D' },
-       { "notify",         optional_argument,  NULL, 'n' },
-       { "sync",           optional_argument,  NULL, 's' },
-       { "info",           optional_argument,  NULL, 'i' },
-       { }
-};
-
-static int kTestCount = 10;
-
-static void usage(void)
-{
-    printf("Usage:\n");
-    printf("    --dump [itemName]   Dump the contents of the kvs store (through proxy)\n");
-    printf("    --clear             Clear the contents of the kvs store (through proxy)\n");
-    printf("    --putcircle         Put a new circle into the kvs store (through proxy)\n");
-    printf("    --direct            Go directly to KVS (bypass proxy)\n");
-    printf("    --notify            Post a notification that the circle key has changed\n");
-    printf("    --sync              Post a notification that the circle key has changed\n");
-    printf("    --info              Dump info about circle and peer status\n");
-}
-
-enum kvscommands
-{
-    kCommandClear = 1,
-    kCommandDump = 2,
-    kCommandPutCircle = 3,
-    kCommandNotify = 4,
-    kCommandSynchronize = 5,
-    kCommandInfo = 6,
-    kCommandHelp
-};
-
-static int command = kCommandHelp;
-static bool useDirect = false;
-
-static void tests(const char *itemName)
-{
-    dispatch_queue_t generalq = dispatch_queue_create("general", DISPATCH_QUEUE_SERIAL);
-    dispatch_group_t work_group = dispatch_group_create();
-
-#if 0
-    if (useDirect)
-    {
-        SOSCloudKeychainServerInit();
-    }
-#endif
-    SOSCloudKeychainSetCallbackMethodXPC(); // call this first
-
-    pass("Command: %d", command);
-    switch (command)
-    {
-        case kCommandClear:
-            ok(testClearAll(generalq, work_group), "test Clear All");
-            break;
-        case kCommandDump:
-            {
-                CFArrayRef keysToGet = NULL;
-                if (itemName)
-                {
-                    CFStringRef itemStr = CFStringCreateWithCString(kCFAllocatorDefault, itemName, kCFStringEncodingUTF8);
-                    pass("Retrieving   : %@", itemStr);
-                    keysToGet = CFArrayCreateForCFTypes(kCFAllocatorDefault, itemStr);
-                    CFReleaseSafe(itemStr);
-                }
-                CFTypeRef objects = testGetObjectsFromCloud(keysToGet, generalq, work_group);
-                CFReleaseSafe(keysToGet);
-                pass("   : %@", objects);
-                displayCircles(objects);
-            }
-            break;
-        case kCommandPutCircle:
-            createAndPutInitialCircle();
-            break;
-        case kCommandNotify:
-            postCircleChangeNotification();
-            break;
-        case kCommandSynchronize:
-            requestSynchronization(generalq, work_group);
-            break;
-        case kCommandInfo:
-            dumpCircleInfo();
-            break;
-        default:
-        case kCommandHelp:
-            usage();
-            break;
-    }
-}
-
-int sc_kvstool(int argc, char *const *argv)
-{
-    char *itemName = NULL;
-//  extern int optind;
-    extern char *optarg;
-    int arg, argSlot;
-
-    while (argSlot = -1, (arg = getopt_long(argc, (char * const *)argv, "ivChpdns", options, &argSlot)) != -1)
-        switch (arg)
-        {
-        case 'd':
-            itemName = (char *)(optarg);
-            command = kCommandDump;
-            break;
-        case 'C':   // should set up to call testClearAll
-            command = kCommandClear;
-            break;
-        case 'p':
-            command = kCommandPutCircle;
-            break;
-        case 'n':
-            command = kCommandNotify;
-            break;
-        case 's':
-            command = kCommandSynchronize;
-            break;
-        case 'i':
-            command = kCommandInfo;
-            break;
-        case 'D':
-            useDirect = true;
-            printf("Using direct calls to KVS\n");
-            break;
-        default:
-            secerror("arg: %s", optarg);
-            break;
-        }
-
-    plan_tests(kTestCount);
-
-    secerror("Command: %d", command);
-    printf("Command: %d\n", command);
-
-       tests(itemName);
-
-       return 0;
-}
index a50be3eee2512ffeed7a41a09262ca17692da56f..f7674f30d0ee44dd0fa43a4970a636e015c91c4c 100644 (file)
@@ -188,11 +188,6 @@ CF_RETURNS_RETAINED CFSetRef SOSAccountCopyBackupPeersAndForceSync(SOSAccountTra
 bool SOSAccountScanForRetired(SOSAccount*  account, SOSCircleRef circle, CFErrorRef *error);
 CF_RETURNS_RETAINED SOSCircleRef SOSAccountCloneCircleWithRetirement(SOSAccount*  account, SOSCircleRef starting_circle, CFErrorRef *error);
 
 bool SOSAccountScanForRetired(SOSAccount*  account, SOSCircleRef circle, CFErrorRef *error);
 CF_RETURNS_RETAINED SOSCircleRef SOSAccountCloneCircleWithRetirement(SOSAccount*  account, SOSCircleRef starting_circle, CFErrorRef *error);
 
-//
-// MARK: Version incompatibility Functions
-//
-CFStringRef SOSAccountCopyIncompatibilityInfo(SOSAccount*  account, CFErrorRef* error);
-
 //
 // MARK: Backup functions
 //
 //
 // MARK: Backup functions
 //
@@ -209,9 +204,6 @@ bool SOSAccountSetBSKBagForAllSlices(SOSAccount*  account, CFDataRef backupSlice
 
 CF_RETURNS_RETAINED SOSBackupSliceKeyBagRef SOSAccountBackupSliceKeyBagForView(SOSAccount*  account, CFStringRef viewName, CFErrorRef* error);
 
 
 CF_RETURNS_RETAINED SOSBackupSliceKeyBagRef SOSAccountBackupSliceKeyBagForView(SOSAccount*  account, CFStringRef viewName, CFErrorRef* error);
 
-bool SOSAccountIsLastBackupPeer(SOSAccount*  account, CFErrorRef *error);
-
-
 //
 // MARK: Recovery Public Key Functions
 //
 //
 // MARK: Recovery Public Key Functions
 //
index cd5951a2545f654c916d3861f1ad7025444e5582..b75b48cfbfe534b0ca3015369681b8e0ec966c83 100644 (file)
@@ -113,7 +113,6 @@ static NSDictionary<OctagonState*, NSNumber*>* SOSStateMap(void);
 @property (readwrite) CKKSPBFileStorage<SOSAccountConfiguration*>* accountConfiguration;
 
 @property CKKSNearFutureScheduler *performBackups;
 @property (readwrite) CKKSPBFileStorage<SOSAccountConfiguration*>* accountConfiguration;
 
 @property CKKSNearFutureScheduler *performBackups;
-@property CKKSNearFutureScheduler *performRingUpdates;
 @end
 #endif
 
 @end
 #endif
 
@@ -124,11 +123,6 @@ static NSDictionary<OctagonState*, NSNumber*>* SOSStateMap(void);
         CFReleaseNull(self->_accountKey);
         CFReleaseNull(self->_accountPrivateKey);
         CFReleaseNull(self->_previousAccountKey);
         CFReleaseNull(self->_accountKey);
         CFReleaseNull(self->_accountPrivateKey);
         CFReleaseNull(self->_previousAccountKey);
-#if OCTAGON
-        [self.performBackups cancel];
-        [self.performRingUpdates cancel];
-        [self.stateMachine haltOperation];
-#endif
     }
 }
 
     }
 }
 
@@ -170,11 +164,16 @@ static NSDictionary<OctagonState*, NSNumber*>* SOSStateMap(void);
 
 -(bool) ensureFactoryCircles
 {
 
 -(bool) ensureFactoryCircles
 {
-    if (self.factory == nil){
+    if (!self){
+        return false;
+    }
+
+    if (!self.factory){
         return false;
     }
 
         return false;
     }
 
-    NSString* circle_name = CFBridgingRelease(SOSDataSourceFactoryCopyName(self.factory));
+    NSString* circle_name = (__bridge_transfer NSString*)SOSDataSourceFactoryCopyName(self.factory);
+
     if (!circle_name){
         return false;
     }
     if (!circle_name){
         return false;
     }
@@ -238,9 +237,6 @@ static NSDictionary<OctagonState*, NSNumber*>* SOSStateMap(void);
 
         self.settings =  [[NSUserDefaults alloc] initWithSuiteName:SOSAccountUserDefaultsSuite];
 
 
         self.settings =  [[NSUserDefaults alloc] initWithSuiteName:SOSAccountUserDefaultsSuite];
 
-        [self ensureFactoryCircles];
-        SOSAccountEnsureUUID(self);
-
 #if OCTAGON
         [self setupStateMachine];
 #endif
 #if OCTAGON
         [self setupStateMachine];
 #endif
@@ -248,13 +244,6 @@ static NSDictionary<OctagonState*, NSNumber*>* SOSStateMap(void);
     return self;
 }
 
     return self;
 }
 
-- (void)startStateMachine
-{
-#if OCTAGON
-    [self.stateMachine startOperation];
-#endif
-}
-
 -(BOOL)isEqual:(id) object
 {
     if(![object isKindOfClass:[SOSAccount class]])
 -(BOOL)isEqual:(id) object
 {
     if(![object isKindOfClass:[SOSAccount class]])
@@ -726,7 +715,7 @@ static bool Flush(CFErrorRef *error) {
     CFReleaseNull(error);
 }
 
     CFReleaseNull(error);
 }
 
-- (void)rpcTriggerSync:(NSArray <NSString *> *)peers complete:(void(^)(bool success, NSError *))complete
+- (void)triggerSync:(NSArray <NSString *> *)peers complete:(void(^)(bool success, NSError *))complete
 {
     __block CFErrorRef localError = NULL;
     __block bool res = false;
 {
     __block CFErrorRef localError = NULL;
     __block bool res = false;
@@ -751,7 +740,7 @@ static bool Flush(CFErrorRef *error) {
     CFReleaseNull(localError);
 }
 
     CFReleaseNull(localError);
 }
 
-- (void)rpcTriggerBackup:(NSArray<NSString *>* _Nullable)backupPeers complete:(void (^)(NSError *error))complete
+- (void)triggerBackup:(NSArray<NSString *>* _Nullable)backupPeers complete:(void (^)(NSError *error))complete
 {
     __block CFErrorRef localError = NULL;
 
 {
     __block CFErrorRef localError = NULL;
 
@@ -768,14 +757,6 @@ static bool Flush(CFErrorRef *error) {
     CFReleaseNull(localError);
 }
 
     CFReleaseNull(localError);
 }
 
-- (void)rpcTriggerRingUpdate:(void (^)(NSError *error))complete
-{
-#if OCTAGON
-    [self triggerRingUpdate];
-#endif
-    complete(NULL);
-}
-
 - (void)getWatchdogParameters:(void (^)(NSDictionary* parameters, NSError* error))complete
 {
     // SecdWatchdog is only available in the secd/securityd - no other binary will contain that class
 - (void)getWatchdogParameters:(void (^)(NSDictionary* parameters, NSError* error))complete
 {
     // SecdWatchdog is only available in the secd/securityd - no other binary will contain that class
@@ -881,10 +862,10 @@ SOSAccount*  SOSAccountCreate(CFAllocatorRef allocator,
                                SOSDataSourceFactoryRef factory) {
 
     SOSAccount* a = [[SOSAccount alloc] initWithGestalt:gestalt factory:factory];
                                SOSDataSourceFactoryRef factory) {
 
     SOSAccount* a = [[SOSAccount alloc] initWithGestalt:gestalt factory:factory];
-    dispatch_sync(a.queue, ^{
-        secnotice("circleop", "Setting account.key_interests_need_updating to true in SOSAccountCreate");
-        a.key_interests_need_updating = true;
-    });
+    [a ensureFactoryCircles];
+    SOSAccountEnsureUUID(a);
+    secnotice("circleop", "Setting account.key_interests_need_updating to true in SOSAccountCreate");
+    a.key_interests_need_updating = true;
     
     return a;
 }
     
     return a;
 }
@@ -1026,26 +1007,6 @@ void SOSAccountSetToNew(SOSAccount*  a)
     a.key_interests_need_updating = true;
 }
 
     a.key_interests_need_updating = true;
 }
 
-bool SOSAccountIsNew(SOSAccount*  account, CFErrorRef *error){
-    bool result = false;
-    SOSAccountTrustClassic* trust = account.trust;
-    if(account.accountKeyIsTrusted != false || trust.departureCode != kSOSNeverAppliedToCircle ||
-       CFSetGetCount((__bridge CFSetRef)trust.retirees) != 0)
-        return result;
-
-    if(trust.retirees != nil)
-        return result;
-    if(trust.expansion != nil)
-        return result;
-
-    if(account.user_private_timer != NULL || account.lock_notification_token != NOTIFY_TOKEN_INVALID)
-        return result;
-
-    result = true;
-
-    return result;
-}
-
 dispatch_queue_t SOSAccountGetQueue(SOSAccount*  account) {
     return account.queue;
 }
 dispatch_queue_t SOSAccountGetQueue(SOSAccount*  account) {
     return account.queue;
 }
@@ -1308,42 +1269,6 @@ bool sosAccountLeaveCircle(SOSAccount* account, SOSCircleRef circle, CFErrorRef*
     return retval;
 }
 
     return retval;
 }
 
-bool sosAccountLeaveRing(SOSAccount*  account, SOSRingRef ring, CFErrorRef* error) {
-    SOSAccountTrustClassic *trust = account.trust;
-    SOSFullPeerInfoRef identity = trust.fullPeerInfo;
-
-    SOSFullPeerInfoRef fpi = identity;
-    if(!fpi) return false;
-    SOSPeerInfoRef     pi = SOSFullPeerInfoGetPeerInfo(fpi);
-    CFStringRef        peerID = SOSPeerInfoGetPeerID(pi);
-    
-    CFErrorRef localError = NULL;
-    
-    bool retval = false;
-    bool writeRing = false;
-    bool writePeerInfo = false;
-    
-    if(SOSRingHasPeerID(ring, peerID)) {
-        writePeerInfo = true;
-    }
-
-    if(writePeerInfo || writeRing) {
-        SOSRingWithdraw(ring, NULL, fpi, error);
-    }
-    
-    if (writeRing) {
-        CFDataRef ring_data = SOSRingCopyEncodedData(ring, error);
-        
-        if (ring_data) {
-            [account.circle_transport kvsRingPostRing:SOSRingGetName(ring) ring:ring_data err:NULL];
-        }
-        CFReleaseNull(ring_data);
-    }
-    retval = true;
-    CFReleaseNull(localError);
-    return retval;
-}
-
 bool SOSAccountPostDebugScope(SOSAccount*  account, CFTypeRef scope, CFErrorRef *error) {
     bool result = false;
     if (account.circle_transport) {
 bool SOSAccountPostDebugScope(SOSAccount*  account, CFTypeRef scope, CFErrorRef *error) {
     bool result = false;
     if (account.circle_transport) {
@@ -1435,28 +1360,26 @@ bool SOSAccountRemoveIncompleteiCloudIdentities(SOSAccount*  account, SOSCircleR
 //
 
 
 //
 
 
-- (bool)_onQueueEnsureInBackupRings {
+bool SOSAccountEnsureInBackupRings(SOSAccount*  account) {
     __block bool result = false;
     __block CFErrorRef error = NULL;
     secnotice("backup", "Ensuring in rings");
 
     __block bool result = false;
     __block CFErrorRef error = NULL;
     secnotice("backup", "Ensuring in rings");
 
-    dispatch_assert_queue(self.queue);
-
-    if(!self.backup_key){
+    if(!account.backup_key){
         return true;
     }
 
         return true;
     }
 
-    if(!SOSBSKBIsGoodBackupPublic((__bridge CFDataRef)self.backup_key, &error)){
+    if(!SOSBSKBIsGoodBackupPublic((__bridge CFDataRef)account.backup_key, &error)){
         secnotice("backupkey", "account backup key isn't valid: %@", error);
         secnotice("backupkey", "account backup key isn't valid: %@", error);
-        self.backup_key = nil;
+        account.backup_key = nil;
         CFReleaseNull(error);
         return false;
     }
 
         CFReleaseNull(error);
         return false;
     }
 
-    NSData *peerBackupKey = (__bridge_transfer NSData*)SOSPeerInfoCopyBackupKey(self.peerInfo);
-    if(![peerBackupKey isEqual:self.backup_key]) {
-        result = SOSAccountUpdatePeerInfo(self, CFSTR("Backup public key"), &error, ^bool(SOSFullPeerInfoRef fpi, CFErrorRef *error) {
-            return SOSFullPeerInfoUpdateBackupKey(fpi, (__bridge CFDataRef)(self.backup_key), error);
+    NSData *peerBackupKey = (__bridge_transfer NSData*)SOSPeerInfoCopyBackupKey(account.peerInfo);
+    if(![peerBackupKey isEqual:account.backup_key]) {
+        result = SOSAccountUpdatePeerInfo(account, CFSTR("Backup public key"), &error, ^bool(SOSFullPeerInfoRef fpi, CFErrorRef *error) {
+            return SOSFullPeerInfoUpdateBackupKey(fpi, (__bridge CFDataRef)(account.backup_key), error);
         });
         if (!result) {
             secnotice("backupkey", "Failed to setup backup public key in peerInfo from account: %@", error);
         });
         if (!result) {
             secnotice("backupkey", "Failed to setup backup public key in peerInfo from account: %@", error);
@@ -1473,12 +1396,12 @@ bool SOSAccountRemoveIncompleteiCloudIdentities(SOSAccount*  account, SOSCircleR
     CFReleaseNull(localError);
 
     // Setup backups the new way.
     CFReleaseNull(localError);
 
     // Setup backups the new way.
-    SOSAccountForEachBackupView(self, ^(const void *value) {
+    SOSAccountForEachBackupView(account, ^(const void *value) {
         CFStringRef viewName = asString(value, NULL);
         CFStringRef viewName = asString(value, NULL);
-        bool resetRing = SOSAccountValidateBackupRingForView(self, viewName, NULL);
+        bool resetRing = SOSAccountValidateBackupRingForView(account, viewName, NULL);
         if(resetRing) {
         if(resetRing) {
-            SOSAccountUpdateBackupRing(self, viewName, NULL, ^SOSRingRef(SOSRingRef existing, CFErrorRef *error) {
-                SOSRingRef newRing = SOSAccountCreateBackupRingForView(self, viewName, error);
+            SOSAccountUpdateBackupRing(account, viewName, NULL, ^SOSRingRef(SOSRingRef existing, CFErrorRef *error) {
+                SOSRingRef newRing = SOSAccountCreateBackupRingForView(account, viewName, error);
                 return newRing;
             });
         }
                 return newRing;
             });
         }
@@ -1936,11 +1859,6 @@ bool SOSAccountRejectApplicants(SOSAccount*  account, CFArrayRef applicants, CFE
     return success;
 }
 
     return success;
 }
 
-
-CFStringRef SOSAccountCopyIncompatibilityInfo(SOSAccount*  account, CFErrorRef* error) {
-    return CFSTR("We're compatible, go away");
-}
-
 enum DepartureReason SOSAccountGetLastDepartureReason(SOSAccount*  account, CFErrorRef* error) {
     SOSAccountTrustClassic *trust = account.trust;
     return trust.departureCode;
 enum DepartureReason SOSAccountGetLastDepartureReason(SOSAccount*  account, CFErrorRef* error) {
     SOSAccountTrustClassic *trust = account.trust;
     return trust.departureCode;
@@ -2009,7 +1927,7 @@ bool SOSAccountEnsurePeerRegistration(SOSAccount*  account, CFErrorRef *error) {
             SOSEngineInitializePeerCoder((SOSEngineRef)[account.kvs_message_transport SOSTransportMessageGetEngine], trust.fullPeerInfo, peer, &localError);
             if (localError)
                 secnotice("updates", "can't initialize transport for peer %@ with %@ (%@)", peer, trust.fullPeerInfo, localError);
             SOSEngineInitializePeerCoder((SOSEngineRef)[account.kvs_message_transport SOSTransportMessageGetEngine], trust.fullPeerInfo, peer, &localError);
             if (localError)
                 secnotice("updates", "can't initialize transport for peer %@ with %@ (%@)", peer, trust.fullPeerInfo, localError);
-            CFReleaseNull(localError);
+            CFReleaseSafe(localError);
         }
     });
     
         }
     });
     
@@ -2028,27 +1946,7 @@ CFTypeRef SOSAccountGetValue(SOSAccount*  account, CFStringRef key, CFErrorRef *
     return (__bridge CFTypeRef)([trust.expansion objectForKey:(__bridge NSString* _Nonnull)(key)]);
 }
 
     return (__bridge CFTypeRef)([trust.expansion objectForKey:(__bridge NSString* _Nonnull)(key)]);
 }
 
-bool SOSAccountAddEscrowRecords(SOSAccount*  account, CFStringRef dsid, CFDictionaryRef record, CFErrorRef *error){
-    CFMutableDictionaryRef escrowRecords = (CFMutableDictionaryRef)SOSAccountGetValue(account, kSOSEscrowRecord, error);
-    CFMutableDictionaryRef escrowCopied = NULL;
-    bool success = false;
-    
-    if(isDictionary(escrowRecords) && escrowRecords != NULL)
-        escrowCopied = CFDictionaryCreateMutableCopy(kCFAllocatorDefault, CFDictionaryGetCount(escrowRecords), escrowRecords);
-    else
-        escrowCopied = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
-    
-    CFDictionaryAddValue(escrowCopied, dsid, record);
-    SOSAccountSetValue(account, kSOSEscrowRecord, escrowCopied, error);
-  
-    if(*error == NULL)
-        success = true;
-    
-    CFReleaseNull(escrowCopied);
-    
-    return success;
-    
-}
+
 
 bool SOSAccountAddEscrowToPeerInfo(SOSAccount*  account, SOSFullPeerInfoRef myPeer, CFErrorRef *error){
     bool success = false;
 
 bool SOSAccountAddEscrowToPeerInfo(SOSAccount*  account, SOSFullPeerInfoRef myPeer, CFErrorRef *error){
     bool success = false;
@@ -2059,16 +1957,13 @@ bool SOSAccountAddEscrowToPeerInfo(SOSAccount*  account, SOSFullPeerInfoRef myPe
     return success;
 }
 
     return success;
 }
 
-- (void)_onQueueRecordRetiredPeersInCircle {
-
-    dispatch_assert_queue(self.queue);
-
-    if (![self isInCircle:NULL]) {
+void SOSAccountRecordRetiredPeersInCircle(SOSAccount* account) {
+    if (![account isInCircle:NULL]) {
         return;
     }
     __block bool updateRings = false;
         return;
     }
     __block bool updateRings = false;
-    SOSAccountTrustClassic *trust = self.trust;
-    [trust modifyCircle:self.circle_transport err:NULL action:^bool (SOSCircleRef circle) {
+    SOSAccountTrustClassic *trust = account.trust;
+    [trust modifyCircle:account.circle_transport err:NULL action:^bool (SOSCircleRef circle) {
         __block bool updated = false;
         CFSetForEach((__bridge CFMutableSetRef)trust.retirees, ^(CFTypeRef element){
             SOSPeerInfoRef retiree = asSOSPeerInfo(element);
         __block bool updated = false;
         CFSetForEach((__bridge CFMutableSetRef)trust.retirees, ^(CFTypeRef element){
             SOSPeerInfoRef retiree = asSOSPeerInfo(element);
@@ -2077,7 +1972,7 @@ bool SOSAccountAddEscrowToPeerInfo(SOSAccount*  account, SOSFullPeerInfoRef myPe
                 updated = true;
                 secnotice("retirement", "Updated retired peer %@ in %@", retiree, circle);
                 CFErrorRef cleanupError = NULL;
                 updated = true;
                 secnotice("retirement", "Updated retired peer %@ in %@", retiree, circle);
                 CFErrorRef cleanupError = NULL;
-                if (![self.trust cleanupAfterPeer:self.kvs_message_transport circleTransport:self.circle_transport seconds:RETIREMENT_FINALIZATION_SECONDS circle:circle cleanupPeer:retiree err:&cleanupError])
+                if (![account.trust cleanupAfterPeer:account.kvs_message_transport circleTransport:account.circle_transport seconds:RETIREMENT_FINALIZATION_SECONDS circle:circle cleanupPeer:retiree err:&cleanupError])
                     secerror("Error cleanup up after peer (%@): %@", retiree, cleanupError);
                 CFReleaseSafe(cleanupError);
                 updateRings = true;
                     secerror("Error cleanup up after peer (%@): %@", retiree, cleanupError);
                 CFReleaseSafe(cleanupError);
                 updateRings = true;
@@ -2086,7 +1981,7 @@ bool SOSAccountAddEscrowToPeerInfo(SOSAccount*  account, SOSFullPeerInfoRef myPe
         return updated;
     }];
     if(updateRings) {
         return updated;
     }];
     if(updateRings) {
-        SOSAccountProcessBackupRings(self, NULL);
+        SOSAccountProcessBackupRings(account, NULL);
     }
 }
 
     }
 }
 
@@ -2235,32 +2130,6 @@ bool SOSAccountCleanupAllKVSKeys(SOSAccount* account, CFErrorRef* error)
     
 }
 
     
 }
 
-bool SOSAccountPopulateKVSWithBadKeys(SOSAccount*  account, CFErrorRef* error) {
-    
-    NSMutableDictionary *testKeysAndValues = [NSMutableDictionary dictionary];
-    [testKeysAndValues setObject:@"deadbeef" forKey:@"-ak|asdfjkl;asdfjk;"];
-    [testKeysAndValues setObject:@"foobar" forKey:@"ak|asdfasdfasdf:qwerqwerqwer"];
-    [testKeysAndValues setObject:@"oldhistorycircle" forKey:@"poak|asdfasdfasdfasdf"];
-    [testKeysAndValues setObject:@"oldhistorycircle" forKey:@"po|asdfasdfasdfasdfasdfasdf"];
-    [testKeysAndValues setObject:@"oldhistorycircle" forKey:@"k>KeyParm"];
-    
-    dispatch_semaphore_t waitSemaphore = dispatch_semaphore_create(0);
-    dispatch_time_t finishTime = dispatch_time(DISPATCH_TIME_NOW, maxTimeToWaitInSeconds);
-    dispatch_queue_t processQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
-
-    CloudKeychainReplyBlock replyBlock = ^ (CFDictionaryRef returnedValues, CFErrorRef error){
-        if (error){
-            secerror("SOSCloudKeychainPutObjectsInCloud returned error: %@", error);
-        }
-        dispatch_semaphore_signal(waitSemaphore);
-    };
-    
-    SOSCloudKeychainPutObjectsInCloud((__bridge CFDictionaryRef)(testKeysAndValues), processQueue, replyBlock);
-    dispatch_semaphore_wait(waitSemaphore, finishTime);
-
-    return true;
-}
-
 SOSPeerInfoRef SOSAccountCopyApplication(SOSAccount*  account, CFErrorRef* error) {
     SOSPeerInfoRef applicant = NULL;
     SOSAccountTrustClassic *trust = account.trust;
 SOSPeerInfoRef SOSAccountCopyApplication(SOSAccount*  account, CFErrorRef* error) {
     SOSPeerInfoRef applicant = NULL;
     SOSAccountTrustClassic *trust = account.trust;
@@ -2926,12 +2795,10 @@ void SOSAccountTimerFiredSendNextMessage(SOSAccountTransaction* txn, NSString* p
  */
 
 OctagonFlag* SOSFlagTriggerBackup = (OctagonFlag*)@"trigger_backup";
  */
 
 OctagonFlag* SOSFlagTriggerBackup = (OctagonFlag*)@"trigger_backup";
-OctagonFlag* SOSFlagTriggerRingUpdate = (OctagonFlag*)@"trigger_ring_update";
 
 OctagonState* SOSStateReady = (OctagonState*)@"ready";
 OctagonState* SOSStateError = (OctagonState*)@"error";
 OctagonState* SOSStatePerformBackup = (OctagonState*)@"perform_backup";
 
 OctagonState* SOSStateReady = (OctagonState*)@"ready";
 OctagonState* SOSStateError = (OctagonState*)@"error";
 OctagonState* SOSStatePerformBackup = (OctagonState*)@"perform_backup";
-OctagonState* SOSStatePerformRingUpdate = (OctagonState*)@"perform_ring_update";
 
 static NSDictionary<OctagonState*, NSNumber*>* SOSStateMap(void) {
     static NSDictionary<OctagonState*, NSNumber*>* map = nil;
 
 static NSDictionary<OctagonState*, NSNumber*>* SOSStateMap(void) {
     static NSDictionary<OctagonState*, NSNumber*>* map = nil;
@@ -2941,7 +2808,6 @@ static NSDictionary<OctagonState*, NSNumber*>* SOSStateMap(void) {
             SOSStateReady:                              @0U,
             SOSStateError:                              @1U,
             SOSStatePerformBackup:                      @2U,
             SOSStateReady:                              @0U,
             SOSStateError:                              @1U,
             SOSStatePerformBackup:                      @2U,
-            SOSStatePerformRingUpdate:                  @3U,
         };
     });
     return map;
         };
     });
     return map;
@@ -2952,8 +2818,7 @@ static NSSet<OctagonFlag*>* SOSFlagsSet(void) {
     static dispatch_once_t onceToken;
     dispatch_once(&onceToken, ^{
         set = [NSSet setWithArray:@[
     static dispatch_once_t onceToken;
     dispatch_once(&onceToken, ^{
         set = [NSSet setWithArray:@[
-            SOSFlagTriggerBackup,
-            SOSFlagTriggerRingUpdate,
+            SOSFlagTriggerBackup
         ]];
     });
     return set;
         ]];
     });
     return set;
@@ -2995,31 +2860,15 @@ static NSSet<OctagonFlag*>* SOSFlagsSet(void) {
         [self addBackupFlag];
     }];
 
         [self addBackupFlag];
     }];
 
-    self.performRingUpdates = [[CKKSNearFutureScheduler alloc] initWithName:@"performRingUpdates"
-                                                               initialDelay:5*NSEC_PER_SEC
-                                                           expontialBackoff:2.0
-                                                               maximumDelay:15*60*NSEC_PER_SEC
-                                                           keepProcessAlive:YES
-                                                  dependencyDescriptionCode:CKKSResultDescriptionNone
-                                                                      block:^{
-        STRONGIFY(self);
-        [self addRingUpdateFlag];
-    }];
-
     SOSAccountConfiguration *conf = self.accountConfiguration.storage;
 
     if (conf.pendingBackupPeers.count) {
         [self addBackupFlag];
     }
     SOSAccountConfiguration *conf = self.accountConfiguration.storage;
 
     if (conf.pendingBackupPeers.count) {
         [self addBackupFlag];
     }
-    if (conf.ringUpdateFlag) {
-        [self addRingUpdateFlag];
-    }
-}
 
 
+    [self.stateMachine startOperation];
+}
 
 
-/*
- * Flag adding to state machine
- */
 
 - (void)addBackupFlag {
     OctagonPendingFlag *pendingFlag = [[OctagonPendingFlag alloc] initWithFlag:SOSFlagTriggerBackup
 
 - (void)addBackupFlag {
     OctagonPendingFlag *pendingFlag = [[OctagonPendingFlag alloc] initWithFlag:SOSFlagTriggerBackup
@@ -3027,122 +2876,34 @@ static NSSet<OctagonFlag*>* SOSFlagsSet(void) {
     [self.stateMachine handlePendingFlag:pendingFlag];
 }
 
     [self.stateMachine handlePendingFlag:pendingFlag];
 }
 
-- (void)addRingUpdateFlag {
-    OctagonPendingFlag *pendingFlag = [[OctagonPendingFlag alloc] initWithFlag:SOSFlagTriggerRingUpdate
-                                                                    conditions:OctagonPendingConditionsDeviceUnlocked];
-    [self.stateMachine handlePendingFlag:pendingFlag];
-}
-
-//Mark: -- Set up state for state machine
-
-
 - (void)triggerBackupForPeers:(NSArray<NSString*>*)backupPeers
 {
 - (void)triggerBackupForPeers:(NSArray<NSString*>*)backupPeers
 {
-    NSMutableSet *pending = [NSMutableSet set];
-    if (backupPeers) {
-        [pending addObjectsFromArray:backupPeers];
-    }
-
-    WEAKIFY(self);
-    dispatch_async(self.stateMachineQueue, ^{
-        STRONGIFY(self);
-
+    dispatch_sync(self.stateMachineQueue, ^{
         SOSAccountConfiguration *storage = self.accountConfiguration.storage;
 
         SOSAccountConfiguration *storage = self.accountConfiguration.storage;
 
+        NSMutableSet *pending = [NSMutableSet set];
         if (storage.pendingBackupPeers) {
             [pending addObjectsFromArray:storage.pendingBackupPeers];
         }
         if (storage.pendingBackupPeers) {
             [pending addObjectsFromArray:storage.pendingBackupPeers];
         }
+        if (backupPeers) {
+            [pending addObjectsFromArray:backupPeers];
+        }
         storage.pendingBackupPeers = [[pending allObjects] mutableCopy];
         [self.accountConfiguration setStorage:storage];
         [self.performBackups trigger];
         storage.pendingBackupPeers = [[pending allObjects] mutableCopy];
         [self.accountConfiguration setStorage:storage];
         [self.performBackups trigger];
-        secnotice("sos-sm", "trigger backup for peers: %@ at %@",
-                  backupPeers, self.performBackups.nextFireTime);
+        secnotice("sos-sm", "trigger backup for peers: %@", backupPeers);
     });
 }
 
     });
 }
 
-- (void)triggerRingUpdate
-{
-
-    WEAKIFY(self);
-    dispatch_async(self.stateMachineQueue, ^{
-        STRONGIFY(self);
-        SOSAccountConfiguration *storage = self.accountConfiguration.storage;
-        storage.ringUpdateFlag = YES;
-        [self.accountConfiguration setStorage:storage];
-        [self.performRingUpdates trigger];
-        secnotice("sos-sm", "trigger ring update at %@",
-                  self.performRingUpdates.nextFireTime);
-    });
-}
-
-//Mark: -- State machine and opertions
-
-- (OctagonStateTransitionOperation *)performBackup {
-
-    WEAKIFY(self);
-    return [OctagonStateTransitionOperation named:@"perform-backup-state"
-                                        intending:SOSStateReady
-                                       errorState:SOSStateError
-                              withBlockTakingSelf:^void(OctagonStateTransitionOperation * _Nonnull op) {
-        STRONGIFY(self);
-        SOSAccountConfiguration *storage = self.accountConfiguration.storage;
-
-        secnotice("sos-sm", "performing backup for %@", storage.pendingBackupPeers);
-
-        if (storage.pendingBackupPeers.count) {
-            SOSCCRequestSyncWithBackupPeerList((__bridge CFArrayRef)storage.pendingBackupPeers);
-            [storage clearPendingBackupPeers];
-        }
-        [self.accountConfiguration setStorage:storage];
-
-        op.nextState = SOSStateReady;
-    }];
-}
-
-- (OctagonStateTransitionOperation *)performRingUpdate {
-
-    WEAKIFY(self);
-    return [OctagonStateTransitionOperation named:@"perform-ring-update"
-                                        intending:SOSStateReady
-                                       errorState:SOSStateError
-                              withBlockTakingSelf:^void(OctagonStateTransitionOperation * _Nonnull op) {
-        STRONGIFY(self);
-
-        SOSAccountConfiguration *storage = self.accountConfiguration.storage;
-        storage.ringUpdateFlag = NO;
-        [self.accountConfiguration setStorage:storage];
-
-        [self performTransaction:^(SOSAccountTransaction * _Nonnull txn) {
-
-            [self _onQueueRecordRetiredPeersInCircle];
-
-            SOSAccountEnsureRecoveryRing(self);
-            [self _onQueueEnsureInBackupRings];
-
-            CFErrorRef localError = NULL;
-            if(![self.circle_transport flushChanges:&localError]){
-                secerror("flush circle failed %@", localError);
-            }
-            CFReleaseNull(localError);
-
-            if(!SecCKKSTestDisableSOS()) {
-                SOSAccountNotifyEngines(self);
-            }
-        }];
-
-        op.nextState = SOSStateReady;
-    }];
-
-}
-
-
 - (CKKSResultOperation<OctagonStateTransitionOperationProtocol>* _Nullable)_onqueueNextStateMachineTransition:(OctagonState*)currentState
                                                                                                         flags:(nonnull OctagonFlags *)flags
                                                                                                  pendingFlags:(nonnull id<OctagonStateOnqueuePendingFlagHandler>)pendingFlagHandler
 {
     dispatch_assert_queue(self.stateMachineQueue);
 
 - (CKKSResultOperation<OctagonStateTransitionOperationProtocol>* _Nullable)_onqueueNextStateMachineTransition:(OctagonState*)currentState
                                                                                                         flags:(nonnull OctagonFlags *)flags
                                                                                                  pendingFlags:(nonnull id<OctagonStateOnqueuePendingFlagHandler>)pendingFlagHandler
 {
     dispatch_assert_queue(self.stateMachineQueue);
 
-    secnotice("sos-sm", "Entering state: %@ [flags: %@]", currentState, flags);
+    WEAKIFY(self);
+
+    secnotice("sos-sm", "currentState: %@ [flags: %@]", currentState, flags);
 
     if ([currentState isEqualToString:SOSStateReady]) {
         if([flags _onqueueContains:SOSFlagTriggerBackup]) {
 
     if ([currentState isEqualToString:SOSStateReady]) {
         if([flags _onqueueContains:SOSFlagTriggerBackup]) {
@@ -3150,21 +2911,30 @@ static NSSet<OctagonFlag*>* SOSFlagsSet(void) {
             return [OctagonStateTransitionOperation named:@"perform-backup-flag"
                                                  entering:SOSStatePerformBackup];
         }
             return [OctagonStateTransitionOperation named:@"perform-backup-flag"
                                                  entering:SOSStatePerformBackup];
         }
-
-        if ([flags _onqueueContains:SOSFlagTriggerRingUpdate]) {
-            [flags _onqueueRemoveFlag:SOSFlagTriggerRingUpdate];
-            return [OctagonStateTransitionOperation named:@"perform-ring-update-flag"
-                                                 entering:SOSStatePerformRingUpdate];
-        }
+        secnotice("sos-sm", "Entering state ready");
         return nil;
         return nil;
-
     } else if ([currentState isEqualToString:SOSStateError]) {
     } else if ([currentState isEqualToString:SOSStateError]) {
+        secnotice("sos-sm", "Entering state error");
         return nil;
         return nil;
-    } else if ([currentState isEqualToString:SOSStatePerformRingUpdate]) {
-        return [self performRingUpdate];
-
     } else if ([currentState isEqualToString:SOSStatePerformBackup]) {
     } else if ([currentState isEqualToString:SOSStatePerformBackup]) {
-        return [self performBackup];
+
+        return [OctagonStateTransitionOperation named:@"perform-backup-state"
+                                            intending:SOSStateReady
+                                           errorState:SOSStateError
+                                  withBlockTakingSelf:^void(OctagonStateTransitionOperation * _Nonnull op) {
+            STRONGIFY(self);
+            SOSAccountConfiguration *storage = self.accountConfiguration.storage;
+
+            secnotice("sos-sm", "performing backup for %@", storage.pendingBackupPeers);
+
+            if (storage.pendingBackupPeers.count) {
+                SOSCCRequestSyncWithBackupPeerList((__bridge CFArrayRef)storage.pendingBackupPeers);
+                [storage clearPendingBackupPeers];
+            }
+            [self.accountConfiguration setStorage:storage];
+
+            op.nextState = SOSStateReady;
+        }];
     }
 
     return nil;
     }
 
     return nil;
index 3a631f1f4a3c0d2e984c3f2185163dd35100cf68..17c724d5b63a762654f01034aefa5719fbc65196 100644 (file)
@@ -629,33 +629,3 @@ exit:
 
     return bskb;
 }
 
     return bskb;
 }
-
-bool SOSAccountIsLastBackupPeer(SOSAccount*  account, CFErrorRef *error) {
-    __block bool retval = false;
-    SOSPeerInfoRef pi = account.peerInfo;
-
-    if(![account isInCircle:error]) {
-        return retval;
-    }
-
-    if(!SOSPeerInfoHasBackupKey(pi))
-        return retval;
-
-    SOSCircleRef circle = [account.trust getCircle:error];
-
-    if(SOSCircleCountValidSyncingPeers(circle, SOSAccountGetTrustedPublicCredential(account, error)) == 1){
-        retval = true;
-        return retval;
-    }
-    // We're in a circle with more than 1 ActiveValidPeers - are they in the backups?
-    SOSAccountForEachBackupView(account, ^(const void *value) {
-        CFStringRef viewname = (CFStringRef) value;
-        SOSBackupSliceKeyBagRef keybag = SOSAccountBackupSliceKeyBagForView(account, viewname, error);
-        require_quiet(keybag, inner_errOut);
-        retval |= ((SOSBSKBCountPeers(keybag) == 1) && (SOSBSKBPeerIsInKeyBag(keybag, pi)));
-    inner_errOut:
-        CFReleaseNull(keybag);
-    });
-
-    return retval;
-}
index bb26993a25e8223d2d13c1b64582d8bf06f00fd4..8bba993c0d7917696448e3ac20e1c24eafb7e787 100644 (file)
@@ -8,5 +8,4 @@ package SOS;
 
 message AccountConfiguration {
     repeated string pendingBackupPeers = 1;
 
 message AccountConfiguration {
     repeated string pendingBackupPeers = 1;
-    optional bool ringUpdateFlag = 2;
 }
 }
index a2d80366e3fda6d9787e0e9725460a6aa152be27..31f28a5cd5ca4e23f3cf5d43e7b506bd7c216218 100644 (file)
@@ -204,21 +204,6 @@ CFDataRef SOSAccountGetCachedPassword(SOSAccount* account, CFErrorRef* error)
 static NSString *SOSUserCredentialAccount = @"SOSUserCredential";
 static NSString *SOSUserCredentialAccessGroup = @"com.apple.security.sos-usercredential";
 
 static NSString *SOSUserCredentialAccount = @"SOSUserCredential";
 static NSString *SOSUserCredentialAccessGroup = @"com.apple.security.sos-usercredential";
 
-__unused static void SOSAccountDeleteStashedAccountKey(SOSAccount* account)
-{
-    NSString *dsid = (__bridge NSString *)SOSAccountGetValue(account, kSOSDSIDKey, NULL);
-    if (dsid == NULL)
-        return;
-
-    NSDictionary * attributes = @{
-        (__bridge id)kSecClass : (__bridge id)kSecClassInternetPassword,
-        (__bridge id)kSecAttrAccount : SOSUserCredentialAccount,
-        (__bridge id)kSecAttrServer : dsid,
-        (__bridge id)kSecAttrAccessGroup : SOSUserCredentialAccessGroup,
-    };
-    (void)SecItemDelete((__bridge CFDictionaryRef)attributes);
-}
-
 void SOSAccountStashAccountKey(SOSAccount* account)
 {
     OSStatus status;
 void SOSAccountStashAccountKey(SOSAccount* account)
 {
     OSStatus status;
index 474335b323614032e900f3f75daf29700b9c0a4d..cae09dfbbfb35c2192deb5b83eb8299dee44f91a 100644 (file)
@@ -154,77 +154,6 @@ CFArrayRef SOSAccountCopyPeers(SOSAccount* account, CFErrorRef *error) {
     });
 }
 
     });
 }
 
-CFDataRef SOSAccountCopyAccountStateFromKeychain(CFErrorRef *error){
-    CFMutableDictionaryRef query = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
-    CFTypeRef result = NULL;
-    CFDictionaryAddValue(query, kSecClass, kSecClassGenericPassword);
-    CFDictionaryAddValue(query, kSecAttrAccessGroup, CFSTR("com.apple.security.sos"));
-    CFDictionaryAddValue(query, kSecAttrAccessible, CFSTR("dku"));
-    CFDictionaryAddValue(query, kSecAttrTombstone, kCFBooleanFalse);
-    CFDictionaryAddValue(query, kSecAttrSynchronizable, kCFBooleanFalse);
-    CFDictionaryAddValue(query, kSecReturnData, kCFBooleanTrue);
-    
-    SecItemCopyMatching(query, &result);
-
-    if(!isData(result)){
-        SOSErrorCreate(kSOSErrorUnexpectedType, error, NULL, CFSTR("Expected CFData, got: %@"), result);
-        CFReleaseNull(result);
-        return NULL;
-    }
-    return result;
-}
-
-bool SOSAccountDeleteAccountStateFromKeychain(CFErrorRef *error){
-    CFMutableDictionaryRef query = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
-    bool result = false;
-    CFDictionaryAddValue(query, kSecClass, kSecClassGenericPassword);
-    CFDictionaryAddValue(query, kSecAttrAccessGroup, CFSTR("com.apple.security.sos"));
-    CFDictionaryAddValue(query, kSecAttrAccessible, CFSTR("dku"));
-    CFDictionaryAddValue(query, kSecAttrTombstone, kCFBooleanFalse);
-    CFDictionaryAddValue(query, kSecAttrSynchronizable, kCFBooleanFalse);
-    
-    result = SecItemDelete(query);
-    return result;
-}
-
-CFDataRef SOSAccountCopyEngineStateFromKeychain(CFErrorRef *error){
-    CFMutableDictionaryRef query = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
-    CFTypeRef result = NULL;
-    CFDictionaryAddValue(query, kSecClass, kSecClassGenericPassword);
-    CFDictionaryAddValue(query, kSecAttrAccount, CFSTR("engine-state"));
-    CFDictionaryAddValue(query, kSecAttrAccessGroup, CFSTR("com.apple.security.sos"));
-    CFDictionaryAddValue(query, kSecAttrAccessible, CFSTR("dk"));
-    CFDictionaryAddValue(query, kSecAttrService, CFSTR("SOSDataSource-ak"));
-    CFDictionaryAddValue(query, kSecAttrTombstone, kCFBooleanFalse);
-    CFDictionaryAddValue(query, kSecAttrSynchronizable, kCFBooleanFalse);
-    CFDictionaryAddValue(query, kSecReturnData, kCFBooleanTrue);
-    
-    SecItemCopyMatching(query, &result);
-    
-    if(!isData(result)){
-        SOSErrorCreate(kSOSErrorUnexpectedType, error, NULL, CFSTR("Expected CFData, got: %@"), result);
-        CFReleaseNull(result);
-        return NULL;
-    }
-    return result;
-}
-
-bool SOSAccountDeleteEngineStateFromKeychain(CFErrorRef *error){
-    CFMutableDictionaryRef query = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
-    bool result = false;
-    CFDictionaryAddValue(query, kSecClass, kSecClassGenericPassword);
-    CFDictionaryAddValue(query, kSecAttrAccount, CFSTR("engine-state"));
-    CFDictionaryAddValue(query, kSecAttrAccessGroup, CFSTR("com.apple.security.sos"));
-    CFDictionaryAddValue(query, kSecAttrAccessible, CFSTR("dk"));
-    CFDictionaryAddValue(query, kSecAttrService, CFSTR("SOSDataSource-ak"));
-    CFDictionaryAddValue(query, kSecAttrTombstone, kCFBooleanFalse);
-    CFDictionaryAddValue(query, kSecAttrSynchronizable, kCFBooleanFalse);
-    
-    result = SecItemDelete(query);
-    return result;
-}
-
-
 CFArrayRef SOSAccountCopyActivePeers(SOSAccount* account, CFErrorRef *error) {
     return SOSAccountCopySortedPeerArray(account, error, ^(SOSCircleRef circle, CFMutableArrayRef appendPeersTo) {
         SOSCircleForEachActivePeer(circle, ^(SOSPeerInfoRef peer) {
 CFArrayRef SOSAccountCopyActivePeers(SOSAccount* account, CFErrorRef *error) {
     return SOSAccountCopySortedPeerArray(account, error, ^(SOSCircleRef circle, CFMutableArrayRef appendPeersTo) {
         SOSCircleForEachActivePeer(circle, ^(SOSPeerInfoRef peer) {
index eb4b7a0f076ec329d1d4b293f9c85d17a5b98d40..5b63e744296ff4a3370da4840f7b429bd1fbe1d3 100644 (file)
@@ -457,10 +457,9 @@ static SOSAccount* SOSAccountCreateFromDER(CFAllocatorRef allocator,
     }
     CFReleaseNull(oldPI);
 
     }
     CFReleaseNull(oldPI);
 
+    SOSAccountEnsureRecoveryRing(account);
 
     [account performTransaction:^(SOSAccountTransaction * _Nonnull txn) {
 
     [account performTransaction:^(SOSAccountTransaction * _Nonnull txn) {
-        SOSAccountEnsureRecoveryRing(account);
-
         secnotice("circleop", "Setting account.key_interests_need_updating to true in SOSAccountCreateFromDER");
         account.key_interests_need_updating = true;
     }];
         secnotice("circleop", "Setting account.key_interests_need_updating to true in SOSAccountCreateFromDER");
         account.key_interests_need_updating = true;
     }];
index 798d1fb17832ee9aa5c7023a9a386669548970f5..249a8e00061f829ff46619c11efdd67bd9eecceb 100644 (file)
@@ -128,7 +128,7 @@ typedef void (^SOSAccountSaveBlock)(CFDataRef flattenedAccount, CFErrorRef flatt
 -(id) init NS_UNAVAILABLE;
 -(id) initWithGestalt:(CFDictionaryRef)gestalt factory:(SOSDataSourceFactoryRef)factory;
 
 -(id) init NS_UNAVAILABLE;
 -(id) initWithGestalt:(CFDictionaryRef)gestalt factory:(SOSDataSourceFactoryRef)factory;
 
-- (void)startStateMachine;
+//- (void)startStateMachine;
 
 void SOSAccountAddSyncablePeerBlock(SOSAccount*  a,
                                     CFStringRef ds_name,
 
 void SOSAccountAddSyncablePeerBlock(SOSAccount*  a,
                                     CFStringRef ds_name,
@@ -145,7 +145,6 @@ void SOSAccountAddSyncablePeerBlock(SOSAccount*  a,
 
 #if OCTAGON
 - (void)triggerBackupForPeers:(NSArray<NSString*>*)backupPeer;
 
 #if OCTAGON
 - (void)triggerBackupForPeers:(NSArray<NSString*>*)backupPeer;
-- (void)triggerRingUpdate;
 #endif
 
 
 #endif
 
 
@@ -206,6 +205,8 @@ bool SOSAccountHandleCircleMessage(SOSAccount* account,
 CF_RETURNS_RETAINED
 CFDictionaryRef SOSAccountHandleRetirementMessages(SOSAccount* account, CFDictionaryRef circle_retirement_messages, CFErrorRef *error);
 
 CF_RETURNS_RETAINED
 CFDictionaryRef SOSAccountHandleRetirementMessages(SOSAccount* account, CFDictionaryRef circle_retirement_messages, CFErrorRef *error);
 
+void SOSAccountRecordRetiredPeersInCircle(SOSAccount* account);
+
 bool SOSAccountHandleUpdateCircle(SOSAccount* account,
                                   SOSCircleRef prospective_circle,
                                   bool writeUpdate,
 bool SOSAccountHandleUpdateCircle(SOSAccount* account,
                                   SOSCircleRef prospective_circle,
                                   bool writeUpdate,
@@ -271,10 +272,10 @@ void SOSAccountPurgeIdentity(SOSAccount*);
 bool sosAccountLeaveCircle(SOSAccount* account, SOSCircleRef circle, CFErrorRef* error);
 bool sosAccountLeaveCircleWithAnalytics(SOSAccount* account, SOSCircleRef circle, NSData* parentData, CFErrorRef* error);
 
 bool sosAccountLeaveCircle(SOSAccount* account, SOSCircleRef circle, CFErrorRef* error);
 bool sosAccountLeaveCircleWithAnalytics(SOSAccount* account, SOSCircleRef circle, NSData* parentData, CFErrorRef* error);
 
-bool sosAccountLeaveRing(SOSAccount*  account, SOSRingRef ring, CFErrorRef* error);
 bool SOSAccountForEachRing(SOSAccount* account, SOSRingRef (^action)(CFStringRef name, SOSRingRef ring));
 bool SOSAccountUpdateBackUp(SOSAccount* account, CFStringRef viewname, CFErrorRef *error);
 void SOSAccountEnsureRecoveryRing(SOSAccount* account);
 bool SOSAccountForEachRing(SOSAccount* account, SOSRingRef (^action)(CFStringRef name, SOSRingRef ring));
 bool SOSAccountUpdateBackUp(SOSAccount* account, CFStringRef viewname, CFErrorRef *error);
 void SOSAccountEnsureRecoveryRing(SOSAccount* account);
+bool SOSAccountEnsureInBackupRings(SOSAccount* account);
 
 bool SOSAccountEnsurePeerRegistration(SOSAccount* account, CFErrorRef *error);
 
 
 bool SOSAccountEnsurePeerRegistration(SOSAccount* account, CFErrorRef *error);
 
@@ -298,15 +299,11 @@ bool SOSAccountClearValue(SOSAccount* account, CFStringRef key, CFErrorRef *erro
 CFTypeRef SOSAccountGetValue(SOSAccount* account, CFStringRef key, CFErrorRef *error);
 
 bool SOSAccountAddEscrowToPeerInfo(SOSAccount* account, SOSFullPeerInfoRef myPeer, CFErrorRef *error);
 CFTypeRef SOSAccountGetValue(SOSAccount* account, CFStringRef key, CFErrorRef *error);
 
 bool SOSAccountAddEscrowToPeerInfo(SOSAccount* account, SOSFullPeerInfoRef myPeer, CFErrorRef *error);
-bool SOSAccountAddEscrowRecords(SOSAccount* account, CFStringRef dsid, CFDictionaryRef record, CFErrorRef *error);
 void SOSAccountRemoveRing(SOSAccount* a, CFStringRef ringName);
 SOSRingRef SOSAccountCopyRingNamed(SOSAccount* a, CFStringRef ringName, CFErrorRef *error);
 void SOSAccountRemoveRing(SOSAccount* a, CFStringRef ringName);
 SOSRingRef SOSAccountCopyRingNamed(SOSAccount* a, CFStringRef ringName, CFErrorRef *error);
-SOSRingRef SOSAccountRingCreateForName(SOSAccount* a, CFStringRef ringName, CFErrorRef *error);
 bool SOSAccountUpdateRingFromRemote(SOSAccount* account, SOSRingRef newRing, CFErrorRef *error);
 bool SOSAccountUpdateRing(SOSAccount* account, SOSRingRef newRing, CFErrorRef *error);
 bool SOSAccountRemoveBackupPeers(SOSAccount* account, CFArrayRef peerIDs, CFErrorRef *error);
 bool SOSAccountUpdateRingFromRemote(SOSAccount* account, SOSRingRef newRing, CFErrorRef *error);
 bool SOSAccountUpdateRing(SOSAccount* account, SOSRingRef newRing, CFErrorRef *error);
 bool SOSAccountRemoveBackupPeers(SOSAccount* account, CFArrayRef peerIDs, CFErrorRef *error);
-bool SOSAccountResetRing(SOSAccount* account, CFStringRef ringName, CFErrorRef *error);
-bool SOSAccountCheckPeerAvailability(SOSAccount* account, CFErrorRef *error);
 bool SOSAccountUpdateNamedRing(SOSAccount* account, CFStringRef ringName, CFErrorRef *error,
                                SOSRingRef (^create)(CFStringRef ringName, CFErrorRef *error),
                                SOSRingRef (^copyModified)(SOSRingRef existing, CFErrorRef *error));
 bool SOSAccountUpdateNamedRing(SOSAccount* account, CFStringRef ringName, CFErrorRef *error,
                                SOSRingRef (^create)(CFStringRef ringName, CFErrorRef *error),
                                SOSRingRef (^copyModified)(SOSRingRef existing, CFErrorRef *error));
@@ -322,12 +319,7 @@ bool SOSAccountUpdateBackupRing(SOSAccount*  account, CFStringRef viewName, CFEr
 // Security tool test/debug functions
 //
 bool SOSAccountPostDebugScope(SOSAccount*  account, CFTypeRef scope, CFErrorRef *error);
 // Security tool test/debug functions
 //
 bool SOSAccountPostDebugScope(SOSAccount*  account, CFTypeRef scope, CFErrorRef *error);
-CFDataRef SOSAccountCopyAccountStateFromKeychain(CFErrorRef *error);
-bool SOSAccountDeleteAccountStateFromKeychain(CFErrorRef *error);
-CFDataRef SOSAccountCopyEngineStateFromKeychain(CFErrorRef *error);
-bool SOSAccountDeleteEngineStateFromKeychain(CFErrorRef *error);
 
 
-bool SOSAccountIsNew(SOSAccount* account, CFErrorRef *error);
 bool SOSAccountCheckForAlwaysOnViews(SOSAccount* account);
 // UUID, no setter just getter and ensuring value.
 void SOSAccountEnsureUUID(SOSAccount* account);
 bool SOSAccountCheckForAlwaysOnViews(SOSAccount* account);
 // UUID, no setter just getter and ensuring value.
 void SOSAccountEnsureUUID(SOSAccount* account);
@@ -351,7 +343,6 @@ NSArray<NSDictionary*>* SOSAccountSortTLKS(NSArray<NSDictionary*>* tlks);
 #endif
 
 bool SOSAccountCleanupAllKVSKeys(SOSAccount* account, CFErrorRef* error);
 #endif
 
 bool SOSAccountCleanupAllKVSKeys(SOSAccount* account, CFErrorRef* error);
-bool SOSAccountPopulateKVSWithBadKeys(SOSAccount*  account, CFErrorRef* error);
 
 @end
 
 
 @end
 
index 39d749939239d09ff496beb2a9a5cd4f73316a53..cd272013ac04f9a12504e5306d4ab610891d012a 100644 (file)
@@ -196,8 +196,6 @@ static void sosRecoveryAlertAndNotify(SOSAccount* account, SOSRecoveryKeyBagRef
 }
 
 void SOSAccountEnsureRecoveryRing(SOSAccount* account) {
 }
 
 void SOSAccountEnsureRecoveryRing(SOSAccount* account) {
-    dispatch_assert_queue(account.queue);
-
     static SOSRecoveryKeyBagRef oldRingRKBG = NULL;
     SOSRecoveryKeyBagRef acctRKBG = SOSAccountCopyRecoveryKeyBagEntry(kCFAllocatorDefault, account, NULL);
     if(!CFEqualSafe(acctRKBG, oldRingRKBG)) {
     static SOSRecoveryKeyBagRef oldRingRKBG = NULL;
     SOSRecoveryKeyBagRef acctRKBG = SOSAccountCopyRecoveryKeyBagEntry(kCFAllocatorDefault, account, NULL);
     if(!CFEqualSafe(acctRKBG, oldRingRKBG)) {
index 30e18ed5fd4ad7d13caa96286f2cec7a227bc0ca..2ebcd729985e0f6fc264387ccb4f76a98ae0b610 100644 (file)
@@ -34,72 +34,12 @@ const CFStringRef kSOSRingOtherSyncable         = CFSTR("Ring-OtherSyncable");
 
 const CFStringRef kSOSRingKey                   = CFSTR("trusted_rings");
 
 
 const CFStringRef kSOSRingKey                   = CFSTR("trusted_rings");
 
-static CFSetRef allCurrentRings(void) {
-    static dispatch_once_t dot;
-    static CFMutableSetRef allRings = NULL;
-    dispatch_once(&dot, ^{
-        allRings = CFSetCreateMutable(NULL, 0, &kCFTypeSetCallBacks);
-        CFSetAddValue(allRings, kSOSRingCircleV2);
-        CFSetAddValue(allRings, kSOSRingKeychainV0);
-        CFSetAddValue(allRings, kSOSRingPCSHyperion);
-        CFSetAddValue(allRings, kSOSRingPCSBladerunner);
-        CFSetAddValue(allRings, kSOSRingPCSLiverpool);
-        CFSetAddValue(allRings, kSOSRingPCSEscrow);
-        CFSetAddValue(allRings, kSOSRingPCSPianoMover);
-        CFSetAddValue(allRings, kSOSRingPCSNotes);
-        CFSetAddValue(allRings, kSOSRingPCSFeldspar);
-        CFSetAddValue(allRings, kSOSRingAppleTV);
-        CFSetAddValue(allRings, kSOSRingHomeKit);
-        CFSetAddValue(allRings, kSOSRingWifi);
-        CFSetAddValue(allRings, kSOSRingPasswords);
-        CFSetAddValue(allRings, kSOSRingCreditCards);
-        CFSetAddValue(allRings, kSOSRingiCloudIdentity);
-        CFSetAddValue(allRings, kSOSRingOtherSyncable);
-    });
-    return allRings;
-}
-
 typedef struct ringDef_t {
     CFStringRef name;
     SOSRingType ringType;
     bool dropWhenLeaving;
 } ringDef, *ringDefPtr;
 
 typedef struct ringDef_t {
     CFStringRef name;
     SOSRingType ringType;
     bool dropWhenLeaving;
 } ringDef, *ringDefPtr;
 
-static ringDefPtr getRingDef(CFStringRef ringName) {
-    static ringDef retval;
-
-    // Defaults
-    retval.name = ringName;
-    retval.dropWhenLeaving = true;
-    retval.ringType = kSOSRingEntropyKeyed;
-
-
-    if(CFSetContainsValue(allCurrentRings(), ringName)) {
-        retval.ringType = kSOSRingBase;
-        retval.dropWhenLeaving = false;
-    } else {
-        retval.ringType = kSOSRingBackup;
-        retval.dropWhenLeaving = false;
-    }
-    return &retval;
-}
-
-__unused static inline void SOSAccountRingForEachRingMatching(SOSAccount* a, void (^action)(SOSRingRef ring), bool (^condition)(SOSRingRef ring)) {
-    CFSetRef allRings = allCurrentRings();
-    CFSetForEach(allRings, ^(const void *value) {
-        CFStringRef ringName = (CFStringRef) value;
-        SOSRingRef ring = [a.trust copyRing:ringName err:NULL];
-        if (condition(ring)) {
-            action(ring);
-        }
-        CFReleaseNull(ring);
-    });
-}
-
-
-
-
-
 static void SOSAccountSetRings(SOSAccount* a, CFMutableDictionaryRef newrings){
     SOSAccountTrustClassic *trust = a.trust;
     [trust.expansion setObject:(__bridge NSMutableDictionary*)newrings forKey:(__bridge NSString* _Nonnull)(kSOSRingKey)];
 static void SOSAccountSetRings(SOSAccount* a, CFMutableDictionaryRef newrings){
     SOSAccountTrustClassic *trust = a.trust;
     [trust.expansion setObject:(__bridge NSMutableDictionary*)newrings forKey:(__bridge NSString* _Nonnull)(kSOSRingKey)];
@@ -175,14 +115,6 @@ SOSRingRef SOSAccountCopyRingNamed(SOSAccount* a, CFStringRef ringName, CFErrorR
     return found;
 }
 
     return found;
 }
 
-/* Unused? */
-SOSRingRef SOSAccountRingCreateForName(SOSAccount* a, CFStringRef ringName, CFErrorRef *error) {
-    ringDefPtr rdef = getRingDef(ringName);
-    if(!rdef) return NULL;
-    SOSRingRef retval = SOSRingCreate(rdef->name, (__bridge CFStringRef) a.peerID, rdef->ringType, error);
-    return retval;
-}
-
 bool SOSAccountUpdateRingFromRemote(SOSAccount* account, SOSRingRef newRing, CFErrorRef *error) {
     require_quiet(SOSAccountHasPublicKey(account, error), errOut);
   
 bool SOSAccountUpdateRingFromRemote(SOSAccount* account, SOSRingRef newRing, CFErrorRef *error) {
     require_quiet(SOSAccountHasPublicKey(account, error), errOut);
   
index 168d096d2f89625fb541b82dc62da012e9548474..1c014b29b2428662d3acfb5aecc156bd6e0d0c99 100644 (file)
@@ -277,10 +277,18 @@ static void SOSViewsSetCachedStatus(SOSAccount *account) {
     }
    
     if(self.account.circle_rings_retirements_need_attention){
     }
    
     if(self.account.circle_rings_retirements_need_attention){
-        self.account.circle_rings_retirements_need_attention = false;
-#if OCTAGON
-        [self.account triggerRingUpdate];
-#endif
+        SOSAccountRecordRetiredPeersInCircle(self.account);
+
+        SOSAccountEnsureRecoveryRing(self.account);
+        SOSAccountEnsureInBackupRings(self.account);
+
+        CFErrorRef localError = NULL;
+        if(![self.account.circle_transport flushChanges:&localError]){
+            secerror("flush circle failed %@", localError);
+        }
+        CFReleaseSafe(localError);
+
+        notifyEngines = true;
     }
 
     if (notifyEngines) {
     }
 
     if (notifyEngines) {
@@ -297,6 +305,7 @@ static void SOSViewsSetCachedStatus(SOSAccount *account) {
         SOSUpdateKeyInterest(self.account);
     }
 
         SOSUpdateKeyInterest(self.account);
     }
 
+    self.account.circle_rings_retirements_need_attention = false;
     self.account.engine_peer_state_needs_repair = false;
 
     [self.account flattenToSaveBlock];
     self.account.engine_peer_state_needs_repair = false;
 
     [self.account flattenToSaveBlock];
index f24e67135a20de79eea9a423530443e0385f63af..9c0cad82bce4d5ffd64c75fd6184e09efaf03c71 100644 (file)
@@ -162,8 +162,6 @@ bool SOSAccountSyncingV0(SOSAccount* account) {
 
 void SOSAccountNotifyEngines(SOSAccount* account)
 {
 
 void SOSAccountNotifyEngines(SOSAccount* account)
 {
-    dispatch_assert_queue(account.queue);
-
     SOSAccountTrustClassic *trust = account.trust;
     SOSFullPeerInfoRef identity = trust.fullPeerInfo;
     SOSCircleRef circle = trust.trustedCircle;
     SOSAccountTrustClassic *trust = account.trust;
     SOSFullPeerInfoRef identity = trust.fullPeerInfo;
     SOSCircleRef circle = trust.trustedCircle;
diff --git a/keychain/SecureObjectSync/SOSBackupInformation.h b/keychain/SecureObjectSync/SOSBackupInformation.h
deleted file mode 100644 (file)
index b11d57b..0000000
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright (c) 2016 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@
- */
-
-//
-//  SOSBackupInformation.h
-//  Security
-//
-
-#ifndef SOSBackupInformation_h
-#define SOSBackupInformation_h
-
-#include <CoreFoundation/CoreFoundation.h>
-#import "keychain/SecureObjectSync/SOSAccountTransaction.h"
-
-enum {
-    noError = 0,
-    noTxnorAcct,
-    noAlloc,
-    noTrustedPubKey,
-    noBSKBs,
-};
-
-extern const CFStringRef kSOSBkpInfoStatus;
-extern const CFStringRef kSOSBkpInfoBSKB;
-extern const CFStringRef kSOSBkpInfoRKBG;
-
-CFDictionaryRef SOSBackupInformation(SOSAccountTransaction* txn, CFErrorRef *error);
-
-#endif /* SOSBackupInformation_h */
diff --git a/keychain/SecureObjectSync/SOSBackupInformation.m b/keychain/SecureObjectSync/SOSBackupInformation.m
deleted file mode 100644 (file)
index 8b9b917..0000000
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * Copyright (c) 2016 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@
- */
-
-//
-//  SOSBackupInformation.c
-//  Security
-//
-
-#include "SOSBackupInformation.h"
-#include "SOSAccountPriv.h"
-#include <CoreFoundation/CFNumber.h>
-#include <utilities/SecCFWrappers.h>
-
-const CFStringRef kSOSBkpInfoStatus   = CFSTR("BkpInfoStatus");
-const CFStringRef kSOSBkpInfoBSKB   = CFSTR("BkpInfoBSKB");
-const CFStringRef kSOSBkpInfoRKBG   = CFSTR("BkpInfoRKBG");
-
-CFDictionaryRef SOSBackupInformation(SOSAccountTransaction* txn, CFErrorRef *error) {
-    CFNumberRef status = NULL;
-    int ibkpInfoStatus;
-    __block bool havebskbcontent = false;
-    SOSAccount *a = txn.account;
-
-    CFMutableDictionaryRef retval = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
-    require_action_quiet(txn && a, errOut, ibkpInfoStatus = noTxnorAcct);
-    require_action_quiet(retval, errOut, ibkpInfoStatus = noAlloc);
-    require_action_quiet(txn, errOut, ibkpInfoStatus = noTxnorAcct);
-
-    require_action_quiet(a.accountKey && a.accountKeyIsTrusted, errOut, ibkpInfoStatus = noTrustedPubKey);
-    CFMutableDictionaryRef bskbders = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
-    SOSAccountForEachRing(a, ^SOSRingRef(CFStringRef name, SOSRingRef ring) {
-        if(SOSRingGetType(ring) == kSOSRingBackup) {
-            CFDataRef bskbder = SOSRingGetPayload(ring, NULL);
-            if(bskbder) CFDictionaryAddValue(bskbders, name, bskbder);
-            havebskbcontent = true;
-        } else if(SOSRingGetType(ring) == kSOSRingRecovery) {
-            CFDataRef rkbgder = SOSRingGetPayload(ring, NULL);
-            if(rkbgder) CFDictionaryAddValue(retval, kSOSBkpInfoRKBG, rkbgder);
-        }
-        return NULL; // we're reporting -  never changing the ring
-    });
-    if(havebskbcontent) {
-        ibkpInfoStatus = noError;
-        CFDictionaryAddValue(retval, kSOSBkpInfoBSKB, bskbders);
-    } else {
-        ibkpInfoStatus = noBSKBs;
-    }
-    CFReleaseNull(bskbders);
-    
-errOut:
-    status = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &ibkpInfoStatus);
-    CFDictionaryAddValue(retval, kSOSBkpInfoStatus, status);
-    CFReleaseNull(status);
-    return retval;
-}
index 68d47e37568dede44dddaf17a18592ca6f64788d..f953e859d816c67fc1cf375e422807e8a5794924 100644 (file)
@@ -581,11 +581,6 @@ static inline bool SOSCircleIsDegenerateReset(SOSCircleRef deGenCircle){
     return SOSCircleHasDegenerateGeneration(deGenCircle) && SOSCircleIsEmpty(deGenCircle);
 }
 
     return SOSCircleHasDegenerateGeneration(deGenCircle) && SOSCircleIsEmpty(deGenCircle);
 }
 
-
-__unused static inline bool SOSCircleIsResignOffering(SOSCircleRef circle, SecKeyRef pubkey) {
-    return SOSCircleCountActiveValidPeers(circle, pubkey) == 1;
-}
-
 static inline SOSConcordanceStatus GetSignersStatus(SOSCircleRef signers_circle, SOSCircleRef status_circle,
                                                     SecKeyRef user_pubKey, SOSPeerInfoRef exclude, CFErrorRef *error) {
     CFStringRef excluded_id = exclude ? SOSPeerInfoGetPeerID(exclude) : NULL;
 static inline SOSConcordanceStatus GetSignersStatus(SOSCircleRef signers_circle, SOSCircleRef status_circle,
                                                     SecKeyRef user_pubKey, SOSPeerInfoRef exclude, CFErrorRef *error) {
     CFStringRef excluded_id = exclude ? SOSPeerInfoGetPeerID(exclude) : NULL;
@@ -831,8 +826,9 @@ static CFStringRef SOSCircleCopyFormatDescription(CFTypeRef aObj, CFDictionaryRe
 }
 
 CFStringRef SOSCircleGetName(SOSCircleRef circle) {
 }
 
 CFStringRef SOSCircleGetName(SOSCircleRef circle) {
-    assert(circle);
-    assert(circle->name);
+    if(!circle || !circle->name) {
+        return NULL;
+    }
     return circle->name;
 }
 
     return circle->name;
 }
 
index 46bbae1e83b4a7d21289cb757339896efb85d912..0f3df9b60b6e81dc9d6bf2cc94d73632a889d5ee 100644 (file)
@@ -160,14 +160,6 @@ bool SOSCCRegisterUserCredentials(CFStringRef user_label, CFDataRef user_passwor
 bool SOSCCWaitForInitialSync(CFErrorRef* error);
 bool SOSCCWaitForInitialSyncWithAnalytics(CFDataRef parentEvent, CFErrorRef* error);
 
 bool SOSCCWaitForInitialSync(CFErrorRef* error);
 bool SOSCCWaitForInitialSyncWithAnalytics(CFDataRef parentEvent, CFErrorRef* error);
 
-/*!
- @function SOSCCCopyYetToSyncViewsList
- @abstract returns views not yet synced
- @param error error to fill in if we have one
- @return List of view names that we haven't synced yet.
- */
-CFArrayRef SOSCCCopyYetToSyncViewsList(CFErrorRef* error);
-
 /*!
  @function SOSCCCanAuthenticate
  @abstract Determines whether we currently have valid credentials to authenticate a circle operation.
 /*!
  @function SOSCCCanAuthenticate
  @abstract Determines whether we currently have valid credentials to authenticate a circle operation.
@@ -268,14 +260,6 @@ bool SOSCCRequestToJoinCircleWithAnalytics(CFDataRef parentEvent, CFErrorRef* er
 bool SOSCCRequestToJoinCircleAfterRestore(CFErrorRef* error);
 bool SOSCCRequestToJoinCircleAfterRestoreWithAnalytics(CFDataRef parentEvent, CFErrorRef* error);
 
 bool SOSCCRequestToJoinCircleAfterRestore(CFErrorRef* error);
 bool SOSCCRequestToJoinCircleAfterRestoreWithAnalytics(CFDataRef parentEvent, CFErrorRef* error);
 
-/*!
- @function SOSCCRequestEnsureFreshParameters
- @abstract function to help debug problems with EnsureFreshParameters
- @param error What went wrong if we tried to refresh parameters
- @result true if we successfully retrieved fresh parameters.  False if we failed.
-*/
-bool SOSCCRequestEnsureFreshParameters(CFErrorRef* error);
-
 /*!
  @function SOSCCAccountSetToNew
  @abstract reset account to new
 /*!
  @function SOSCCAccountSetToNew
  @abstract reset account to new
@@ -345,18 +329,6 @@ bool SOSCCLoggedOutOfAccount(CFErrorRef* error);
  */
 bool SOSCCBailFromCircle_BestEffort(uint64_t limit_in_seconds, CFErrorRef* error);
 
  */
 bool SOSCCBailFromCircle_BestEffort(uint64_t limit_in_seconds, CFErrorRef* error);
 
-/*!
- @function SOSCCSignedOut
- @abstract Attempts to publish a retirement ticket for the current device.
- @param immediate If we should remove the device immediately or to leave the circle with best effort.
- @param error What went wrong trying to remove ourselves.
- @result true if we posted the ticket. False if there was an error.
- @discussion This attempts to post a retirement ticket that should
- result in other devices removing this device from the circle.  It does so
- with a 5 second timeout or immediately. 
- */
-bool SOSCCSignedOut(bool immediate, CFErrorRef* error);
-
 /*!
  @function SOSCCCopyApplicantPeerInfo
  @abstract Get the list of peers wishing admittance.
 /*!
  @function SOSCCCopyApplicantPeerInfo
  @abstract Get the list of peers wishing admittance.
@@ -490,14 +462,6 @@ enum DepartureReason SOSCCGetLastDepartureReason(CFErrorRef *error);
 
 bool SOSCCSetLastDepartureReason(enum DepartureReason reason, CFErrorRef *error);
 
 
 bool SOSCCSetLastDepartureReason(enum DepartureReason reason, CFErrorRef *error);
 
-/*!
- @function SOSCCGetIncompatibilityInfo
- @abstract Returns the information (string, hopefully URL) that will lead to an explanation of why you have an incompatible circle.
- @param error What went wrong if we returned NULL.
- */
-CFStringRef SOSCCCopyIncompatibilityInfo(CFErrorRef *error);
-
-
 /*
     Views
     
 /*
     Views
     
@@ -653,34 +617,6 @@ CFDataRef SOSCopyDeviceBackupPublicKey(CFDataRef entropy, CFErrorRef *error);
  */
 bool SOSCCRegisterSingleRecoverySecret(CFDataRef aks_bag, bool forV0Only, CFErrorRef *error);
 
  */
 bool SOSCCRegisterSingleRecoverySecret(CFDataRef aks_bag, bool forV0Only, CFErrorRef *error);
 
-
-/*!
- @function SOSCCIsThisDeviceLastBackup
- @param error Why this query can't be accepted.
- @result true if this is the last backup device, false otherwise.
- */
-
-bool SOSCCIsThisDeviceLastBackup(CFErrorRef *error);
-
-/*!
- @function SOSCCSetEscrowRecord
- @param escrow_label Account label
- @param tries Number of attempts
- @param error What went wrong trying to set the escrow label
- @result true if we saved the escrow record, false if we had an error
- @discussion persist escrow records in the account object or the peer info
- */
-bool SOSCCSetEscrowRecord(CFStringRef escrow_label, uint64_t tries, CFErrorRef *error);
-
-/*!
- @function SOSCCCopyEscrowRecord
- @param error What went wrong trying to set the escrow label
- @result dictionary of the escrow record, false if we had an error, dictionary will be of format: [account label: <dictionary>], dictionary will contain (ex):   "Burned Recovery Attempt Attestation Date" = "[2015-08-19 15:21]";
-                                     "Burned Recovery Attempt Count" = 8;
- @discussion for debugging - retrieve the escrow record
- */
-CFDictionaryRef SOSCCCopyEscrowRecord(CFErrorRef *error);
-
 /*!
  @function SOSCCCopyApplication
  @param error What went wrong getting the applicant peerInfo.
 /*!
  @function SOSCCCopyApplication
  @param error What went wrong getting the applicant peerInfo.
index d1af2e1abcbfc71a8b23607c27518fd4f1117051..a186bb76c64809e481a912de45e45684dcde2ede 100644 (file)
@@ -142,65 +142,6 @@ static bool sfsigninanalytics_bool_error_request(enum SecXPCOperation op, CFData
     return result;
 }
 
     return result;
 }
 
-static bool cfstring_to_error_request(enum SecXPCOperation op, CFStringRef string, CFErrorRef* error)
-{
-    __block bool result = false;
-   
-    secdebug("sosops","enter - operation: %d", op);
-    securityd_send_sync_and_do(op, error, ^bool(xpc_object_t message, CFErrorRef *error) {
-        xpc_object_t xString = _CFXPCCreateXPCObjectFromCFObject(string);
-        bool success = false;
-        if (xString){
-            xpc_dictionary_set_value(message, kSecXPCKeyString, xString);
-            success = true;
-            xString = nil;
-        }
-        return success;
-    }, ^bool(xpc_object_t response, __unused CFErrorRef *error) {
-        result = xpc_dictionary_get_bool(response, kSecXPCKeyResult);
-        return result;
-    });
-    return result;
-}
-
-static SOSRingStatus cfstring_to_uint64_request(enum SecXPCOperation op, CFStringRef string, CFErrorRef* error)
-{
-    __block bool result = false;
-    
-    secdebug("sosops","enter - operation: %d", op);
-    securityd_send_sync_and_do(op, error, ^bool(xpc_object_t message, CFErrorRef *error) {
-        xpc_object_t xString = _CFXPCCreateXPCObjectFromCFObject(string);
-        bool success = false;
-        if (xString){
-            xpc_dictionary_set_value(message, kSecXPCKeyString, xString);
-            success = true;
-            xString = nil;
-        }
-        return success;
-    }, ^bool(xpc_object_t response, __unused CFErrorRef *error) {
-        result = xpc_dictionary_get_int64(response, kSecXPCKeyResult);
-        return result;
-    });
-    return result;
-}
-
-static CFStringRef simple_cfstring_error_request(enum SecXPCOperation op, CFErrorRef* error)
-{
-    __block CFStringRef result = NULL;
-    
-    secdebug("sosops","enter - operation: %d", op);
-    securityd_send_sync_and_do(op, error, NULL, ^bool(xpc_object_t response, __unused CFErrorRef *error) {
-        const char *c_string = xpc_dictionary_get_string(response, kSecXPCKeyResult);
-
-        if (c_string) {
-            result = CFStringCreateWithBytes(kCFAllocatorDefault, (const UInt8*)c_string, strlen(c_string), kCFStringEncodingUTF8, false);
-        }
-        
-        return c_string != NULL;
-    });
-    return result;
-}
-
 static bool simple_bool_error_request(enum SecXPCOperation op, CFErrorRef* error)
 {
     __block bool result = false;
 static bool simple_bool_error_request(enum SecXPCOperation op, CFErrorRef* error)
 {
     __block bool result = false;
@@ -270,33 +211,6 @@ static CFSetRef cfset_cfset_to_cfset_error_request(enum SecXPCOperation op, CFSe
     return result;
 }
 
     return result;
 }
 
-
-static bool escrow_to_bool_error_request(enum SecXPCOperation op, CFStringRef escrow_label, uint64_t tries, CFErrorRef* error)
-{
-    __block bool result = false;
-    
-    securityd_send_sync_and_do(op, error, ^bool(xpc_object_t message, CFErrorRef *error) {
-      
-        bool success = false;
-        xpc_object_t xEscrowLabel = _CFXPCCreateXPCObjectFromCFObject(escrow_label);
-        if (xEscrowLabel){
-            xpc_dictionary_set_value(message, kSecXPCKeyEscrowLabel, xEscrowLabel);
-            success = true;
-            xEscrowLabel = nil;
-        }
-        if(tries){
-            xpc_dictionary_set_int64(message, kSecXPCKeyTriesLabel, tries);
-            success = true;
-        }
-            
-        return success;
-    }, ^bool(xpc_object_t response, __unused CFErrorRef *error) {
-        result = xpc_dictionary_get_bool(response, kSecXPCKeyResult);
-        return result;
-    });
-    return result;
-}
-
 static CF_RETURNS_RETAINED CFArrayRef simple_array_error_request(enum SecXPCOperation op, CFErrorRef* error)
 {
     __block CFArrayRef result = NULL;
 static CF_RETURNS_RETAINED CFArrayRef simple_array_error_request(enum SecXPCOperation op, CFErrorRef* error)
 {
     __block CFArrayRef result = NULL;
@@ -335,28 +249,6 @@ static CF_RETURNS_RETAINED CFArrayRef der_array_error_request(enum SecXPCOperati
     return result;
 }
 
     return result;
 }
 
-static CFDictionaryRef strings_to_dictionary_error_request(enum SecXPCOperation op, CFErrorRef* error)
-{
-    __block CFDictionaryRef result = NULL;
-    
-    secdebug("sosops","enter - operation: %d", op);
-    
-    if (securityd_send_sync_and_do(op, error, NULL, ^bool(xpc_object_t response, CFErrorRef *error) {
-        xpc_object_t temp_result = xpc_dictionary_get_value(response, kSecXPCKeyResult);
-        if(temp_result)
-            result = _CFXPCCreateCFObjectFromXPCObject(temp_result);
-        return result != NULL;
-    })){
-        
-        if (!isDictionary(result)) {
-            SOSErrorCreate(kSOSErrorUnexpectedType, error, NULL, CFSTR("Expected dictionary, got: %@"), result);
-            CFReleaseNull(result);
-        }
-        
-    }
-    return result;
-}
-
 static int simple_int_error_request(enum SecXPCOperation op, CFErrorRef* error)
 {
     __block int result = 0;
 static int simple_int_error_request(enum SecXPCOperation op, CFErrorRef* error)
 {
     __block int result = 0;
@@ -578,7 +470,7 @@ static bool uint64_t_to_bool_error_request(enum SecXPCOperation op,
                                            CFErrorRef* error)
 {
     __block bool result = false;
                                            CFErrorRef* error)
 {
     __block bool result = false;
-    
+
     securityd_send_sync_and_do(op, error, ^bool(xpc_object_t message, CFErrorRef *error) {
         xpc_dictionary_set_uint64(message, kSecXPCLimitInMinutes, number);
         return true;
     securityd_send_sync_and_do(op, error, ^bool(xpc_object_t message, CFErrorRef *error) {
         xpc_dictionary_set_uint64(message, kSecXPCLimitInMinutes, number);
         return true;
@@ -586,46 +478,6 @@ static bool uint64_t_to_bool_error_request(enum SecXPCOperation op,
         result = xpc_dictionary_get_bool(response, kSecXPCKeyResult);
         return result;
     });
         result = xpc_dictionary_get_bool(response, kSecXPCKeyResult);
         return result;
     });
-    
-    return result;
-}
-
-static bool cfstring_and_cfdata_to_cfdata_cfdata_error_request(enum SecXPCOperation op, CFStringRef viewName, CFDataRef input, CFDataRef* data, CFDataRef* data2, CFErrorRef* error) {
-    secdebug("sosops", "enter - operation: %d", op);
-    __block bool result = false;
-    securityd_send_sync_and_do(op, error, ^bool(xpc_object_t message, CFErrorRef *error) {
-        xpc_object_t xviewname = _CFXPCCreateXPCObjectFromCFObject(viewName);
-        xpc_object_t xinput = _CFXPCCreateXPCObjectFromCFObject(input);
-        bool success = false;
-        if (xviewname && xinput){
-            xpc_dictionary_set_value(message, kSecXPCKeyViewName, xviewname);
-            xpc_dictionary_set_value(message, kSecXPCData, xinput);
-            success = true;
-            xviewname = nil;
-            xinput = nil;
-        }
-        return success;
-    }, ^bool(xpc_object_t response, __unused CFErrorRef *error) {
-        result = xpc_dictionary_get_bool(response, kSecXPCKeyResult);
-
-        xpc_object_t temp_result = xpc_dictionary_get_value(response, kSecXPCData);
-        if ((NULL != temp_result) && data) {
-            *data = _CFXPCCreateCFObjectFromXPCObject(temp_result);
-        }
-        temp_result = xpc_dictionary_get_value(response, kSecXPCKeyKeybag);
-        if ((NULL != temp_result) && data2) {
-            *data2 = _CFXPCCreateCFObjectFromXPCObject(temp_result);
-        }
-
-        return result;
-    });
-
-    if (data &&!isData(*data)) {
-        SOSErrorCreate(kSOSErrorUnexpectedType, error, NULL, CFSTR("Expected CFData, got: %@"), *data);
-    }
-    if (data2 &&!isData(*data2)) {
-        SOSErrorCreate(kSOSErrorUnexpectedType, error, NULL, CFSTR("Expected CFData, got: %@"), *data2);
-    }
 
     return result;
 }
 
     return result;
 }
@@ -731,16 +583,6 @@ bool SOSCCAccountHasPublicKey(CFErrorRef *error)
     
 }
 
     
 }
 
-bool SOSCCAccountIsNew(CFErrorRef *error)
-{
-    sec_trace_enter_api(NULL);
-    sec_trace_return_bool_api(^{
-        do_if_registered(soscc_AccountIsNew, error);
-        
-        return simple_bool_error_request(kSecXPCOpAccountIsNew, error);
-    }, NULL)
-}
-
 bool SOSCCWaitForInitialSyncWithAnalytics(CFDataRef parentEvent, CFErrorRef* error)
 {
     sec_trace_enter_api(NULL);
 bool SOSCCWaitForInitialSyncWithAnalytics(CFDataRef parentEvent, CFErrorRef* error)
 {
     sec_trace_enter_api(NULL);
@@ -761,75 +603,6 @@ bool SOSCCWaitForInitialSync(CFErrorRef* error)
     }, NULL)
 }
 
     }, NULL)
 }
 
-CFArrayRef SOSCCCopyYetToSyncViewsList(CFErrorRef* error)
-{
-    sec_trace_enter_api(NULL);
-    sec_trace_return_api(CFArrayRef, ^{
-        do_if_registered(soscc_CopyYetToSyncViewsList, error);
-
-        return simple_array_error_request(kSecXPCOpCopyYetToSyncViews, error);
-    }, NULL)
-}
-
-bool SOSCCRequestEnsureFreshParameters(CFErrorRef* error)
-{
-    sec_trace_enter_api(NULL);
-    sec_trace_return_bool_api(^{
-        do_if_registered(soscc_RequestEnsureFreshParameters, error);
-        
-        return simple_bool_error_request(kSecXPCOpRequestEnsureFreshParameters, error);
-    }, NULL)
-}
-
-CFStringRef SOSCCGetAllTheRings(CFErrorRef *error){
-    sec_trace_enter_api(NULL);
-    sec_trace_return_api(CFStringRef, ^{
-        do_if_registered(soscc_GetAllTheRings, error);
-        
-        
-        return simple_cfstring_error_request(kSecXPCOpGetAllTheRings, error);
-    }, NULL)
-}
-bool SOSCCApplyToARing(CFStringRef ringName, CFErrorRef* error)
-{
-    sec_trace_enter_api(NULL);
-    sec_trace_return_bool_api(^{
-        do_if_registered(soscc_ApplyToARing, ringName, error);
-        
-        return cfstring_to_error_request(kSecXPCOpApplyToARing, ringName, error);
-    }, NULL)
-}
-
-bool SOSCCWithdrawlFromARing(CFStringRef ringName, CFErrorRef* error)
-{
-    sec_trace_enter_api(NULL);
-    sec_trace_return_bool_api(^{
-        do_if_registered(soscc_WithdrawlFromARing, ringName, error);
-        
-        return cfstring_to_error_request(kSecXPCOpWithdrawlFromARing, ringName, error);
-    }, NULL)
-}
-
-SOSRingStatus SOSCCRingStatus(CFStringRef ringName, CFErrorRef* error)
-{
-    sec_trace_enter_api(NULL);
-    sec_trace_return_api(SOSRingStatus, ^{
-        do_if_registered(soscc_RingStatus, ringName, error);
-        
-        return cfstring_to_uint64_request(kSecXPCOpRingStatus, ringName, error);
-    }, CFSTR("SOSCCStatus=%d"))
-}
-
-bool SOSCCEnableRing(CFStringRef ringName, CFErrorRef* error)
-{
-    sec_trace_enter_api(NULL);
-    sec_trace_return_bool_api(^{
-        do_if_registered(soscc_EnableRing, ringName, error);
-        
-        return cfstring_to_error_request(kSecXPCOpEnableRing, ringName, error);
-    }, NULL)
-}
-
 bool SOSCCAccountSetToNew(CFErrorRef *error)
 {
        secwarning("SOSCCAccountSetToNew called");
 bool SOSCCAccountSetToNew(CFErrorRef *error)
 {
        secwarning("SOSCCAccountSetToNew called");
@@ -927,22 +700,11 @@ bool SOSCCBailFromCircle_BestEffort(uint64_t limit_in_seconds, CFErrorRef* error
     sec_trace_enter_api(NULL);
     sec_trace_return_bool_api(^{
         do_if_registered(soscc_BailFromCircle, limit_in_seconds, error);
     sec_trace_enter_api(NULL);
     sec_trace_return_bool_api(^{
         do_if_registered(soscc_BailFromCircle, limit_in_seconds, error);
-        
+
         return uint64_t_to_bool_error_request(kSecXPCOpBailFromCircle, limit_in_seconds, error);
     }, NULL)
 }
 
         return uint64_t_to_bool_error_request(kSecXPCOpBailFromCircle, limit_in_seconds, error);
     }, NULL)
 }
 
-bool SOSCCSignedOut(bool immediate, CFErrorRef* error)
-{
-    uint64_t limit = strtoul(optarg, NULL, 10);
-    
-    if(immediate)
-        return SOSCCRemoveThisDeviceFromCircle(error);
-    else
-        return SOSCCBailFromCircle_BestEffort(limit, error);
-    
-}
-
 CFArrayRef SOSCCCopyPeerPeerInfo(CFErrorRef* error)
 {
     sec_trace_enter_api(NULL);
 CFArrayRef SOSCCCopyPeerPeerInfo(CFErrorRef* error)
 {
     sec_trace_enter_api(NULL);
@@ -1032,43 +794,6 @@ CFArrayRef SOSCCCopyViewUnawarePeerInfo(CFErrorRef* error)
     }, CFSTR("return=%@"));
 }
 
     }, CFSTR("return=%@"));
 }
 
-CFDataRef SOSCCCopyAccountState(CFErrorRef* error)
-{
-    sec_trace_enter_api(NULL);
-    sec_trace_return_api(CFDataRef, ^{
-        do_if_registered(soscc_CopyAccountState, error);
-        
-        return data_to_error_request(kSecXPCOpCopyAccountData, error);
-    }, CFSTR("return=%@"));
-}
-
-bool SOSCCDeleteAccountState(CFErrorRef *error)
-{
-    sec_trace_enter_api(NULL);
-    sec_trace_return_api(bool, ^{
-        do_if_registered(soscc_DeleteAccountState, error);
-        return simple_bool_error_request(kSecXPCOpDeleteAccountData, error);
-    }, NULL);
-}
-CFDataRef SOSCCCopyEngineData(CFErrorRef* error)
-{
-    sec_trace_enter_api(NULL);
-    sec_trace_return_api(CFDataRef, ^{
-        do_if_registered(soscc_CopyEngineData, error);
-        
-        return data_to_error_request(kSecXPCOpCopyEngineData, error);
-    }, CFSTR("return=%@"));
-}
-
-bool SOSCCDeleteEngineState(CFErrorRef *error)
-{
-    sec_trace_enter_api(NULL);
-    sec_trace_return_api(bool, ^{
-        do_if_registered(soscc_DeleteEngineState, error);
-        return simple_bool_error_request(kSecXPCOpDeleteEngineData, error);
-    }, NULL);
-}
-
 SOSPeerInfoRef SOSCCCopyMyPeerInfo(CFErrorRef *error)
 {
     sec_trace_enter_api(NULL);
 SOSPeerInfoRef SOSCCCopyMyPeerInfo(CFErrorRef *error)
 {
     sec_trace_enter_api(NULL);
@@ -1412,15 +1137,6 @@ bool SOSCCSetLastDepartureReason(enum DepartureReason reason, CFErrorRef *error)
        }, NULL)
 }
 
        }, NULL)
 }
 
-CFStringRef SOSCCCopyIncompatibilityInfo(CFErrorRef* error) {
-    sec_trace_enter_api(NULL);
-    sec_trace_return_api(CFStringRef, ^{
-           do_if_registered(soscc_CopyIncompatibilityInfo, error);
-        
-           return simple_cfstring_error_request(kSecXPCOpCopyIncompatibilityInfo, error);
-    }, NULL)
-}
-
 bool SOSCCProcessEnsurePeerRegistration(CFErrorRef* error){
     secnotice("updates", "enter SOSCCProcessEnsurePeerRegistration");
     sec_trace_enter_api(NULL);
 bool SOSCCProcessEnsurePeerRegistration(CFErrorRef* error){
     secnotice("updates", "enter SOSCCProcessEnsurePeerRegistration");
     sec_trace_enter_api(NULL);
@@ -1667,47 +1383,6 @@ bool SOSCCIsContinuityUnlockSyncing(void) {
     return sosIsViewSetSyncing(sizeof(views)/sizeof(views[0]), views);
 }
 
     return sosIsViewSetSyncing(sizeof(views)/sizeof(views[0]), views);
 }
 
-
-bool SOSCCSetEscrowRecord(CFStringRef escrow_label, uint64_t tries, CFErrorRef *error ){
-    secnotice("escrow", "enter SOSCCSetEscrowRecord");
-    sec_trace_enter_api(NULL);
-    sec_trace_return_bool_api(^{
-        do_if_registered(soscc_SetEscrowRecords, escrow_label, tries, error);
-        
-        return escrow_to_bool_error_request(kSecXPCOpSetEscrowRecord, escrow_label, tries, error);
-    }, NULL)
-}
-
-CFDictionaryRef SOSCCCopyEscrowRecord(CFErrorRef *error){
-    secnotice("escrow", "enter SOSCCCopyEscrowRecord");
-    sec_trace_enter_api(NULL);
-    sec_trace_return_api(CFDictionaryRef, ^{
-        do_if_registered(soscc_CopyEscrowRecords, error);
-        
-        return strings_to_dictionary_error_request(kSecXPCOpGetEscrowRecord, error);
-    }, CFSTR("return=%@"))
-
-}
-
-CFDictionaryRef SOSCCCopyBackupInformation(CFErrorRef *error) {
-    secnotice("escrow", "enter SOSCCCopyBackupInformation");
-    sec_trace_enter_api(NULL);
-    sec_trace_return_api(CFDictionaryRef, ^{
-        do_if_registered(soscc_CopyBackupInformation, error);
-        return strings_to_dictionary_error_request(kSecXPCOpCopyBackupInformation, error);
-    }, CFSTR("return=%@"))
-}
-
-bool SOSWrapToBackupSliceKeyBagForView(CFStringRef viewName, CFDataRef input, CFDataRef* output, CFDataRef* bskbEncoded, CFErrorRef* error) {
-    sec_trace_enter_api(NULL);
-    sec_trace_return_bool_api(^{
-        do_if_registered(sosbskb_WrapToBackupSliceKeyBagForView, viewName, input, output, bskbEncoded, error);
-
-        return cfstring_and_cfdata_to_cfdata_cfdata_error_request(kSecXPCOpWrapToBackupSliceKeyBagForView, viewName, input, output, bskbEncoded, error);
-    }, NULL)
-}
-
-
 SOSPeerInfoRef SOSCCCopyApplication(CFErrorRef *error) {
     secnotice("hsa2PB", "enter SOSCCCopyApplication applicant");
     sec_trace_enter_api(NULL);
 SOSPeerInfoRef SOSCCCopyApplication(CFErrorRef *error) {
     secnotice("hsa2PB", "enter SOSCCCopyApplication applicant");
     sec_trace_enter_api(NULL);
@@ -1730,18 +1405,6 @@ bool SOSCCCleanupKVSKeys(CFErrorRef *error) {
     return false;
 }
 
     return false;
 }
 
-bool SOSCCTestPopulateKVSWithBadKeys(CFErrorRef *error) {
-    secnotice("cleanup-keys", "enter SOSCCPopulateKVSWithBadKeys");
-    sec_trace_enter_api(NULL);
-    sec_trace_return_bool_api(^{
-        do_if_registered(soscc_SOSCCTestPopulateKVSWithBadKeys, error);
-        
-        return simple_bool_error_request(kSecXPCOpPopulateKVS, error);
-    }, NULL)
-    
-    return false;
-}
-
 CFDataRef SOSCCCopyCircleJoiningBlob(SOSPeerInfoRef applicant, CFErrorRef *error) {
     secnotice("hsa2PB", "enter SOSCCCopyCircleJoiningBlob approver");
     sec_trace_enter_api(NULL);
 CFDataRef SOSCCCopyCircleJoiningBlob(SOSPeerInfoRef applicant, CFErrorRef *error) {
     secnotice("hsa2PB", "enter SOSCCCopyCircleJoiningBlob approver");
     sec_trace_enter_api(NULL);
@@ -1776,16 +1439,6 @@ bool SOSCCJoinWithCircleJoiningBlob(CFDataRef joiningBlob, PiggyBackProtocolVers
     }, NULL)
 }
 
     }, NULL)
 }
 
-bool SOSCCIsThisDeviceLastBackup(CFErrorRef *error) {
-    secnotice("peer", "enter SOSCCIsThisDeviceLastBackup");
-    sec_trace_enter_api(NULL);
-    sec_trace_return_bool_api(^{
-        do_if_registered(soscc_IsThisDeviceLastBackup, error);
-        
-        return simple_bool_error_request(kSecXPCOpIsThisDeviceLastBackup, error);
-    }, NULL)
-}
-
 CFBooleanRef SOSCCPeersHaveViewsEnabled(CFArrayRef viewNames, CFErrorRef *error) {
     secnotice("view-enabled", "enter SOSCCPeersHaveViewsEnabled");
     sec_trace_enter_api(NULL);
 CFBooleanRef SOSCCPeersHaveViewsEnabled(CFArrayRef viewNames, CFErrorRef *error) {
     secnotice("view-enabled", "enter SOSCCPeersHaveViewsEnabled");
     sec_trace_enter_api(NULL);
index 36e5dfbd6ab693da5e8d2f97e554ac675359ae38..b174896fb48b4dd5599911755a38b9046470fb81 100644 (file)
@@ -56,7 +56,6 @@ bool SOSCCPurgeUserCredentials(CFErrorRef* error);
 CFStringRef SOSCCGetStatusDescription(SOSCCStatus status);
 CFStringRef SOSCCGetViewResultDescription(SOSViewResultCode vrc);
 bool SOSCCAccountHasPublicKey(CFErrorRef *error);
 CFStringRef SOSCCGetStatusDescription(SOSCCStatus status);
 CFStringRef SOSCCGetViewResultDescription(SOSViewResultCode vrc);
 bool SOSCCAccountHasPublicKey(CFErrorRef *error);
-bool SOSCCAccountIsNew(CFErrorRef *error);
 
 /*!
  @function SOSCCProcessSyncWithPeers
 
 /*!
  @function SOSCCProcessSyncWithPeers
@@ -75,14 +74,6 @@ SyncWithAllPeersReason SOSCCProcessSyncWithAllPeers(CFErrorRef* error);
 
 bool SOSCCProcessEnsurePeerRegistration(CFErrorRef* error);
 
 
 bool SOSCCProcessEnsurePeerRegistration(CFErrorRef* error);
 
-//Rings
-CFStringRef SOSCCGetAllTheRings(CFErrorRef *error);
-
-bool SOSCCApplyToARing(CFStringRef ringName, CFErrorRef* error);
-bool SOSCCWithdrawlFromARing(CFStringRef ringName, CFErrorRef* error);
-int SOSCCRingStatus(CFStringRef ringName, CFErrorRef* error);   // TODO: this returns SOSRingStatus
-bool SOSCCEnableRing(CFStringRef ringName, CFErrorRef* error);
-
 bool SOSCCCleanupKVSKeys(CFErrorRef *error);
 
 
 bool SOSCCCleanupKVSKeys(CFErrorRef *error);
 
 
@@ -93,27 +84,10 @@ bool SOSCCCleanupKVSKeys(CFErrorRef *error);
  */
 SOSPeerInfoRef SOSCCCopyMyPeerInfo(CFErrorRef *error);
 
  */
 SOSPeerInfoRef SOSCCCopyMyPeerInfo(CFErrorRef *error);
 
-/*!
- @function SOSWrapToBackupSliceKeyBagForView
- @abstract Encrypts the given plaintext, and wraps the encryption key to the backup slice keybag for this view
- @param viewName The view to wrap to
- @param input The plaintext to encrypt
- @param output The ciphertext
- @param bskbEncoded The encoded backup slice keybag used to wrap the data
- @param error What went wrong if we returned false
- */
-bool SOSWrapToBackupSliceKeyBagForView(CFStringRef viewName, CFDataRef input, CFDataRef* output, CFDataRef* bskbEncoded, CFErrorRef* error);
-
 //
 // Security Tool calls
 //
 //
 // Security Tool calls
 //
-CFDataRef SOSCCCopyAccountState(CFErrorRef* error);
-bool SOSCCDeleteAccountState(CFErrorRef *error);
-CFDataRef SOSCCCopyEngineData(CFErrorRef* error);
-bool SOSCCDeleteEngineState(CFErrorRef *error);
 CFDataRef SOSCCCopyRecoveryPublicKey(CFErrorRef *error);
 CFDataRef SOSCCCopyRecoveryPublicKey(CFErrorRef *error);
-CFDictionaryRef SOSCCCopyBackupInformation(CFErrorRef *error);
-bool SOSCCTestPopulateKVSWithBadKeys(CFErrorRef *error);
 CFDataRef SOSCCCopyInitialSyncData(SOSInitialSyncFlags flags, CFErrorRef *error);
 
 void SOSCCForEachEngineStateAsStringFromArray(CFArrayRef states, void (^block)(CFStringRef oneStateString));
 CFDataRef SOSCCCopyInitialSyncData(SOSInitialSyncFlags flags, CFErrorRef *error);
 
 void SOSCCForEachEngineStateAsStringFromArray(CFArrayRef states, void (^block)(CFStringRef oneStateString));
index aaf58c28cb1f1aeeeba48602d18787f379029aec..d700b69e3749e035f3f8a872f61ebe4487c7b5a5 100644 (file)
@@ -62,10 +62,9 @@ _SOSControlSetupInterface(NSXPCInterface *interface)
     [interface setClasses:errClasses forSelector:@selector(joinCircleWithBlob:version:complete:) argumentIndex:1 ofReply:YES];
     [interface setClasses:errClasses forSelector:@selector(initialSyncCredentials:complete:) argumentIndex:1 ofReply:YES];
     [interface setClasses:errClasses forSelector:@selector(importInitialSyncCredentials:complete:) argumentIndex:1 ofReply:YES];
     [interface setClasses:errClasses forSelector:@selector(joinCircleWithBlob:version:complete:) argumentIndex:1 ofReply:YES];
     [interface setClasses:errClasses forSelector:@selector(initialSyncCredentials:complete:) argumentIndex:1 ofReply:YES];
     [interface setClasses:errClasses forSelector:@selector(importInitialSyncCredentials:complete:) argumentIndex:1 ofReply:YES];
+    [interface setClasses:errClasses forSelector:@selector(triggerSync:complete:) argumentIndex:1 ofReply:YES];
     [interface setClasses:errClasses forSelector:@selector(getWatchdogParameters:) argumentIndex:1 ofReply:YES];
     [interface setClasses:errClasses forSelector:@selector(setWatchdogParmeters:complete:) argumentIndex:0 ofReply:YES];
     [interface setClasses:errClasses forSelector:@selector(ghostBust:complete:) argumentIndex:1 ofReply:YES];
     [interface setClasses:errClasses forSelector:@selector(getWatchdogParameters:) argumentIndex:1 ofReply:YES];
     [interface setClasses:errClasses forSelector:@selector(setWatchdogParmeters:complete:) argumentIndex:0 ofReply:YES];
     [interface setClasses:errClasses forSelector:@selector(ghostBust:complete:) argumentIndex:1 ofReply:YES];
-    [interface setClasses:errClasses forSelector:@selector(rpcTriggerSync:complete:) argumentIndex:1 ofReply:YES];
-    [interface setClasses:errClasses forSelector:@selector(rpcTriggerBackup:complete:) argumentIndex:0 ofReply:YES];
-    [interface setClasses:errClasses forSelector:@selector(rpcTriggerRingUpdate:) argumentIndex:0 ofReply:YES];
+    [interface setClasses:errClasses forSelector:@selector(triggerBackup:complete:) argumentIndex:0 ofReply:YES];
 }
 }
index 5f8a748d97d8af95d444b10dcb277382669b7a9b..46332efa14ac77568bf85cbb5c1899884efe4e4c 100644 (file)
     [self.account importInitialSyncCredentials:items complete:complete];
 }
 
     [self.account importInitialSyncCredentials:items complete:complete];
 }
 
-- (void)rpcTriggerSync:(NSArray <NSString *> *)peers complete:(void(^)(bool success, NSError *))complete
+- (void)triggerSync:(NSArray <NSString *> *)peers complete:(void(^)(bool success, NSError *))complete
 {
 {
-    [self.account rpcTriggerSync:peers complete:complete];
+    if (![self checkEntitlement:(__bridge NSString *)kSecEntitlementKeychainCloudCircle]) {
+        complete(false, [NSError errorWithDomain:(__bridge NSString *)kSOSErrorDomain code:kSOSEntitlementMissing userInfo:NULL]);
+        return;
+    }
+
+    [self.account triggerSync:peers complete:complete];
 }
 
 - (void)getWatchdogParameters:(void (^)(NSDictionary* parameters, NSError* error))complete
 }
 
 - (void)getWatchdogParameters:(void (^)(NSDictionary* parameters, NSError* error))complete
     [self.account ghostBustInfo:complete];
 }
 
     [self.account ghostBustInfo:complete];
 }
 
-- (void)rpcTriggerBackup:(NSArray<NSString *>* _Nullable)backupPeers complete:(void (^)(NSError *error))complete
+- (void)triggerBackup:(NSArray<NSString *>* _Nullable)backupPeers complete:(void (^)(NSError *error))complete
 {
 {
-    [self.account rpcTriggerBackup:backupPeers complete:complete];
+    [self.account triggerBackup:backupPeers complete:complete];
 }
 
 }
 
-- (void)rpcTriggerRingUpdate:(void (^)(NSError *))complete {
-    [self.account rpcTriggerRingUpdate:complete];
-}
 
 @end
 
 
 @end
 
index 66d25d5eba843f92ab7fc84c19e10c397510c7a8..4417dd34b64408179c64bc684efc73a49526f599 100644 (file)
@@ -10,20 +10,14 @@ _SOSCCAccountGetAccountPrivateCredential
 _SOSCCAccountGetPublicKey
 _SOSCCAccountGetKeyCircleGeneration
 _SOSCCAccountHasPublicKey
 _SOSCCAccountGetPublicKey
 _SOSCCAccountGetKeyCircleGeneration
 _SOSCCAccountHasPublicKey
-_SOSCCAccountIsNew
 _SOSCCAccountSetToNew
 _SOSCCBailFromCircle_BestEffort
 _SOSCCCanAuthenticate
 _SOSCCAccountSetToNew
 _SOSCCBailFromCircle_BestEffort
 _SOSCCCanAuthenticate
-_SOSCCCopyAccountState
 _SOSCCCopyApplicantPeerInfo
 _SOSCCCopyApplication
 _SOSCCCopyApplicantPeerInfo
 _SOSCCCopyApplication
-_SOSCCCopyBackupInformation
 _SOSCCCopyCircleJoiningBlob
 _SOSCCCopyConcurringPeerPeerInfo
 _SOSCCCopyCircleJoiningBlob
 _SOSCCCopyConcurringPeerPeerInfo
-_SOSCCCopyEngineData
-_SOSCCCopyEscrowRecord
 _SOSCCCopyGenerationPeerInfo
 _SOSCCCopyGenerationPeerInfo
-_SOSCCCopyIncompatibilityInfo
 _SOSCCCopyMyPeerInfo
 _SOSCCCopyMyPeerWithNewDeviceRecoverySecret
 _SOSCCCopyNotValidPeerPeerInfo
 _SOSCCCopyMyPeerInfo
 _SOSCCCopyMyPeerWithNewDeviceRecoverySecret
 _SOSCCCopyNotValidPeerPeerInfo
@@ -32,11 +26,7 @@ _SOSCCCopyRecoveryPublicKey
 _SOSCCCopyRetirementPeerInfo
 _SOSCCCopyValidPeerPeerInfo
 _SOSCCCopyViewUnawarePeerInfo
 _SOSCCCopyRetirementPeerInfo
 _SOSCCCopyValidPeerPeerInfo
 _SOSCCCopyViewUnawarePeerInfo
-_SOSCCCopyYetToSyncViewsList
-_SOSCCDeleteAccountState
-_SOSCCDeleteEngineState
 _SOSCCCleanupKVSKeys
 _SOSCCCleanupKVSKeys
-_SOSCCTestPopulateKVSWithBadKeys
 _SOSCCForEachEngineStateAsString
 _SOSCCForEachEngineStateAsStringFromArray
 _SOSCCGetLastDepartureReason
 _SOSCCForEachEngineStateAsString
 _SOSCCForEachEngineStateAsStringFromArray
 _SOSCCGetLastDepartureReason
@@ -64,7 +54,6 @@ _SOSCCRemovePeersFromCircle
 _SOSCCRemovePeersFromCircleWithAnalytics
 _SOSCCRemoveThisDeviceFromCircle
 _SOSCCRemoveThisDeviceFromCircleWithAnalytics
 _SOSCCRemovePeersFromCircleWithAnalytics
 _SOSCCRemoveThisDeviceFromCircle
 _SOSCCRemoveThisDeviceFromCircleWithAnalytics
-_SOSCCRequestEnsureFreshParameters
 _SOSCCRequestToJoinCircle
 _SOSCCRequestToJoinCircleWithAnalytics
 _SOSCCRequestToJoinCircleAfterRestore
 _SOSCCRequestToJoinCircle
 _SOSCCRequestToJoinCircleWithAnalytics
 _SOSCCRequestToJoinCircleAfterRestore
@@ -73,12 +62,10 @@ _SOSCCResetToEmpty
 _SOSCCResetToEmptyWithAnalytics
 _SOSCCResetToOffering
 _SOSCCSendToPeerIsPending
 _SOSCCResetToEmptyWithAnalytics
 _SOSCCResetToOffering
 _SOSCCSendToPeerIsPending
-_SOSCCSetEscrowRecord
 _SOSCCSetLastDepartureReason
 _SOSCCSetUserCredentials
 _SOSCCSetUserCredentialsAndDSID
 _SOSCCSetUserCredentialsAndDSIDWithAnalytics
 _SOSCCSetLastDepartureReason
 _SOSCCSetUserCredentials
 _SOSCCSetUserCredentialsAndDSID
 _SOSCCSetUserCredentialsAndDSIDWithAnalytics
-_SOSCCSignedOut
 _SOSCCThisDeviceIsInCircle
 _SOSCCThisDeviceIsInCircleNonCached
 _SOSCCTryUserCredentials
 _SOSCCThisDeviceIsInCircle
 _SOSCCThisDeviceIsInCircleNonCached
 _SOSCCTryUserCredentials
@@ -126,14 +113,12 @@ _SOSPeerInfoCopyBackupKey
 _SOSPeerInfoCopyDeviceID
 _SOSPeerInfoCopyEnabledViews
 _SOSPeerInfoCopyEncodedData
 _SOSPeerInfoCopyDeviceID
 _SOSPeerInfoCopyEnabledViews
 _SOSPeerInfoCopyEncodedData
-_SOSPeerInfoCopyEscrowRecord
 _SOSPeerInfoCopyOctagonSigningPublicKey
 _SOSPeerInfoCopyOctagonEncryptionPublicKey
 _SOSPeerInfoCopyPeerGestalt
 _SOSPeerInfoCopyPubKey
 _SOSPeerInfoCopyTransportType
 _SOSPeerInfoCopyWithBackupKeyUpdate
 _SOSPeerInfoCopyOctagonSigningPublicKey
 _SOSPeerInfoCopyOctagonEncryptionPublicKey
 _SOSPeerInfoCopyPeerGestalt
 _SOSPeerInfoCopyPubKey
 _SOSPeerInfoCopyTransportType
 _SOSPeerInfoCopyWithBackupKeyUpdate
-_SOSPeerInfoCopyWithEscrowRecordUpdate
 _SOSPeerInfoCopyWithGestaltUpdate
 _SOSPeerInfoCopyWithPing
 _SOSPeerInfoCopyWithReplacedEscrowRecords
 _SOSPeerInfoCopyWithGestaltUpdate
 _SOSPeerInfoCopyWithPing
 _SOSPeerInfoCopyWithReplacedEscrowRecords
@@ -189,13 +174,6 @@ _SOSFullPeerInfoGetPeerInfo
 _SOSCircleAcceptPeerFromHSA2
 _SOSFullPeerInfoUpdate
 
 _SOSCircleAcceptPeerFromHSA2
 _SOSFullPeerInfoUpdate
 
-_SOSCCGetAllTheRings
-_SOSCCApplyToARing
-_SOSCCWithdrawlFromARing
-_SOSCCRingStatus
-_SOSCCEnableRing
-_SOSCCIsThisDeviceLastBackup
-
 _SOSCloudKeychainRemoveKeys
 
 _SOSCloudTransportSetDefaultTransport
 _SOSCloudKeychainRemoveKeys
 
 _SOSCloudTransportSetDefaultTransport
@@ -246,7 +224,6 @@ _der_encode_BackupSliceKeyBag
 _der_sizeof_BackupSliceKeyBag
 _bskbRkbgPrefix
 
 _der_sizeof_BackupSliceKeyBag
 _bskbRkbgPrefix
 
-_SOSWrapToBackupSliceKeyBagForView
 _SOSBSKBHasRecoveryKey
 _SOSBSKBHasThisRecoveryKey
 
 _SOSBSKBHasRecoveryKey
 _SOSBSKBHasThisRecoveryKey
 
@@ -415,7 +392,6 @@ _SOSCircleVerifyPeerSignatureExists
 _SOSCircleWithdrawRequest
 _debugDumpCircle
 
 _SOSCircleWithdrawRequest
 _debugDumpCircle
 
-_SOSFullPeerInfoAddEscrowRecord
 _SOSFullPeerInfoCopyDeviceKey
 _SOSFullPeerInfoCopyEncodedData
 _SOSFullPeerInfoCopyFullPeerInfo
 _SOSFullPeerInfoCopyDeviceKey
 _SOSFullPeerInfoCopyEncodedData
 _SOSFullPeerInfoCopyFullPeerInfo
index 2d265c6e0aab4c3d4a61d9a17b9d1340f7148f9b..dce3a3f8e954290a9d4a24788bc5d26db23ba3b3 100644 (file)
@@ -80,8 +80,6 @@ bool SOSFullPeerInfoUpdateV2Dictionary(SOSFullPeerInfoRef peer, CFDictionaryRef
 
 bool SOSFullPeerInfoUpdateBackupKey(SOSFullPeerInfoRef peer, CFDataRef backupKey, CFErrorRef* error);
 
 
 bool SOSFullPeerInfoUpdateBackupKey(SOSFullPeerInfoRef peer, CFDataRef backupKey, CFErrorRef* error);
 
-bool SOSFullPeerInfoAddEscrowRecord(SOSFullPeerInfoRef peer, CFStringRef dsid, CFDictionaryRef escrowRecord, CFErrorRef* error);
-
 bool SOSFullPeerInfoReplaceEscrowRecords(SOSFullPeerInfoRef peer, CFDictionaryRef escrowRecords, CFErrorRef* error);
 
 bool SOSFullPeerInfoUpdateToCurrent(SOSFullPeerInfoRef peer, CFSetRef minimumViews, CFSetRef excludedViews);
 bool SOSFullPeerInfoReplaceEscrowRecords(SOSFullPeerInfoRef peer, CFDictionaryRef escrowRecords, CFErrorRef* error);
 
 bool SOSFullPeerInfoUpdateToCurrent(SOSFullPeerInfoRef peer, CFSetRef minimumViews, CFSetRef excludedViews);
index 7b2bee13609520cc2b6c78e1902265c7fa7c9fce..adea27880f366c513dd08c2895fadcff17820bb9 100644 (file)
@@ -354,13 +354,6 @@ bool SOSFullPeerInfoUpdateBackupKey(SOSFullPeerInfoRef peer, CFDataRef backupKey
     });
 }
 
     });
 }
 
-bool SOSFullPeerInfoAddEscrowRecord(SOSFullPeerInfoRef peer, CFStringRef dsid, CFDictionaryRef escrowRecord, CFErrorRef* error)
-{
-    return SOSFullPeerInfoUpdate(peer, error, ^SOSPeerInfoRef(SOSPeerInfoRef peer, SecKeyRef key, CFErrorRef *error) {
-        return SOSPeerInfoCopyWithEscrowRecordUpdate(kCFAllocatorDefault, peer, dsid, escrowRecord, key, error);
-    });
-}
-
 bool SOSFullPeerInfoReplaceEscrowRecords(SOSFullPeerInfoRef peer, CFDictionaryRef escrowRecords, CFErrorRef* error)
 {
     return SOSFullPeerInfoUpdate(peer, error, ^SOSPeerInfoRef(SOSPeerInfoRef peer, SecKeyRef key, CFErrorRef *error) {
 bool SOSFullPeerInfoReplaceEscrowRecords(SOSFullPeerInfoRef peer, CFDictionaryRef escrowRecords, CFErrorRef* error)
 {
     return SOSFullPeerInfoUpdate(peer, error, ^SOSPeerInfoRef(SOSPeerInfoRef peer, SecKeyRef key, CFErrorRef *error) {
index 253891782e0a0b248404e0970367d18083751250..0e6579b4ea8bb0bd909978acaefca600f5cd325e 100644 (file)
@@ -77,7 +77,6 @@ bool SOSPeerInfoVersionIsCurrent(SOSPeerInfoRef pi);
 bool SOSPeerInfoVersionHasV2Data(SOSPeerInfoRef pi);
 SOSPeerInfoRef SOSPeerInfoCopyWithGestaltUpdate(CFAllocatorRef allocator, SOSPeerInfoRef toCopy, CFDictionaryRef gestalt, SecKeyRef signingKey, CFErrorRef* error);
 SOSPeerInfoRef SOSPeerInfoCopyWithBackupKeyUpdate(CFAllocatorRef allocator, SOSPeerInfoRef toCopy, CFDataRef backupKey, SecKeyRef signingKey, CFErrorRef* error);
 bool SOSPeerInfoVersionHasV2Data(SOSPeerInfoRef pi);
 SOSPeerInfoRef SOSPeerInfoCopyWithGestaltUpdate(CFAllocatorRef allocator, SOSPeerInfoRef toCopy, CFDictionaryRef gestalt, SecKeyRef signingKey, CFErrorRef* error);
 SOSPeerInfoRef SOSPeerInfoCopyWithBackupKeyUpdate(CFAllocatorRef allocator, SOSPeerInfoRef toCopy, CFDataRef backupKey, SecKeyRef signingKey, CFErrorRef* error);
-SOSPeerInfoRef SOSPeerInfoCopyWithEscrowRecordUpdate(CFAllocatorRef allocator, SOSPeerInfoRef toCopy, CFStringRef dsid, CFDictionaryRef escrowRecord, SecKeyRef signingKey, CFErrorRef *error);
 SOSPeerInfoRef SOSPeerInfoCopyWithReplacedEscrowRecords(CFAllocatorRef allocator, SOSPeerInfoRef toCopy, CFDictionaryRef escrowRecords, SecKeyRef signingKey, CFErrorRef *error);
 
 
 SOSPeerInfoRef SOSPeerInfoCopyWithReplacedEscrowRecords(CFAllocatorRef allocator, SOSPeerInfoRef toCopy, CFDictionaryRef escrowRecords, SecKeyRef signingKey, CFErrorRef *error);
 
 
@@ -104,7 +103,6 @@ CF_RETURNS_RETAINED CFDateRef SOSPeerInfoGetApplicationDate(SOSPeerInfoRef pi);
 //
 bool SOSPeerInfoHasBackupKey(SOSPeerInfoRef peer);
 CFDataRef SOSPeerInfoCopyBackupKey(SOSPeerInfoRef peer);
 //
 bool SOSPeerInfoHasBackupKey(SOSPeerInfoRef peer);
 CFDataRef SOSPeerInfoCopyBackupKey(SOSPeerInfoRef peer);
-CFMutableDictionaryRef SOSPeerInfoCopyEscrowRecord(SOSPeerInfoRef peer);
 
 //
 // DER Import Export
 
 //
 // DER Import Export
index f75c33216a9372f9e65632fe2921fb460fba6a68..915cc40376b2055ee4b5f0b45525232e6eff7647 100644 (file)
@@ -456,36 +456,6 @@ SOSPeerInfoRef SOSPeerInfoCopyWithBackupKeyUpdate(CFAllocatorRef allocator, SOSP
     });
 }
 
     });
 }
 
-static CFDictionaryRef SOSPeerInfoUpdateAndCopyRecord(SOSPeerInfoRef peer, CFStringRef dsid, CFDictionaryRef escrowRecord){
-   
-    CFMutableDictionaryRef existingEscrowRecords = SOSPeerInfoCopyEscrowRecord(peer);
-    
-    if(escrowRecord == NULL && existingEscrowRecords != NULL)
-    {
-        CFDictionaryRemoveValue(existingEscrowRecords, dsid);
-        return existingEscrowRecords;
-    }
-    
-    if(existingEscrowRecords == NULL)
-        existingEscrowRecords = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
-    
-    CFDictionarySetValue(existingEscrowRecords, dsid, escrowRecord);
-    
-    return existingEscrowRecords;
-}
-
-
-SOSPeerInfoRef SOSPeerInfoCopyWithEscrowRecordUpdate(CFAllocatorRef allocator, SOSPeerInfoRef toCopy, CFStringRef dsid, CFDictionaryRef escrowRecord, SecKeyRef signingKey, CFErrorRef *error) {
-    return SOSPeerInfoCopyWithModification(allocator, toCopy, signingKey, error,
-                                           ^bool(SOSPeerInfoRef peerToModify, CFErrorRef *error) {
-                                               
-            CFDictionaryRef updatedEscrowRecords = SOSPeerInfoUpdateAndCopyRecord(peerToModify, dsid, escrowRecord);
-            SOSPeerInfoV2DictionarySetValue(peerToModify, sEscrowRecord, updatedEscrowRecords);
-            CFReleaseNull(updatedEscrowRecords);
-            return true;
-    });
-}
-
 SOSPeerInfoRef SOSPeerInfoCopyWithReplacedEscrowRecords(CFAllocatorRef allocator, SOSPeerInfoRef toCopy, CFDictionaryRef escrowRecords, SecKeyRef signingKey, CFErrorRef *error) {
     return SOSPeerInfoCopyWithModification(allocator, toCopy, signingKey, error,
                                            ^bool(SOSPeerInfoRef peerToModify, CFErrorRef *error) {
 SOSPeerInfoRef SOSPeerInfoCopyWithReplacedEscrowRecords(CFAllocatorRef allocator, SOSPeerInfoRef toCopy, CFDictionaryRef escrowRecords, SecKeyRef signingKey, CFErrorRef *error) {
     return SOSPeerInfoCopyWithModification(allocator, toCopy, signingKey, error,
                                            ^bool(SOSPeerInfoRef peerToModify, CFErrorRef *error) {
@@ -500,10 +470,6 @@ CFDataRef SOSPeerInfoCopyBackupKey(SOSPeerInfoRef peer) {
     return SOSPeerInfoV2DictionaryCopyData(peer, sBackupKeyKey);
 }
 
     return SOSPeerInfoV2DictionaryCopyData(peer, sBackupKeyKey);
 }
 
-CFMutableDictionaryRef SOSPeerInfoCopyEscrowRecord(SOSPeerInfoRef peer){
-    return SOSPeerInfoV2DictionaryCopyDictionary(peer, sEscrowRecord);
-}
-
 bool SOSPeerInfoHasBackupKey(SOSPeerInfoRef peer) {
     CFDataRef bk = SOSPeerInfoCopyBackupKey(peer);
     bool success = bk != NULL;
 bool SOSPeerInfoHasBackupKey(SOSPeerInfoRef peer) {
     CFDataRef bk = SOSPeerInfoCopyBackupKey(peer);
     bool success = bk != NULL;
index 5562db2882abe11dbb3b3c9d467d74dec9fef406..87c682fec597de7c1de0608bfde5b24dd2a0aad1 100644 (file)
@@ -25,7 +25,6 @@
 //AGGD
 NSString* const SecSOSAggdMaxRenegotiation   = @"com.apple.security.sos.otrrenegotiationmaxretries";
 
 //AGGD
 NSString* const SecSOSAggdMaxRenegotiation   = @"com.apple.security.sos.otrrenegotiationmaxretries";
 
-__unused static int initialOTRTimeoutValue = 60; //best round trip time in KVS plus extra for good measure
 static int maxRetryCount = 7; //max number of times to attempt restarting OTR negotiation
 
 bool SOSPeerOTRTimerHaveReachedMaxRetryAllowance(SOSAccount* account, NSString* peerid){
 static int maxRetryCount = 7; //max number of times to attempt restarting OTR negotiation
 
 bool SOSPeerOTRTimerHaveReachedMaxRetryAllowance(SOSAccount* account, NSString* peerid){
index 25dd226bc6431b9151ecbb72355a5ff56d6a4e9c..8c97f793a1c1bf171565c9ce9f36ea22f04b8f27 100644 (file)
@@ -45,10 +45,6 @@ CFTypeID SOSRingGetTypeID(void);
 
 SOSRingRef SOSRingCreate(CFStringRef name, CFStringRef myPeerID, SOSRingType type, CFErrorRef *error);
 bool SOSRingResetToEmpty(SOSRingRef ring, CFStringRef myPeerID, CFErrorRef *error);
 
 SOSRingRef SOSRingCreate(CFStringRef name, CFStringRef myPeerID, SOSRingType type, CFErrorRef *error);
 bool SOSRingResetToEmpty(SOSRingRef ring, CFStringRef myPeerID, CFErrorRef *error);
-bool SOSRingResetToOffering(SOSRingRef ring, __unused SecKeyRef user_privkey, SOSFullPeerInfoRef requestor, CFErrorRef *error);
-SOSRingStatus SOSRingDeviceIsInRing(SOSRingRef ring, CFStringRef peerID);
-bool SOSRingApply(SOSRingRef ring, SecKeyRef user_pubkey, SOSFullPeerInfoRef requestor, CFErrorRef *error);
-bool SOSRingWithdraw(SOSRingRef ring, SecKeyRef user_privkey, SOSFullPeerInfoRef requestor, CFErrorRef *error);
 bool SOSRingGenerationSign(SOSRingRef ring, SecKeyRef user_privkey, SOSFullPeerInfoRef requestor, CFErrorRef *error);
 bool SOSRingConcordanceSign(SOSRingRef ring, SOSFullPeerInfoRef requestor, CFErrorRef *error);
 SOSConcordanceStatus SOSRingConcordanceTrust(SOSFullPeerInfoRef me, CFSetRef peers,
 bool SOSRingGenerationSign(SOSRingRef ring, SecKeyRef user_privkey, SOSFullPeerInfoRef requestor, CFErrorRef *error);
 bool SOSRingConcordanceSign(SOSRingRef ring, SOSFullPeerInfoRef requestor, CFErrorRef *error);
 SOSConcordanceStatus SOSRingConcordanceTrust(SOSFullPeerInfoRef me, CFSetRef peers,
index 6a5ed6be29232f3b8e70dcda68081870c71866cf..e7b6e995aab0c1398cdfc0aa24d5fcfcd6fb1a98 100644 (file)
@@ -80,63 +80,6 @@ bool SOSRingResetToEmpty(SOSRingRef ring, CFStringRef myPeerID, CFErrorRef *erro
     return ringTypes[type]->sosRingResetToEmpty(ring, myPeerID, error);
 }
 
     return ringTypes[type]->sosRingResetToEmpty(ring, myPeerID, error);
 }
 
-bool SOSRingResetToOffering(SOSRingRef ring, SecKeyRef user_privkey, SOSFullPeerInfoRef requestor, CFErrorRef *error) {
-    SOSRingAssertStable(ring);
-    SOSRingType type = SOSRingGetType(ring);
-    if(!SOSRingValidType(type)){
-        SOSCreateError(kSOSErrorUnexpectedType, CFSTR("Not valid ring type"), NULL, error);
-        return false;
-    }
-    if(!ringTypes[type]->sosRingResetToOffering){
-        SOSCreateError(kSOSErrorUnexpectedType, CFSTR("Not valid ring type"), NULL, error);
-        return false;
-    }
-    return ringTypes[type]->sosRingResetToOffering(ring, user_privkey, requestor, error);
-}
-
-SOSRingStatus SOSRingDeviceIsInRing(SOSRingRef ring, CFStringRef peerID) {
-    SOSRingAssertStable(ring);
-    SOSRingType type = SOSRingGetType(ring);
-    if(!(SOSRingValidType(type))){
-        return kSOSRingError;
-    }
-       if(!(ringTypes[type]->sosRingDeviceIsInRing)){
-           return kSOSRingError;
-       }
-    return ringTypes[type]->sosRingDeviceIsInRing(ring, peerID);
-}
-
-bool SOSRingApply(SOSRingRef ring, SecKeyRef user_pubkey, SOSFullPeerInfoRef fpi, CFErrorRef *error) {
-    SOSRingAssertStable(ring);
-    SOSRingType type = SOSRingGetType(ring);
-    if(!(SOSRingValidType(type))){
-        SOSCreateError(kSOSErrorUnexpectedType, CFSTR("Not valid ring type"), NULL, error);
-        return false;
-    }
-    if(!(ringTypes[type]->sosRingApply)){
-        return true;
-    }
-    if(!(SOSPeerInfoApplicationVerify(SOSFullPeerInfoGetPeerInfo(fpi), user_pubkey, error))){
-        SOSCreateError(kSOSErrorBadSignature, CFSTR("FullPeerInfo fails userkey signature check"), NULL, error);
-        return false;
-    }
-
-    return ringTypes[type]->sosRingApply(ring, user_pubkey, fpi, error);
-}
-
-bool SOSRingWithdraw(SOSRingRef ring, SecKeyRef user_privkey, SOSFullPeerInfoRef requestor, CFErrorRef *error) {
-    SOSRingAssertStable(ring);
-    SOSRingType type = SOSRingGetType(ring);
-    if(!SOSRingValidType(type)){
-        SOSCreateError(kSOSErrorUnexpectedType, CFSTR("Not valid ring type"), NULL, error);
-        return false;
-    }
-    if(!ringTypes[type]->sosRingWithdraw)
-        return true;
-
-    return ringTypes[type]->sosRingWithdraw(ring, user_privkey, requestor, error);
-}
-
 bool SOSRingGenerationSign(SOSRingRef ring, SecKeyRef user_privkey, SOSFullPeerInfoRef requestor, CFErrorRef *error) {
     SOSRingAssertStable(ring);
     SOSRingType type = SOSRingGetType(ring);
 bool SOSRingGenerationSign(SOSRingRef ring, SecKeyRef user_privkey, SOSFullPeerInfoRef requestor, CFErrorRef *error) {
     SOSRingAssertStable(ring);
     SOSRingType type = SOSRingGetType(ring);
index bb0d62215e15b48fbc9d8ea8fc2cfb1a55b84a63..ddff4c19b057f831bf8c9fc6049afd43059054c3 100644 (file)
@@ -82,12 +82,6 @@ SOSRingRef SOSRingAllocate(void) {
     return (SOSRingRef) CFTypeAllocate(SOSRing, struct __OpaqueSOSRing, ALLOCATOR);
 }
 
     return (SOSRingRef) CFTypeAllocate(SOSRing, struct __OpaqueSOSRing, ALLOCATOR);
 }
 
-__unused static bool addValueToDict(CFMutableDictionaryRef thedict, CFStringRef key, CFTypeRef value) {
-    if(!value) return false;
-    CFDictionaryAddValue(thedict, key, value);
-    return true;
-}
-
 static bool setValueInDict(CFMutableDictionaryRef thedict, CFStringRef key, CFTypeRef value) {
     if(!value) return false;
     CFDictionarySetValue(thedict, key, value);
 static bool setValueInDict(CFMutableDictionaryRef thedict, CFStringRef key, CFTypeRef value) {
     if(!value) return false;
     CFDictionarySetValue(thedict, key, value);
index 8457983684571c89db881dd2207d2b24e1171a3f..32e280564e6abe12ce45a86ff79096db7c6a57e5 100644 (file)
@@ -117,23 +117,6 @@ static bool SOSRingConcordanceSign_V0(SOSRingRef ring, SOSFullPeerInfoRef reques
 }
 
 
 }
 
 
-__unused static bool SOSRingSetPayload_V0(SOSRingRef ring, SecKeyRef user_privkey, CFDataRef payload, SOSFullPeerInfoRef requestor, CFErrorRef *error) {
-    CFStringRef myPeerID = SOSPeerInfoGetPeerID(SOSFullPeerInfoGetPeerInfo(requestor));
-    SecKeyRef priv = SOSFullPeerInfoCopyDeviceKey(requestor, error);
-    bool retval = priv && myPeerID &&
-    SOSRingSetLastModifier(ring, myPeerID) &&
-    SOSRingSetPayload_Internal(ring, payload) &&
-    SOSRingGenerationSign_Internal(ring, priv, error);
-    if(user_privkey) SOSRingConcordanceSign_Internal(ring, user_privkey, error);
-    CFReleaseNull(priv);
-    return retval;
-}
-
-__unused static CFDataRef SOSRingGetPayload_V0(SOSRingRef ring, CFErrorRef *error) {
-    return SOSRingGetPayload_Internal(ring);
-}
-
-
 ringFuncStruct ringsV0 = {
     "V0",
     1,
 ringFuncStruct ringsV0 = {
     "V0",
     1,
index a9e748aef107662c4eec7d15f373e589bd2d60d0..65f2d9e645f8bf653b03227b3e742ee933e8ad5a 100644 (file)
@@ -124,14 +124,12 @@ typedef NS_OPTIONS(uint32_t, SOSAccountGhostBustingOptions) {
 - (void)initialSyncCredentials:(uint32_t)flags complete:(void (^)(NSArray *, NSError *))complete;
 - (void)importInitialSyncCredentials:(NSArray *)items complete:(void (^)(bool success, NSError *))complete;
 
 - (void)initialSyncCredentials:(uint32_t)flags complete:(void (^)(NSArray *, NSError *))complete;
 - (void)importInitialSyncCredentials:(NSArray *)items complete:(void (^)(bool success, NSError *))complete;
 
-- (void)rpcTriggerSync:(NSArray <NSString *> *)peers complete:(void(^)(bool success, NSError *))complete;
+- (void)triggerSync:(NSArray <NSString *> *)peers complete:(void(^)(bool success, NSError *))complete;
 
 - (void)getWatchdogParameters:(void (^)(NSDictionary* parameters, NSError* error))complete;
 - (void)setWatchdogParmeters:(NSDictionary*)parameters complete:(void (^)(NSError* error))complete;
 
 
 - (void)getWatchdogParameters:(void (^)(NSDictionary* parameters, NSError* error))complete;
 - (void)setWatchdogParmeters:(NSDictionary*)parameters complete:(void (^)(NSError* error))complete;
 
-- (void)rpcTriggerBackup:(NSArray<NSString *>*)backupPeers complete:(void (^)(NSError *error))complete;
-- (void)rpcTriggerRingUpdate:(void (^)(NSError *error))complete;
-
+- (void)triggerBackup:(NSArray<NSString *>*)backupPeers complete:(void (^)(NSError *error))complete;
 @end
 #endif
 
 @end
 #endif
 
index 141de9d9f8a9d03be66440f4aff193534685cc26..6d7d9ec5670204167e5e31b3c0e1de676d227f00 100644 (file)
@@ -35,28 +35,14 @@ SECURITY_COMMAND(
        "\n"
        "Account/Circle Management\n"
        "    -a     accept all applicants\n"
        "\n"
        "Account/Circle Management\n"
        "    -a     accept all applicants\n"
-       "    -l     [reason] sign out of circle + set custom departure reason\n"
-       "    -q     sign out of circle\n"
        "    -r     reject all applicants\n"
        "    -r     reject all applicants\n"
-       "    -E     ensure fresh parameters\n"
     "    -b     device|all|single Register a backup bag - THIS RESETS BACKUPS!\n"
     "    -b     device|all|single Register a backup bag - THIS RESETS BACKUPS!\n"
-    "    -A     Apply to a ring\n"
-    "    -B     Withdrawl from a ring\n"
-    "    -G     Enable Ring\n"
-    "    -F     Ring Status\n"
-    "    -I     Dump Ring Information\n"
        "    -N     (re-)set to new account (USE WITH CARE: device will not leave circle before resetting account!)\n"
        "    -O     reset to offering\n"
        "    -R     reset circle\n"
        "    -N     (re-)set to new account (USE WITH CARE: device will not leave circle before resetting account!)\n"
        "    -O     reset to offering\n"
        "    -R     reset circle\n"
-       "    -X     [limit]  best effort bail from circle in limit seconds\n"
     "    -o     list view unaware peers in circle\n"
     "    -0     boot view unaware peers from circle\n"
     "    -o     list view unaware peers in circle\n"
     "    -0     boot view unaware peers from circle\n"
-    "    -1     grab account state from the keychain\n"
-    "    -2     delete account state from the keychain\n"
-    "    -3     grab engine state from the keychain\n"
-    "    -4     delete engine state from the keychain\n"
     "    -5     cleanup old KVS keys in KVS\n"
     "    -5     cleanup old KVS keys in KVS\n"
-    "    -6     [test]populate KVS with garbage KVS keys\n"
     "\n"
     "Circle Tools\n"
     "    --remove-peer SPID     Remove a peer identified by the first 8 or more\n"
     "\n"
     "Circle Tools\n"
     "    --remove-peer SPID     Remove a peer identified by the first 8 or more\n"
@@ -79,9 +65,7 @@ SECURITY_COMMAND(
     "                            wifi|passwords|creditcards|icloudidentity|othersyncable\n"
     "    -L     list all known view and their status\n"
        "    -U     purge private key material cache\n"
     "                            wifi|passwords|creditcards|icloudidentity|othersyncable\n"
     "    -L     list all known view and their status\n"
        "    -U     purge private key material cache\n"
-    "    -V     Report View Sync Status on all known clients.\n"
-    "    -Y     Report yet to initial sync views\n"
-    "    -H     Set escrow record.\n"
-    "    -J     Get the escrow record.\n"
-    "    -M     Check peer availability.\n",
+    "    -V     Report View Sync Status on all known clients.\n",
        "Keychain Syncing controls." )
        "Keychain Syncing controls." )
+
+
index 4d17e4e594c37557cb03075a2f8130159500bed0..5cb4adc9473cfc58115896917539282125239179 100644 (file)
@@ -59,7 +59,6 @@
 
 #include "keychain_sync.h"
 #include "keychain_log.h"
 
 #include "keychain_sync.h"
 #include "keychain_log.h"
-#include "syncbackup.h"
 
 #include "secToolFileIO.h"
 #include "secViewDisplay.h"
 
 #include "secToolFileIO.h"
 #include "secViewDisplay.h"
@@ -329,30 +328,6 @@ static bool clientViewStatus(CFErrorRef *error) {
     return false;
 }
 
     return false;
 }
 
-
-static bool dumpYetToSync(CFErrorRef *error) {
-    CFArrayRef yetToSyncViews = SOSCCCopyYetToSyncViewsList(error);
-
-    bool hadError = yetToSyncViews;
-
-    if (yetToSyncViews) {
-        __block CFStringRef separator = CFSTR("");
-
-        printmsg(CFSTR("Yet to sync views: ["), NULL);
-
-        CFArrayForEach(yetToSyncViews, ^(const void *value) {
-            if (isString(value)) {
-                printmsg(CFSTR("%@%@"), separator, value);
-
-                separator = CFSTR(", ");
-            }
-        });
-        printmsg(CFSTR("]\n"), NULL);
-    }
-
-    return !hadError;
-}
-
 #pragma mark -
 #pragma mark --remove-peer
 
 #pragma mark -
 #pragma mark --remove-peer
 
@@ -468,30 +443,20 @@ keychain_sync(int argc, char * const *argv)
         "
         "Account/Circle Management"
         "    -a     accept all applicants"
         "
         "Account/Circle Management"
         "    -a     accept all applicants"
-        "    -l     [reason] sign out of circle + set custom departure reason"
-        "    -q     sign out of circle"
         "    -r     reject all applicants"
         "    -r     reject all applicants"
-        "    -E     ensure fresh parameters"
      "    -b     device|all|single Register a backup bag - THIS RESETS BACKUPS!\n"
      "    -b     device|all|single Register a backup bag - THIS RESETS BACKUPS!\n"
-     "    -A     Apply to a ring\n"
-     "    -B     Withdrawl from a ring\n"
-     "    -G     Enable Ring\n"
-     "    -F     Ring Status\n"
-     "    -I     Dump Ring Information\n"
 
         "    -N     (re-)set to new account (USE WITH CARE: device will not leave circle before resetting account!)"
         "    -O     reset to offering"
         "    -R     reset circle"
 
         "    -N     (re-)set to new account (USE WITH CARE: device will not leave circle before resetting account!)"
         "    -O     reset to offering"
         "    -R     reset circle"
-        "    -X     [limit]  best effort bail from circle in limit seconds"
      "    -o     list view unaware peers in circle"
      "    -0     boot view unaware peers from circle"
      "    -o     list view unaware peers in circle"
      "    -0     boot view unaware peers from circle"
-     "    -1     grab account state from the keychain"
-     "    -2     delete account state from the keychain"
-     "    -3     grab engine state from the keychain"
-     "    -4     delete engine state from the keychain"
      "    -5     cleanup old KVS keys in KVS"
      "    -5     cleanup old KVS keys in KVS"
-     "    -6     [test]populate KVS with garbage KVS keys
         "
         "
+     "Circle Tools\n"
+     "    --remove-peer SPID     Remove a peer identified by the first 8 or more\n"
+     "                           characters of its spid. Specify multiple times to\n"
+     "                           remove more than one peer.\n"
         "Password"
         "    -P     [label:]password  set password (optionally for a given label) for sync"
         "    -T     [label:]password  try password (optionally for a given label) for sync"
         "Password"
         "    -P     [label:]password  set password (optionally for a given label) for sync"
         "    -T     [label:]password  try password (optionally for a given label) for sync"
@@ -509,9 +474,6 @@ keychain_sync(int argc, char * const *argv)
      "    -L     list all known view and their status"
         "    -U     purge private key material cache\n"
      "    -V     Report View Sync Status on all known clients.\n"
      "    -L     list all known view and their status"
         "    -U     purge private key material cache\n"
      "    -V     Report View Sync Status on all known clients.\n"
-     "    -H     Set escrow record.\n"
-     "    -J     Get the escrow record.\n"
-     "    -M     Check peer availability.\n"
      */
     enum {
         SYNC_REMOVE_PEER,
      */
     enum {
         SYNC_REMOVE_PEER,
@@ -527,359 +489,156 @@ keychain_sync(int argc, char * const *argv)
     CFMutableArrayRef peers2remove = NULL;
     SOSLogSetOutputTo(NULL, NULL);
 
     CFMutableArrayRef peers2remove = NULL;
     SOSLogSetOutputTo(NULL, NULL);
 
-    while ((ch = getopt_long(argc, argv, "ab:deg:hikl:mopq:rSv:w:x:zA:B:MNJCDEF:HG:ILOP:RT:UWX:VY0123456", longopts, NULL)) != -1)
+    while ((ch = getopt_long(argc, argv, "ab:deikmorv:NCDLOP:RT:UWV05", longopts, NULL)) != -1)
         switch  (ch) {
         switch  (ch) {
-               case 'l':
-               {
-                       fprintf(outFile, "Signing out of circle\n");
-                       hadError = !SOSCCSignedOut(true, &error);
-                       if (!hadError) {
-                               errno = 0;
-                               int reason = (int) strtoul(optarg, NULL, 10);
-                               if (errno != 0 ||
-                                       reason < kSOSDepartureReasonError ||
-                                       reason >= kSOSNumDepartureReasons) {
-                                       fprintf(errFile, "Invalid custom departure reason %s\n", optarg);
-                               } else {
-                                       fprintf(outFile, "Setting custom departure reason %d\n", reason);
-                                       hadError = !SOSCCSetLastDepartureReason(reason, &error);
-                                       notify_post(kSOSCCCircleChangedNotification);
-                               }
-                       }
-                       break;
-               }
-                       
-               case 'q':
-               {
-                       fprintf(outFile, "Signing out of circle\n");
-                       bool signOutImmediately = false;
-                       if (strcasecmp(optarg, "true") == 0) {
-                               signOutImmediately = true;
-                       } else if (strcasecmp(optarg, "false") == 0) {
-                               signOutImmediately = false;
-                       } else {
-                               fprintf(outFile, "Please provide a \"true\" or \"false\" whether you'd like to leave the circle immediately\n");
-                       }
-                       hadError = !SOSCCSignedOut(signOutImmediately, &error);
-                       notify_post(kSOSCCCircleChangedNotification);
-                       break;
-               }
-               case 'e':
-                       fprintf(outFile, "Turning ON keychain syncing\n");
-                       hadError = requestToJoinCircle(&error);
-                       break;
-                       
-               case 'd':
-                       fprintf(outFile, "Turning OFF keychain syncing\n");
-                       hadError = !SOSCCRemoveThisDeviceFromCircle(&error);
-                       break;
-                       
-               case 'a':
-               {
-                       CFArrayRef applicants = SOSCCCopyApplicantPeerInfo(NULL);
-                       if (applicants) {
-                               hadError = !SOSCCAcceptApplicants(applicants, &error);
-                               CFRelease(applicants);
-                       } else {
-                               fprintf(errFile, "No applicants to accept\n");
-                       }
-                       break;
-               }
-                       
-               case 'r':
-               {
-                       CFArrayRef applicants = SOSCCCopyApplicantPeerInfo(NULL);
-                       if (applicants) {
-                               hadError = !SOSCCRejectApplicants(applicants, &error);
-                               CFRelease(applicants);
-                       } else {
-                               fprintf(errFile, "No applicants to reject\n");
-                       }
-                       break;
-               }
-                       
-               case 'i':
-                       SOSCCDumpCircleInformation();
-                       SOSCCDumpEngineInformation();
-                       break;
-                       
-               case 'k':
-                       notify_post("com.apple.security.cloudkeychain.forceupdate");
-                       break;
-
-        case 'o':
-        {
-            SOSCCDumpViewUnwarePeers();
-            break;
-        }
-
-        case '0':
-        {
-            CFArrayRef unawares = SOSCCCopyViewUnawarePeerInfo(&error);
-            if (unawares) {
-                hadError = !SOSCCRemovePeersFromCircle(unawares, &error);
-            } else {
-                hadError = true;
+            case 'a':
+            {
+                CFArrayRef applicants = SOSCCCopyApplicantPeerInfo(NULL);
+                if (applicants) {
+                    hadError = !SOSCCAcceptApplicants(applicants, &error);
+                    CFRelease(applicants);
+                } else {
+                    fprintf(errFile, "No applicants to accept\n");
+                }
+                break;
             }
             }
-            CFReleaseNull(unawares);
-            break;
-        }
-        case '1':
-        {
-            CFDataRef accountState = SOSCCCopyAccountState(&error);
-            if (accountState) {
-                printmsg(CFSTR(" %@\n"), CFDataCopyHexString(accountState));
-            } else {
-                hadError = true;
+            case 'b':
+            {
+                hadError = setBag(optarg, &error);
+                break;
             }
             }
-            CFReleaseNull(accountState);
-            break;
-        }
-        case '2':
-        {
-            bool status = SOSCCDeleteAccountState(&error);
-            if (status) {
-                printmsg(CFSTR("Deleted account from the keychain %d\n"), status);
-            } else {
-                hadError = true;
+            case 'd':
+            {
+                fprintf(outFile, "Turning OFF keychain syncing\n");
+                hadError = !SOSCCRemoveThisDeviceFromCircle(&error);
+                break;
             }
             }
-            break;
-        }
-        case '3':
-        {
-            CFDataRef engineState = SOSCCCopyEngineData(&error);
-            if (engineState) {
-                printmsg(CFSTR(" %@\n"), CFDataCopyHexString(engineState));
-            } else {
-                hadError = true;
+            case 'e':
+            {
+                fprintf(outFile, "Turning ON keychain syncing\n");
+                hadError = requestToJoinCircle(&error);
+                break;
             }
             }
-            CFReleaseNull(engineState);
-            break;
-        }
-        case '4':
-        {
-            bool status = SOSCCDeleteEngineState(&error);
-            if (status) {
-                printmsg(CFSTR("Deleted engine-state from the keychain %d\n"), status);
-            } else {
-                hadError = true;
+            case 'i':
+            {
+                SOSCCDumpCircleInformation();
+                SOSCCDumpEngineInformation();
+                break;
             }
             }
-            break;
-        }
-        case '5' :
-        {
-            bool result = SOSCCCleanupKVSKeys(&error);
-            if(result)
+            case 'k':
             {
             {
-                printmsg(CFSTR("Got all the keys from KVS %d\n"), result);
-            }else {
-                hadError = true;
+                notify_post("com.apple.security.cloudkeychain.forceupdate");
+                break;
             }
             }
-            break;
-        }
-        case '6' :
-        {
-            bool result = SOSCCTestPopulateKVSWithBadKeys(&error);
-            if(result)
-                {
-                    printmsg(CFSTR("Populated KVS with garbage %d\n"), result);
-                }else {
+            case 'm':
+            {
+                hadError = !dumpMyPeer(&error);
+                break;
+            }
+            case 'o':
+            {
+                SOSCCDumpViewUnwarePeers();
+                break;
+            }
+            case 'r':
+            {
+                CFArrayRef applicants = SOSCCCopyApplicantPeerInfo(NULL);
+                if (applicants)        {
+                    hadError = !SOSCCRejectApplicants(applicants, &error);
+                    CFRelease(applicants);
+                } else {
+                    fprintf(errFile, "No applicants to reject\n");
+                }
+                break;
+            }
+            case 'v':
+            {
+                hadError = !viewcmd(optarg, &error);
+                break;
+            }
+            case 'C':
+            {
+                hadError = clearAllKVS(&error);
+                break;
+            }
+            case 'D':
+            {
+                (void)SOSCCDumpCircleKVSInformation(optarg);
+                break;
+            }
+            case 'L':
+            {
+                hadError = !listviewcmd(&error);
+                break;
+            }
+            case 'N':
+            {
+                hadError = !SOSCCAccountSetToNew(&error);
+                if (!hadError)
+                    notify_post(kSOSCCCircleChangedNotification);
+                break;
+            }
+            case 'O':
+            {
+                hadError = !SOSCCResetToOffering(&error);
+                break;
+            }
+            case 'P':
+            {
+                hadError = setPassword(optarg, &error);
+                break;
+            }
+            case 'R':
+            {
+                hadError = !SOSCCResetToEmpty(&error);
+                break;
+            }
+            case 'T':
+            {
+                hadError = tryPassword(optarg, &error);
+                break;
+            }
+            case 'U':
+            {
+                hadError = !SOSCCPurgeUserCredentials(&error);
+                break;
+            }
+            case 'V':
+            {
+                hadError = clientViewStatus(&error);
+                break;
+            }
+            case 'W':
+            {
+                hadError = syncAndWait(&error);
+                break;
+            }
+            case '0':
+            {
+                CFArrayRef unawares = SOSCCCopyViewUnawarePeerInfo(&error);
+                if (unawares) {
+                    hadError = !SOSCCRemovePeersFromCircle(unawares, &error);
+                } else {
                     hadError = true;
                 }
                     hadError = true;
                 }
+                CFReleaseNull(unawares);
                 break;
                 break;
-        }
-               case 'E':
-               {
-                       fprintf(outFile, "Ensuring Fresh Parameters\n");
-                       bool result = SOSCCRequestEnsureFreshParameters(&error);
-                       if (error) {
-                               hadError = true;
-                               break;
-                       }
-                       if (result) {
-                               fprintf(outFile, "Refreshed Parameters Ensured!\n");
-                       } else {
-                               fprintf(outFile, "Problem trying to ensure fresh parameters\n");
-                       }
-                       break;
-               }
-               case 'A':
-               {
-                       fprintf(outFile, "Applying to Ring\n");
-                       CFStringRef ringName = CFStringCreateWithCString(kCFAllocatorDefault, (char *)optarg, kCFStringEncodingUTF8);
-                       hadError = SOSCCApplyToARing(ringName, &error);
-            CFReleaseNull(ringName);
-                       break;
-               }
-               case 'B':
-               {
-                       fprintf(outFile, "Withdrawing from Ring\n");
-                       CFStringRef ringName = CFStringCreateWithCString(kCFAllocatorDefault, (char *)optarg, kCFStringEncodingUTF8);
-                       hadError = SOSCCWithdrawlFromARing(ringName, &error);
-            CFReleaseNull(ringName);
-                       break;
-               }
-               case 'F':
-               {
-                       fprintf(outFile, "Status of this device in the Ring\n");
-                       CFStringRef ringName = CFStringCreateWithCString(kCFAllocatorDefault, (char *)optarg, kCFStringEncodingUTF8);
-                       hadError = SOSCCRingStatus(ringName, &error);
-            CFReleaseNull(ringName);
-                       break;
-               }
-               case 'G':
-               {
-                       fprintf(outFile, "Enabling Ring\n");
-                       CFStringRef ringName = CFStringCreateWithCString(kCFAllocatorDefault, (char *)optarg, kCFStringEncodingUTF8);
-                       hadError = SOSCCEnableRing(ringName, &error);
-            CFReleaseNull(ringName);
-                       break;
-               }
-        case 'H':
-        {
-            fprintf(outFile, "Setting random escrow record\n");
-            bool success = SOSCCSetEscrowRecord(CFSTR("label"), 8, &error);
-            if(success)
-                hadError = false;
-            else
-                hadError = true;
-            break;
-        }
-        case 'J':
-        {
-            CFDictionaryRef attempts = SOSCCCopyEscrowRecord(&error);
-            if(attempts){
-                CFDictionaryForEach(attempts, ^(const void *key, const void *value) {
-                    if(isString(key)){
-                        char *keyString = CFStringToCString(key);
-                        fprintf(outFile, "%s:\n", keyString);
-                        free(keyString);
-                    }
-                    if(isDictionary(value)){
-                        CFDictionaryForEach(value, ^(const void *key, const void *value) {
-                            if(isString(key)){
-                                char *keyString = CFStringToCString(key);
-                                fprintf(outFile, "%s: ", keyString);
-                                free(keyString);
-                            }
-                            if(isString(value)){
-                                char *time = CFStringToCString(value);
-                                fprintf(outFile, "timestamp: %s\n", time);
-                                free(time);
-                            }
-                            else if(isNumber(value)){
-                                uint64_t tries;
-                                CFNumberGetValue(value, kCFNumberLongLongType, &tries);
-                                fprintf(outFile, "date: %llu\n", tries);
-                            }
-                        });
-                    }
-                  
-               });
             }
             }
-            CFReleaseNull(attempts);
-            hadError = false;
-            break;
-        }
-               case 'I':
-               {
-                       fprintf(outFile, "Printing all the rings\n");
-                       CFStringRef ringdescription = SOSCCGetAllTheRings(&error);
-                       if(!ringdescription)
-                               hadError = true;
-                       else
-                               fprintf(outFile, "Rings: %s", CFStringToCString(ringdescription));
-                       
-                       break;
-               }
-
-               case 'N':
-                       hadError = !SOSCCAccountSetToNew(&error);
-                       if (!hadError)
-                               notify_post(kSOSCCCircleChangedNotification);
-                       break;
-
-               case 'R':
-                       hadError = !SOSCCResetToEmpty(&error);
-                       break;
-
-               case 'O':
-                       hadError = !SOSCCResetToOffering(&error);
-                       break;
-
-               case 'm':
-                       hadError = !dumpMyPeer(&error);
-                       break;
-
-               case 'C':
-                       hadError = clearAllKVS(&error);
-                       break;
-
-               case 'P':
-                       hadError = setPassword(optarg, &error);
-                       break;
-
-               case 'T':
-                       hadError = tryPassword(optarg, &error);
-                       break;
-
-               case 'X':
-               {
-                       uint64_t limit = strtoul(optarg, NULL, 10);
-                       hadError = !SOSCCBailFromCircle_BestEffort(limit, &error);
-                       break;
-               }
-
-               case 'U':
-                       hadError = !SOSCCPurgeUserCredentials(&error);
-                       break;
-
-               case 'D':
-                       (void)SOSCCDumpCircleKVSInformation(optarg);
-                       break;
-
-               case 'W':
-                       hadError = syncAndWait(&error);
-                       break;
-
-               case 'v':
-                       hadError = !viewcmd(optarg, &error);
-                       break;
-
-        case 'V':
-            hadError = clientViewStatus(&error);
-            break;
-        case 'L':
-            hadError = !listviewcmd(&error);
-            break;
-
-        case 'b':
-            hadError = setBag(optarg, &error);
-            break;
-
-        case 'Y':
-            hadError = dumpYetToSync(&error);
-            break;
-        case 0:
-            switch (action) {
-            case SYNC_REMOVE_PEER: {
-                CFStringRef optstr;
-                optstr = CFStringCreateWithCString(NULL, optarg, kCFStringEncodingUTF8);
-                if (peers2remove == NULL) {
-                    peers2remove = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
+            case '5' :
+            {
+                bool result = SOSCCCleanupKVSKeys(&error);
+                if(result)
+                {
+                    printmsg(CFSTR("Got all the keys from KVS %d\n"), result);
+                }else {
+                    hadError = true;
                 }
                 }
-                CFArrayAppendValue(peers2remove, optstr);
-                CFRelease(optstr);
                 break;
             }
                 break;
             }
+            case '?':
             default:
                 return SHOW_USAGE_MESSAGE;
             default:
                 return SHOW_USAGE_MESSAGE;
-            }
-            break;
-        case '?':
-        default:
-            return SHOW_USAGE_MESSAGE;
-    }
+        }
 
     if (peers2remove != NULL) {
         hadError = !doRemovePeers(peers2remove, &error);
 
     if (peers2remove != NULL) {
         hadError = !doRemovePeers(peers2remove, &error);
index f836de1f370de253c8781f311d534d7be6363233..beca9f352e04d62b5e3b5a80ad80f1bf42b15ef5 100644 (file)
@@ -133,6 +133,7 @@ recovery_key(int argc, char * const *argv)
                 CDPFollowUpContext *context = [CDPFollowUpContext contextForRecoveryKeyRepair];
                 context.force = true;
 
                 CDPFollowUpContext *context = [CDPFollowUpContext contextForRecoveryKeyRepair];
                 context.force = true;
 
+                secnotice("followup", "Posting a follow up (for SOS) of type recovery key");
                 [cdpd postFollowUpWithContext:context error:&localError];
                 if(localError){
                     printmsg(CFSTR("Request to CoreCDP to follow up failed: %@\n"), localError);
                 [cdpd postFollowUpWithContext:context error:&localError];
                 if(localError){
                     printmsg(CFSTR("Request to CoreCDP to follow up failed: %@\n"), localError);
diff --git a/keychain/SecureObjectSync/Tool/syncbackup.h b/keychain/SecureObjectSync/Tool/syncbackup.h
deleted file mode 100644 (file)
index ccacc3c..0000000
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Copyright (c) 2013-2014 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@
- */
-
-//
-//  syncbackup.h
-//  sec
-//
-//
-//
-
-
-#include "SecurityTool/sharedTool/security_tool_commands.h"
-
-SECURITY_COMMAND(
-                 "syncbackup", syncbackup,
-                 "[options]\n"
-                 "    -i     info (current status)\n"
-                 "    -l     list backup slice keybag membership and recovery status"
-                 "\n",
-                 "iCloud Circle Backup Information")
-
diff --git a/keychain/SecureObjectSync/Tool/syncbackup.m b/keychain/SecureObjectSync/Tool/syncbackup.m
deleted file mode 100644 (file)
index 5216ad1..0000000
+++ /dev/null
@@ -1,146 +0,0 @@
-
-/*
- * Copyright (c) 2003-2007,2009-2010,2013-2016 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@
- *
- */
-
-//
-//  syncbackup.c
-//  sec
-//
-//
-//
-
-#include "syncbackup.h"
-
-
-#include <stdio.h>
-#include <CoreFoundation/CoreFoundation.h>
-
-#include <Security/SecureObjectSync/SOSCloudCircle.h>
-#include <Security/SecureObjectSync/SOSCloudCircleInternal.h>
-#include "keychain/SecureObjectSync/SOSBackupInformation.h"
-#include "keychain/SecureObjectSync/SOSRecoveryKeyBag.h"
-#include <Security/SecureObjectSync/SOSBackupSliceKeyBag.h>
-
-#include <utilities/SecCFWrappers.h>
-
-#include "SecurityTool/sharedTool/readline.h"
-#include "secToolFileIO.h"
-
-
-static bool dumpBackupInfo(CFErrorRef *error) {
-    CFReleaseNull(*error);
-    bool isLast = SOSCCIsThisDeviceLastBackup(error);
-    
-    printmsg(CFSTR("This %s the last backup peer.\n"), (isLast) ? "is": "isn't");
-    return *error != NULL;
-}
-
-static bool longListing(CFErrorRef *error) {
-    CFDataRef rkbgder = NULL;
-    CFDictionaryRef bskbders = NULL;
-    
-    CFDictionaryRef backupInfo = SOSCCCopyBackupInformation(error);
-    SOSRecoveryKeyBagRef rkbg = NULL;
-    CFNumberRef status = CFDictionaryGetValue(backupInfo, kSOSBkpInfoStatus);
-    int infoStatus;
-    CFNumberGetValue(status, kCFNumberIntType, &infoStatus);
-    
-    switch(infoStatus) {
-        case noError:
-            rkbgder = CFDictionaryGetValue(backupInfo, kSOSBkpInfoRKBG);
-            bskbders = CFDictionaryGetValue(backupInfo, kSOSBkpInfoBSKB);
-            break;
-        case noTxnorAcct:
-            break;
-        case noAlloc:
-            break;
-        case noTrustedPubKey:
-            break;
-        case noBSKBs:
-            rkbgder = CFDictionaryGetValue(backupInfo, kSOSBkpInfoRKBG);
-            break;
-        default:
-            break;
-    }
-    
-    if(rkbgder) {
-        rkbg = SOSRecoveryKeyBagCreateFromData(kCFAllocatorDefault, rkbgder, NULL);
-        printmsg(CFSTR("Recovery Keybag: %@\n"), rkbg);
-    }
-    
-    if(bskbders) {
-        CFDataRef rkPub = NULL;
-        if(rkbg) rkPub = SOSRecoveryKeyBagGetKeyData(rkbg, NULL);
-        CFDictionaryForEach(bskbders, ^(const void *key, const void *value) {
-            CFDataRef bskbder = asData(value, NULL);
-            SOSBackupSliceKeyBagRef bskb = SOSBackupSliceKeyBagCreateFromData(kCFAllocatorDefault, bskbder, NULL);
-            if(bskb) {
-                bool reckeyPresent = (rkPub && SOSBKSBPrefixedKeyIsInKeyBag(bskb, bskbRkbgPrefix, rkPub));
-                printmsg(CFSTR("BackupSliceKeybag %@: Recovery Key %s; %@\n"), key, (reckeyPresent) ? "Present": "Absent ", bskb);
-                CFReleaseNull(bskb);
-            }
-        });
-    }
-    CFReleaseNull(backupInfo);
-    CFReleaseNull(rkbg);
-    return *error != NULL;
-}
-
-
-
-int
-syncbackup(int argc, char * const *argv)
-{
-    /*
-     "Circle Backup Information"
-     "    -i     info (current status)"
-     
-     */
-    SOSLogSetOutputTo(NULL, NULL);
-    
-    int ch, result = 0;
-    CFErrorRef error = NULL;
-    bool hadError = false;
-    
-    while ((ch = getopt(argc, argv, "il")) != -1)
-        switch  (ch) {
-                
-            case 'i':
-                hadError = dumpBackupInfo(&error);
-                break;
-                
-            case 'l':
-                hadError = longListing(&error);
-                break;
-                
-            case '?':
-            default:
-                return SHOW_USAGE_MESSAGE;
-        }
-    
-    if (hadError)
-        printerr(CFSTR("Error: %@\n"), error);
-    
-    return result;
-}
index c381e08a2fea9ce62098e130f0d9e593a2e0de67..1930811b07c1ba3e61d94d468cc51646fa851a1e 100644 (file)
@@ -15,9 +15,9 @@ __attribute__((visibility("hidden")))
 @interface SOSAccountConfiguration : PBCodable <NSCopying>
 {
     NSMutableArray<NSString *> *_pendingBackupPeers;
 @interface SOSAccountConfiguration : PBCodable <NSCopying>
 {
     NSMutableArray<NSString *> *_pendingBackupPeers;
-    BOOL _ringUpdateFlag;
+    BOOL _sbdBackup;
     struct {
     struct {
-        int ringUpdateFlag:1;
+        int sbdBackup:1;
     } _has;
 }
 
     } _has;
 }
 
@@ -29,8 +29,8 @@ __attribute__((visibility("hidden")))
 - (NSString *)pendingBackupPeersAtIndex:(NSUInteger)idx;
 + (Class)pendingBackupPeersType;
 
 - (NSString *)pendingBackupPeersAtIndex:(NSUInteger)idx;
 + (Class)pendingBackupPeersType;
 
-@property (nonatomic) BOOL hasRingUpdateFlag;
-@property (nonatomic) BOOL ringUpdateFlag;
+@property (nonatomic) BOOL hasSbdBackup;
+@property (nonatomic) BOOL sbdBackup;
 
 // Performs a shallow copy into other
 - (void)copyTo:(SOSAccountConfiguration *)other;
 
 // Performs a shallow copy into other
 - (void)copyTo:(SOSAccountConfiguration *)other;
index 0314479aa90696e61bf31ed1c66202df38c26294..f394355f408a4ed6132f3d591082b6ee8ed58d21 100644 (file)
 {
     return [NSString class];
 }
 {
     return [NSString class];
 }
-@synthesize ringUpdateFlag = _ringUpdateFlag;
-- (void)setRingUpdateFlag:(BOOL)v
-{
-    _has.ringUpdateFlag = YES;
-    _ringUpdateFlag = v;
-}
-- (void)setHasRingUpdateFlag:(BOOL)f
-{
-    _has.ringUpdateFlag = f;
-}
-- (BOOL)hasRingUpdateFlag
-{
-    return _has.ringUpdateFlag;
-}
 
 - (NSString *)description
 {
 
 - (NSString *)description
 {
     {
         [dict setObject:self->_pendingBackupPeers forKey:@"pendingBackupPeers"];
     }
     {
         [dict setObject:self->_pendingBackupPeers forKey:@"pendingBackupPeers"];
     }
-    if (self->_has.ringUpdateFlag)
-    {
-        [dict setObject:[NSNumber numberWithBool:self->_ringUpdateFlag] forKey:@"ringUpdateFlag"];
-    }
     return dict;
 }
 
     return dict;
 }
 
@@ -97,12 +79,6 @@ BOOL SOSAccountConfigurationReadFrom(__unsafe_unretained SOSAccountConfiguration
                 }
             }
             break;
                 }
             }
             break;
-            case 2 /* ringUpdateFlag */:
-            {
-                self->_has.ringUpdateFlag = YES;
-                self->_ringUpdateFlag = PBReaderReadBOOL(reader);
-            }
-            break;
             default:
                 if (!PBReaderSkipValueWithTag(reader, tag, aType))
                     return NO;
             default:
                 if (!PBReaderSkipValueWithTag(reader, tag, aType))
                     return NO;
@@ -125,13 +101,6 @@ BOOL SOSAccountConfigurationReadFrom(__unsafe_unretained SOSAccountConfiguration
             PBDataWriterWriteStringField(writer, s_pendingBackupPeers, 1);
         }
     }
             PBDataWriterWriteStringField(writer, s_pendingBackupPeers, 1);
         }
     }
-    /* ringUpdateFlag */
-    {
-        if (self->_has.ringUpdateFlag)
-        {
-            PBDataWriterWriteBOOLField(writer, self->_ringUpdateFlag, 2);
-        }
-    }
 }
 
 - (void)copyTo:(SOSAccountConfiguration *)other
 }
 
 - (void)copyTo:(SOSAccountConfiguration *)other
@@ -145,11 +114,6 @@ BOOL SOSAccountConfigurationReadFrom(__unsafe_unretained SOSAccountConfiguration
             [other addPendingBackupPeers:[self pendingBackupPeersAtIndex:i]];
         }
     }
             [other addPendingBackupPeers:[self pendingBackupPeersAtIndex:i]];
         }
     }
-    if (self->_has.ringUpdateFlag)
-    {
-        other->_ringUpdateFlag = _ringUpdateFlag;
-        other->_has.ringUpdateFlag = YES;
-    }
 }
 
 - (id)copyWithZone:(NSZone *)zone
 }
 
 - (id)copyWithZone:(NSZone *)zone
@@ -160,11 +124,6 @@ BOOL SOSAccountConfigurationReadFrom(__unsafe_unretained SOSAccountConfiguration
         NSString *vCopy = [v copyWithZone:zone];
         [copy addPendingBackupPeers:vCopy];
     }
         NSString *vCopy = [v copyWithZone:zone];
         [copy addPendingBackupPeers:vCopy];
     }
-    if (self->_has.ringUpdateFlag)
-    {
-        copy->_ringUpdateFlag = _ringUpdateFlag;
-        copy->_has.ringUpdateFlag = YES;
-    }
     return copy;
 }
 
     return copy;
 }
 
@@ -174,8 +133,6 @@ BOOL SOSAccountConfigurationReadFrom(__unsafe_unretained SOSAccountConfiguration
     return [other isMemberOfClass:[self class]]
     &&
     ((!self->_pendingBackupPeers && !other->_pendingBackupPeers) || [self->_pendingBackupPeers isEqual:other->_pendingBackupPeers])
     return [other isMemberOfClass:[self class]]
     &&
     ((!self->_pendingBackupPeers && !other->_pendingBackupPeers) || [self->_pendingBackupPeers isEqual:other->_pendingBackupPeers])
-    &&
-    ((self->_has.ringUpdateFlag && other->_has.ringUpdateFlag && ((self->_ringUpdateFlag && other->_ringUpdateFlag) || (!self->_ringUpdateFlag && !other->_ringUpdateFlag))) || (!self->_has.ringUpdateFlag && !other->_has.ringUpdateFlag))
     ;
 }
 
     ;
 }
 
@@ -184,8 +141,6 @@ BOOL SOSAccountConfigurationReadFrom(__unsafe_unretained SOSAccountConfiguration
     return 0
     ^
     [self->_pendingBackupPeers hash]
     return 0
     ^
     [self->_pendingBackupPeers hash]
-    ^
-    (self->_has.ringUpdateFlag ? PBHashInt((NSUInteger)self->_ringUpdateFlag) : 0)
     ;
 }
 
     ;
 }
 
@@ -195,11 +150,6 @@ BOOL SOSAccountConfigurationReadFrom(__unsafe_unretained SOSAccountConfiguration
     {
         [self addPendingBackupPeers:iter_pendingBackupPeers];
     }
     {
         [self addPendingBackupPeers:iter_pendingBackupPeers];
     }
-    if (other->_has.ringUpdateFlag)
-    {
-        self->_ringUpdateFlag = other->_ringUpdateFlag;
-        self->_has.ringUpdateFlag = YES;
-    }
 }
 
 @end
 }
 
 @end
diff --git a/keychain/Trieste/.swiftlint.yml b/keychain/Trieste/.swiftlint.yml
new file mode 100644 (file)
index 0000000..9ddd0d1
--- /dev/null
@@ -0,0 +1,3 @@
+disabled_rules:
+    - force_cast
+    - force_try
index a97ab461bf2cdc44de99ab57380486e56ff23dc3..9689ecaea15664390f231b2ab3f6b21e347c04b9 100644 (file)
@@ -77,7 +77,7 @@ final class OctagonTests: CDTTestCase {
             }
 
             addTeardownBlock {
             }
 
             addTeardownBlock {
-                if (self.signedIn) {
+                if self.signedIn {
                     do {
                         let listAccounts = try device.executeFile(atPath: "/usr/local/bin/accounts_tool", withArguments: ["--no-confirmation", "deleteAccountsForUsername", self.username!])
                         XCTAssertEqual(listAccounts.returnCode, 0, "deleteAccountsForUsername")
                     do {
                         let listAccounts = try device.executeFile(atPath: "/usr/local/bin/accounts_tool", withArguments: ["--no-confirmation", "deleteAccountsForUsername", self.username!])
                         XCTAssertEqual(listAccounts.returnCode, 0, "deleteAccountsForUsername")
@@ -89,7 +89,7 @@ final class OctagonTests: CDTTestCase {
                 device.relinquish()
             }
 
                 device.relinquish()
             }
 
-            if (self.username != nil) {
+            if self.username != nil {
                 CDALog(at: .infoLevel, "Signing in to iCloud here \(self.username!)")
 
                 let listAccounts = try device.executeFile(atPath: "/usr/local/bin/accounts_tool", withArguments: ["listAccounts", "-v"])
                 CDALog(at: .infoLevel, "Signing in to iCloud here \(self.username!)")
 
                 let listAccounts = try device.executeFile(atPath: "/usr/local/bin/accounts_tool", withArguments: ["listAccounts", "-v"])
@@ -150,13 +150,13 @@ final class OctagonTests: CDTTestCase {
 
     func sosStatus(_ device: CDAIOSDevice, verbose: Bool = false) throws {
         let result = try device.executeFile(atPath: securityTool, withArguments: ["sync", "-i"])
 
     func sosStatus(_ device: CDAIOSDevice, verbose: Bool = false) throws {
         let result = try device.executeFile(atPath: securityTool, withArguments: ["sync", "-i"])
-        if (verbose) {
+        if verbose {
             print("security sync -i\n\(String(data: result.standardOutput, encoding: .utf8)!)\n")
         }
     }
     func ckksStatus(_ device: CDAIOSDevice, verbose: Bool = false) throws -> NSDictionary {
         let ckks = try device.executeFile(atPath: self.ckksTool, withArguments: ["status", "--json"])
             print("security sync -i\n\(String(data: result.standardOutput, encoding: .utf8)!)\n")
         }
     }
     func ckksStatus(_ device: CDAIOSDevice, verbose: Bool = false) throws -> NSDictionary {
         let ckks = try device.executeFile(atPath: self.ckksTool, withArguments: ["status", "--json"])
-        if (verbose) {
+        if verbose {
             print("ckks status\n\(String(data: ckks.standardOutput, encoding: .utf8)!)\n")
         }
 
             print("ckks status\n\(String(data: ckks.standardOutput, encoding: .utf8)!)\n")
         }
 
@@ -164,7 +164,7 @@ final class OctagonTests: CDTTestCase {
     }
 
     func sosApplication(_ device: CDAIOSDevice, verbose: Bool = false) throws {
     }
 
     func sosApplication(_ device: CDAIOSDevice, verbose: Bool = false) throws {
-        if (self.password != nil) {
+        if self.password != nil {
 
             print("submitting application\n")
 
 
             print("submitting application\n")
 
@@ -182,7 +182,7 @@ final class OctagonTests: CDTTestCase {
     }
 
     func sosApprove(_ device: CDAIOSDevice, verbose: Bool = false) throws {
     }
 
     func sosApprove(_ device: CDAIOSDevice, verbose: Bool = false) throws {
-        if (self.password != nil) {
+        if self.password != nil {
 
             print("approving applications\n")
 
 
             print("approving applications\n")
 
@@ -200,7 +200,7 @@ final class OctagonTests: CDTTestCase {
     }
 
     func forceResetSOS(_ device: CDAIOSDevice, resetCKKS: Bool = false) throws {
     }
 
     func forceResetSOS(_ device: CDAIOSDevice, resetCKKS: Bool = false) throws {
-        if (self.password != nil) {
+        if self.password != nil {
             _ = try device.executeFile(atPath: securityTool, withArguments: ["sync", "-P", self.password!])
             _ = try device.executeFile(atPath: securityTool, withArguments: ["sync", "-R"])
             _ = try device.executeFile(atPath: securityTool, withArguments: ["sync", "-C"])
             _ = try device.executeFile(atPath: securityTool, withArguments: ["sync", "-P", self.password!])
             _ = try device.executeFile(atPath: securityTool, withArguments: ["sync", "-R"])
             _ = try device.executeFile(atPath: securityTool, withArguments: ["sync", "-C"])
@@ -213,7 +213,7 @@ final class OctagonTests: CDTTestCase {
             print("sleeping some to allow cdpd, cloudd and friends to catch up \n")
             sleep(4)
 
             print("sleeping some to allow cdpd, cloudd and friends to catch up \n")
             sleep(4)
 
-            if (resetCKKS) {
+            if resetCKKS {
                 _ = try device.executeFile(atPath: self.ckksTool, withArguments: ["reset-cloudkit"])
 
                 print("sleeps some after ckksctl reset (should be removed)\n")
                 _ = try device.executeFile(atPath: self.ckksTool, withArguments: ["reset-cloudkit"])
 
                 print("sleeps some after ckksctl reset (should be removed)\n")
@@ -234,7 +234,7 @@ final class OctagonTests: CDTTestCase {
     }
 
     func test2DeviceSOS() throws {
     }
 
     func test2DeviceSOS() throws {
-        if (self.password == nil) {
+        if self.password == nil {
             print("this test only works with password")
             return
         }
             print("this test only works with password")
             return
         }
@@ -272,9 +272,9 @@ final class OctagonTests: CDTTestCase {
 
             for i in 0..<2 {
                 CDALog(at: .infoLevel, "Reset \(i)")
 
             for i in 0..<2 {
                 CDALog(at: .infoLevel, "Reset \(i)")
-                octagon.octagonReset("altDSID", complete: { _, error in
+                octagon.octagonReset("altDSID") { _, error in
                     CDTAssert(error == nil, "Octagon wasn't reset, error was \(String(describing: error))")
                     CDTAssert(error == nil, "Octagon wasn't reset, error was \(String(describing: error))")
-                })
+                }
             }
         }
 
             }
         }
 
index ff8a89298ae6b5a86ced49656b8c32c975a050eb..d18c8a9f35b6dd165c40c0112b65738e7cf45edc 100644 (file)
@@ -166,7 +166,7 @@ class BottledPeer: NSObject {
         let ciphertext = _SFAuthenticatedCiphertext.init(ciphertext: ac.ciphertext, authenticationCode: ac.authenticationCode, initializationVector: ac.initializationVector)
 
         let clearContentsData = try op.decrypt(ciphertext, with: escrowKeys.symmetricKey)
         let ciphertext = _SFAuthenticatedCiphertext.init(ciphertext: ac.ciphertext, authenticationCode: ac.authenticationCode, initializationVector: ac.initializationVector)
 
         let clearContentsData = try op.decrypt(ciphertext, with: escrowKeys.symmetricKey)
-        if clearContentsData.count == 0 {
+        if clearContentsData.isEmpty {
             throw Error.OTErrorDecryptionFailure
         }
 
             throw Error.OTErrorDecryptionFailure
         }
 
index 04bb5f276f352245d5f53e921c72dddc3567938b..4669d72f4e7175ef70103cdba88dd18b67f6cf5e 100644 (file)
@@ -28,7 +28,7 @@ let OT_ESCROW_SIGNING_HKDF_SIZE = 56
 let OT_ESCROW_ENCRYPTION_HKDF_SIZE = 56
 let OT_ESCROW_SYMMETRIC_HKDF_SIZE = 32
 
 let OT_ESCROW_ENCRYPTION_HKDF_SIZE = 56
 let OT_ESCROW_SYMMETRIC_HKDF_SIZE = 32
 
-enum escrowKeyType: Int {
+enum EscrowKeyType: Int {
     case kOTEscrowKeySigning = 1
     case kOTEscrowKeyEncryption = 2
     case kOTEscrowKeySymmetric = 3
     case kOTEscrowKeySigning = 1
     case kOTEscrowKeyEncryption = 2
     case kOTEscrowKeySymmetric = 3
@@ -46,13 +46,13 @@ class EscrowKeys: NSObject {
         self.secret = secret
         self.bottleSalt = bottleSalt
 
         self.secret = secret
         self.bottleSalt = bottleSalt
 
-        let encryptionKeyData = try EscrowKeys.generateEscrowKey(keyType: escrowKeyType.kOTEscrowKeyEncryption, masterSecret: secret, bottleSalt: bottleSalt)
+        let encryptionKeyData = try EscrowKeys.generateEscrowKey(keyType: EscrowKeyType.kOTEscrowKeyEncryption, masterSecret: secret, bottleSalt: bottleSalt)
         self.encryptionKey = _SFECKeyPair.init(secKey: try EscrowKeys.createSecKey(keyData: encryptionKeyData))
 
         self.encryptionKey = _SFECKeyPair.init(secKey: try EscrowKeys.createSecKey(keyData: encryptionKeyData))
 
-        let signingKeyData = try EscrowKeys.generateEscrowKey(keyType: escrowKeyType.kOTEscrowKeySigning, masterSecret: secret, bottleSalt: bottleSalt)
+        let signingKeyData = try EscrowKeys.generateEscrowKey(keyType: EscrowKeyType.kOTEscrowKeySigning, masterSecret: secret, bottleSalt: bottleSalt)
         self.signingKey = _SFECKeyPair.init(secKey: try EscrowKeys.createSecKey(keyData: signingKeyData))
 
         self.signingKey = _SFECKeyPair.init(secKey: try EscrowKeys.createSecKey(keyData: signingKeyData))
 
-        let symmetricKeyData = try EscrowKeys.generateEscrowKey(keyType: escrowKeyType.kOTEscrowKeySymmetric, masterSecret: secret, bottleSalt: bottleSalt)
+        let symmetricKeyData = try EscrowKeys.generateEscrowKey(keyType: EscrowKeyType.kOTEscrowKeySymmetric, masterSecret: secret, bottleSalt: bottleSalt)
         let specifier = _SFAESKeySpecifier.init(bitSize: TPHObjectiveC.aes256BitSize())
         self.symmetricKey = try _SFAESKey.init(data: symmetricKeyData, specifier: specifier)
 
         let specifier = _SFAESKeySpecifier.init(bitSize: TPHObjectiveC.aes256BitSize())
         self.symmetricKey = try _SFAESKey.init(data: symmetricKeyData, specifier: specifier)
 
@@ -62,34 +62,31 @@ class EscrowKeys: NSObject {
         _ = try EscrowKeys.storeEscrowedSymmetricKey(keyData: self.symmetricKey.keyData, label: escrowSigningPubKeyHash)
     }
 
         _ = try EscrowKeys.storeEscrowedSymmetricKey(keyData: self.symmetricKey.keyData, label: escrowSigningPubKeyHash)
     }
 
-    class func generateEscrowKey(keyType: escrowKeyType, masterSecret: Data, bottleSalt: String) throws -> (Data) {
+    class func generateEscrowKey(keyType: EscrowKeyType, masterSecret: Data, bottleSalt: String) throws -> (Data) {
         var keyLength: Int
         var info: Data
         var derivedKey: Data
         var finalKey = Data()
 
         switch keyType {
         var keyLength: Int
         var info: Data
         var derivedKey: Data
         var finalKey = Data()
 
         switch keyType {
-        case escrowKeyType.kOTEscrowKeySymmetric:
+        case EscrowKeyType.kOTEscrowKeySymmetric:
             keyLength = OT_ESCROW_SYMMETRIC_HKDF_SIZE
 
             let infoString = Array("Escrow Symmetric Key".utf8)
             info = Data(bytes: infoString, count: infoString.count)
 
             keyLength = OT_ESCROW_SYMMETRIC_HKDF_SIZE
 
             let infoString = Array("Escrow Symmetric Key".utf8)
             info = Data(bytes: infoString, count: infoString.count)
 
-            break
-        case escrowKeyType.kOTEscrowKeyEncryption:
+        case EscrowKeyType.kOTEscrowKeyEncryption:
             keyLength = OT_ESCROW_ENCRYPTION_HKDF_SIZE
 
             let infoString = Array("Escrow Encryption Private Key".utf8)
             info = Data(bytes: infoString, count: infoString.count)
 
             keyLength = OT_ESCROW_ENCRYPTION_HKDF_SIZE
 
             let infoString = Array("Escrow Encryption Private Key".utf8)
             info = Data(bytes: infoString, count: infoString.count)
 
-            break
-        case escrowKeyType.kOTEscrowKeySigning:
+        case EscrowKeyType.kOTEscrowKeySigning:
             keyLength = OT_ESCROW_SIGNING_HKDF_SIZE
 
             let infoString = Array("Escrow Signing Private Key".utf8)
             info = Data(bytes: infoString, count: infoString.count)
 
             keyLength = OT_ESCROW_SIGNING_HKDF_SIZE
 
             let infoString = Array("Escrow Signing Private Key".utf8)
             info = Data(bytes: infoString, count: infoString.count)
 
-            break
         }
 
         guard let cp = ccec_cp_384() else {
         }
 
         guard let cp = ccec_cp_384() else {
@@ -119,10 +116,10 @@ class EscrowKeys: NSObject {
                             throw EscrowKeysError.corecryptoKeyGeneration(corecryptoError: status)
                         }
 
                             throw EscrowKeysError.corecryptoKeyGeneration(corecryptoError: status)
                         }
 
-                        if(keyType == escrowKeyType.kOTEscrowKeySymmetric) {
+                        if keyType == EscrowKeyType.kOTEscrowKeySymmetric {
                             finalKey = Data(buffer: derivedKeyBytes.bindMemory(to: UInt8.self))
                             return
                             finalKey = Data(buffer: derivedKeyBytes.bindMemory(to: UInt8.self))
                             return
-                        } else if(keyType == escrowKeyType.kOTEscrowKeyEncryption || keyType == escrowKeyType.kOTEscrowKeySigning) {
+                        } else if keyType == EscrowKeyType.kOTEscrowKeyEncryption || keyType == EscrowKeyType.kOTEscrowKeySigning {
                             status = ccec_generate_key_deterministic(cp,
                                                                      derivedKeyBytes.count, derivedKeyBytes.bindMemory(to: UInt8.self).baseAddress!,
                                                                      ccDRBGGetRngState(),
                             status = ccec_generate_key_deterministic(cp,
                                                                      derivedKeyBytes.count, derivedKeyBytes.bindMemory(to: UInt8.self).baseAddress!,
                                                                      ccDRBGGetRngState(),
@@ -156,7 +153,7 @@ class EscrowKeys: NSObject {
         return key
     }
 
         return key
     }
 
-    class func setKeyMaterialInKeychain(query: Dictionary<CFString, Any>) throws -> (Bool) {
+    class func setKeyMaterialInKeychain(query: [CFString: Any]) throws -> (Bool) {
         var result = false
 
         var results: CFTypeRef?
         var result = false
 
         var results: CFTypeRef?
@@ -165,7 +162,7 @@ class EscrowKeys: NSObject {
         if status == errSecSuccess {
             result = true
         } else if status == errSecDuplicateItem {
         if status == errSecSuccess {
             result = true
         } else if status == errSecDuplicateItem {
-            var updateQuery: Dictionary<CFString, Any> = query
+            var updateQuery: [CFString: Any] = query
             updateQuery[kSecClass] = nil
 
             status = SecItemUpdate(query as CFDictionary, updateQuery as CFDictionary)
             updateQuery[kSecClass] = nil
 
             status = SecItemUpdate(query as CFDictionary, updateQuery as CFDictionary)
@@ -240,8 +237,8 @@ class EscrowKeys: NSObject {
         return try EscrowKeys.setKeyMaterialInKeychain(query: query)
     }
 
         return try EscrowKeys.setKeyMaterialInKeychain(query: query)
     }
 
-    class func retrieveEscrowKeysFromKeychain(label: String) throws -> [Dictionary <CFString, Any>]? {
-        var keySet: [Dictionary<CFString, Any>]?
+    class func retrieveEscrowKeysFromKeychain(label: String) throws -> [ [CFString: Any]]? {
+        var keySet: [[CFString: Any]]?
 
         let query: [CFString: Any] = [
             kSecClass: kSecClassKey,
 
         let query: [CFString: Any] = [
             kSecClass: kSecClassKey,
@@ -261,10 +258,10 @@ class EscrowKeys: NSObject {
         }
 
         if result != nil {
         }
 
         if result != nil {
-            if let dictionaryArray = result as? [Dictionary<CFString, Any>] {
+            if let dictionaryArray = result as? [[CFString: Any]] {
                 keySet = dictionaryArray
             } else {
                 keySet = dictionaryArray
             } else {
-                if let dictionary = result as? Dictionary<CFString, Any> {
+                if let dictionary = result as? [CFString: Any] {
                     keySet = [dictionary]
                 } else {
                     keySet = nil
                     keySet = [dictionary]
                 } else {
                     keySet = nil
index 0a062b7910c361672deba6c2274855fedf7761b3..64dfe9f122a5044102f17423eb7588d27b7e136a 100644 (file)
@@ -39,9 +39,9 @@ class Client: TrustedPeersHelperProtocol {
 
     func logComplete(function: String, container: ContainerName, error: Error?) {
         if let error = error {
 
     func logComplete(function: String, container: ContainerName, error: Error?) {
         if let error = error {
-            os_log("%@ errored for %@: %@", log: tplogDebug, type: .default, function, container.description, error as CVarArg)
+            os_log("%{public}@ errored for %{public}@: %{public}@", log: tplogDebug, type: .default, function, container.description, error as CVarArg)
         } else {
         } else {
-            os_log("%@ finished for %@", log: tplogDebug, type: .default, function, container.description)
+            os_log("%{public}@ finished for %{public}@", log: tplogDebug, type: .default, function, container.description)
         }
     }
 
         }
     }
 
@@ -53,14 +53,14 @@ class Client: TrustedPeersHelperProtocol {
     func dump(withContainer container: String, context: String, reply: @escaping ([AnyHashable: Any]?, Error?) -> Void) {
         do {
             let containerName = ContainerName(container: container, context: context)
     func dump(withContainer container: String, context: String, reply: @escaping ([AnyHashable: Any]?, Error?) -> Void) {
         do {
             let containerName = ContainerName(container: container, context: context)
-            os_log("Dumping for %@", log: tplogDebug, type: .default, containerName.description)
+            os_log("Dumping for %{public}@", log: tplogDebug, type: .default, containerName.description)
             let container = try self.containerMap.findOrCreate(name: containerName)
             container.dump { result, error in
                 self.logComplete(function: "Dumping", container: container.name, error: error)
                 reply(result, CKXPCSuitableError(error))
             }
         } catch {
             let container = try self.containerMap.findOrCreate(name: containerName)
             container.dump { result, error in
                 self.logComplete(function: "Dumping", container: container.name, error: error)
                 reply(result, CKXPCSuitableError(error))
             }
         } catch {
-            os_log("Dumping failed for (%@, %@): %@", log: tplogDebug, type: .default, container, context, error as CVarArg)
+            os_log("Dumping failed for (%{public}@, %{public}@): %{public}@", log: tplogDebug, type: .default, container, context, error as CVarArg)
             reply(nil, CKXPCSuitableError(error))
         }
     }
             reply(nil, CKXPCSuitableError(error))
         }
     }
@@ -70,14 +70,14 @@ class Client: TrustedPeersHelperProtocol {
                      reply: @escaping (String?, TPPeerPermanentInfo?, TPPeerStableInfo?, TPPeerDynamicInfo?, Error?) -> Void) {
         do {
             let containerName = ContainerName(container: container, context: context)
                      reply: @escaping (String?, TPPeerPermanentInfo?, TPPeerStableInfo?, TPPeerDynamicInfo?, Error?) -> Void) {
         do {
             let containerName = ContainerName(container: container, context: context)
-            os_log("Dumping peer for %@", log: tplogDebug, type: .default, containerName.description)
+            os_log("Dumping peer for %{public}@", log: tplogDebug, type: .default, containerName.description)
             let container = try self.containerMap.findOrCreate(name: containerName)
             container.dumpEgoPeer { peerID, perm, stable, dyn, error in
                 self.logComplete(function: "Dumping peer", container: container.name, error: error)
                 reply(peerID, perm, stable, dyn, CKXPCSuitableError(error))
             }
         } catch {
             let container = try self.containerMap.findOrCreate(name: containerName)
             container.dumpEgoPeer { peerID, perm, stable, dyn, error in
                 self.logComplete(function: "Dumping peer", container: container.name, error: error)
                 reply(peerID, perm, stable, dyn, CKXPCSuitableError(error))
             }
         } catch {
-            os_log("Dumping peer failed for (%@, %@): %@", log: tplogDebug, type: .default, container, context, error as CVarArg)
+            os_log("Dumping peer failed for (%{public}@, %{public}@): %{public}@", log: tplogDebug, type: .default, container, context, error as CVarArg)
             reply(nil, nil, nil, nil, CKXPCSuitableError(error))
         }
     }
             reply(nil, nil, nil, nil, CKXPCSuitableError(error))
         }
     }
@@ -88,7 +88,7 @@ class Client: TrustedPeersHelperProtocol {
             let container = try self.containerMap.findOrCreate(name: containerName)
             container.trustStatus(reply: reply)
         } catch {
             let container = try self.containerMap.findOrCreate(name: containerName)
             container.trustStatus(reply: reply)
         } catch {
-            os_log("Trust status failed for (%@, %@): %@", log: tplogDebug, type: .default, container, context, error as CVarArg)
+            os_log("Trust status failed for (%{public}@, %{public}@): %{public}@", log: tplogDebug, type: .default, container, context, error as CVarArg)
             reply(TrustedPeersHelperEgoPeerStatus(egoPeerID: nil,
                                                   status: TPPeerStatus.unknown,
                                                   viablePeerCountsByModelID: [:],
             reply(TrustedPeersHelperEgoPeerStatus(egoPeerID: nil,
                                                   status: TPPeerStatus.unknown,
                                                   viablePeerCountsByModelID: [:],
@@ -102,11 +102,11 @@ class Client: TrustedPeersHelperProtocol {
     func fetchTrustState(withContainer container: String, context: String, reply: @escaping (TrustedPeersHelperPeerState?, [TrustedPeersHelperPeer]?, Error?) -> Void) {
         do {
             let containerName = ContainerName(container: container, context: context)
     func fetchTrustState(withContainer container: String, context: String, reply: @escaping (TrustedPeersHelperPeerState?, [TrustedPeersHelperPeer]?, Error?) -> Void) {
         do {
             let containerName = ContainerName(container: container, context: context)
-            os_log("Fetch Trust State for %@", log: tplogDebug, type: .default, containerName.description)
+            os_log("Fetch Trust State for %{public}@", log: tplogDebug, type: .default, containerName.description)
             let container = try self.containerMap.findOrCreate(name: containerName)
             container.fetchTrustState(reply: reply)
         } catch {
             let container = try self.containerMap.findOrCreate(name: containerName)
             container.fetchTrustState(reply: reply)
         } catch {
-            os_log("Fetch Trust State failed for (%@, %@): %@", log: tplogDebug, type: .default, container, context, error as CVarArg)
+            os_log("Fetch Trust State failed for (%{public}@, %{public}@): %{public}@", log: tplogDebug, type: .default, container, context, error as CVarArg)
             reply(nil, nil, CKXPCSuitableError(error))
         }
     }
             reply(nil, nil, CKXPCSuitableError(error))
         }
     }
@@ -114,13 +114,13 @@ class Client: TrustedPeersHelperProtocol {
     func reset(withContainer container: String, context: String, resetReason: CuttlefishResetReason, reply: @escaping (Error?) -> Void) {
         do {
             let containerName = ContainerName(container: container, context: context)
     func reset(withContainer container: String, context: String, resetReason: CuttlefishResetReason, reply: @escaping (Error?) -> Void) {
         do {
             let containerName = ContainerName(container: container, context: context)
-            os_log("Resetting for %@", log: tplogDebug, type: .default, containerName.description)
+            os_log("Resetting for %{public}@", log: tplogDebug, type: .default, containerName.description)
             let container = try self.containerMap.findOrCreate(name: containerName)
             container.reset(resetReason: resetReason) { error in
                 self.logComplete(function: "Resetting", container: container.name, error: error)
                 reply(CKXPCSuitableError(error)) }
         } catch {
             let container = try self.containerMap.findOrCreate(name: containerName)
             container.reset(resetReason: resetReason) { error in
                 self.logComplete(function: "Resetting", container: container.name, error: error)
                 reply(CKXPCSuitableError(error)) }
         } catch {
-            os_log("Resetting failed for (%@, %@): %@", log: tplogDebug, type: .default, container, context, error as CVarArg)
+            os_log("Resetting failed for (%{public}@, %{public}@): %{public}@", log: tplogDebug, type: .default, container, context, error as CVarArg)
             reply(CKXPCSuitableError(error))
         }
     }
             reply(CKXPCSuitableError(error))
         }
     }
@@ -128,14 +128,14 @@ class Client: TrustedPeersHelperProtocol {
     func localReset(withContainer container: String, context: String, reply: @escaping (Error?) -> Void) {
         do {
             let containerName = ContainerName(container: container, context: context)
     func localReset(withContainer container: String, context: String, reply: @escaping (Error?) -> Void) {
         do {
             let containerName = ContainerName(container: container, context: context)
-            os_log("Performing local reset for %@", log: tplogDebug, type: .default, containerName.description)
+            os_log("Performing local reset for %{public}@", log: tplogDebug, type: .default, containerName.description)
             let container = try self.containerMap.findOrCreate(name: containerName)
             container.localReset { error in
                 self.logComplete(function: "Local reset", container: container.name, error: error)
                 reply(CKXPCSuitableError(error))
             }
         } catch {
             let container = try self.containerMap.findOrCreate(name: containerName)
             container.localReset { error in
                 self.logComplete(function: "Local reset", container: container.name, error: error)
                 reply(CKXPCSuitableError(error))
             }
         } catch {
-            os_log("Local reset failed for (%@, %@): %@", log: tplogDebug, type: .default, container, context, error as CVarArg)
+            os_log("Local reset failed for (%{public}@, %{public}@): %{public}@", log: tplogDebug, type: .default, container, context, error as CVarArg)
             reply(CKXPCSuitableError(error))
         }
     }
             reply(CKXPCSuitableError(error))
         }
     }
@@ -143,17 +143,18 @@ class Client: TrustedPeersHelperProtocol {
     func setAllowedMachineIDsWithContainer(_ container: String,
                                            context: String,
                                            allowedMachineIDs: Set<String>,
     func setAllowedMachineIDsWithContainer(_ container: String,
                                            context: String,
                                            allowedMachineIDs: Set<String>,
+                                           honorIDMSListChanges: Bool,
                                            reply: @escaping (Bool, Error?) -> Void) {
         do {
             let containerName = ContainerName(container: container, context: context)
                                            reply: @escaping (Bool, Error?) -> Void) {
         do {
             let containerName = ContainerName(container: container, context: context)
-            os_log("Setting allowed machineIDs for %@ to %@", log: tplogDebug, type: .default, containerName.description, allowedMachineIDs)
+            os_log("Setting allowed machineIDs for %{public}@ to %{public}@", log: tplogDebug, type: .default, containerName.description, allowedMachineIDs)
             let container = try self.containerMap.findOrCreate(name: containerName)
             let container = try self.containerMap.findOrCreate(name: containerName)
-            container.setAllowedMachineIDs(allowedMachineIDs) { differences, error in
+            container.setAllowedMachineIDs(allowedMachineIDs, honorIDMSListChanges: honorIDMSListChanges) { differences, error in
                 self.logComplete(function: "Setting allowed machineIDs", container: container.name, error: error)
                 reply(differences, CKXPCSuitableError(error))
             }
         } catch {
                 self.logComplete(function: "Setting allowed machineIDs", container: container.name, error: error)
                 reply(differences, CKXPCSuitableError(error))
             }
         } catch {
-            os_log("Setting allowed machineIDs failed for (%@, %@): %@", log: tplogDebug, type: .default, container, context, error as CVarArg)
+            os_log("Setting allowed machineIDs failed for (%{public}@, %{public}@): %{public}@", log: tplogDebug, type: .default, container, context, error as CVarArg)
             reply(false, CKXPCSuitableError(error))
         }
     }
             reply(false, CKXPCSuitableError(error))
         }
     }
@@ -164,14 +165,14 @@ class Client: TrustedPeersHelperProtocol {
                               reply: @escaping (Error?) -> Void) {
         do {
             let containerName = ContainerName(container: container, context: context)
                               reply: @escaping (Error?) -> Void) {
         do {
             let containerName = ContainerName(container: container, context: context)
-            os_log("Adding allowed machineIDs for %@: %@", log: tplogDebug, type: .default, containerName.description, machineIDs)
+            os_log("Adding allowed machineIDs for %{public}@: %{public}@", log: tplogDebug, type: .default, containerName.description, machineIDs)
             let container = try self.containerMap.findOrCreate(name: containerName)
             container.addAllow(machineIDs) { error in
                 self.logComplete(function: "Adding allowed machineIDs", container: container.name, error: error)
                 reply(CKXPCSuitableError(error))
             }
         } catch {
             let container = try self.containerMap.findOrCreate(name: containerName)
             container.addAllow(machineIDs) { error in
                 self.logComplete(function: "Adding allowed machineIDs", container: container.name, error: error)
                 reply(CKXPCSuitableError(error))
             }
         } catch {
-            os_log("Adding allowed machineID failed for (%@, %@): %@", log: tplogDebug, type: .default, container, context, error as CVarArg)
+            os_log("Adding allowed machineID failed for (%{public}@, %{public}@): %{public}@", log: tplogDebug, type: .default, container, context, error as CVarArg)
             reply(CKXPCSuitableError(error))
         }
     }
             reply(CKXPCSuitableError(error))
         }
     }
@@ -182,14 +183,14 @@ class Client: TrustedPeersHelperProtocol {
                                  reply: @escaping (Error?) -> Void) {
         do {
             let containerName = ContainerName(container: container, context: context)
                                  reply: @escaping (Error?) -> Void) {
         do {
             let containerName = ContainerName(container: container, context: context)
-            os_log("Removing allowed machineIDs for %@: %@", log: tplogDebug, type: .default, containerName.description, machineIDs)
+            os_log("Removing allowed machineIDs for %{public}@: %{public}@", log: tplogDebug, type: .default, containerName.description, machineIDs)
             let container = try self.containerMap.findOrCreate(name: containerName)
             container.removeAllow(machineIDs) { error in
                 self.logComplete(function: "Removing allowed machineIDs", container: container.name, error: error)
                 reply(CKXPCSuitableError(error))
             }
         } catch {
             let container = try self.containerMap.findOrCreate(name: containerName)
             container.removeAllow(machineIDs) { error in
                 self.logComplete(function: "Removing allowed machineIDs", container: container.name, error: error)
                 reply(CKXPCSuitableError(error))
             }
         } catch {
-            os_log("Removing allowed machineID failed for (%@, %@): %@", log: tplogDebug, type: .default, container, context, error as CVarArg)
+            os_log("Removing allowed machineID failed for (%{public}@, %{public}@): %{public}@", log: tplogDebug, type: .default, container, context, error as CVarArg)
             reply(CKXPCSuitableError(error))
         }
     }
             reply(CKXPCSuitableError(error))
         }
     }
@@ -197,14 +198,14 @@ class Client: TrustedPeersHelperProtocol {
     func fetchAllowedMachineIDs(withContainer container: String, context: String, reply: @escaping (Set<String>?, Error?) -> Void) {
         do {
             let containerName = ContainerName(container: container, context: context)
     func fetchAllowedMachineIDs(withContainer container: String, context: String, reply: @escaping (Set<String>?, Error?) -> Void) {
         do {
             let containerName = ContainerName(container: container, context: context)
-            os_log("Fetching allowed machineIDs for %@", log: tplogDebug, type: .default, containerName.description)
+            os_log("Fetching allowed machineIDs for %{public}@", log: tplogDebug, type: .default, containerName.description)
             let container = try self.containerMap.findOrCreate(name: containerName)
             let container = try self.containerMap.findOrCreate(name: containerName)
-            container.fetchAllowedMachineIDs() { mids, error in
+            container.fetchAllowedMachineIDs { mids, error in
                 self.logComplete(function: "Fetched allowed machineIDs", container: container.name, error: error)
                 reply(mids, CKXPCSuitableError(error))
             }
         } catch {
                 self.logComplete(function: "Fetched allowed machineIDs", container: container.name, error: error)
                 reply(mids, CKXPCSuitableError(error))
             }
         } catch {
-            os_log("Fetching allowed machineIDs failed for (%@, %@): %@", log: tplogDebug, type: .default, container, context, error as CVarArg)
+            os_log("Fetching allowed machineIDs failed for (%{public}@, %{public}@): %{public}@", log: tplogDebug, type: .default, container, context, error as CVarArg)
             reply(nil, CKXPCSuitableError(error))
         }
     }
             reply(nil, CKXPCSuitableError(error))
         }
     }
@@ -212,13 +213,13 @@ class Client: TrustedPeersHelperProtocol {
     func fetchEgoEpoch(withContainer container: String, context: String, reply: @escaping (UInt64, Error?) -> Void) {
         do {
             let containerName = ContainerName(container: container, context: context)
     func fetchEgoEpoch(withContainer container: String, context: String, reply: @escaping (UInt64, Error?) -> Void) {
         do {
             let containerName = ContainerName(container: container, context: context)
-            os_log("retrieving epoch for %@", log: tplogDebug, type: .default, containerName.description)
+            os_log("retrieving epoch for %{public}@", log: tplogDebug, type: .default, containerName.description)
             let container = try self.containerMap.findOrCreate(name: containerName)
             container.getEgoEpoch { epoch, error in
                 reply(epoch, CKXPCSuitableError(error))
             }
         } catch {
             let container = try self.containerMap.findOrCreate(name: containerName)
             container.getEgoEpoch { epoch, error in
                 reply(epoch, CKXPCSuitableError(error))
             }
         } catch {
-            os_log("Epoch retrieval failed for (%@, %@): %@", log: tplogDebug, type: .default, container, context, error as CVarArg)
+            os_log("Epoch retrieval failed for (%{public}@, %{public}@): %{public}@", log: tplogDebug, type: .default, container, context, error as CVarArg)
             reply(0, CKXPCSuitableError(error))
         }
     }
             reply(0, CKXPCSuitableError(error))
         }
     }
@@ -233,14 +234,14 @@ class Client: TrustedPeersHelperProtocol {
                  deviceName: String?,
                  serialNumber: String,
                  osVersion: String,
                  deviceName: String?,
                  serialNumber: String,
                  osVersion: String,
-                 policyVersion: NSNumber?,
+                 policyVersion: TPPolicyVersion?,
                  policySecrets: [String: Data]?,
                  signingPrivKeyPersistentRef: Data?,
                  encPrivKeyPersistentRef: Data?,
                  policySecrets: [String: Data]?,
                  signingPrivKeyPersistentRef: Data?,
                  encPrivKeyPersistentRef: Data?,
-                 reply: @escaping (String?, Data?, Data?, Data?, Data?, Error?) -> Void) {
+                 reply: @escaping (String?, Data?, Data?, Data?, Data?, Set<String>?, TPPolicy?, Error?) -> Void) {
         do {
             let containerName = ContainerName(container: container, context: context)
         do {
             let containerName = ContainerName(container: container, context: context)
-            os_log("Preparing new identity for %@", log: tplogDebug, type: .default, containerName.description)
+            os_log("Preparing new identity for %{public}@", log: tplogDebug, type: .default, containerName.description)
             let container = try self.containerMap.findOrCreate(name: containerName)
             container.prepare(epoch: epoch,
                               machineID: machineID,
             let container = try self.containerMap.findOrCreate(name: containerName)
             container.prepare(epoch: epoch,
                               machineID: machineID,
@@ -250,16 +251,16 @@ class Client: TrustedPeersHelperProtocol {
                               deviceName: deviceName,
                               serialNumber: serialNumber,
                               osVersion: osVersion,
                               deviceName: deviceName,
                               serialNumber: serialNumber,
                               osVersion: osVersion,
-                              policyVersion: policyVersion?.uint64Value,
+                              policyVersion: policyVersion,
                               policySecrets: policySecrets,
                               signingPrivateKeyPersistentRef: signingPrivKeyPersistentRef,
                               policySecrets: policySecrets,
                               signingPrivateKeyPersistentRef: signingPrivKeyPersistentRef,
-                              encryptionPrivateKeyPersistentRef: encPrivKeyPersistentRef) { peerID, permanentInfo, permanentInfoSig, stableInfo, stableInfoSig, error in
+                              encryptionPrivateKeyPersistentRef: encPrivKeyPersistentRef) { peerID, permanentInfo, permanentInfoSig, stableInfo, stableInfoSig, views, policy, error in
                                 self.logComplete(function: "Prepare", container: container.name, error: error)
                                 self.logComplete(function: "Prepare", container: container.name, error: error)
-                                reply(peerID, permanentInfo, permanentInfoSig, stableInfo, stableInfoSig, CKXPCSuitableError(error))
+                                reply(peerID, permanentInfo, permanentInfoSig, stableInfo, stableInfoSig, views, policy, CKXPCSuitableError(error))
             }
         } catch {
             }
         } catch {
-            os_log("Prepare failed for (%@, %@): %@", log: tplogDebug, type: .default, container, context, error as CVarArg)
-            reply(nil, nil, nil, nil, nil, CKXPCSuitableError(error))
+            os_log("Prepare failed for (%{public}@, %{public}@): %{public}@", log: tplogDebug, type: .default, container, context, error as CVarArg)
+            reply(nil, nil, nil, nil, nil, nil, nil, CKXPCSuitableError(error))
         }
     }
 
         }
     }
 
@@ -271,7 +272,7 @@ class Client: TrustedPeersHelperProtocol {
                    reply: @escaping (String?, [CKRecord]?, Error?) -> Void) {
         do {
             let containerName = ContainerName(container: container, context: context)
                    reply: @escaping (String?, [CKRecord]?, Error?) -> Void) {
         do {
             let containerName = ContainerName(container: container, context: context)
-            os_log("Establishing %@", log: tplogDebug, type: .default, containerName.description)
+            os_log("Establishing %{public}@", log: tplogDebug, type: .default, containerName.description)
             let container = try self.containerMap.findOrCreate(name: containerName)
             container.establish(ckksKeys: ckksKeys,
                                 tlkShares: tlkShares,
             let container = try self.containerMap.findOrCreate(name: containerName)
             container.establish(ckksKeys: ckksKeys,
                                 tlkShares: tlkShares,
@@ -279,7 +280,7 @@ class Client: TrustedPeersHelperProtocol {
                                     self.logComplete(function: "Establishing", container: container.name, error: error)
                                     reply(peerID, keyHierarchyRecords, CKXPCSuitableError(error)) }
         } catch {
                                     self.logComplete(function: "Establishing", container: container.name, error: error)
                                     reply(peerID, keyHierarchyRecords, CKXPCSuitableError(error)) }
         } catch {
-            os_log("Establishing failed for (%@, %@): %@", log: tplogDebug, type: .default, container, context, error as CVarArg)
+            os_log("Establishing failed for (%{public}@, %{public}@): %{public}@", log: tplogDebug, type: .default, container, context, error as CVarArg)
             reply(nil, nil, CKXPCSuitableError(error))
         }
     }
             reply(nil, nil, CKXPCSuitableError(error))
         }
     }
@@ -295,7 +296,7 @@ class Client: TrustedPeersHelperProtocol {
                reply: @escaping (Data?, Data?, Error?) -> Void) {
         do {
             let containerName = ContainerName(container: container, context: context)
                reply: @escaping (Data?, Data?, Error?) -> Void) {
         do {
             let containerName = ContainerName(container: container, context: context)
-            os_log("Vouching %@", log: tplogDebug, type: .default, containerName.description)
+            os_log("Vouching %{public}@", log: tplogDebug, type: .default, containerName.description)
             let container = try self.containerMap.findOrCreate(name: containerName)
             container.vouch(peerID: peerID,
                             permanentInfo: permanentInfo,
             let container = try self.containerMap.findOrCreate(name: containerName)
             container.vouch(peerID: peerID,
                             permanentInfo: permanentInfo,
@@ -306,7 +307,7 @@ class Client: TrustedPeersHelperProtocol {
                                 self.logComplete(function: "Vouching", container: container.name, error: error)
                                 reply(voucher, voucherSig, CKXPCSuitableError(error)) }
         } catch {
                                 self.logComplete(function: "Vouching", container: container.name, error: error)
                                 reply(voucher, voucherSig, CKXPCSuitableError(error)) }
         } catch {
-            os_log("Vouching failed for (%@, %@): %@", log: tplogDebug, type: .default, container, context, error as CVarArg)
+            os_log("Vouching failed for (%{public}@, %{public}@): %{public}@", log: tplogDebug, type: .default, container, context, error as CVarArg)
             reply(nil, nil, CKXPCSuitableError(error))
         }
     }
             reply(nil, nil, CKXPCSuitableError(error))
         }
     }
@@ -314,17 +315,17 @@ class Client: TrustedPeersHelperProtocol {
     func preflightVouchWithBottle(withContainer container: String,
                                   context: String,
                                   bottleID: String,
     func preflightVouchWithBottle(withContainer container: String,
                                   context: String,
                                   bottleID: String,
-                                  reply: @escaping (String?, Error?) -> Void) {
+                                  reply: @escaping (String?, Set<String>?, TPPolicy?, Error?) -> Void) {
         do {
             let containerName = ContainerName(container: container, context: context)
         do {
             let containerName = ContainerName(container: container, context: context)
-            os_log("Preflight Vouch With Bottle %@", log: tplogDebug, type: .default, containerName.description)
+            os_log("Preflight Vouch With Bottle %{public}@", log: tplogDebug, type: .default, containerName.description)
             let container = try self.containerMap.findOrCreate(name: containerName)
             let container = try self.containerMap.findOrCreate(name: containerName)
-            container.preflightVouchWithBottle(bottleID: bottleID) { peerID, error in
+            container.preflightVouchWithBottle(bottleID: bottleID) { peerID, viewSet, policy, error in
                 self.logComplete(function: "Preflight Vouch With Bottle", container: container.name, error: error)
                 self.logComplete(function: "Preflight Vouch With Bottle", container: container.name, error: error)
-                reply(peerID, CKXPCSuitableError(error)) }
+                reply(peerID, viewSet, policy, CKXPCSuitableError(error)) }
         } catch {
         } catch {
-            os_log("Preflighting Vouch With Bottle failed for (%@, %@): %@", log: tplogDebug, type: .default, container, context, error as CVarArg)
-            reply(nil, CKXPCSuitableError(error))
+            os_log("Preflighting Vouch With Bottle failed for (%{public}@, %{public}@): %{public}@", log: tplogDebug, type: .default, container, context, error as CVarArg)
+            reply(nil, nil, nil, CKXPCSuitableError(error))
         }
     }
 
         }
     }
 
@@ -334,17 +335,35 @@ class Client: TrustedPeersHelperProtocol {
                          entropy: Data,
                          bottleSalt: String,
                          tlkShares: [CKKSTLKShare],
                          entropy: Data,
                          bottleSalt: String,
                          tlkShares: [CKKSTLKShare],
-                         reply: @escaping (Data?, Data?, Error?) -> Void) {
+                         reply: @escaping (Data?, Data?, Int64, Int64, Error?) -> Void) {
         do {
             let containerName = ContainerName(container: container, context: context)
         do {
             let containerName = ContainerName(container: container, context: context)
-            os_log("Vouching With Bottle %@", log: tplogDebug, type: .default, containerName.description)
+            os_log("Vouching With Bottle %{public}@", log: tplogDebug, type: .default, containerName.description)
             let container = try self.containerMap.findOrCreate(name: containerName)
             let container = try self.containerMap.findOrCreate(name: containerName)
-            container.vouchWithBottle(bottleID: bottleID, entropy: entropy, bottleSalt: bottleSalt, tlkShares: tlkShares) { voucher, voucherSig, error in
+            container.vouchWithBottle(bottleID: bottleID, entropy: entropy, bottleSalt: bottleSalt, tlkShares: tlkShares) { voucher, voucherSig, uniqueTLKsRecovered, totalTLKSharesRecovered, error in
                 self.logComplete(function: "Vouching With Bottle", container: container.name, error: error)
                 self.logComplete(function: "Vouching With Bottle", container: container.name, error: error)
-                reply(voucher, voucherSig, CKXPCSuitableError(error)) }
+                reply(voucher, voucherSig, uniqueTLKsRecovered, totalTLKSharesRecovered, CKXPCSuitableError(error)) }
         } catch {
         } catch {
-            os_log("Vouching with Bottle failed for (%@, %@): %@", log: tplogDebug, type: .default, container, context, error as CVarArg)
-            reply(nil, nil, CKXPCSuitableError(error))
+            os_log("Vouching with Bottle failed for (%{public}@, %{public}@): %{public}@", log: tplogDebug, type: .default, container, context, error as CVarArg)
+            reply(nil, nil, 0, 0, CKXPCSuitableError(error))
+        }
+    }
+
+    func preflightVouchWithRecoveryKey(withContainer container: String,
+                                  context: String,
+                                  recoveryKey: String,
+                                  salt: String,
+                                  reply: @escaping (String?, Set<String>?, TPPolicy?, Error?) -> Void) {
+        do {
+            let containerName = ContainerName(container: container, context: context)
+            os_log("Preflight Vouch With RecoveryKey %{public}@", log: tplogDebug, type: .default, containerName.description)
+            let container = try self.containerMap.findOrCreate(name: containerName)
+            container.preflightVouchWithRecoveryKey(recoveryKey: recoveryKey, salt: salt) { rkID, viewSet, policy, error in
+                self.logComplete(function: "Preflight Vouch With RecoveryKey", container: container.name, error: error)
+                reply(rkID, viewSet, policy, CKXPCSuitableError(error)) }
+        } catch {
+            os_log("Preflighting Vouch With RecoveryKey failed for (%{public}@, %{public}@): %{public}@", log: tplogDebug, type: .default, container, context, error as CVarArg)
+            reply(nil, nil, nil, CKXPCSuitableError(error))
         }
     }
 
         }
     }
 
@@ -356,13 +375,13 @@ class Client: TrustedPeersHelperProtocol {
                               reply: @escaping (Data?, Data?, Error?) -> Void) {
         do {
             let containerName = ContainerName(container: container, context: context)
                               reply: @escaping (Data?, Data?, Error?) -> Void) {
         do {
             let containerName = ContainerName(container: container, context: context)
-            os_log("Vouching With Recovery Key %@", log: tplogDebug, type: .default, containerName.description)
+            os_log("Vouching With Recovery Key %{public}@", log: tplogDebug, type: .default, containerName.description)
             let container = try self.containerMap.findOrCreate(name: containerName)
             container.vouchWithRecoveryKey(recoveryKey: recoveryKey, salt: salt, tlkShares: tlkShares) { voucher, voucherSig, error in
                 self.logComplete(function: "Vouching With Recovery Key", container: container.name, error: error)
                 reply(voucher, voucherSig, CKXPCSuitableError(error)) }
         } catch {
             let container = try self.containerMap.findOrCreate(name: containerName)
             container.vouchWithRecoveryKey(recoveryKey: recoveryKey, salt: salt, tlkShares: tlkShares) { voucher, voucherSig, error in
                 self.logComplete(function: "Vouching With Recovery Key", container: container.name, error: error)
                 reply(voucher, voucherSig, CKXPCSuitableError(error)) }
         } catch {
-            os_log("Vouching with Recovery Key failed for (%@, %@): %@", log: tplogDebug, type: .default, container, context, error as CVarArg)
+            os_log("Vouching with Recovery Key failed for (%{public}@, %{public}@): %{public}@", log: tplogDebug, type: .default, container, context, error as CVarArg)
             reply(nil, nil, CKXPCSuitableError(error))
         }
     }
             reply(nil, nil, CKXPCSuitableError(error))
         }
     }
@@ -374,18 +393,22 @@ class Client: TrustedPeersHelperProtocol {
               ckksKeys: [CKKSKeychainBackedKeySet],
               tlkShares: [CKKSTLKShare],
               preapprovedKeys: [Data],
               ckksKeys: [CKKSKeychainBackedKeySet],
               tlkShares: [CKKSTLKShare],
               preapprovedKeys: [Data],
-              reply: @escaping (String?, [CKRecord]?, Error?) -> Void) {
+              reply: @escaping (String?, [CKRecord]?, Set<String>?, TPPolicy?, Error?) -> Void) {
         do {
             let containerName = ContainerName(container: container, context: context)
         do {
             let containerName = ContainerName(container: container, context: context)
-            os_log("Joining %@", log: tplogDebug, type: .default, containerName.description)
+            os_log("Joining %{public}@", log: tplogDebug, type: .default, containerName.description)
             let container = try self.containerMap.findOrCreate(name: containerName)
             container.join(voucherData: voucherData,
                            voucherSig: voucherSig,
                            ckksKeys: ckksKeys,
                            tlkShares: tlkShares,
             let container = try self.containerMap.findOrCreate(name: containerName)
             container.join(voucherData: voucherData,
                            voucherSig: voucherSig,
                            ckksKeys: ckksKeys,
                            tlkShares: tlkShares,
-                           preapprovedKeys: preapprovedKeys) { peerID, keyHierarchyRecords, error in reply(peerID, keyHierarchyRecords, CKXPCSuitableError(error)) }
+                           preapprovedKeys: preapprovedKeys) { peerID, keyHierarchyRecords, views, policy, error in
+                            reply(peerID, keyHierarchyRecords, views, policy, CKXPCSuitableError(error))
+
+            }
         } catch {
         } catch {
-            reply(nil, nil, CKXPCSuitableError(error))
+            os_log("Joining failed for (%{public}@, %{public}@): %{public}@", log: tplogDebug, type: .default, container, context, error as CVarArg)
+            reply(nil, nil, nil, nil, CKXPCSuitableError(error))
         }
     }
 
         }
     }
 
@@ -394,10 +417,11 @@ class Client: TrustedPeersHelperProtocol {
                                   reply: @escaping (Bool, Error?) -> Void) {
         do {
             let containerName = ContainerName(container: container, context: context)
                                   reply: @escaping (Bool, Error?) -> Void) {
         do {
             let containerName = ContainerName(container: container, context: context)
-            os_log("Attempting to preflight a preapproved join for %@", log: tplogDebug, type: .default, containerName.description)
+            os_log("Attempting to preflight a preapproved join for %{public}@", log: tplogDebug, type: .default, containerName.description)
             let container = try self.containerMap.findOrCreate(name: containerName)
             container.preflightPreapprovedJoin { success, error in reply(success, CKXPCSuitableError(error)) }
         } catch {
             let container = try self.containerMap.findOrCreate(name: containerName)
             container.preflightPreapprovedJoin { success, error in reply(success, CKXPCSuitableError(error)) }
         } catch {
+            os_log("preflightPreapprovedJoin failed for (%{public}@, %{public}@): %{public}@", log: tplogDebug, type: .default, container, context, error as CVarArg)
             reply(false, CKXPCSuitableError(error))
         }
     }
             reply(false, CKXPCSuitableError(error))
         }
     }
@@ -407,16 +431,18 @@ class Client: TrustedPeersHelperProtocol {
                                 ckksKeys: [CKKSKeychainBackedKeySet],
                                 tlkShares: [CKKSTLKShare],
                                 preapprovedKeys: [Data],
                                 ckksKeys: [CKKSKeychainBackedKeySet],
                                 tlkShares: [CKKSTLKShare],
                                 preapprovedKeys: [Data],
-                                reply: @escaping (String?, [CKRecord]?, Error?) -> Void) {
+                                reply: @escaping (String?, [CKRecord]?, Set<String>?, TPPolicy?, Error?) -> Void) {
         do {
             let containerName = ContainerName(container: container, context: context)
         do {
             let containerName = ContainerName(container: container, context: context)
-            os_log("Attempting a preapproved join for %@", log: tplogDebug, type: .default, containerName.description)
+            os_log("Attempting a preapproved join for %{public}@", log: tplogDebug, type: .default, containerName.description)
             let container = try self.containerMap.findOrCreate(name: containerName)
             container.preapprovedJoin(ckksKeys: ckksKeys,
                                       tlkShares: tlkShares,
             let container = try self.containerMap.findOrCreate(name: containerName)
             container.preapprovedJoin(ckksKeys: ckksKeys,
                                       tlkShares: tlkShares,
-                                      preapprovedKeys: preapprovedKeys) { peerID, keyHierarchyRecords, error in reply(peerID, keyHierarchyRecords, CKXPCSuitableError(error)) }
+                                      preapprovedKeys: preapprovedKeys) { peerID, keyHierarchyRecords, viewSet, policy, error in
+                                        reply(peerID, keyHierarchyRecords, viewSet, policy, CKXPCSuitableError(error)) }
         } catch {
         } catch {
-            reply(nil, nil, CKXPCSuitableError(error))
+            os_log("attemptPreapprovedJoin failed for (%{public}@, %{public}@): %{public}@", log: tplogDebug, type: .default, container, context, error as CVarArg)
+            reply(nil, nil, nil, nil, CKXPCSuitableError(error))
         }
     }
 
         }
     }
 
@@ -430,7 +456,7 @@ class Client: TrustedPeersHelperProtocol {
                 reply: @escaping (TrustedPeersHelperPeerState?, Error?) -> Void) {
         do {
             let containerName = ContainerName(container: container, context: context)
                 reply: @escaping (TrustedPeersHelperPeerState?, Error?) -> Void) {
         do {
             let containerName = ContainerName(container: container, context: context)
-            os_log("Updating %@", log: tplogDebug, type: .default, containerName.description)
+            os_log("Updating %{public}@", log: tplogDebug, type: .default, containerName.description)
             let container = try self.containerMap.findOrCreate(name: containerName)
             container.update(deviceName: deviceName,
                              serialNumber: serialNumber,
             let container = try self.containerMap.findOrCreate(name: containerName)
             container.update(deviceName: deviceName,
                              serialNumber: serialNumber,
@@ -438,21 +464,23 @@ class Client: TrustedPeersHelperProtocol {
                              policyVersion: policyVersion?.uint64Value,
                              policySecrets: policySecrets) { state, error in reply(state, CKXPCSuitableError(error)) }
         } catch {
                              policyVersion: policyVersion?.uint64Value,
                              policySecrets: policySecrets) { state, error in reply(state, CKXPCSuitableError(error)) }
         } catch {
+            os_log("update failed for (%{public}@, %{public}@): %{public}@", log: tplogDebug, type: .default, container, context, error as CVarArg)
             reply(nil, CKXPCSuitableError(error))
         }
     }
 
     func setPreapprovedKeysWithContainer(_ container: String,
             reply(nil, CKXPCSuitableError(error))
         }
     }
 
     func setPreapprovedKeysWithContainer(_ container: String,
-                               context: String,
-                               preapprovedKeys: [Data],
-                               reply: @escaping (Error?) -> Void) {
+                                         context: String,
+                                         preapprovedKeys: [Data],
+                                         reply: @escaping (TrustedPeersHelperPeerState?, Error?) -> Void) {
         do {
             let containerName = ContainerName(container: container, context: context)
         do {
             let containerName = ContainerName(container: container, context: context)
-            os_log("Updating %@", log: tplogDebug, type: .default, containerName.description)
+            os_log("setPreapprovedKeysWithContainer %{public}@", log: tplogDebug, type: .default, containerName.description)
             let container = try self.containerMap.findOrCreate(name: containerName)
             let container = try self.containerMap.findOrCreate(name: containerName)
-            container.set(preapprovedKeys: preapprovedKeys) { error in reply(CKXPCSuitableError(error)) }
+            container.set(preapprovedKeys: preapprovedKeys) { state, error in reply(state, CKXPCSuitableError(error)) }
         } catch {
         } catch {
-            reply(CKXPCSuitableError(error))
+            os_log("setPreapprovedKeysWithContainer failed for (%{public}@, %{public}@): %{public}@", log: tplogDebug, type: .default, container, context, error as CVarArg)
+            reply(nil, CKXPCSuitableError(error))
         }
     }
 
         }
     }
 
@@ -463,12 +491,13 @@ class Client: TrustedPeersHelperProtocol {
                     reply: @escaping ([CKRecord]?, Error?) -> Void) {
         do {
             let containerName = ContainerName(container: container, context: context)
                     reply: @escaping ([CKRecord]?, Error?) -> Void) {
         do {
             let containerName = ContainerName(container: container, context: context)
-            os_log("Updating TLKs for %@", log: tplogDebug, type: .default, containerName.description)
+            os_log("Updating TLKs for %{public}@", log: tplogDebug, type: .default, containerName.description)
             let container = try self.containerMap.findOrCreate(name: containerName)
             container.updateTLKs(ckksKeys: ckksKeys,
                                  tlkShares: tlkShares,
                                  reply: reply)
         } catch {
             let container = try self.containerMap.findOrCreate(name: containerName)
             container.updateTLKs(ckksKeys: ckksKeys,
                                  tlkShares: tlkShares,
                                  reply: reply)
         } catch {
+            os_log("updateTLKs failed for (%{public}@, %{public}@): %{public}@", log: tplogDebug, type: .default, container, context, error as CVarArg)
             reply(nil, CKXPCSuitableError(error))
         }
     }
             reply(nil, CKXPCSuitableError(error))
         }
     }
@@ -478,12 +507,13 @@ class Client: TrustedPeersHelperProtocol {
                                  reply: @escaping (Error?) -> Void) {
         do {
             let containerName = ContainerName(container: container, context: context)
                                  reply: @escaping (Error?) -> Void) {
         do {
             let containerName = ContainerName(container: container, context: context)
-            os_log("Departing %@", log: tplogDebug, type: .default, containerName.description)
+            os_log("Departing %{public}@", log: tplogDebug, type: .default, containerName.description)
             let container = try self.containerMap.findOrCreate(name: containerName)
             container.departByDistrustingSelf { error in
                 reply(CKXPCSuitableError(error))
             }
         } catch {
             let container = try self.containerMap.findOrCreate(name: containerName)
             container.departByDistrustingSelf { error in
                 reply(CKXPCSuitableError(error))
             }
         } catch {
+            os_log("departByDistrustingSelf failed for (%{public}@, %{public}@): %{public}@", log: tplogDebug, type: .default, container, context, error as CVarArg)
             reply(CKXPCSuitableError(error))
         }
     }
             reply(CKXPCSuitableError(error))
         }
     }
@@ -494,12 +524,13 @@ class Client: TrustedPeersHelperProtocol {
                          reply: @escaping (Error?) -> Void) {
         do {
             let containerName = ContainerName(container: container, context: context)
                          reply: @escaping (Error?) -> Void) {
         do {
             let containerName = ContainerName(container: container, context: context)
-            os_log("Distrusting %@ in %@", log: tplogDebug, type: .default, peerIDs, containerName.description)
+            os_log("Distrusting %{public}@ in %{public}@", log: tplogDebug, type: .default, peerIDs, containerName.description)
             let container = try self.containerMap.findOrCreate(name: containerName)
             container.distrust(peerIDs: peerIDs) { error in
                 reply(CKXPCSuitableError(error))
             }
         } catch {
             let container = try self.containerMap.findOrCreate(name: containerName)
             container.distrust(peerIDs: peerIDs) { error in
                 reply(CKXPCSuitableError(error))
             }
         } catch {
+            os_log("distrustPeerIDs failed for (%{public}@, %{public}@): %{public}@", log: tplogDebug, type: .default, container, context, error as CVarArg)
             reply(CKXPCSuitableError(error))
         }
     }
             reply(CKXPCSuitableError(error))
         }
     }
@@ -507,12 +538,13 @@ class Client: TrustedPeersHelperProtocol {
     func fetchViableBottles(withContainer container: String, context: String, reply: @escaping ([String]?, [String]?, Error?) -> Void) {
         do {
             let containerName = ContainerName(container: container, context: context)
     func fetchViableBottles(withContainer container: String, context: String, reply: @escaping ([String]?, [String]?, Error?) -> Void) {
         do {
             let containerName = ContainerName(container: container, context: context)
-            os_log("fetchViableBottles in %@", log: tplogDebug, type: .default, containerName.description)
+            os_log("fetchViableBottles in %{public}@", log: tplogDebug, type: .default, containerName.description)
             let container = try self.containerMap.findOrCreate(name: containerName)
             container.fetchViableBottles { sortedBottleIDs, partialBottleIDs, error in
                 reply(sortedBottleIDs, partialBottleIDs, CKXPCSuitableError(error))
             }
         } catch {
             let container = try self.containerMap.findOrCreate(name: containerName)
             container.fetchViableBottles { sortedBottleIDs, partialBottleIDs, error in
                 reply(sortedBottleIDs, partialBottleIDs, CKXPCSuitableError(error))
             }
         } catch {
+            os_log("fetchViableBottles failed for (%{public}@, %{public}@): %{public}@", log: tplogDebug, type: .default, container, context, error as CVarArg)
             reply(nil, nil, CKXPCSuitableError(error))
         }
     }
             reply(nil, nil, CKXPCSuitableError(error))
         }
     }
@@ -520,43 +552,46 @@ class Client: TrustedPeersHelperProtocol {
     func fetchEscrowContents(withContainer container: String, context: String, reply: @escaping (Data?, String?, Data?, Error?) -> Void) {
         do {
             let containerName = ContainerName(container: container, context: context)
     func fetchEscrowContents(withContainer container: String, context: String, reply: @escaping (Data?, String?, Data?, Error?) -> Void) {
         do {
             let containerName = ContainerName(container: container, context: context)
-            os_log("fetchEscrowContents in %@", log: tplogDebug, type: .default, containerName.description)
+            os_log("fetchEscrowContents in %{public}@", log: tplogDebug, type: .default, containerName.description)
             let container = try self.containerMap.findOrCreate(name: containerName)
             container.fetchEscrowContents { entropy, bottleID, signingPublicKey, error in
                 reply(entropy, bottleID, signingPublicKey, CKXPCSuitableError(error))
             }
         } catch {
             let container = try self.containerMap.findOrCreate(name: containerName)
             container.fetchEscrowContents { entropy, bottleID, signingPublicKey, error in
                 reply(entropy, bottleID, signingPublicKey, CKXPCSuitableError(error))
             }
         } catch {
+            os_log("fetchEscrowContents failed for (%{public}@, %{public}@): %{public}@", log: tplogDebug, type: .default, container, context, error as CVarArg)
             reply(nil, nil, nil, CKXPCSuitableError(error))
         }
     }
 
             reply(nil, nil, nil, CKXPCSuitableError(error))
         }
     }
 
-    func fetchPolicy(withContainer container: String,
-                     context: String,
-                     reply: @escaping (TPPolicy?, Error?) -> Void) {
+    func fetchCurrentPolicy(withContainer container: String,
+                            context: String,
+                            reply: @escaping (Set<String>?, TPPolicy?, Error?) -> Void) {
         do {
             let containerName = ContainerName(container: container, context: context)
         do {
             let containerName = ContainerName(container: container, context: context)
-            os_log("Fetching policy for %@", log: tplogDebug, type: .default, containerName.description)
+            os_log("Fetching policy+views for %{public}@", log: tplogDebug, type: .default, containerName.description)
             let container = try self.containerMap.findOrCreate(name: containerName)
             let container = try self.containerMap.findOrCreate(name: containerName)
-            container.fetchPolicy { policy, error in
-                reply(policy, CKXPCSuitableError(error))
+            container.fetchCurrentPolicy { viewList, policy, error in
+                reply(viewList, policy, CKXPCSuitableError(error))
             }
         } catch {
             }
         } catch {
-            reply(nil, CKXPCSuitableError(error))
+            os_log("fetchCurrentPolicy failed for (%{public}@, %{public}@): %{public}@", log: tplogDebug, type: .default, container, context, error as CVarArg)
+            reply(nil, nil, CKXPCSuitableError(error))
         }
     }
 
     func fetchPolicyDocuments(withContainer container: String,
                               context: String,
         }
     }
 
     func fetchPolicyDocuments(withContainer container: String,
                               context: String,
-                              keys: [NSNumber: String],
-                              reply: @escaping ([NSNumber: [String]]?, Error?) -> Void) {
+                              versions: Set<TPPolicyVersion>,
+                              reply: @escaping ([TPPolicyVersion: Data]?, Error?) -> Void) {
         do {
             let containerName = ContainerName(container: container, context: context)
         do {
             let containerName = ContainerName(container: container, context: context)
-            os_log("Fetching policy documents %@ with keys: %@", log: tplogDebug, type: .default, containerName.description, keys)
+            os_log("Fetching policy documents %{public}@ with versions: %{public}@", log: tplogDebug, type: .default, containerName.description, versions)
             let container = try self.containerMap.findOrCreate(name: containerName)
             let container = try self.containerMap.findOrCreate(name: containerName)
-            container.fetchPolicyDocuments(keys: keys) { entries, error in
+            container.fetchPolicyDocuments(versions: versions) { entries, error in
                 reply(entries, CKXPCSuitableError(error))
             }
         } catch {
                 reply(entries, CKXPCSuitableError(error))
             }
         } catch {
+            os_log("fetchPolicyDocuments failed for (%{public}@, %{public}@): %{public}@", log: tplogDebug, type: .default, container, context, error as CVarArg)
             reply(nil, CKXPCSuitableError(error))
         }
     }
             reply(nil, CKXPCSuitableError(error))
         }
     }
@@ -564,7 +599,7 @@ class Client: TrustedPeersHelperProtocol {
     func validatePeers(withContainer container: String, context: String, reply: @escaping ([AnyHashable: Any]?, Error?) -> Void) {
         do {
             let containerName = ContainerName(container: container, context: context)
     func validatePeers(withContainer container: String, context: String, reply: @escaping ([AnyHashable: Any]?, Error?) -> Void) {
         do {
             let containerName = ContainerName(container: container, context: context)
-            os_log("ValidatePeers for %@", log: tplogDebug, type: .default, containerName.description)
+            os_log("ValidatePeers for %{public}@", log: tplogDebug, type: .default, containerName.description)
             let container = try self.containerMap.findOrCreate(name: containerName)
             let request = ValidatePeersRequest()
             container.validatePeers(request: request) { result, error in
             let container = try self.containerMap.findOrCreate(name: containerName)
             let request = ValidatePeersRequest()
             container.validatePeers(request: request) { result, error in
@@ -572,7 +607,7 @@ class Client: TrustedPeersHelperProtocol {
                 reply(result, CKXPCSuitableError(error))
             }
         } catch {
                 reply(result, CKXPCSuitableError(error))
             }
         } catch {
-            os_log("ValidatePeers failed for (%@, %@): %@", log: tplogDebug, type: .default, container, context, error as CVarArg)
+            os_log("ValidatePeers failed for (%{public}@, %{public}@): %{public}@", log: tplogDebug, type: .default, container, context, error as CVarArg)
             reply(nil, CKXPCSuitableError(error))
         }
     }
             reply(nil, CKXPCSuitableError(error))
         }
     }
@@ -580,14 +615,14 @@ class Client: TrustedPeersHelperProtocol {
     func setRecoveryKeyWithContainer(_ container: String, context: String, recoveryKey: String, salt: String, ckksKeys: [CKKSKeychainBackedKeySet], reply: @escaping (Error?) -> Void) {
         do {
             let containerName = ContainerName(container: container, context: context)
     func setRecoveryKeyWithContainer(_ container: String, context: String, recoveryKey: String, salt: String, ckksKeys: [CKKSKeychainBackedKeySet], reply: @escaping (Error?) -> Void) {
         do {
             let containerName = ContainerName(container: container, context: context)
-            os_log("SetRecoveryKey for %@", log: tplogDebug, type: .default, containerName.description)
+            os_log("SetRecoveryKey for %{public}@", log: tplogDebug, type: .default, containerName.description)
             let container = try self.containerMap.findOrCreate(name: containerName)
             container.setRecoveryKey(recoveryKey: recoveryKey, salt: salt, ckksKeys: ckksKeys) { error in
                 self.logComplete(function: "setRecoveryKey", container: container.name, error: error)
                 reply(CKXPCSuitableError(error))
             }
         } catch {
             let container = try self.containerMap.findOrCreate(name: containerName)
             container.setRecoveryKey(recoveryKey: recoveryKey, salt: salt, ckksKeys: ckksKeys) { error in
                 self.logComplete(function: "setRecoveryKey", container: container.name, error: error)
                 reply(CKXPCSuitableError(error))
             }
         } catch {
-            os_log("SetRecoveryKey failed for (%@, %@): %@", log: tplogDebug, type: .default, container, context, error as CVarArg)
+            os_log("SetRecoveryKey failed for (%{public}@, %{public}@): %{public}@", log: tplogDebug, type: .default, container, context, error as CVarArg)
             reply(CKXPCSuitableError(error))
         }
     }
             reply(CKXPCSuitableError(error))
         }
     }
@@ -595,7 +630,7 @@ class Client: TrustedPeersHelperProtocol {
     func reportHealth(withContainer container: String, context: String, stateMachineState: String, trustState: String, reply: @escaping (Error?) -> Void) {
         do {
             let containerName = ContainerName(container: container, context: context)
     func reportHealth(withContainer container: String, context: String, stateMachineState: String, trustState: String, reply: @escaping (Error?) -> Void) {
         do {
             let containerName = ContainerName(container: container, context: context)
-            os_log("ReportHealth for %@", log: tplogDebug, type: .default, containerName.description)
+            os_log("ReportHealth for %{public}@", log: tplogDebug, type: .default, containerName.description)
             let container = try self.containerMap.findOrCreate(name: containerName)
             let request = ReportHealthRequest.with {
                 $0.stateMachineState = stateMachineState
             let container = try self.containerMap.findOrCreate(name: containerName)
             let request = ReportHealthRequest.with {
                 $0.stateMachineState = stateMachineState
@@ -605,7 +640,7 @@ class Client: TrustedPeersHelperProtocol {
                 reply(CKXPCSuitableError(error))
             }
         } catch {
                 reply(CKXPCSuitableError(error))
             }
         } catch {
-            os_log("ReportHealth failed for (%@, %@): %@", log: tplogDebug, type: .default, container, context, error as CVarArg)
+            os_log("ReportHealth failed for (%{public}@, %{public}@): %{public}@", log: tplogDebug, type: .default, container, context, error as CVarArg)
             reply(CKXPCSuitableError(error))
         }
     }
             reply(CKXPCSuitableError(error))
         }
     }
@@ -613,56 +648,42 @@ class Client: TrustedPeersHelperProtocol {
     func pushHealthInquiry(withContainer container: String, context: String, reply: @escaping (Error?) -> Void) {
         do {
             let containerName = ContainerName(container: container, context: context)
     func pushHealthInquiry(withContainer container: String, context: String, reply: @escaping (Error?) -> Void) {
         do {
             let containerName = ContainerName(container: container, context: context)
-            os_log("PushHealthInquiry for %@", log: tplogDebug, type: .default, containerName.description)
+            os_log("PushHealthInquiry for %{public}@", log: tplogDebug, type: .default, containerName.description)
             let container = try self.containerMap.findOrCreate(name: containerName)
             container.pushHealthInquiry { error in
                 self.logComplete(function: "pushHealthInquiry", container: container.name, error: error)
                 reply(CKXPCSuitableError(error))
             }
         } catch {
             let container = try self.containerMap.findOrCreate(name: containerName)
             container.pushHealthInquiry { error in
                 self.logComplete(function: "pushHealthInquiry", container: container.name, error: error)
                 reply(CKXPCSuitableError(error))
             }
         } catch {
-            os_log("PushHealthInquiry failed for (%@, %@): %@", log: tplogDebug, type: .default, container, context, error as CVarArg)
+            os_log("PushHealthInquiry failed for (%{public}@, %{public}@): %{public}@", log: tplogDebug, type: .default, container, context, error as CVarArg)
             reply(CKXPCSuitableError(error))
         }
     }
 
             reply(CKXPCSuitableError(error))
         }
     }
 
-    func getViewsWithContainer(_ container: String, context: String, inViews: [String], reply: @escaping ([String]?, Error?) -> Void) {
-        do {
-            let containerName = ContainerName(container: container, context: context)
-            os_log("GetViews (%@) for %@", log: tplogDebug, type: .default, inViews, containerName.description)
-            let container = try self.containerMap.findOrCreate(name: containerName)
-            container.getViews(inViews: inViews) { outViews, error in
-                reply(outViews, CKXPCSuitableError(error))
-            }
-        } catch {
-            os_log("GetViews failed for (%@, %@): %@", log: tplogDebug, type: .default, container, context, error as CVarArg)
-            reply(nil, CKXPCSuitableError(error))
-        }
-    }
-
-    func requestHealthCheck(withContainer container: String, context: String, requiresEscrowCheck: Bool, reply: @escaping (Bool, Bool, Bool, Error?) -> Void) {
+    func requestHealthCheck(withContainer container: String, context: String, requiresEscrowCheck: Bool, reply: @escaping (Bool, Bool, Bool, Bool, Error?) -> Void) {
         do {
             let containerName = ContainerName(container: container, context: context)
         do {
             let containerName = ContainerName(container: container, context: context)
-            os_log("Health Check! requiring escrow check? %d for %@", log: tplogDebug, type: .default, requiresEscrowCheck, containerName.description)
+            os_log("Health Check! requiring escrow check? %d for %{public}@", log: tplogDebug, type: .default, requiresEscrowCheck, containerName.description)
             let container = try self.containerMap.findOrCreate(name: containerName)
             let container = try self.containerMap.findOrCreate(name: containerName)
-            container.requestHealthCheck(requiresEscrowCheck: requiresEscrowCheck) { postRepair, postEscrow, postReset, error in
-                reply(postRepair, postEscrow, postReset, CKXPCSuitableError(error))
+            container.requestHealthCheck(requiresEscrowCheck: requiresEscrowCheck) { postRepair, postEscrow, postReset, leaveTrust, error in
+                reply(postRepair, postEscrow, postReset, leaveTrust, CKXPCSuitableError(error))
             }
         } catch {
             }
         } catch {
-            os_log("Health Check! failed for (%@, %@): %@", log: tplogDebug, type: .default, container, context, error as CVarArg)
-            reply(false, false, false, CKXPCSuitableError(error))
+            os_log("Health Check! failed for (%{public}@, %{public}@): %{public}@", log: tplogDebug, type: .default, container, context, error as CVarArg)
+            reply(false, false, false, false, CKXPCSuitableError(error))
         }
     }
 
     func getSupportAppInfo(withContainer container: String, context: String, reply: @escaping (Data?, Error?) -> Void) {
                 do {
             let containerName = ContainerName(container: container, context: context)
         }
     }
 
     func getSupportAppInfo(withContainer container: String, context: String, reply: @escaping (Data?, Error?) -> Void) {
                 do {
             let containerName = ContainerName(container: container, context: context)
-            os_log("getSupportInfo %d for %@", log: tplogDebug, type: .default, containerName.description)
+            os_log("getSupportInfo %d for %{public}@", log: tplogDebug, type: .default, containerName.description)
             let container = try self.containerMap.findOrCreate(name: containerName)
             container.getSupportAppInfo { info, error in
                 reply(info, CKXPCSuitableError(error))
             }
         } catch {
             let container = try self.containerMap.findOrCreate(name: containerName)
             container.getSupportAppInfo { info, error in
                 reply(info, CKXPCSuitableError(error))
             }
         } catch {
-            os_log("getSupportInfo failed for (%@, %@): %@", log: tplogDebug, type: .default, container, context, error as CVarArg)
+            os_log("getSupportInfo failed for (%{public}@, %{public}@): %{public}@", log: tplogDebug, type: .default, container, context, error as CVarArg)
             reply(nil, CKXPCSuitableError(error))
         }
 
             reply(nil, CKXPCSuitableError(error))
         }
 
index fffd68b15587078f20f49bb6814719b6545f45c6..b5ac7067ccdd4672124b24076aebab86180f0b03 100644 (file)
@@ -1,5 +1,5 @@
 /*
 /*
- * Copyright (c) 2018 Apple Inc. All Rights Reserved.
+ * Copyright (c) 2018 - 2020 Apple Inc. All Rights Reserved.
  *
  * @APPLE_LICENSE_HEADER_START@
  *
  *
  * @APPLE_LICENSE_HEADER_START@
  *
@@ -99,6 +99,7 @@ public enum ContainerError: Error {
     case failedToStoreSecret(errorCode: Int)
     case unknownSecurityFoundationError
     case failedToSerializeData
     case failedToStoreSecret(errorCode: Int)
     case unknownSecurityFoundationError
     case failedToSerializeData
+    case unknownInternalError
 }
 
 extension ContainerError: LocalizedError {
 }
 
 extension ContainerError: LocalizedError {
@@ -188,6 +189,8 @@ extension ContainerError: LocalizedError {
             return "SecurityFoundation returned an unknown type"
         case .failedToSerializeData:
             return "Failed to encode protobuf data"
             return "SecurityFoundation returned an unknown type"
         case .failedToSerializeData:
             return "Failed to encode protobuf data"
+        case .unknownInternalError:
+            return "Internal code failed, but didn't return error"
         }
     }
 }
         }
     }
 }
@@ -286,6 +289,8 @@ extension ContainerError: CustomNSError {
             return 43
         case .failedToSerializeData:
             return 44
             return 43
         case .failedToSerializeData:
             return 44
+        case .unknownInternalError:
+            return 45
         }
     }
 
         }
     }
 
@@ -427,14 +432,14 @@ func loadEgoKeyPair(identifier: String, resultHandler: @escaping (_SFECKeyPair?,
 func loadEgoKeys(peerID: String, resultHandler: @escaping (OctagonSelfPeerKeys?, Error?) -> Void) {
     loadEgoKeyPair(identifier: signingKeyIdentifier(peerID: peerID)) { signingKey, error in
         guard let signingKey = signingKey else {
 func loadEgoKeys(peerID: String, resultHandler: @escaping (OctagonSelfPeerKeys?, Error?) -> Void) {
     loadEgoKeyPair(identifier: signingKeyIdentifier(peerID: peerID)) { signingKey, error in
         guard let signingKey = signingKey else {
-            os_log("Unable to load signing key: %@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "error missing")
+            os_log("Unable to load signing key: %{public}@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "error missing")
             resultHandler(nil, error)
             return
         }
 
         loadEgoKeyPair(identifier: encryptionKeyIdentifier(peerID: peerID)) { encryptionKey, error in
             guard let encryptionKey = encryptionKey else {
             resultHandler(nil, error)
             return
         }
 
         loadEgoKeyPair(identifier: encryptionKeyIdentifier(peerID: peerID)) { encryptionKey, error in
             guard let encryptionKey = encryptionKey else {
-                os_log("Unable to load encryption key: %@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "error missing")
+                os_log("Unable to load encryption key: %{public}@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "error missing")
                 resultHandler(nil, error)
                 return
             }
                 resultHandler(nil, error)
                 return
             }
@@ -543,8 +548,12 @@ func makeTLKShares(ckksTLKs: [CKKSKeychainBackedKey]?, asPeer: CKKSSelfPeer, toP
     }.compactMap { $0 }
 }
 
     }.compactMap { $0 }
 }
 
-func extract(tlkShares: [CKKSTLKShare], peer: CKKSSelfPeer) {
-    os_log("Attempting to recover %d TLK shares for peer %@", log: tplogDebug, type: .default, tlkShares.count, peer.peerID)
+@discardableResult
+func extract(tlkShares: [CKKSTLKShare], peer: OctagonSelfPeerKeys, model: TPModel) -> (Int64, Int64) {
+    os_log("Attempting to recover %d TLK shares for peer %{public}@", log: tplogDebug, type: .default, tlkShares.count, peer.peerID)
+    var tlksRecovered: Set<String> = Set()
+    var sharesRecovered: Int64 = 0
+
     for share in tlkShares {
         guard share.receiverPeerID == peer.peerID else {
             os_log("Skipping %@ (wrong peerID)", log: tplogDebug, type: .default, share)
     for share in tlkShares {
         guard share.receiverPeerID == peer.peerID else {
             os_log("Skipping %@ (wrong peerID)", log: tplogDebug, type: .default, share)
@@ -552,18 +561,39 @@ func extract(tlkShares: [CKKSTLKShare], peer: CKKSSelfPeer) {
         }
 
         do {
         }
 
         do {
-            // TODO: how should we handle peer sets here?
+            var trustedPeers: [AnyHashable] = [peer]
+
+            if let egoPeer = model.peer(withID: peer.peerID) {
+                egoPeer.trustedPeerIDs.forEach { trustedPeerID in
+                    if let peer = model.peer(withID: trustedPeerID) {
+                        let peerObj = CKKSActualPeer(peerID: trustedPeerID,
+                                                     encryptionPublicKey: (peer.permanentInfo.encryptionPubKey as! _SFECPublicKey),
+                                                     signing: (peer.permanentInfo.signingPubKey as! _SFECPublicKey),
+                                                     viewList: [])
+
+                        trustedPeers.append(peerObj)
+                    } else {
+                        os_log("No peer for trusted ID %{public}@", log: tplogDebug, type: .default, trustedPeerID)
+                    }
+                }
+            } else {
+                os_log("No ego peer in model; no trusted peers", log: tplogDebug, type: .default)
+            }
+
             let key = try share.recoverTLK(peer,
             let key = try share.recoverTLK(peer,
-                                           trustedPeers: [peer as! AnyHashable],
+                                           trustedPeers: Set(trustedPeers),
                                            ckrecord: nil)
 
             try key.saveMaterialToKeychain()
                                            ckrecord: nil)
 
             try key.saveMaterialToKeychain()
+            tlksRecovered.insert(key.uuid)
+            sharesRecovered += 1
             os_log("Recovered %@ (from %@)", log: tplogDebug, type: .default, key, share)
         } catch {
             os_log("Recovered %@ (from %@)", log: tplogDebug, type: .default, key, share)
         } catch {
-            os_log("Failed to recover share %@: %@", log: tplogDebug, type: .default, share, error as CVarArg)
+            os_log("Failed to recover share %@: %{public}@", log: tplogDebug, type: .default, share, error as CVarArg)
         }
     }
 
         }
     }
 
+    return (Int64(tlksRecovered.count), sharesRecovered)
 }
 
 struct ContainerState {
 }
 
 struct ContainerState {
@@ -581,8 +611,6 @@ internal struct StableChanges {
     let osVersion: String?
     let policyVersion: UInt64?
     let policySecrets: [String: Data]?
     let osVersion: String?
     let policyVersion: UInt64?
     let policySecrets: [String: Data]?
-    let recoverySigningPubKey: Data?
-    var recoveryEncryptionPubKey: Data?
 }
 
 // CoreData doesn't handle creating an identical model from an identical URL. Help it out.
 }
 
 // CoreData doesn't handle creating an identical model from an identical URL. Help it out.
@@ -617,6 +645,17 @@ struct ContainerName: Hashable, CustomStringConvertible {
     }
 }
 
     }
 }
 
+extension ContainerMO {
+    func egoStableInfo() -> TPPeerStableInfo? {
+        guard let egoStableData = self.egoPeerStableInfo,
+            let egoStableSig = self.egoPeerStableInfoSig else {
+            return nil
+        }
+
+        return TPPeerStableInfo(data: egoStableData, sig: egoStableSig)
+    }
+}
+
 /// This maps to a Cuttlefish service backed by a CloudKit container,
 /// and a corresponding local Core Data persistent container.
 ///
 /// This maps to a Cuttlefish service backed by a CloudKit container,
 /// and a corresponding local Core Data persistent container.
 ///
@@ -645,7 +684,6 @@ class Container: NSObject {
     // moc.perform() or moc.performAndWait().
     internal var containerMO: ContainerMO
     internal var model: TPModel
     // moc.perform() or moc.performAndWait().
     internal var containerMO: ContainerMO
     internal var model: TPModel
-
     /**
      Construct a Container.
 
     /**
      Construct a Container.
 
@@ -713,7 +751,6 @@ class Container: NSObject {
         self.containerMO = containerMO!
         self.cuttlefish = cuttlefish
         self.model = model!
         self.containerMO = containerMO!
         self.cuttlefish = cuttlefish
         self.model = model!
-
         super.init()
     }
 
         super.init()
     }
 
@@ -736,26 +773,26 @@ class Container: NSObject {
                     do {
                         try model.update(stableInfo, forPeerWithID: permanentInfo.peerID)
                     } catch {
                     do {
                         try model.update(stableInfo, forPeerWithID: permanentInfo.peerID)
                     } catch {
-                        os_log("loadModel unable to update stable info for peer(%@): %@", log: tplogDebug, type: .default, peer, error as CVarArg)
+                        os_log("loadModel unable to update stable info for peer(%{public}@): %{public}@", log: tplogDebug, type: .default, peer, error as CVarArg)
                     }
                 } else {
                     }
                 } else {
-                    os_log("loadModel: peer %@ has unparseable stable info", log: tplogDebug, type: .default, permanentInfo.peerID)
+                    os_log("loadModel: peer %{public}@ has unparseable stable info", log: tplogDebug, type: .default, permanentInfo.peerID)
                 }
             } else {
                 }
             } else {
-                os_log("loadModel: peer %@ has no stable info", log: tplogDebug, type: .default, permanentInfo.peerID)
+                os_log("loadModel: peer %{public}@ has no stable info", log: tplogDebug, type: .default, permanentInfo.peerID)
             }
             if let data = peer.dynamicInfo, let sig = peer.dynamicInfoSig {
                 if let dynamicInfo = TPPeerDynamicInfo(data: data as Data, sig: sig as Data) {
                     do {
                         try model.update(dynamicInfo, forPeerWithID: permanentInfo.peerID)
                     } catch {
             }
             if let data = peer.dynamicInfo, let sig = peer.dynamicInfoSig {
                 if let dynamicInfo = TPPeerDynamicInfo(data: data as Data, sig: sig as Data) {
                     do {
                         try model.update(dynamicInfo, forPeerWithID: permanentInfo.peerID)
                     } catch {
-                        os_log("loadModel unable to update dynamic info for peer(%@): %@", log: tplogDebug, type: .default, peer, error as CVarArg)
+                        os_log("loadModel unable to update dynamic info for peer(%{public}@): %{public}@", log: tplogDebug, type: .default, peer, error as CVarArg)
                     }
                 } else {
                     }
                 } else {
-                    os_log("loadModel: peer %@ has unparseable dynamic info", log: tplogDebug, type: .default, permanentInfo.peerID)
+                    os_log("loadModel: peer %{public}@ has unparseable dynamic info", log: tplogDebug, type: .default, permanentInfo.peerID)
                 }
             } else {
                 }
             } else {
-                os_log("loadModel: peer %@ has no dynamic info", log: tplogDebug, type: .default, permanentInfo.peerID)
+                os_log("loadModel: peer %{public}@ has no dynamic info", log: tplogDebug, type: .default, permanentInfo.peerID)
             }
             peer.vouchers?.forEach {
                 let v = $0 as! VoucherMO
             }
             peer.vouchers?.forEach {
                 let v = $0 as! VoucherMO
@@ -767,6 +804,20 @@ class Container: NSObject {
             }
         }
 
             }
         }
 
+        if let recoveryKeySigningSPKI = containerMO.recoveryKeySigningSPKI,
+            let recoveryKeyEncyryptionSPKI = containerMO.recoveryKeyEncryptionSPKI {
+            model.setRecoveryKeys(TPRecoveryKeyPair(signingSPKI: recoveryKeySigningSPKI, encryptionSPKI: recoveryKeyEncyryptionSPKI))
+        } else {
+            // If the ego peer has an RK set, tell the model to use that one
+            // This is a hack to work around TPH databases which don't have the RK set on the container due to previously running old software
+            if let egoStableInfo = containerMO.egoStableInfo(),
+                egoStableInfo.recoverySigningPublicKey.count > 0,
+                egoStableInfo.recoveryEncryptionPublicKey.count > 0 {
+                os_log("loadModel: recovery key not set in model, but is set on ego peer", log: tplogDebug, type: .default)
+                model.setRecoveryKeys(TPRecoveryKeyPair(signingSPKI: egoStableInfo.recoverySigningPublicKey, encryptionSPKI: egoStableInfo.recoveryEncryptionPublicKey))
+            }
+        }
+
         // Register persisted policies (cached from cuttlefish)
         let policies = containerMO.policies as? Set<PolicyMO>
         policies?.forEach { policyMO in
         // Register persisted policies (cached from cuttlefish)
         let policies = containerMO.policies as? Set<PolicyMO>
         policies?.forEach { policyMO in
@@ -787,10 +838,10 @@ class Container: NSObject {
         let allowedMachineIDs = Set(knownMachines.filter { $0.status == TPMachineIDStatus.allowed.rawValue }.compactMap { $0.machineID })
         let disallowedMachineIDs = Set(knownMachines.filter { $0.status == TPMachineIDStatus.disallowed.rawValue }.compactMap { $0.machineID })
 
         let allowedMachineIDs = Set(knownMachines.filter { $0.status == TPMachineIDStatus.allowed.rawValue }.compactMap { $0.machineID })
         let disallowedMachineIDs = Set(knownMachines.filter { $0.status == TPMachineIDStatus.disallowed.rawValue }.compactMap { $0.machineID })
 
-        os_log("loadModel: allowedMachineIDs: %@", log: tplogDebug, type: .default, allowedMachineIDs)
-        os_log("loadModel: disallowedMachineIDs: %@", log: tplogDebug, type: .default, disallowedMachineIDs)
+        os_log("loadModel: allowedMachineIDs: %{public}@", log: tplogDebug, type: .default, allowedMachineIDs)
+        os_log("loadModel: disallowedMachineIDs: %{public}@", log: tplogDebug, type: .default, disallowedMachineIDs)
 
 
-        if allowedMachineIDs.count == 0 {
+        if allowedMachineIDs.isEmpty {
             os_log("loadModel: no allowedMachineIDs?", log: tplogDebug, type: .default)
         }
 
             os_log("loadModel: no allowedMachineIDs?", log: tplogDebug, type: .default)
         }
 
@@ -866,7 +917,7 @@ class Container: NSObject {
                 guard returnError == nil else {
                     var isLocked = false
                     if let error = (loadError as NSError?) {
                 guard returnError == nil else {
                     var isLocked = false
                     if let error = (loadError as NSError?) {
-                        os_log("trust status: Unable to load ego keys: %@", log: tplogDebug, type: .default, error as CVarArg)
+                        os_log("trust status: Unable to load ego keys: %{public}@", log: tplogDebug, type: .default, error as CVarArg)
                         if error.code == errSecItemNotFound && error.domain == NSOSStatusErrorDomain {
                             os_log("trust status: Lost the ego key pair, returning 'excluded' in hopes of fixing up the identity", log: tplogDebug, type: .debug)
                             isExcluded = true
                         if error.code == errSecItemNotFound && error.domain == NSOSStatusErrorDomain {
                             os_log("trust status: Lost the ego key pair, returning 'excluded' in hopes of fixing up the identity", log: tplogDebug, type: .debug)
                             isExcluded = true
@@ -943,7 +994,7 @@ class Container: NSObject {
         let reply: (TrustedPeersHelperEgoPeerStatus, Error?) -> Void = {
             // Suppress logging of successful replies here; it's not that useful
             let logType: OSLogType = $1 == nil ? .debug : .info
         let reply: (TrustedPeersHelperEgoPeerStatus, Error?) -> Void = {
             // Suppress logging of successful replies here; it's not that useful
             let logType: OSLogType = $1 == nil ? .debug : .info
-            os_log("trustStatus complete: %@ %@",
+            os_log("trustStatus complete: %{public}@ %{public}@",
                    log: tplogTrace, type: logType, TPPeerStatusToString($0.egoStatus), traceError($1))
 
             self.semaphore.signal()
                    log: tplogTrace, type: logType, TPPeerStatusToString($0.egoStatus), traceError($1))
 
             self.semaphore.signal()
@@ -955,7 +1006,7 @@ class Container: NSObject {
                 self.fetchAndPersistChanges { fetchError in
                     guard fetchError == nil else {
                         if let error = fetchError {
                 self.fetchAndPersistChanges { fetchError in
                     guard fetchError == nil else {
                         if let error = fetchError {
-                            os_log("Unable to fetch changes, trust status is unknown: %@", log: tplogDebug, type: .default, error as CVarArg)
+                            os_log("Unable to fetch changes, trust status is unknown: %{public}@", log: tplogDebug, type: .default, error as CVarArg)
                         }
 
                         let egoStatus = TrustedPeersHelperEgoPeerStatus(egoPeerID: nil,
                         }
 
                         let egoStatus = TrustedPeersHelperEgoPeerStatus(egoPeerID: nil,
@@ -980,7 +1031,7 @@ class Container: NSObject {
 
     func fetchTrustState(reply: @escaping (TrustedPeersHelperPeerState?, [TrustedPeersHelperPeer]?, Error?) -> Void) {
         let reply: (TrustedPeersHelperPeerState?, [TrustedPeersHelperPeer]?, Error?) -> Void = {
 
     func fetchTrustState(reply: @escaping (TrustedPeersHelperPeerState?, [TrustedPeersHelperPeer]?, Error?) -> Void) {
         let reply: (TrustedPeersHelperPeerState?, [TrustedPeersHelperPeer]?, Error?) -> Void = {
-            os_log("fetch trust state complete: %@ %@",
+            os_log("fetch trust state complete: %{public}@ %{public}@",
                    log: tplogTrace, type: .info, String(reflecting: $0), traceError($2))
             reply($0, $1, $2)
         }
                    log: tplogTrace, type: .info, String(reflecting: $0), traceError($2))
             reply($0, $1, $2)
         }
@@ -998,7 +1049,7 @@ class Container: NSObject {
                 }
 
                 let isPreapproved = self.model.hasPotentiallyTrustedPeerPreapprovingKey(permanentInfo.signingPubKey.spki())
                 }
 
                 let isPreapproved = self.model.hasPotentiallyTrustedPeerPreapprovingKey(permanentInfo.signingPubKey.spki())
-                os_log("fetchTrustState: ego peer is %@", log: tplogDebug, type: .default, isPreapproved ? "preapproved" : "not yet preapproved")
+                os_log("fetchTrustState: ego peer is %{public}@", log: tplogDebug, type: .default, isPreapproved ? "preapproved" : "not yet preapproved")
 
                 let egoStableInfo = self.model.getStableInfoForPeer(withID: egoPeerID)
 
 
                 let egoStableInfo = self.model.getStableInfoForPeer(withID: egoPeerID)
 
@@ -1015,22 +1066,21 @@ class Container: NSObject {
                     egoPeer.trustedPeerIDs.forEach { trustedPeerID in
                         if let peer = self.model.peer(withID: trustedPeerID) {
                             let peerViews = try? self.model.getViewsForPeer(peer.permanentInfo,
                     egoPeer.trustedPeerIDs.forEach { trustedPeerID in
                         if let peer = self.model.peer(withID: trustedPeerID) {
                             let peerViews = try? self.model.getViewsForPeer(peer.permanentInfo,
-                                                                            stableInfo: peer.stableInfo,
-                                                                            inViews: Set())
+                                                                            stableInfo: peer.stableInfo)
 
                             tphPeers.append(TrustedPeersHelperPeer(peerID: trustedPeerID,
                                                                    signingSPKI: peer.permanentInfo.signingPubKey.spki(),
                                                                    encryptionSPKI: peer.permanentInfo.encryptionPubKey.spki(),
                                                                    viewList: peerViews ?? Set()))
                         } else {
 
                             tphPeers.append(TrustedPeersHelperPeer(peerID: trustedPeerID,
                                                                    signingSPKI: peer.permanentInfo.signingPubKey.spki(),
                                                                    encryptionSPKI: peer.permanentInfo.encryptionPubKey.spki(),
                                                                    viewList: peerViews ?? Set()))
                         } else {
-                            os_log("No peer for trusted ID %@", log: tplogDebug, type: .default, trustedPeerID)
+                            os_log("No peer for trusted ID %{public}@", log: tplogDebug, type: .default, trustedPeerID)
                         }
                     }
                 } else {
                     os_log("No ego peer in model; no trusted peers", log: tplogDebug, type: .default)
                 }
 
                         }
                     }
                 } else {
                     os_log("No ego peer in model; no trusted peers", log: tplogDebug, type: .default)
                 }
 
-                os_log("Returning trust state: %@ %@", log: tplogDebug, type: .default, egoPeerStatus, tphPeers)
+                os_log("Returning trust state: %{public}@ %@", log: tplogDebug, type: .default, egoPeerStatus, tphPeers)
                 reply(egoPeerStatus, tphPeers, nil)
             } else {
                 // With no ego peer ID, there are no trusted peers
                 reply(egoPeerStatus, tphPeers, nil)
             } else {
                 // With no ego peer ID, there are no trusted peers
@@ -1042,7 +1092,7 @@ class Container: NSObject {
 
     func dump(reply: @escaping ([AnyHashable: Any]?, Error?) -> Void) {
         let reply: ([AnyHashable: Any]?, Error?) -> Void = {
 
     func dump(reply: @escaping ([AnyHashable: Any]?, Error?) -> Void) {
         let reply: ([AnyHashable: Any]?, Error?) -> Void = {
-            os_log("dump complete: %@",
+            os_log("dump complete: %{public}@",
                    log: tplogTrace, type: .info, traceError($1))
             reply($0, $1)
        }
                    log: tplogTrace, type: .info, traceError($1))
             reply($0, $1)
        }
@@ -1083,7 +1133,7 @@ class Container: NSObject {
 
     func dumpEgoPeer(reply: @escaping (String?, TPPeerPermanentInfo?, TPPeerStableInfo?, TPPeerDynamicInfo?, Error?) -> Void) {
        let reply: (String?, TPPeerPermanentInfo?, TPPeerStableInfo?, TPPeerDynamicInfo?, Error?) -> Void = {
 
     func dumpEgoPeer(reply: @escaping (String?, TPPeerPermanentInfo?, TPPeerStableInfo?, TPPeerDynamicInfo?, Error?) -> Void) {
        let reply: (String?, TPPeerPermanentInfo?, TPPeerStableInfo?, TPPeerDynamicInfo?, Error?) -> Void = {
-           os_log("dumpEgoPeer complete: %@", log: tplogTrace, type: .info, traceError($4))
+           os_log("dumpEgoPeer complete: %{public}@", log: tplogTrace, type: .info, traceError($4))
            reply($0, $1, $2, $3, $4)
        }
        self.moc.performAndWait {
            reply($0, $1, $2, $3, $4)
        }
        self.moc.performAndWait {
@@ -1104,15 +1154,15 @@ class Container: NSObject {
     func validatePeers(request: ValidatePeersRequest, reply: @escaping ([AnyHashable: Any]?, Error?) -> Void) {
         self.semaphore.wait()
         let reply: ([AnyHashable: Any]?, Error?) -> Void = {
     func validatePeers(request: ValidatePeersRequest, reply: @escaping ([AnyHashable: Any]?, Error?) -> Void) {
         self.semaphore.wait()
         let reply: ([AnyHashable: Any]?, Error?) -> Void = {
-            os_log("validatePeers complete %@", log: tplogTrace, type: .info, traceError($1))
+            os_log("validatePeers complete %{public}@", log: tplogTrace, type: .info, traceError($1))
             self.semaphore.signal()
             reply($0, $1)
         }
 
         self.cuttlefish.validatePeers(request) { response, error in
             self.semaphore.signal()
             reply($0, $1)
         }
 
         self.cuttlefish.validatePeers(request) { response, error in
-            os_log("ValidatePeers(%@): %@, error: %@", log: tplogDebug, "\(String(describing: request))", "\(String(describing: response))", "\(String(describing: error))")
+            os_log("ValidatePeers(%{public}@): %{public}@, error: %{public}@", log: tplogDebug, "\(String(describing: request))", "\(String(describing: response))", "\(String(describing: error))")
             guard let response = response, error == nil else {
             guard let response = response, error == nil else {
-                os_log("validatePeers failed: %@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "no error")
+                os_log("validatePeers failed: %{public}@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "no error")
                 reply(nil, error ?? ContainerError.cloudkitResponseMissing)
                 return
             }
                 reply(nil, error ?? ContainerError.cloudkitResponseMissing)
                 return
             }
@@ -1125,49 +1175,10 @@ class Container: NSObject {
         }
     }
 
         }
     }
 
-    func getViews(inViews: [String], reply: @escaping ([String]?, Error?) -> Void) {
-        let reply: ([String]?, Error?) -> Void = {
-            os_log("getViews complete %@", log: tplogTrace, type: .info, traceError($1))
-            reply($0, $1)
-        }
-        self.moc.performAndWait {
-            guard let egoPeerID = self.containerMO.egoPeerID,
-                let egoPermData = self.containerMO.egoPeerPermanentInfo,
-                let egoPermSig = self.containerMO.egoPeerPermanentInfoSig,
-                let egoStableData = self.containerMO.egoPeerStableInfo,
-                let egoStableSig = self.containerMO.egoPeerStableInfoSig
-            else {
-                 os_log("getViews failed to find ego peer information", log: tplogDebug, type: .error)
-                 reply(nil, ContainerError.noPreparedIdentity)
-                 return
-            }
-            guard let stableInfo = TPPeerStableInfo(data: egoStableData, sig: egoStableSig) else {
-                 os_log("getViews failed to create TPPeerStableInfo", log: tplogDebug, type: .error)
-                 reply(nil, ContainerError.invalidStableInfoOrSig)
-                 return
-            }
-
-            let keyFactory = TPECPublicKeyFactory()
-            guard let permanentInfo = TPPeerPermanentInfo(peerID: egoPeerID, data: egoPermData, sig: egoPermSig, keyFactory: keyFactory) else {
-                 os_log("getViews failed to create TPPeerPermanentInfo", log: tplogDebug, type: .error)
-                 reply(nil, ContainerError.invalidPermanentInfoOrSig)
-                 return
-            }
-
-            do {
-                let views = try self.model.getViewsForPeer(permanentInfo, stableInfo: stableInfo, inViews: Set(inViews))
-                reply(Array(views), nil)
-            } catch {
-                reply(nil, error)
-                return
-            }
-        }
-    }
-
     func reset(resetReason: CuttlefishResetReason, reply: @escaping (Error?) -> Void) {
         self.semaphore.wait()
         let reply: (Error?) -> Void = {
     func reset(resetReason: CuttlefishResetReason, reply: @escaping (Error?) -> Void) {
         self.semaphore.wait()
         let reply: (Error?) -> Void = {
-            os_log("reset complete %@", log: tplogTrace, type: .info, traceError($0))
+            os_log("reset complete %{public}@", log: tplogTrace, type: .info, traceError($0))
             self.semaphore.signal()
             reply($0)
         }
             self.semaphore.signal()
             reply($0)
         }
@@ -1178,9 +1189,9 @@ class Container: NSObject {
                 $0.resetReason = resetReason
             }
             self.cuttlefish.reset(request) { response, error in
                 $0.resetReason = resetReason
             }
             self.cuttlefish.reset(request) { response, error in
-                os_log("Reset(%@): %@, error: %@", log: tplogDebug, "\(String(describing: request))", "\(String(describing: response))", "\(String(describing: error))")
+                os_log("Reset(%{public}@): %{public}@, error: %{public}@", log: tplogDebug, "\(String(describing: request))", "\(String(describing: response))", "\(String(describing: error))")
                 guard let response = response, error == nil else {
                 guard let response = response, error == nil else {
-                    os_log("reset failed: %@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "no error")
+                    os_log("reset failed: %{public}@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "no error")
                     reply(error ?? ContainerError.cloudkitResponseMissing)
                     return
                 }
                     reply(error ?? ContainerError.cloudkitResponseMissing)
                     return
                 }
@@ -1196,7 +1207,7 @@ class Container: NSObject {
                         os_log("reset succeded", log: tplogDebug, type: .default)
                         reply(nil)
                     } catch {
                         os_log("reset succeded", log: tplogDebug, type: .default)
                         reply(nil)
                     } catch {
-                        os_log("reset persist failed: %@", log: tplogDebug, type: .default, (error as CVarArg))
+                        os_log("reset persist failed: %{public}@", log: tplogDebug, type: .default, (error as CVarArg))
                         reply(error)
                     }
                 }
                         reply(error)
                     }
                 }
@@ -1207,7 +1218,7 @@ class Container: NSObject {
     func localReset(reply: @escaping (Error?) -> Void) {
         self.semaphore.wait()
         let reply: (Error?) -> Void = {
     func localReset(reply: @escaping (Error?) -> Void) {
         self.semaphore.wait()
         let reply: (Error?) -> Void = {
-            os_log("localReset complete %@", log: tplogTrace, type: .info, traceError($0))
+            os_log("localReset complete %{public}@", log: tplogTrace, type: .info, traceError($0))
             self.semaphore.signal()
             reply($0)
         }
             self.semaphore.signal()
             reply($0)
         }
@@ -1240,7 +1251,7 @@ class Container: NSObject {
         }
     }
 
         }
     }
 
-    // policyVersion should only be non-nil for testing, to override prevailingPolicyVersion
+    // policyVersion should only be non-nil for testing, to override prevailingPolicyVersion.versionNumber
     func prepare(epoch: UInt64,
                  machineID: String,
                  bottleSalt: String,
     func prepare(epoch: UInt64,
                  machineID: String,
                  bottleSalt: String,
@@ -1249,17 +1260,17 @@ class Container: NSObject {
                  deviceName: String?,
                  serialNumber: String,
                  osVersion: String,
                  deviceName: String?,
                  serialNumber: String,
                  osVersion: String,
-                 policyVersion: UInt64?,
+                 policyVersion: TPPolicyVersion?,
                  policySecrets: [String: Data]?,
                  signingPrivateKeyPersistentRef: Data?,
                  encryptionPrivateKeyPersistentRef: Data?,
                  policySecrets: [String: Data]?,
                  signingPrivateKeyPersistentRef: Data?,
                  encryptionPrivateKeyPersistentRef: Data?,
-                 reply: @escaping (String?, Data?, Data?, Data?, Data?, Error?) -> Void) {
+                 reply: @escaping (String?, Data?, Data?, Data?, Data?, Set<String>?, TPPolicy?, Error?) -> Void) {
         self.semaphore.wait()
         self.semaphore.wait()
-        let reply: (String?, Data?, Data?, Data?, Data?, Error?) -> Void = {
-            os_log("prepare complete peerID: %@ %@",
-                   log: tplogTrace, type: .info, ($0 ?? "NULL") as CVarArg, traceError($5))
+        let reply: (String?, Data?, Data?, Data?, Data?, Set<String>?, TPPolicy?, Error?) -> Void = {
+            os_log("prepare complete peerID: %{public}@ %{public}@",
+                   log: tplogTrace, type: .info, ($0 ?? "NULL") as CVarArg, traceError($7))
             self.semaphore.signal()
             self.semaphore.signal()
-            reply($0, $1, $2, $3, $4, $5)
+            reply($0, $1, $2, $3, $4, $5, $6, $7)
         }
 
         // Create a new peer identity with random keys, and store the keys in keychain
         }
 
         // Create a new peer identity with random keys, and store the keys in keychain
@@ -1270,6 +1281,7 @@ class Container: NSObject {
             signingKeyPair = try self.loadOrCreateKeyPair(privateKeyPersistentRef: signingPrivateKeyPersistentRef)
             encryptionKeyPair = try self.loadOrCreateKeyPair(privateKeyPersistentRef: encryptionPrivateKeyPersistentRef)
 
             signingKeyPair = try self.loadOrCreateKeyPair(privateKeyPersistentRef: signingPrivateKeyPersistentRef)
             encryptionKeyPair = try self.loadOrCreateKeyPair(privateKeyPersistentRef: encryptionPrivateKeyPersistentRef)
 
+            // <rdar://problem/56270219> Octagon: use epoch transmitted across pairing channel
             permanentInfo = try TPPeerPermanentInfo(machineID: machineID,
                                                     modelID: modelID,
                                                     epoch: 1,
             permanentInfo = try TPPeerPermanentInfo(machineID: machineID,
                                                     modelID: modelID,
                                                     epoch: 1,
@@ -1278,7 +1290,7 @@ class Container: NSObject {
                                                     peerIDHashAlgo: TPHashAlgo.SHA256)
 
         } catch {
                                                     peerIDHashAlgo: TPHashAlgo.SHA256)
 
         } catch {
-            reply(nil, nil, nil, nil, nil, error)
+            reply(nil, nil, nil, nil, nil, nil, nil, error)
             return
         }
 
             return
         }
 
@@ -1294,63 +1306,73 @@ class Container: NSObject {
 
             _ = try saveSecret(bottle.secret, label: peerID)
         } catch {
 
             _ = try saveSecret(bottle.secret, label: peerID)
         } catch {
-            os_log("bottle creation failed: %@", log: tplogDebug, type: .default, error as CVarArg)
-            reply(nil, nil, nil, nil, nil, error)
+            os_log("bottle creation failed: %{public}@", log: tplogDebug, type: .default, error as CVarArg)
+            reply(nil, nil, nil, nil, nil, nil, nil, error)
             return
         }
 
         saveEgoKeyPair(signingKeyPair, identifier: signingKeyIdentifier(peerID: peerID)) { success, error in
             guard success else {
             return
         }
 
         saveEgoKeyPair(signingKeyPair, identifier: signingKeyIdentifier(peerID: peerID)) { success, error in
             guard success else {
-                os_log("Unable to save signing key: %@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "error missing")
-                reply(nil, nil, nil, nil, nil, error ?? ContainerError.failedToStoreIdentity)
+                os_log("Unable to save signing key: %{public}@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "error missing")
+                reply(nil, nil, nil, nil, nil, nil, nil, error ?? ContainerError.failedToStoreIdentity)
                 return
             }
             saveEgoKeyPair(encryptionKeyPair, identifier: encryptionKeyIdentifier(peerID: peerID)) { success, error in
                 guard success else {
                 return
             }
             saveEgoKeyPair(encryptionKeyPair, identifier: encryptionKeyIdentifier(peerID: peerID)) { success, error in
                 guard success else {
-                    os_log("Unable to save encryption key: %@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "error missing")
-                    reply(nil, nil, nil, nil, nil, error ?? ContainerError.failedToStoreIdentity)
+                    os_log("Unable to save encryption key: %{public}@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "error missing")
+                    reply(nil, nil, nil, nil, nil, nil, nil, error ?? ContainerError.failedToStoreIdentity)
                     return
                 }
 
                     return
                 }
 
-                // Save the prepared identity as containerMO.egoPeer* and its bottle
-                self.moc.performAndWait {
-                    do {
-                        let policyVersion = policyVersion ?? prevailingPolicyVersion
-                        let policyDoc = try self.getPolicyDoc(policyVersion)
-
-                        let stableInfo = TPPeerStableInfo(clock: 1,
-                                                          policyVersion: policyDoc.policyVersion,
-                                                          policyHash: policyDoc.policyHash,
-                                                          policySecrets: policySecrets,
-                                                          deviceName: deviceName,
-                                                          serialNumber: serialNumber,
-                                                          osVersion: osVersion,
-                                                          signing: signingKeyPair,
-                                                          recoverySigningPubKey: nil,
-                                                          recoveryEncryptionPubKey: nil,
-                                                          error: nil)
-
-                        self.containerMO.egoPeerID = permanentInfo.peerID
-                        self.containerMO.egoPeerPermanentInfo = permanentInfo.data
-                        self.containerMO.egoPeerPermanentInfoSig = permanentInfo.sig
-                        self.containerMO.egoPeerStableInfo = stableInfo.data
-                        self.containerMO.egoPeerStableInfoSig = stableInfo.sig
-
-                        let bottleMO = BottleMO(context: self.moc)
-                        bottleMO.peerID = bottle.peerID
-                        bottleMO.bottleID = bottle.bottleID
-                        bottleMO.escrowedSigningSPKI = bottle.escrowSigningSPKI
-                        bottleMO.signatureUsingEscrowKey = bottle.signatureUsingEscrowKey
-                        bottleMO.signatureUsingPeerKey = bottle.signatureUsingPeerKey
-                        bottleMO.contents = bottle.contents
-
-                        self.containerMO.addToBottles(bottleMO)
+                let policyVersion = policyVersion ?? prevailingPolicyVersion
+                self.fetchPolicyDocumentWithSemaphore(version: policyVersion) { policyDoc, policyFetchError in
+                    guard let policyDoc = policyDoc, policyFetchError == nil else {
+                        os_log("Unable to fetch policy: %{public}@", log: tplogDebug, type: .default, (policyFetchError as CVarArg?) ?? "error missing")
+                        reply(nil, nil, nil, nil, nil, nil, nil, error ?? ContainerError.unknownInternalError)
+                        return
+                    }
 
 
-                        try self.moc.save()
+                    // Save the prepared identity as containerMO.egoPeer* and its bottle
+                    self.moc.performAndWait {
+                        do {
 
 
-                        reply(permanentInfo.peerID, permanentInfo.data, permanentInfo.sig, stableInfo.data, stableInfo.sig, nil)
-                    } catch {
-                        reply(nil, nil, nil, nil, nil, error)
+                            let stableInfo = TPPeerStableInfo(clock: 1,
+                                                              frozenPolicyVersion: frozenPolicyVersion,
+                                                              flexiblePolicyVersion: policyDoc.version,
+                                                              policySecrets: policySecrets,
+                                                              deviceName: deviceName,
+                                                              serialNumber: serialNumber,
+                                                              osVersion: osVersion,
+                                                              signing: signingKeyPair,
+                                                              recoverySigningPubKey: nil,
+                                                              recoveryEncryptionPubKey: nil,
+                                                              error: nil)
+
+                            self.containerMO.egoPeerID = permanentInfo.peerID
+                            self.containerMO.egoPeerPermanentInfo = permanentInfo.data
+                            self.containerMO.egoPeerPermanentInfoSig = permanentInfo.sig
+                            self.containerMO.egoPeerStableInfo = stableInfo.data
+                            self.containerMO.egoPeerStableInfoSig = stableInfo.sig
+
+                            let bottleMO = BottleMO(context: self.moc)
+                            bottleMO.peerID = bottle.peerID
+                            bottleMO.bottleID = bottle.bottleID
+                            bottleMO.escrowedSigningSPKI = bottle.escrowSigningSPKI
+                            bottleMO.signatureUsingEscrowKey = bottle.signatureUsingEscrowKey
+                            bottleMO.signatureUsingPeerKey = bottle.signatureUsingPeerKey
+                            bottleMO.contents = bottle.contents
+
+                            self.containerMO.addToBottles(bottleMO)
+
+                            let (syncingViews, policy) = try self.policyAndViewsFor(permanentInfo: permanentInfo, stableInfo: stableInfo)
+
+                            try self.moc.save()
+
+                            reply(permanentInfo.peerID, permanentInfo.data, permanentInfo.sig, stableInfo.data, stableInfo.sig, syncingViews, policy, nil)
+                        } catch {
+                            os_log("Unable to save identity: %{public}@", log: tplogDebug, type: .default, error as CVarArg)
+                            reply(nil, nil, nil, nil, nil, nil, nil, error)
+                        }
                     }
                 }
             }
                     }
                 }
             }
@@ -1358,7 +1380,7 @@ class Container: NSObject {
     }
     func getEgoEpoch(reply: @escaping (UInt64, Error?) -> Void) {
         let reply: (UInt64, Error?) -> Void = {
     }
     func getEgoEpoch(reply: @escaping (UInt64, Error?) -> Void) {
         let reply: (UInt64, Error?) -> Void = {
-            os_log("getEgoEpoch complete: %d %@", log: tplogTrace, type: .info, $0, traceError($1))
+            os_log("getEgoEpoch complete: %d %{public}@", log: tplogTrace, type: .info, $0, traceError($1))
             reply($0, $1)
         }
 
             reply($0, $1)
         }
 
@@ -1381,7 +1403,7 @@ class Container: NSObject {
                    reply: @escaping (String?, [CKRecord], Error?) -> Void) {
         self.semaphore.wait()
         let reply: (String?, [CKRecord], Error?) -> Void = {
                    reply: @escaping (String?, [CKRecord], Error?) -> Void) {
         self.semaphore.wait()
         let reply: (String?, [CKRecord], Error?) -> Void = {
-            os_log("establish complete peer: %@ %@",
+            os_log("establish complete peer: %{public}@ %{public}@",
                    log: tplogTrace, type: .default, ($0 ?? "NULL") as CVarArg, traceError($2))
             self.semaphore.signal()
             reply($0, $1, $2)
                    log: tplogTrace, type: .default, ($0 ?? "NULL") as CVarArg, traceError($2))
             self.semaphore.signal()
             reply($0, $1, $2)
@@ -1391,7 +1413,9 @@ class Container: NSObject {
             self.onqueueEstablish(ckksKeys: ckksKeys,
                                   tlkShares: tlkShares,
                                   preapprovedKeys: preapprovedKeys,
             self.onqueueEstablish(ckksKeys: ckksKeys,
                                   tlkShares: tlkShares,
                                   preapprovedKeys: preapprovedKeys,
-                                  reply: reply)
+                                  reply: { peerID, ckrecords, _, _, error in
+                                    reply(peerID, ckrecords, error)
+            })
         }
     }
 
         }
     }
 
@@ -1402,10 +1426,76 @@ class Container: NSObject {
         ttr.trigger()
     }
 
         ttr.trigger()
     }
 
+    func fetchAfterEstablish(ckksKeys: [CKKSKeychainBackedKeySet],
+                             tlkShares: [CKKSTLKShare],
+                             reply: @escaping (String?, [CKRecord], Set<String>?, TPPolicy?, Error?) -> Void) {
+        self.moc.performAndWait {
+            do {
+                try self.deleteLocalCloudKitData()
+            } catch {
+                os_log("fetchAfterEstablish failed to reset local data: %{public}@", log: tplogDebug, type: .default, error as CVarArg)
+                reply(nil, [], nil, nil, error)
+                return
+            }
+            self.onqueueFetchAndPersistChanges { error in
+                guard error == nil else {
+                    os_log("fetchAfterEstablish failed to fetch changes: %{public}@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "error missing")
+                    reply(nil, [], nil, nil, error)
+                    return
+                }
+
+                self.moc.performAndWait {
+                    guard let egoPeerID = self.containerMO.egoPeerID,
+                          let egoPermData = self.containerMO.egoPeerPermanentInfo,
+                          let egoPermSig = self.containerMO.egoPeerPermanentInfoSig,
+                          let egoStableData = self.containerMO.egoPeerStableInfo,
+                          let egoStableSig = self.containerMO.egoPeerStableInfoSig
+                    else {
+                        os_log("fetchAfterEstablish: failed to fetch egoPeerID", log: tplogDebug, type: .default)
+                        reply(nil, [], nil, nil, ContainerError.noPreparedIdentity)
+                        return
+                    }
+                    guard self.model.hasPeer(withID: egoPeerID) else {
+                        os_log("fetchAfterEstablish: did not find peer %{public}@ in model", log: tplogDebug, type: .default, egoPeerID)
+                        reply(nil, [], nil, nil, ContainerError.invalidPeerID)
+                        return
+                    }
+                    let keyFactory = TPECPublicKeyFactory()
+                    guard let selfPermanentInfo = TPPeerPermanentInfo(peerID: egoPeerID, data: egoPermData, sig: egoPermSig, keyFactory: keyFactory) else {
+                        reply(nil, [], nil, nil, ContainerError.invalidPermanentInfoOrSig)
+                        return
+                    }
+                    guard let selfStableInfo = TPPeerStableInfo(data: egoStableData, sig: egoStableSig) else {
+                        os_log("cannot create TPPeerStableInfo", log: tplogDebug, type: .default)
+                        reply(nil, [], nil, nil, ContainerError.invalidStableInfoOrSig)
+                        return
+                    }
+                    self.onqueueUpdateTLKs(ckksKeys: ckksKeys, tlkShares: tlkShares) { ckrecords, error in
+                        guard error == nil else {
+                            os_log("fetchAfterEstablish failed to update TLKs: %{public}@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "error missing")
+                            reply(nil, [], nil, nil, error)
+                            return
+                        }
+
+                        do {
+                            let (syncingViews, policy) = try self.policyAndViewsFor(permanentInfo: selfPermanentInfo,
+                                                                                    stableInfo: selfStableInfo)
+                            os_log("fetchAfterEstablish succeeded", log: tplogDebug, type: .default)
+                            reply(egoPeerID, ckrecords ?? [], syncingViews, policy, nil)
+                        } catch {
+                            os_log("fetchAfterEstablish failed: %{public}@", log: tplogDebug, type: .default, (error as CVarArg))
+                            reply(nil, [], nil, nil, error)
+                        }
+                    }
+                }
+            }
+        }
+    }
+
     func onqueueEstablish(ckksKeys: [CKKSKeychainBackedKeySet],
                           tlkShares: [CKKSTLKShare],
                           preapprovedKeys: [Data]?,
     func onqueueEstablish(ckksKeys: [CKKSKeychainBackedKeySet],
                           tlkShares: [CKKSTLKShare],
                           preapprovedKeys: [Data]?,
-                          reply: @escaping (String?, [CKRecord], Error?) -> Void) {
+                          reply: @escaping (String?, [CKRecord], Set<String>?, TPPolicy?, Error?) -> Void) {
         // Fetch ego peer identity from local storage.
         guard let egoPeerID = self.containerMO.egoPeerID,
             let egoPermData = self.containerMO.egoPeerPermanentInfo,
         // Fetch ego peer identity from local storage.
         guard let egoPeerID = self.containerMO.egoPeerID,
             let egoPermData = self.containerMO.egoPeerPermanentInfo,
@@ -1413,31 +1503,31 @@ class Container: NSObject {
             let egoStableData = self.containerMO.egoPeerStableInfo,
             let egoStableSig = self.containerMO.egoPeerStableInfoSig
             else {
             let egoStableData = self.containerMO.egoPeerStableInfo,
             let egoStableSig = self.containerMO.egoPeerStableInfoSig
             else {
-                reply(nil, [], ContainerError.noPreparedIdentity)
+                reply(nil, [], nil, nil, ContainerError.noPreparedIdentity)
                 return
         }
 
         let keyFactory = TPECPublicKeyFactory()
         guard let selfPermanentInfo = TPPeerPermanentInfo(peerID: egoPeerID, data: egoPermData, sig: egoPermSig, keyFactory: keyFactory) else {
                 return
         }
 
         let keyFactory = TPECPublicKeyFactory()
         guard let selfPermanentInfo = TPPeerPermanentInfo(peerID: egoPeerID, data: egoPermData, sig: egoPermSig, keyFactory: keyFactory) else {
-            reply(nil, [], ContainerError.invalidPermanentInfoOrSig)
+            reply(nil, [], nil, nil, ContainerError.invalidPermanentInfoOrSig)
             return
         }
         guard let selfStableInfo = TPPeerStableInfo(data: egoStableData, sig: egoStableSig) else {
             os_log("cannot create TPPeerStableInfo", log: tplogDebug, type: .default)
             return
         }
         guard let selfStableInfo = TPPeerStableInfo(data: egoStableData, sig: egoStableSig) else {
             os_log("cannot create TPPeerStableInfo", log: tplogDebug, type: .default)
-            reply(nil, [], ContainerError.invalidStableInfoOrSig)
+            reply(nil, [], nil, nil, ContainerError.invalidStableInfoOrSig)
             return
         }
         guard self.onqueueMachineIDAllowedByIDMS(machineID: selfPermanentInfo.machineID) else {
             return
         }
         guard self.onqueueMachineIDAllowedByIDMS(machineID: selfPermanentInfo.machineID) else {
-            os_log("establish: self machineID %@ not on list", log: tplogDebug, type: .debug, selfPermanentInfo.machineID)
+            os_log("establish: self machineID %{public}@ not on list", log: tplogDebug, type: .debug, selfPermanentInfo.machineID)
             self.onqueueTTRUntrusted()
             self.onqueueTTRUntrusted()
-            reply(nil, [], ContainerError.preparedIdentityNotOnAllowedList(selfPermanentInfo.machineID))
+            reply(nil, [], nil, nil, ContainerError.preparedIdentityNotOnAllowedList(selfPermanentInfo.machineID))
             return
         }
 
         loadEgoKeys(peerID: egoPeerID) { egoPeerKeys, error in
              guard let egoPeerKeys = egoPeerKeys else {
             return
         }
 
         loadEgoKeys(peerID: egoPeerID) { egoPeerKeys, error in
              guard let egoPeerKeys = egoPeerKeys else {
-                os_log("Don't have my own peer keys; can't establish: %@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "error missing")
-                reply(nil, [], error)
+                os_log("Don't have my own peer keys; can't establish: %{public}@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "error missing")
+                reply(nil, [], nil, nil, error)
                 return
             }
             self.moc.performAndWait {
                 return
             }
             self.moc.performAndWait {
@@ -1449,8 +1539,8 @@ class Container: NSObject {
 
                     allTLKShares = octagonShares + sosShares
                 } catch {
 
                     allTLKShares = octagonShares + sosShares
                 } catch {
-                    os_log("Unable to make TLKShares for self: %@", log: tplogDebug, type: .default, error as CVarArg)
-                    reply(nil, [], error)
+                    os_log("Unable to make TLKShares for self: %{public}@", log: tplogDebug, type: .default, error as CVarArg)
+                    reply(nil, [], nil, nil, error)
                     return
                 }
 
                     return
                 }
 
@@ -1464,9 +1554,9 @@ class Container: NSObject {
                                                              signing: egoPeerKeys.signingKey,
                                                              currentMachineIDs: self.onqueueCurrentMIDList())
 
                                                              signing: egoPeerKeys.signingKey,
                                                              currentMachineIDs: self.onqueueCurrentMIDList())
 
-                    os_log("dynamic info: %@", log: tplogDebug, type: .default, dynamicInfo)
+                    os_log("dynamic info: %{public}@", log: tplogDebug, type: .default, dynamicInfo)
                 } catch {
                 } catch {
-                    reply(nil, [], error)
+                    reply(nil, [], nil, nil, error)
                     return
                 }
 
                     return
                 }
 
@@ -1483,24 +1573,24 @@ class Container: NSObject {
                 do {
                     bottle = try self.assembleBottle(egoPeerID: egoPeerID)
                 } catch {
                 do {
                     bottle = try self.assembleBottle(egoPeerID: egoPeerID)
                 } catch {
-                    reply(nil, [], error)
+                    reply(nil, [], nil, nil, error)
                     return
                 }
                     return
                 }
-                os_log("Beginning establish for peer %@", log: tplogDebug, type: .default, egoPeerID)
-                os_log("Establish permanentInfo: %@", log: tplogDebug, type: .debug, egoPermData.base64EncodedString())
-                os_log("Establish permanentInfoSig: %@", log: tplogDebug, type: .debug, egoPermSig.base64EncodedString())
-                os_log("Establish stableInfo: %@", log: tplogDebug, type: .debug, egoStableData.base64EncodedString())
-                os_log("Establish stableInfoSig: %@", log: tplogDebug, type: .debug, egoStableSig.base64EncodedString())
-                os_log("Establish dynamicInfo: %@", log: tplogDebug, type: .debug, peer.dynamicInfoAndSig.peerDynamicInfo.base64EncodedString())
-                os_log("Establish dynamicInfoSig: %@", log: tplogDebug, type: .debug, peer.dynamicInfoAndSig.sig.base64EncodedString())
+                os_log("Beginning establish for peer %{public}@", log: tplogDebug, type: .default, egoPeerID)
+                os_log("Establish permanentInfo: %{public}@", log: tplogDebug, type: .debug, egoPermData.base64EncodedString())
+                os_log("Establish permanentInfoSig: %{public}@", log: tplogDebug, type: .debug, egoPermSig.base64EncodedString())
+                os_log("Establish stableInfo: %{public}@", log: tplogDebug, type: .debug, egoStableData.base64EncodedString())
+                os_log("Establish stableInfoSig: %{public}@", log: tplogDebug, type: .debug, egoStableSig.base64EncodedString())
+                os_log("Establish dynamicInfo: %{public}@", log: tplogDebug, type: .debug, peer.dynamicInfoAndSig.peerDynamicInfo.base64EncodedString())
+                os_log("Establish dynamicInfoSig: %{public}@", log: tplogDebug, type: .debug, peer.dynamicInfoAndSig.sig.base64EncodedString())
 
                 os_log("Establish introducing %d key sets, %d tlk shares", log: tplogDebug, type: .default, viewKeys.count, allTLKShares.count)
 
                 do {
 
                 os_log("Establish introducing %d key sets, %d tlk shares", log: tplogDebug, type: .default, viewKeys.count, allTLKShares.count)
 
                 do {
-                    os_log("Establish bottle: %@", log: tplogDebug, type: .debug, try bottle.serializedData().base64EncodedString())
-                    os_log("Establish peer: %@", log: tplogDebug, type: .debug, try peer.serializedData().base64EncodedString())
+                    os_log("Establish bottle: %{public}@", log: tplogDebug, type: .debug, try bottle.serializedData().base64EncodedString())
+                    os_log("Establish peer: %{public}@", log: tplogDebug, type: .debug, try peer.serializedData().base64EncodedString())
                 } catch {
                 } catch {
-                    os_log("Establish unable to encode bottle/peer: %@", log: tplogDebug, type: .debug, error as CVarArg)
+                    os_log("Establish unable to encode bottle/peer: %{public}@", log: tplogDebug, type: .debug, error as CVarArg)
                 }
 
                 let request = EstablishRequest.with {
                 }
 
                 let request = EstablishRequest.with {
@@ -1510,16 +1600,23 @@ class Container: NSObject {
                     $0.tlkShares = allTLKShares
                 }
                 self.cuttlefish.establish(request) { response, error in
                     $0.tlkShares = allTLKShares
                 }
                 self.cuttlefish.establish(request) { response, error in
-                    os_log("Establish(%@): %@, error: %@", log: tplogDebug, "\(String(describing: request))", "\(String(describing: response))", "\(String(describing: error))")
-                    os_log("Establish: viewKeys: %@", String(describing: viewKeys))
+                    os_log("Establish(%{public}@): %{public}@, error: %{public}@", log: tplogDebug, "\(String(describing: request))", "\(String(describing: response))", "\(String(describing: error))")
+                    os_log("Establish: viewKeys: %{public}@", String(describing: viewKeys))
                     guard let response = response, error == nil else {
                     guard let response = response, error == nil else {
-                        os_log("establish failed: %@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "no error")
-                        reply(nil, [], error ?? ContainerError.cloudkitResponseMissing)
-                        return
+                        switch error {
+                        case CuttlefishErrorMatcher(code: CuttlefishErrorCode.establishFailed):
+                            os_log("establish returned failed, trying fetch", log: tplogDebug, type: .default)
+                            self.fetchAfterEstablish(ckksKeys: ckksKeys, tlkShares: tlkShares, reply: reply)
+                            return
+                        default:
+                            os_log("establish failed: %{public}@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "no error")
+                            reply(nil, [], nil, nil, error ?? ContainerError.cloudkitResponseMissing)
+                            return
+                        }
                     }
 
                     do {
                     }
 
                     do {
-                        os_log("Establish returned changes: %@", log: tplogDebug, type: .default, try response.changes.jsonString())
+                        os_log("Establish returned changes: %{public}@", log: tplogDebug, type: .default, try response.changes.jsonString())
                     } catch {
                         os_log("Establish returned changes, but they can't be serialized", log: tplogDebug, type: .default)
                     }
                     } catch {
                         os_log("Establish returned changes, but they can't be serialized", log: tplogDebug, type: .default)
                     }
@@ -1527,6 +1624,9 @@ class Container: NSObject {
                     let keyHierarchyRecords = response.zoneKeyHierarchyRecords.compactMap { CKRecord($0) }
 
                     do {
                     let keyHierarchyRecords = response.zoneKeyHierarchyRecords.compactMap { CKRecord($0) }
 
                     do {
+                        let (syncingViews, policy) = try self.policyAndViewsFor(permanentInfo: selfPermanentInfo,
+                                                                                stableInfo: selfStableInfo)
+
                         try self.persist(changes: response.changes)
 
                         guard response.changes.more == false else {
                         try self.persist(changes: response.changes)
 
                         guard response.changes.more == false else {
@@ -1535,22 +1635,22 @@ class Container: NSObject {
                             self.fetchAndPersistChanges { fetchError in
                                 guard fetchError == nil else {
                                     // This is an odd error condition: we might be able to fetch again and be in a good state...
                             self.fetchAndPersistChanges { fetchError in
                                 guard fetchError == nil else {
                                     // This is an odd error condition: we might be able to fetch again and be in a good state...
-                                    os_log("fetch-after-establish failed: %@", log: tplogDebug, type: .default, (fetchError as CVarArg?) ?? "no error")
-                                    reply(nil, keyHierarchyRecords, fetchError)
+                                    os_log("fetch-after-establish failed: %{public}@", log: tplogDebug, type: .default, (fetchError as CVarArg?) ?? "no error")
+                                    reply(nil, keyHierarchyRecords, nil, nil, fetchError)
                                     return
                                 }
 
                                 os_log("fetch-after-establish succeeded", log: tplogDebug, type: .default)
                                     return
                                 }
 
                                 os_log("fetch-after-establish succeeded", log: tplogDebug, type: .default)
-                                reply(egoPeerID, keyHierarchyRecords, nil)
+                                reply(egoPeerID, keyHierarchyRecords, nil, nil, nil)
                             }
                             return
                         }
 
                         os_log("establish succeeded", log: tplogDebug, type: .default)
                             }
                             return
                         }
 
                         os_log("establish succeeded", log: tplogDebug, type: .default)
-                        reply(egoPeerID, keyHierarchyRecords, nil)
+                        reply(egoPeerID, keyHierarchyRecords, syncingViews, policy, nil)
                     } catch {
                     } catch {
-                        os_log("establish handling failed: %@", log: tplogDebug, type: .default, (error as CVarArg))
-                        reply(nil, keyHierarchyRecords, error)
+                        os_log("establish handling failed: %{public}@", log: tplogDebug, type: .default, (error as CVarArg))
+                        reply(nil, keyHierarchyRecords, nil, nil, error)
                     }
                 }
             }
                     }
                 }
             }
@@ -1560,7 +1660,7 @@ class Container: NSObject {
     func setRecoveryKey(recoveryKey: String, salt: String, ckksKeys: [CKKSKeychainBackedKeySet], reply: @escaping (Error?) -> Void) {
         self.semaphore.wait()
         let reply: (Error?) -> Void = {
     func setRecoveryKey(recoveryKey: String, salt: String, ckksKeys: [CKKSKeychainBackedKeySet], reply: @escaping (Error?) -> Void) {
         self.semaphore.wait()
         let reply: (Error?) -> Void = {
-            os_log("setRecoveryKey complete: %@", log: tplogTrace, type: .info, traceError($0))
+            os_log("setRecoveryKey complete: %{public}@", log: tplogTrace, type: .info, traceError($0))
             self.semaphore.signal()
             reply($0)
         }
             self.semaphore.signal()
             reply($0)
         }
@@ -1578,7 +1678,7 @@ class Container: NSObject {
             do {
                 recoveryKeys = try RecoveryKey(recoveryKeyString: recoveryKey, recoverySalt: salt)
             } catch {
             do {
                 recoveryKeys = try RecoveryKey(recoveryKeyString: recoveryKey, recoverySalt: salt)
             } catch {
-                os_log("failed to create recovery keys: %@", log: tplogDebug, type: .default, error as CVarArg)
+                os_log("failed to create recovery keys: %{public}@", log: tplogDebug, type: .default, error as CVarArg)
                 reply(ContainerError.failedToCreateRecoveryKey)
                 return
             }
                 reply(ContainerError.failedToCreateRecoveryKey)
                 return
             }
@@ -1586,8 +1686,8 @@ class Container: NSObject {
             let signingPublicKey: Data = recoveryKeys.peerKeys.signingVerificationKey.keyData
             let encryptionPublicKey: Data = recoveryKeys.peerKeys.encryptionVerificationKey.keyData
 
             let signingPublicKey: Data = recoveryKeys.peerKeys.signingVerificationKey.keyData
             let encryptionPublicKey: Data = recoveryKeys.peerKeys.encryptionVerificationKey.keyData
 
-            os_log("setRecoveryKey signingPubKey: %@", log: tplogDebug, type: .debug, signingPublicKey.base64EncodedString())
-            os_log("setRecoveryKey encryptionPubKey: %@", log: tplogDebug, type: .debug, encryptionPublicKey.base64EncodedString())
+            os_log("setRecoveryKey signingPubKey: %@", log: tplogDebug, type: .default, signingPublicKey.base64EncodedString())
+            os_log("setRecoveryKey encryptionPubKey: %@", log: tplogDebug, type: .default, encryptionPublicKey.base64EncodedString())
 
             guard let stableInfoData = self.containerMO.egoPeerStableInfo else {
                 os_log("stableInfo does not exist", log: tplogDebug, type: .default)
 
             guard let stableInfoData = self.containerMO.egoPeerStableInfo else {
                 os_log("stableInfo does not exist", log: tplogDebug, type: .default)
@@ -1623,7 +1723,7 @@ class Container: NSObject {
 
             loadEgoKeyPair(identifier: signingKeyIdentifier(peerID: egoPeerID)) { signingKeyPair, error in
                 guard let signingKeyPair = signingKeyPair else {
 
             loadEgoKeyPair(identifier: signingKeyIdentifier(peerID: egoPeerID)) { signingKeyPair, error in
                 guard let signingKeyPair = signingKeyPair else {
-                    os_log("handle: no signing key pair: %@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "no error")
+                    os_log("handle: no signing key pair: %{public}@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "no error")
                     reply(error)
                     return
                 }
                     reply(error)
                     return
                 }
@@ -1634,12 +1734,12 @@ class Container: NSObject {
                                                           toPeer: recoveryKeys.peerKeys,
                                                           epoch: Int(permanentInfo.epoch))
 
                                                           toPeer: recoveryKeys.peerKeys,
                                                           epoch: Int(permanentInfo.epoch))
 
-                        let policyVersion = stableInfo.policyVersion
-                        let policyDoc = try self.getPolicyDoc(policyVersion)
+                        let policyVersion = stableInfo.bestPolicyVersion()
+                        let policyDoc = try self.getPolicyDoc(policyVersion.versionNumber)
 
                         let updatedStableInfo = TPPeerStableInfo(clock: stableInfo.clock + 1,
 
                         let updatedStableInfo = TPPeerStableInfo(clock: stableInfo.clock + 1,
-                                                                 policyVersion: policyDoc.policyVersion,
-                                                                 policyHash: policyDoc.policyHash,
+                                                                 frozenPolicyVersion: frozenPolicyVersion,
+                                                                 flexiblePolicyVersion: policyDoc.version,
                                                                  policySecrets: stableInfo.policySecrets,
                                                                  deviceName: stableInfo.deviceName,
                                                                  serialNumber: stableInfo.serialNumber,
                                                                  policySecrets: stableInfo.policySecrets,
                                                                  deviceName: stableInfo.deviceName,
                                                                  serialNumber: stableInfo.serialNumber,
@@ -1660,9 +1760,9 @@ class Container: NSObject {
                         }
 
                         self.cuttlefish.setRecoveryKey(request) { response, error in
                         }
 
                         self.cuttlefish.setRecoveryKey(request) { response, error in
-                            os_log("SetRecoveryKey(%@): %@, error: %@", log: tplogDebug, "\(String(describing: request))", "\(String(describing: response))", "\(String(describing: error))")
+                            os_log("SetRecoveryKey(%{public}@): %{public}@, error: %{public}@", log: tplogDebug, "\(String(describing: request))", "\(String(describing: response))", "\(String(describing: error))")
                             guard let response = response, error == nil else {
                             guard let response = response, error == nil else {
-                                os_log("setRecoveryKey failed: %@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "no error")
+                                os_log("setRecoveryKey failed: %{public}@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "no error")
                                 reply(error ?? ContainerError.cloudkitResponseMissing)
                                 return
                             }
                                 reply(error ?? ContainerError.cloudkitResponseMissing)
                                 return
                             }
@@ -1676,7 +1776,7 @@ class Container: NSObject {
                                     os_log("setRecoveryKey succeeded", log: tplogDebug, type: .default)
                                     reply(nil)
                                 } catch {
                                     os_log("setRecoveryKey succeeded", log: tplogDebug, type: .default)
                                     reply(nil)
                                 } catch {
-                                    os_log("setRecoveryKey handling failed: %@", log: tplogDebug, type: .default, (error as CVarArg))
+                                    os_log("setRecoveryKey handling failed: %{public}@", log: tplogDebug, type: .default, (error as CVarArg))
                                     reply(error)
                                 }
                             }
                                     reply(error)
                                 }
                             }
@@ -1715,13 +1815,13 @@ class Container: NSObject {
         if shouldPerformFetch == true {
             self.fetchViableBottlesWithSemaphore { _, _, error in
                 guard error == nil else {
         if shouldPerformFetch == true {
             self.fetchViableBottlesWithSemaphore { _, _, error in
                 guard error == nil else {
-                    os_log("fetchViableBottles failed: %@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "no error")
+                    os_log("fetchViableBottles failed: %{public}@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "no error")
                     reply(nil, error)
                     return
                 }
 
                 guard let newBottles = self.containerMO.bottles as? Set<BottleMO> else {
                     reply(nil, error)
                     return
                 }
 
                 guard let newBottles = self.containerMO.bottles as? Set<BottleMO> else {
-                    os_log("no bottles on container: %@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "no error")
+                    os_log("no bottles on container: %{public}@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "no error")
                     reply(nil, ContainerError.noBottlesPresent)
                     return
                 }
                     reply(nil, ContainerError.noBottlesPresent)
                     return
                 }
@@ -1731,7 +1831,7 @@ class Container: NSObject {
                     return
                 }
 
                     return
                 }
 
-                os_log("onqueueFindBottle found bottle: %@", log: tplogDebug, type: .default, newBottles)
+                os_log("onqueueFindBottle found bottle: %{public}@", log: tplogDebug, type: .default, newBottles)
 
                 bottles = newBottles.filter {
                     $0.bottleID == bottleID
 
                 bottles = newBottles.filter {
                     $0.bottleID == bottleID
@@ -1784,7 +1884,7 @@ class Container: NSObject {
 
             _ = try BottledPeer.verifyBottleSignature(data: bottledContents, signature: signatureUsingPeerKey, pubKey: signingKey)
         } catch {
 
             _ = try BottledPeer.verifyBottleSignature(data: bottledContents, signature: signatureUsingPeerKey, pubKey: signingKey)
         } catch {
-            os_log("Verification of bottled signature failed: %@", log: tplogDebug, type: .default, error as CVarArg)
+            os_log("Verification of bottled signature failed: %{public}@", log: tplogDebug, type: .default, error as CVarArg)
             throw ContainerError.failedToCreateBottledPeer
         }
 
             throw ContainerError.failedToCreateBottledPeer
         }
 
@@ -1804,84 +1904,61 @@ class Container: NSObject {
                                        signatureUsingEscrow: signatureUsingEscrowKey,
                                        signatureUsingPeerKey: signatureUsingPeerKey)
             } catch {
                                        signatureUsingEscrow: signatureUsingEscrowKey,
                                        signatureUsingPeerKey: signatureUsingPeerKey)
             } catch {
-                os_log("Creation of Bottled Peer failed: %@", log: tplogDebug, type: .default, error as CVarArg)
+                os_log("Creation of Bottled Peer failed: %{public}@", log: tplogDebug, type: .default, error as CVarArg)
                 throw ContainerError.failedToCreateBottledPeer
             }
         }
     }
 
                 throw ContainerError.failedToCreateBottledPeer
             }
         }
     }
 
-    func preflightVouchWithBottle(bottleID: String,
-                                  reply: @escaping (String?, Error?) -> Void) {
-        self.semaphore.wait()
-        let reply: (String?, Error?) -> Void = {
-            os_log("preflightVouchWithBottle complete: %@",
-                   log: tplogTrace, type: .info, traceError($1))
-            self.semaphore.signal()
-            reply($0, $1)
-        }
-
-        self.moc.performAndWait {
-            self.onqueueFindBottle(bottleID: bottleID) { bottleMO, error in
-                guard let bottleMO = bottleMO else {
-                    os_log("preflightVouchWithBottle found no bottle: %@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "")
-                    reply(nil, error)
-                    return
-                }
-
-                reply(bottleMO.peerID, nil)
-            }
-        }
-    }
-
     func vouchWithBottle(bottleID: String,
                          entropy: Data,
                          bottleSalt: String,
                          tlkShares: [CKKSTLKShare],
     func vouchWithBottle(bottleID: String,
                          entropy: Data,
                          bottleSalt: String,
                          tlkShares: [CKKSTLKShare],
-                         reply: @escaping (Data?, Data?, Error?) -> Void) {
+                         reply: @escaping (Data?, Data?, Int64, Int64, Error?) -> Void) {
         self.semaphore.wait()
         self.semaphore.wait()
-        let reply: (Data?, Data?, Error?) -> Void = {
-            os_log("vouchWithBottle complete: %@",
-                   log: tplogTrace, type: .info, traceError($2))
+        let reply: (Data?, Data?, Int64, Int64, Error?) -> Void = {
+            os_log("vouchWithBottle complete: %{public}@",
+                   log: tplogTrace, type: .info, traceError($4))
             self.semaphore.signal()
             self.semaphore.signal()
-            reply($0, $1, $2)
+            reply($0, $1, $2, $3, $4)
         }
 
         }
 
-        self.fetchAndPersistChanges { error in
+        self.fetchAndPersistChangesIfNeeded { error in
             guard error == nil else {
             guard error == nil else {
-                os_log("vouchWithBottle unable to fetch changes: %@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "")
-                reply(nil, nil, error)
+                os_log("vouchWithBottle unable to fetch changes: %{public}@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "")
+                reply(nil, nil, 0, 0, error)
                 return
             }
 
             self.onqueueFindBottle(bottleID: bottleID) { returnedBMO, error in
                 self.moc.performAndWait {
                     guard error == nil else {
                 return
             }
 
             self.onqueueFindBottle(bottleID: bottleID) { returnedBMO, error in
                 self.moc.performAndWait {
                     guard error == nil else {
-                        os_log("vouchWithBottle unable to find bottle for escrow record id: %@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "")
-                        reply(nil, nil, error)
+                        os_log("vouchWithBottle unable to find bottle for escrow record id: %{public}@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "")
+                        reply(nil, nil, 0, 0, error)
                         return
                     }
 
                     guard let bmo: BottleMO = returnedBMO else {
                         return
                     }
 
                     guard let bmo: BottleMO = returnedBMO else {
-                        os_log("vouchWithBottle bottle is nil: %@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "")
-                        reply(nil, nil, error)
+                        os_log("vouchWithBottle bottle is nil: %{public}@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "")
+                        reply(nil, nil, 0, 0, error)
                         return
                     }
 
                     guard let bottledContents = bmo.contents else {
                         return
                     }
 
                     guard let bottledContents = bmo.contents else {
-                        reply(nil, nil, ContainerError.bottleDoesNotContainContents)
+                        reply(nil, nil, 0, 0, ContainerError.bottleDoesNotContainContents)
                         return
                     }
                     guard let signatureUsingEscrowKey = bmo.signatureUsingEscrowKey else {
                         return
                     }
                     guard let signatureUsingEscrowKey = bmo.signatureUsingEscrowKey else {
-                        reply(nil, nil, ContainerError.bottleDoesNotContainEscrowKeySignature)
+                        reply(nil, nil, 0, 0, ContainerError.bottleDoesNotContainEscrowKeySignature)
                         return
                     }
 
                     guard let signatureUsingPeerKey = bmo.signatureUsingPeerKey else {
                         return
                     }
 
                     guard let signatureUsingPeerKey = bmo.signatureUsingPeerKey else {
-                        reply(nil, nil, ContainerError.bottleDoesNotContainerPeerKeySignature)
+                        reply(nil, nil, 0, 0, ContainerError.bottleDoesNotContainerPeerKeySignature)
                         return
                     }
                     guard let sponsorPeerID = bmo.peerID else {
                         return
                     }
                     guard let sponsorPeerID = bmo.peerID else {
-                        reply(nil, nil, ContainerError.bottleDoesNotContainPeerID)
+                        reply(nil, nil, 0, 0, ContainerError.bottleDoesNotContainPeerID)
                         return
                     }
 
                         return
                     }
 
@@ -1889,19 +1966,19 @@ class Container: NSObject {
                     do {
                         guard let sponsorPeer = self.model.peer(withID: sponsorPeerID) else {
                             os_log("vouchWithBottle: Unable to find peer that created the bottle", log: tplogDebug, type: .default)
                     do {
                         guard let sponsorPeer = self.model.peer(withID: sponsorPeerID) else {
                             os_log("vouchWithBottle: Unable to find peer that created the bottle", log: tplogDebug, type: .default)
-                            reply(nil, nil, ContainerError.bottleCreatingPeerNotFound)
+                            reply(nil, nil, 0, 0, ContainerError.bottleCreatingPeerNotFound)
                             return
                         }
                         guard let signingKey: _SFECPublicKey = sponsorPeer.permanentInfo.signingPubKey as? _SFECPublicKey else {
                             os_log("vouchWithBottle: Unable to create a sponsor public key", log: tplogDebug, type: .default)
                             return
                         }
                         guard let signingKey: _SFECPublicKey = sponsorPeer.permanentInfo.signingPubKey as? _SFECPublicKey else {
                             os_log("vouchWithBottle: Unable to create a sponsor public key", log: tplogDebug, type: .default)
-                            reply(nil, nil, ContainerError.signatureVerificationFailed)
+                            reply(nil, nil, 0, 0, ContainerError.signatureVerificationFailed)
                             return
                         }
 
                         _ = try BottledPeer.verifyBottleSignature(data: bottledContents, signature: signatureUsingPeerKey, pubKey: signingKey)
                     } catch {
                             return
                         }
 
                         _ = try BottledPeer.verifyBottleSignature(data: bottledContents, signature: signatureUsingPeerKey, pubKey: signingKey)
                     } catch {
-                        os_log("vouchWithBottle: Verification of bottled signature failed: %@", log: tplogDebug, type: .default, error as CVarArg)
-                        reply(nil, nil, ContainerError.failedToCreateBottledPeer)
+                        os_log("vouchWithBottle: Verification of bottled signature failed: %{public}@", log: tplogDebug, type: .default, error as CVarArg)
+                        reply(nil, nil, 0, 0, ContainerError.failedToCreateBottledPeer)
                         return
                     }
 
                         return
                     }
 
@@ -1924,53 +2001,53 @@ class Container: NSObject {
                                                           signatureUsingPeerKey: signatureUsingPeerKey)
                         } catch {
 
                                                           signatureUsingPeerKey: signatureUsingPeerKey)
                         } catch {
 
-                            os_log("Creation of Bottled Peer failed: %@", log: tplogDebug, type: .default, error as CVarArg)
-                            reply(nil, nil, ContainerError.failedToCreateBottledPeer)
+                            os_log("Creation of Bottled Peer failed: %{public}@", log: tplogDebug, type: .default, error as CVarArg)
+                            reply(nil, nil, 0, 0, ContainerError.failedToCreateBottledPeer)
                             return
                         }
                     }
 
                             return
                         }
                     }
 
-                    os_log("Have a bottle for peer %@", log: tplogDebug, type: .default, bottledPeer.peerID)
+                    os_log("Have a bottle for peer %{public}@", log: tplogDebug, type: .default, bottledPeer.peerID)
 
                     // Extract any TLKs we have been given
 
                     // Extract any TLKs we have been given
-                    extract(tlkShares: tlkShares, peer: bottledPeer.peerKeys)
+                    let (uniqueTLKsRecovered, totalSharesRecovered) = extract(tlkShares: tlkShares, peer: bottledPeer.peerKeys, model: self.model)
 
                     self.moc.performAndWait {
                         // I must have an ego identity in order to vouch using bottle
                         guard let egoPeerID = self.containerMO.egoPeerID else {
                             os_log("As a nonmember, can't vouch for someone else", log: tplogDebug, type: .default)
 
                     self.moc.performAndWait {
                         // I must have an ego identity in order to vouch using bottle
                         guard let egoPeerID = self.containerMO.egoPeerID else {
                             os_log("As a nonmember, can't vouch for someone else", log: tplogDebug, type: .default)
-                            reply(nil, nil, ContainerError.nonMember)
+                            reply(nil, nil, 0, 0, ContainerError.nonMember)
                             return
                         }
                         guard let permanentInfo = self.containerMO.egoPeerPermanentInfo else {
                             os_log("permanentInfo does not exist", log: tplogDebug, type: .default)
                             return
                         }
                         guard let permanentInfo = self.containerMO.egoPeerPermanentInfo else {
                             os_log("permanentInfo does not exist", log: tplogDebug, type: .default)
-                            reply(nil, nil, ContainerError.nonMember)
+                            reply(nil, nil, 0, 0, ContainerError.nonMember)
                             return
                         }
                         guard let permanentInfoSig = self.containerMO.egoPeerPermanentInfoSig else {
                             os_log("permanentInfoSig does not exist", log: tplogDebug, type: .default)
                             return
                         }
                         guard let permanentInfoSig = self.containerMO.egoPeerPermanentInfoSig else {
                             os_log("permanentInfoSig does not exist", log: tplogDebug, type: .default)
-                            reply(nil, nil, ContainerError.nonMember)
+                            reply(nil, nil, 0, 0, ContainerError.nonMember)
                             return
                         }
                         guard let stableInfo = self.containerMO.egoPeerStableInfo else {
                             os_log("stableInfo does not exist", log: tplogDebug, type: .default)
                             return
                         }
                         guard let stableInfo = self.containerMO.egoPeerStableInfo else {
                             os_log("stableInfo does not exist", log: tplogDebug, type: .default)
-                            reply(nil, nil, ContainerError.nonMember)
+                            reply(nil, nil, 0, 0, ContainerError.nonMember)
                             return
                         }
                         guard let stableInfoSig = self.containerMO.egoPeerStableInfoSig else {
                             os_log("stableInfoSig does not exist", log: tplogDebug, type: .default)
                             return
                         }
                         guard let stableInfoSig = self.containerMO.egoPeerStableInfoSig else {
                             os_log("stableInfoSig does not exist", log: tplogDebug, type: .default)
-                            reply(nil, nil, ContainerError.nonMember)
+                            reply(nil, nil, 0, 0, ContainerError.nonMember)
                             return
                         }
                         let keyFactory = TPECPublicKeyFactory()
                         guard let beneficiaryPermanentInfo = TPPeerPermanentInfo(peerID: egoPeerID, data: permanentInfo, sig: permanentInfoSig, keyFactory: keyFactory) else {
                             os_log("Invalid permenent info or signature; can't vouch for them", log: tplogDebug, type: .default)
                             return
                         }
                         let keyFactory = TPECPublicKeyFactory()
                         guard let beneficiaryPermanentInfo = TPPeerPermanentInfo(peerID: egoPeerID, data: permanentInfo, sig: permanentInfoSig, keyFactory: keyFactory) else {
                             os_log("Invalid permenent info or signature; can't vouch for them", log: tplogDebug, type: .default)
-                            reply(nil, nil, ContainerError.invalidPermanentInfoOrSig)
+                            reply(nil, nil, 0, 0, ContainerError.invalidPermanentInfoOrSig)
                             return
                         }
                         guard let beneficiaryStableInfo = TPPeerStableInfo(data: stableInfo, sig: stableInfoSig) else {
                             os_log("Invalid stableinfo or signature; van't vouch for them", log: tplogDebug, type: .default)
                             return
                         }
                         guard let beneficiaryStableInfo = TPPeerStableInfo(data: stableInfo, sig: stableInfoSig) else {
                             os_log("Invalid stableinfo or signature; van't vouch for them", log: tplogDebug, type: .default)
-                            reply(nil, nil, ContainerError.invalidStableInfoOrSig)
+                            reply(nil, nil, 0, 0, ContainerError.invalidStableInfoOrSig)
                             return
                         }
 
                             return
                         }
 
@@ -1980,11 +2057,11 @@ class Container: NSObject {
                                                                        withSponsorID: sponsorPeerID,
                                                                        reason: TPVoucherReason.restore,
                                                                        signing: bottledPeer.peerKeys.signingKey)
                                                                        withSponsorID: sponsorPeerID,
                                                                        reason: TPVoucherReason.restore,
                                                                        signing: bottledPeer.peerKeys.signingKey)
-                            reply(voucher.data, voucher.sig, nil)
+                            reply(voucher.data, voucher.sig, uniqueTLKsRecovered, totalSharesRecovered, nil)
                             return
                         } catch {
                             return
                         } catch {
-                            os_log("Error creating voucher: %@", log: tplogDebug, type: .default, error as CVarArg)
-                            reply(nil, nil, error)
+                            os_log("Error creating voucher with bottle: %{public}@", log: tplogDebug, type: .default, error as CVarArg)
+                            reply(nil, nil, 0, 0, error)
                             return
                         }
                     }
                             return
                         }
                     }
@@ -1999,7 +2076,7 @@ class Container: NSObject {
                               reply: @escaping (Data?, Data?, Error?) -> Void) {
         self.semaphore.wait()
         let reply: (Data?, Data?, Error?) -> Void = {
                               reply: @escaping (Data?, Data?, Error?) -> Void) {
         self.semaphore.wait()
         let reply: (Data?, Data?, Error?) -> Void = {
-            os_log("vouchWithRecoveryKey complete: %@",
+            os_log("vouchWithRecoveryKey complete: %{public}@",
                    log: tplogTrace, type: .info, traceError($2))
             self.semaphore.signal()
             reply($0, $1, $2)
                    log: tplogTrace, type: .info, traceError($2))
             self.semaphore.signal()
             reply($0, $1, $2)
@@ -2051,18 +2128,18 @@ class Container: NSObject {
             do {
                 recoveryKeys = try RecoveryKey(recoveryKeyString: recoveryKey, recoverySalt: salt)
             } catch {
             do {
                 recoveryKeys = try RecoveryKey(recoveryKeyString: recoveryKey, recoverySalt: salt)
             } catch {
-                os_log("failed to create recovery keys: %@", log: tplogDebug, type: .default, error as CVarArg)
+                os_log("failed to create recovery keys: %{public}@", log: tplogDebug, type: .default, error as CVarArg)
                 reply(nil, nil, ContainerError.failedToCreateRecoveryKey)
                 return
             }
 
                 reply(nil, nil, ContainerError.failedToCreateRecoveryKey)
                 return
             }
 
-            extract(tlkShares: tlkShares, peer: recoveryKeys.peerKeys)
+            extract(tlkShares: tlkShares, peer: recoveryKeys.peerKeys, model: self.model)
 
             let signingPublicKey: Data = recoveryKeys.peerKeys.signingKey.publicKey.keyData
             let encryptionPublicKey: Data = recoveryKeys.peerKeys.encryptionKey.publicKey.keyData
 
 
             let signingPublicKey: Data = recoveryKeys.peerKeys.signingKey.publicKey.keyData
             let encryptionPublicKey: Data = recoveryKeys.peerKeys.encryptionKey.publicKey.keyData
 
-            os_log("vouchWithRecoveryKey signingPubKey: %@", log: tplogDebug, type: .debug, signingPublicKey.base64EncodedString())
-            os_log("vouchWithRecoveryKey encryptionPubKey: %@", log: tplogDebug, type: .debug, encryptionPublicKey.base64EncodedString())
+            os_log("vouchWithRecoveryKey signingPubKey: %@", log: tplogDebug, type: .default, signingPublicKey.base64EncodedString())
+            os_log("vouchWithRecoveryKey encryptionPubKey: %@", log: tplogDebug, type: .default, encryptionPublicKey.base64EncodedString())
 
             guard self.model.isRecoveryKeyEnrolled() else {
                 os_log("Recovery Key is not enrolled", log: tplogDebug, type: .default)
 
             guard self.model.isRecoveryKeyEnrolled() else {
                 os_log("Recovery Key is not enrolled", log: tplogDebug, type: .default)
@@ -2086,7 +2163,7 @@ class Container: NSObject {
                 reply(voucher.data, voucher.sig, nil)
                 return
             } catch {
                 reply(voucher.data, voucher.sig, nil)
                 return
             } catch {
-                os_log("Error creating voucher using recovery key set: %@", log: tplogDebug, type: .default, error as CVarArg)
+                os_log("Error creating voucher using recovery key set: %{public}@", log: tplogDebug, type: .default, error as CVarArg)
                 reply(nil, nil, error)
                 return
             }
                 reply(nil, nil, error)
                 return
             }
@@ -2102,7 +2179,7 @@ class Container: NSObject {
                reply: @escaping (Data?, Data?, Error?) -> Void) {
         self.semaphore.wait()
         let reply: (Data?, Data?, Error?) -> Void = {
                reply: @escaping (Data?, Data?, Error?) -> Void) {
         self.semaphore.wait()
         let reply: (Data?, Data?, Error?) -> Void = {
-            os_log("vouch complete: %@", log: tplogTrace, type: .info, traceError($2))
+            os_log("vouch complete: %{public}@", log: tplogTrace, type: .info, traceError($2))
             self.semaphore.signal()
             reply($0, $1, $2)
         }
             self.semaphore.signal()
             reply($0, $1, $2)
         }
@@ -2138,65 +2215,79 @@ class Container: NSObject {
 
             loadEgoKeys(peerID: egoPeerID) { egoPeerKeys, error in
                 guard let egoPeerKeys = egoPeerKeys else {
 
             loadEgoKeys(peerID: egoPeerID) { egoPeerKeys, error in
                 guard let egoPeerKeys = egoPeerKeys else {
-                    os_log("Don't have my own keys: can't vouch for %@: %@", log: tplogDebug, type: .default, beneficiaryPermanentInfo, (error as CVarArg?) ?? "no error")
+                    os_log("Don't have my own keys: can't vouch for %{public}@(%{public}@): %{public}@", log: tplogDebug, type: .default, peerID, beneficiaryPermanentInfo, (error as CVarArg?) ?? "no error")
                     reply(nil, nil, error)
                     return
                 }
                     reply(nil, nil, error)
                     return
                 }
-                self.moc.performAndWait {
-                    let voucher: TPVoucher
-                    do {
-                        voucher = try self.model.createVoucher(forCandidate: beneficiaryPermanentInfo,
-                                                               stableInfo: beneficiaryStableInfo,
-                                                               withSponsorID: egoPeerID,
-                                                               reason: TPVoucherReason.secureChannel,
-                                                               signing: egoPeerKeys.signingKey)
 
 
-                    } catch {
-                        os_log("Error creating voucher: %@", log: tplogDebug, type: .default, error as CVarArg)
-                        reply(nil, nil, error)
+                self.fetchPolicyDocumentsWithSemaphore(versions: Set([beneficiaryStableInfo.bestPolicyVersion()])) { _, policyFetchError in
+                    guard policyFetchError == nil else {
+                        os_log("Unknown policy for beneficiary: %{public}@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "no error")
+                        reply(nil, nil, policyFetchError)
                         return
                     }
 
                         return
                     }
 
-                    // And generate and upload any tlkShares
-                    // Note that this might not be the whole list: <rdar://problem/47899980> Octagon: Limited Peers
-                    let tlkShares: [TLKShare]
-                    do {
-                        // Note: we only want to send up TLKs for uploaded ckks zones
-                        let ckksTLKs = ckksKeys.filter { !$0.newUpload }.map { $0.tlk }
+                    self.moc.performAndWait {
+                        let voucher: TPVoucher
+                        do {
+                            voucher = try self.model.createVoucher(forCandidate: beneficiaryPermanentInfo,
+                                                                   stableInfo: beneficiaryStableInfo,
+                                                                   withSponsorID: egoPeerID,
+                                                                   reason: TPVoucherReason.secureChannel,
+                                                                   signing: egoPeerKeys.signingKey)
+                        } catch {
+                            os_log("Error creating voucher: %{public}@", log: tplogDebug, type: .default, error as CVarArg)
+                            reply(nil, nil, error)
+                            return
+                        }
 
 
-                        tlkShares = try makeTLKShares(ckksTLKs: ckksTLKs,
-                                                      asPeer: egoPeerKeys,
-                                                      toPeer: beneficiaryPermanentInfo,
-                                                      epoch: Int(selfPermanentInfo.epoch))
-                    } catch {
-                        os_log("Unable to make TLKShares for beneficiary %@: %@", log: tplogDebug, type: .default, beneficiaryPermanentInfo, error as CVarArg)
-                        reply(nil, nil, error)
-                        return
-                    }
+                        // And generate and upload any tlkShares
+                        let tlkShares: [TLKShare]
+                        do {
+                            // Note that this might not be the whole list, so filter some of them out
+                            let peerViews = try? self.model.getViewsForPeer(beneficiaryPermanentInfo,
+                                                                            stableInfo: beneficiaryStableInfo)
+
+                            // Note: we only want to send up TLKs for uploaded ckks zones
+                            let ckksTLKs = ckksKeys
+                                .filter { !$0.newUpload }
+                                .filter { peerViews?.contains($0.tlk.zoneID.zoneName) ?? false }
+                                .map { $0.tlk }
+
+                            tlkShares = try makeTLKShares(ckksTLKs: ckksTLKs,
+                                                          asPeer: egoPeerKeys,
+                                                          toPeer: beneficiaryPermanentInfo,
+                                                          epoch: Int(selfPermanentInfo.epoch))
+                        } catch {
+                            os_log("Unable to make TLKShares for beneficiary %{public}@(%{public}@): %{public}@", log: tplogDebug, type: .default, peerID, beneficiaryPermanentInfo, error as CVarArg)
+                            reply(nil, nil, error)
+                            return
+                        }
 
 
-                    guard !tlkShares.isEmpty else {
-                        os_log("No TLKShares to upload for new peer, returning voucher", log: tplogDebug, type: .default)
-                        reply(voucher.data, voucher.sig, nil)
-                        return
-                    }
+                        guard !tlkShares.isEmpty else {
+                            os_log("No TLKShares to upload for new peer, returning voucher", log: tplogDebug, type: .default)
+                            reply(voucher.data, voucher.sig, nil)
+                            return
+                        }
 
 
-                    self.cuttlefish.updateTrust(changeToken: self.containerMO.changeToken ?? "",
-                                                peerID: egoPeerID,
-                                                stableInfoAndSig: nil,
-                                                dynamicInfoAndSig: nil,
-                                                tlkShares: tlkShares,
-                                                viewKeys: []) { response, error in
-                                                    guard let response = response, error == nil else {
-                                                        os_log("Unable to upload new tlkshares: %@", log: tplogDebug, type: .default, error as CVarArg? ?? "no error")
-                                                        reply(voucher.data, voucher.sig, error ?? ContainerError.cloudkitResponseMissing)
-                                                        return
-                                                    }
-
-                                                    let newKeyRecords = response.zoneKeyHierarchyRecords.map(CKRecord.init)
-                                                    os_log("Uploaded new tlkshares: %@", log: tplogDebug, type: .default, newKeyRecords)
-                                                    // We don't need to save these; CKKS will refetch them as needed
-
-                                                    reply(voucher.data, voucher.sig, nil)
+                        self.cuttlefish.updateTrust(changeToken: self.containerMO.changeToken ?? "",
+                                                    peerID: egoPeerID,
+                                                    stableInfoAndSig: nil,
+                                                    dynamicInfoAndSig: nil,
+                                                    tlkShares: tlkShares,
+                                                    viewKeys: []) { response, error in
+                                                        guard let response = response, error == nil else {
+                                                            os_log("Unable to upload new tlkshares: %{public}@", log: tplogDebug, type: .default, error as CVarArg? ?? "no error")
+                                                            reply(voucher.data, voucher.sig, error ?? ContainerError.cloudkitResponseMissing)
+                                                            return
+                                                        }
+
+                                                        let newKeyRecords = response.zoneKeyHierarchyRecords.map(CKRecord.init)
+                                                        os_log("Uploaded new tlkshares: %@", log: tplogDebug, type: .default, newKeyRecords)
+                                                        // We don't need to save these; CKKS will refetch them as needed
+
+                                                        reply(voucher.data, voucher.sig, nil)
+                        }
                     }
                 }
             }
                     }
                 }
             }
@@ -2206,7 +2297,7 @@ class Container: NSObject {
     func departByDistrustingSelf(reply: @escaping (Error?) -> Void) {
         self.semaphore.wait()
         let reply: (Error?) -> Void = {
     func departByDistrustingSelf(reply: @escaping (Error?) -> Void) {
         self.semaphore.wait()
         let reply: (Error?) -> Void = {
-            os_log("departByDistrustingSelf complete: %@", log: tplogTrace, type: .info, traceError($0))
+            os_log("departByDistrustingSelf complete: %{public}@", log: tplogTrace, type: .info, traceError($0))
             self.semaphore.signal()
             reply($0)
         }
             self.semaphore.signal()
             reply($0)
         }
@@ -2226,7 +2317,7 @@ class Container: NSObject {
                   reply: @escaping (Error?) -> Void) {
         self.semaphore.wait()
         let reply: (Error?) -> Void = {
                   reply: @escaping (Error?) -> Void) {
         self.semaphore.wait()
         let reply: (Error?) -> Void = {
-            os_log("distrust complete: %@", log: tplogTrace, type: .info, traceError($0))
+            os_log("distrust complete: %{public}@", log: tplogTrace, type: .info, traceError($0))
             self.semaphore.signal()
             reply($0)
         }
             self.semaphore.signal()
             reply($0)
         }
@@ -2259,7 +2350,7 @@ class Container: NSObject {
 
         loadEgoKeyPair(identifier: signingKeyIdentifier(peerID: egoPeerID)) { signingKeyPair, error in
             guard let signingKeyPair = signingKeyPair else {
 
         loadEgoKeyPair(identifier: signingKeyIdentifier(peerID: egoPeerID)) { signingKeyPair, error in
             guard let signingKeyPair = signingKeyPair else {
-                os_log("No longer have signing key pair; can't sign distrust: %@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "nil")
+                os_log("No longer have signing key pair; can't sign distrust: %{public}@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "nil")
                 reply(error)
                 return
             }
                 reply(error)
                 return
             }
@@ -2275,13 +2366,13 @@ class Container: NSObject {
                                                                              currentMachineIDs: self.onqueueCurrentMIDList())
 
                 } catch {
                                                                              currentMachineIDs: self.onqueueCurrentMIDList())
 
                 } catch {
-                    os_log("Error preparing dynamic info: %@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "nil")
+                    os_log("Error preparing dynamic info: %{public}@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "nil")
                     reply(error)
                     return
                 }
 
                 let signedDynamicInfo = SignedPeerDynamicInfo(dynamicInfo)
                     reply(error)
                     return
                 }
 
                 let signedDynamicInfo = SignedPeerDynamicInfo(dynamicInfo)
-                os_log("attempting distrust for %@ with: %@", log: tplogDebug, type: .default, peerIDs, dynamicInfo)
+                os_log("attempting distrust for %{public}@ with: %{public}@", log: tplogDebug, type: .default, peerIDs, dynamicInfo)
 
                 let request = UpdateTrustRequest.with {
                     $0.changeToken = self.containerMO.changeToken ?? ""
 
                 let request = UpdateTrustRequest.with {
                     $0.changeToken = self.containerMO.changeToken ?? ""
@@ -2289,9 +2380,9 @@ class Container: NSObject {
                     $0.dynamicInfoAndSig = signedDynamicInfo
                 }
                 self.cuttlefish.updateTrust(request) { response, error in
                     $0.dynamicInfoAndSig = signedDynamicInfo
                 }
                 self.cuttlefish.updateTrust(request) { response, error in
-                    os_log("UpdateTrust(%@): %@, error: %@", log: tplogDebug, "\(String(describing: request))", "\(String(describing: response))", "\(String(describing: error))")
+                    os_log("UpdateTrust(%{public}@): %{public}@, error: %{public}@", log: tplogDebug, "\(String(describing: request))", "\(String(describing: response))", "\(String(describing: error))")
                     guard let response = response, error == nil else {
                     guard let response = response, error == nil else {
-                        os_log("updateTrust failed: %@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "no error")
+                        os_log("updateTrust failed: %{public}@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "no error")
                         reply(error ?? ContainerError.cloudkitResponseMissing)
                         return
                     }
                         reply(error ?? ContainerError.cloudkitResponseMissing)
                         return
                     }
@@ -2301,7 +2392,7 @@ class Container: NSObject {
                         os_log("distrust succeeded", log: tplogDebug, type: .default)
                         reply(nil)
                     } catch {
                         os_log("distrust succeeded", log: tplogDebug, type: .default)
                         reply(nil)
                     } catch {
-                        os_log("distrust handling failed: %@", log: tplogDebug, type: .default, (error as CVarArg))
+                        os_log("distrust handling failed: %{public}@", log: tplogDebug, type: .default, (error as CVarArg))
                         reply(error)
                     }
                 }
                         reply(error)
                     }
                 }
@@ -2312,7 +2403,7 @@ class Container: NSObject {
     func fetchEscrowContents(reply: @escaping (Data?, String?, Data?, Error?) -> Void) {
         self.semaphore.wait()
         let reply: (Data?, String?, Data?, Error?) -> Void = {
     func fetchEscrowContents(reply: @escaping (Data?, String?, Data?, Error?) -> Void) {
         self.semaphore.wait()
         let reply: (Data?, String?, Data?, Error?) -> Void = {
-            os_log("fetchEscrowContents complete: %@", log: tplogTrace, type: .info, traceError($3))
+            os_log("fetchEscrowContents complete: %{public}@", log: tplogTrace, type: .info, traceError($3))
             self.semaphore.signal()
             reply($0, $1, $2, $3)
         }
             self.semaphore.signal()
             reply($0, $1, $2, $3)
         }
@@ -2344,7 +2435,7 @@ class Container: NSObject {
                 }
                 entropy = loaded
             } catch {
                 }
                 entropy = loaded
             } catch {
-                os_log("fetchEscrowContents failed to load entropy: %@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "no error")
+                os_log("fetchEscrowContents failed to load entropy: %{public}@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "no error")
                 reply(nil, nil, nil, error)
                 return
             }
                 reply(nil, nil, nil, error)
                 return
             }
@@ -2361,7 +2452,7 @@ class Container: NSObject {
     func fetchViableBottles(reply: @escaping ([String]?, [String]?, Error?) -> Void) {
         self.semaphore.wait()
         let reply: ([String]?, [String]?, Error?) -> Void = {
     func fetchViableBottles(reply: @escaping ([String]?, [String]?, Error?) -> Void) {
         self.semaphore.wait()
         let reply: ([String]?, [String]?, Error?) -> Void = {
-            os_log("fetchViableBottles complete: %@", log: tplogTrace, type: .info, traceError($2))
+            os_log("fetchViableBottles complete: %{public}@", log: tplogTrace, type: .info, traceError($2))
             self.semaphore.signal()
             reply($0, $1, $2)
         }
             self.semaphore.signal()
             reply($0, $1, $2)
         }
@@ -2397,7 +2488,7 @@ class Container: NSObject {
         let cachedBottles: TPCachedViableBottles = self.model.currentCachedViableBottlesSet()
         self.moc.performAndWait {
             if self.onqueueCachedBottlesContainEgoPeerBottle(cachedBottles: cachedBottles)
         let cachedBottles: TPCachedViableBottles = self.model.currentCachedViableBottlesSet()
         self.moc.performAndWait {
             if self.onqueueCachedBottlesContainEgoPeerBottle(cachedBottles: cachedBottles)
-                && (cachedBottles.viableBottles.count > 0 || cachedBottles.partialBottles.count > 0) {
+                && (!cachedBottles.viableBottles.isEmpty || !cachedBottles.partialBottles.isEmpty) {
                 os_log("returning from fetchViableBottles, using cached bottles", log: tplogDebug, type: .default)
                 reply(cachedBottles.viableBottles, cachedBottles.partialBottles, nil)
                 return
                 os_log("returning from fetchViableBottles, using cached bottles", log: tplogDebug, type: .default)
                 reply(cachedBottles.viableBottles, cachedBottles.partialBottles, nil)
                 return
@@ -2405,7 +2496,7 @@ class Container: NSObject {
 
             self.cuttlefish.fetchViableBottles { response, error in
                 guard error == nil else {
 
             self.cuttlefish.fetchViableBottles { response, error in
                 guard error == nil else {
-                    os_log("fetchViableBottles failed: %@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "no error")
+                    os_log("fetchViableBottles failed: %{public}@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "no error")
                     reply(nil, nil, error)
                     return
                 }
                     reply(nil, nil, error)
                     return
                 }
@@ -2413,7 +2504,7 @@ class Container: NSObject {
                 self.moc.performAndWait {
 
                     guard let escrowPairs = response?.viableBottles else {
                 self.moc.performAndWait {
 
                     guard let escrowPairs = response?.viableBottles else {
-                        os_log("fetchViableBottles returned no viable bottles: %@", log: tplogDebug, type: .default)
+                        os_log("fetchViableBottles returned no viable bottles", log: tplogDebug, type: .default)
                         reply([], [], nil)
                         return
                     }
                         reply([], [], nil)
                         return
                     }
@@ -2422,14 +2513,14 @@ class Container: NSObject {
                     if let partial = response?.partialBottles {
                         partialPairs = partial
                     } else {
                     if let partial = response?.partialBottles {
                         partialPairs = partial
                     } else {
-                        os_log("fetchViableBottles returned no partially viable bottles, but that's ok: %@", log: tplogDebug, type: .default)
+                        os_log("fetchViableBottles returned no partially viable bottles, but that's ok", log: tplogDebug, type: .default)
                     }
 
                     let viableBottleIDs = escrowPairs.compactMap { $0.bottle.bottleID }
                     }
 
                     let viableBottleIDs = escrowPairs.compactMap { $0.bottle.bottleID }
-                    os_log("fetchViableBottles returned viable bottles: %@", log: tplogDebug, type: .default, viableBottleIDs)
+                    os_log("fetchViableBottles returned viable bottles: %{public}@", log: tplogDebug, type: .default, viableBottleIDs)
 
                     let partialBottleIDs = partialPairs.compactMap { $0.bottle.bottleID }
 
                     let partialBottleIDs = partialPairs.compactMap { $0.bottle.bottleID }
-                    os_log("fetchViableBottles returned partial bottles: %@", log: tplogDebug, type: .default, partialBottleIDs)
+                    os_log("fetchViableBottles returned partial bottles: %{public}@", log: tplogDebug, type: .default, partialBottleIDs)
 
                     escrowPairs.forEach { pair in
                         let bottle = pair.bottle
 
                     escrowPairs.forEach { pair in
                         let bottle = pair.bottle
@@ -2458,7 +2549,7 @@ class Container: NSObject {
                         bmo.signatureUsingPeerKey = bottle.signatureUsingPeerKey
                         bmo.contents = bottle.contents
 
                         bmo.signatureUsingPeerKey = bottle.signatureUsingPeerKey
                         bmo.contents = bottle.contents
 
-                        os_log("fetchViableBottles saving new bottle: %@", log: tplogDebug, type: .default, bmo)
+                        os_log("fetchViableBottles saving new bottle: %{public}@", log: tplogDebug, type: .default, bmo)
                         self.containerMO.addToBottles(bmo)
                     }
 
                         self.containerMO.addToBottles(bmo)
                     }
 
@@ -2489,7 +2580,7 @@ class Container: NSObject {
                         bmo.signatureUsingPeerKey = bottle.signatureUsingPeerKey
                         bmo.contents = bottle.contents
 
                         bmo.signatureUsingPeerKey = bottle.signatureUsingPeerKey
                         bmo.contents = bottle.contents
 
-                        os_log("fetchViableBottles saving new bottle: %@", log: tplogDebug, type: .default, bmo)
+                        os_log("fetchViableBottles saving new bottle: %{public}@", log: tplogDebug, type: .default, bmo)
                         self.containerMO.addToBottles(bmo)
                     }
 
                         self.containerMO.addToBottles(bmo)
                     }
 
@@ -2500,7 +2591,7 @@ class Container: NSObject {
                         self.model.setViableBottles(cached)
                         reply(viableBottleIDs, partialBottleIDs, nil)
                     } catch {
                         self.model.setViableBottles(cached)
                         reply(viableBottleIDs, partialBottleIDs, nil)
                     } catch {
-                        os_log("fetchViableBottles unable to save bottles: %@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "no error")
+                        os_log("fetchViableBottles unable to save bottles: %{public}@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "no error")
                         reply(nil, nil, error)
                     }
 
                         reply(nil, nil, error)
                     }
 
@@ -2509,124 +2600,124 @@ class Container: NSObject {
         }
     }
 
         }
     }
 
-    func fetchPolicy(reply: @escaping (TPPolicy?, Error?) -> Void) {
+    func fetchCurrentPolicy(reply: @escaping (Set<String>?, TPPolicy?, Error?) -> Void) {
         self.semaphore.wait()
         self.semaphore.wait()
-        let reply: (TPPolicy?, Error?) -> Void = {
-            os_log("fetchPolicy complete: %@", log: tplogTrace, type: .info, traceError($1))
+        let reply: (Set<String>?, TPPolicy?, Error?) -> Void = {
+            os_log("fetchCurrentPolicy complete: %{public}@", log: tplogTrace, type: .info, traceError($2))
             self.semaphore.signal()
             self.semaphore.signal()
-            reply($0, $1)
+            reply($0, $1, $2)
         }
 
         self.moc.performAndWait {
         }
 
         self.moc.performAndWait {
-            var keys: [NSNumber: String] = [:]
-
-             guard let stableInfoData = self.containerMO.egoPeerStableInfo,
-                 let stableInfoSig = self.containerMO.egoPeerStableInfoSig else {
-                 os_log("fetchPolicy failed to find ego peer stableinfodata/sig", log: tplogDebug, type: .error)
-                 reply(nil, ContainerError.noPreparedIdentity)
-                 return
-             }
-             guard let stableInfo = TPPeerStableInfo(data: stableInfoData, sig: stableInfoSig) else {
-                 os_log("fetchPolicy failed to create TPPeerStableInfo", log: tplogDebug, type: .error)
-                 reply(nil, ContainerError.invalidStableInfoOrSig)
-                 return
-             }
-
-             let policyVersionCounter = stableInfo.policyVersion
-             let policyVersion = NSNumber(value: policyVersionCounter)
-             keys[policyVersion] = stableInfo.policyHash
-
-            if let policyDocument = self.model.policy(withVersion: policyVersionCounter) {
-                os_log("fetchPolicy: have a local version of policy %@: %@", log: tplogDebug, type: .default, policyVersion, policyDocument)
-                do {
-                    let policy = try policyDocument.policy(withSecrets: stableInfo.policySecrets, decrypter: Decrypter())
-                    reply(policy, nil)
-                    return
-                } catch {
-                    os_log("TPPolicyDocument failed: %@", log: tplogDebug, type: .default, error as CVarArg)
-                    reply(nil, error)
+            guard let egoPeerID = self.containerMO.egoPeerID,
+                let egoPermData = self.containerMO.egoPeerPermanentInfo,
+                let egoPermSig = self.containerMO.egoPeerPermanentInfoSig,
+                let stableInfoData = self.containerMO.egoPeerStableInfo,
+                let stableInfoSig = self.containerMO.egoPeerStableInfoSig else {
+                    os_log("fetchCurrentPolicy failed to find ego peer information", log: tplogDebug, type: .error)
+                    reply(nil, nil, ContainerError.noPreparedIdentity)
                     return
                     return
-                }
             }
 
             }
 
-            self.fetchPolicyDocuments(keys: keys) { result, error in
-                guard error == nil else {
-                    reply(nil, error)
-                    return
-                }
-                guard let result = result else {
-                    os_log("fetchPolicy: nil policies returned")
-                    reply(nil, ContainerError.policyDocumentDoesNotValidate)
-                    return
-                }
-                guard result.count == 1 else {
-                    os_log("fetchPolicy: wrong length returned")
-                    reply(nil, ContainerError.policyDocumentDoesNotValidate)
-                    return
-                }
-                guard let r = result[policyVersion] else {
-                    os_log("fetchPolicy: version not found")
-                    reply(nil, ContainerError.unknownPolicyVersion(policyVersion.uint64Value))
-                    return
-                }
-                guard let data = r[1].data(using: .utf8) else {
-                    os_log("fetchPolicy: failed to convert data")
-                    reply(nil, ContainerError.unknownPolicyVersion(policyVersion.uint64Value))
-                    return
-                }
-                guard let pd = TPPolicyDocument.policyDoc(withHash: r[0], data: data) else {
-                    os_log("fetchPolicy: pd is nil")
-                    reply(nil, ContainerError.policyDocumentDoesNotValidate)
-                    return
-                }
-                do {
-                    let policy = try pd.policy(withSecrets: stableInfo.policySecrets, decrypter: Decrypter())
-                    reply(policy, nil)
-                } catch {
-                    os_log("TPPolicyDocument: %@", log: tplogDebug, type: .default, error as CVarArg)
-                    reply(nil, error)
-                }
+            let keyFactory = TPECPublicKeyFactory()
+            guard let permanentInfo = TPPeerPermanentInfo(peerID: egoPeerID, data: egoPermData, sig: egoPermSig, keyFactory: keyFactory) else {
+                os_log("fetchCurrentPolicy failed to create TPPeerPermanentInfo", log: tplogDebug, type: .error)
+                reply(nil, nil, ContainerError.invalidPermanentInfoOrSig)
+                return
+            }
+            guard let stableInfo = TPPeerStableInfo(data: stableInfoData, sig: stableInfoSig) else {
+                os_log("fetchCurrentPolicy failed to create TPPeerStableInfo", log: tplogDebug, type: .error)
+                reply(nil, nil, ContainerError.invalidStableInfoOrSig)
+                return
+            }
+
+            do {
+                let (views, policy) = try self.policyAndViewsFor(permanentInfo: permanentInfo, stableInfo: stableInfo)
+                reply(views, policy, nil)
+                return
+            } catch {
+                os_log("TPPolicyDocument failed: %{public}@", log: tplogDebug, type: .default, error as CVarArg)
+                reply(nil, nil, error)
+                return
             }
         }
     }
 
             }
         }
     }
 
+    func policyAndViewsFor(permanentInfo: TPPeerPermanentInfo, stableInfo: TPPeerStableInfo) throws -> (Set<String>, TPPolicy) {
+        let policyVersion = stableInfo.bestPolicyVersion()
+        guard let policyDocument = self.model.policy(withVersion: policyVersion.versionNumber) else {
+            os_log("current policy is missing?", log: tplogDebug, type: .default)
+            throw ContainerError.unknownPolicyVersion(policyVersion.versionNumber)
+        }
+
+        let policy = try policyDocument.policy(withSecrets: stableInfo.policySecrets, decrypter: Decrypter())
+        let views = try policy.views(forModel: permanentInfo.modelID)
+
+        return (views, policy)
+    }
+
     // All-or-nothing: return an error in case full list cannot be returned.
     // Completion handler data format: [version : [hash, data]]
     // All-or-nothing: return an error in case full list cannot be returned.
     // Completion handler data format: [version : [hash, data]]
-    func fetchPolicyDocuments(keys: [NSNumber: String],
-                              reply: @escaping ([NSNumber: [String]]?, Error?) -> Void) {
+    func fetchPolicyDocuments(versions: Set<TPPolicyVersion>,
+                              reply: @escaping ([TPPolicyVersion: Data]?, Error?) -> Void) {
         self.semaphore.wait()
         self.semaphore.wait()
-        let reply: ([NSNumber: [String]]?, Error?) -> Void = {
-            os_log("fetchPolicyDocuments complete: %@", log: tplogTrace, type: .info, traceError($1))
+        let reply: ([TPPolicyVersion: Data]?, Error?) -> Void = {
+            os_log("fetchPolicyDocuments complete: %{public}@", log: tplogTrace, type: .info, traceError($1))
             self.semaphore.signal()
             reply($0, $1)
         }
 
             self.semaphore.signal()
             reply($0, $1)
         }
 
-        var keys = keys
-        var docs: [NSNumber: [String]] = [:]
+        self.fetchPolicyDocumentsWithSemaphore(versions: versions) { policyDocuments, fetchError in
+            reply(policyDocuments.flatMap { $0.mapValues { policyDoc in policyDoc.protobuf } }, fetchError)
+        }
+    }
+
+    func fetchPolicyDocumentWithSemaphore(version: TPPolicyVersion,
+                                          reply: @escaping (TPPolicyDocument?, Error?) -> Void) {
+        self.fetchPolicyDocumentsWithSemaphore(versions: Set([version])) { versions, fetchError in
+            guard fetchError == nil else {
+                reply(nil, fetchError)
+                return
+            }
+
+            guard let doc = versions?[version] else {
+                os_log("fetchPolicyDocument: didn't return policy of version: %{public}@", log: tplogDebug, versions ?? "no versions")
+                reply(nil, ContainerError.unknownPolicyVersion(version.versionNumber))
+                return
+            }
+
+            reply(doc, nil)
+        }
+    }
+
+    func fetchPolicyDocumentsWithSemaphore(versions: Set<TPPolicyVersion>,
+                                           reply: @escaping ([TPPolicyVersion: TPPolicyDocument]?, Error?) -> Void) {
+        var remaining = versions
+        var docs: [TPPolicyVersion: TPPolicyDocument] = [:]
 
         self.moc.performAndWait {
 
         self.moc.performAndWait {
-            for (version, hash) in keys {
-                if let policydoc = try? self.getPolicyDoc(version.uint64Value), policydoc.policyHash == hash {
-                    docs[version] = [policydoc.policyHash, policydoc.protobuf.base64EncodedString()]
-                    keys[version] = nil
+            for version in remaining {
+                if let policydoc = try? self.getPolicyDoc(version.versionNumber), policydoc.version.policyHash == version.policyHash {
+                    docs[policydoc.version] = policydoc
+                    remaining.remove(version)
                 }
             }
         }
 
                 }
             }
         }
 
-        if keys.isEmpty {
+        guard !remaining.isEmpty else {
             reply(docs, nil)
             return
         }
 
         let request = FetchPolicyDocumentsRequest.with {
             reply(docs, nil)
             return
         }
 
         let request = FetchPolicyDocumentsRequest.with {
-            $0.keys = keys.map { key, value in
-                PolicyDocumentKey.with { $0.version = key.uint64Value; $0.hash = value }}
+            $0.keys = remaining.map { version in
+                PolicyDocumentKey.with { $0.version = version.versionNumber; $0.hash = version.policyHash }}
         }
 
         self.cuttlefish.fetchPolicyDocuments(request) { response, error in
         }
 
         self.cuttlefish.fetchPolicyDocuments(request) { response, error in
-            os_log("FetchPolicyDocuments(%@): %@, error: %@", log: tplogDebug, "\(String(describing: request))", "\(String(describing: response))", "\(String(describing: error))")
+            os_log("FetchPolicyDocuments(%{public}@): %{public}@, error: %{public}@", log: tplogDebug, "\(String(describing: request))", "\(String(describing: response))", "\(String(describing: error))")
             guard let response = response, error == nil else {
             guard let response = response, error == nil else {
-                os_log("FetchPolicyDocuments failed: %@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "no error")
+                os_log("FetchPolicyDocuments failed: %{public}@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "no error")
                 reply(nil, error ?? ContainerError.cloudkitResponseMissing)
                 return
             }
                 reply(nil, error ?? ContainerError.cloudkitResponseMissing)
                 return
             }
@@ -2636,20 +2727,28 @@ class Container: NSObject {
                     // TODO: validate the policy's signature
 
                     guard let doc = TPPolicyDocument.policyDoc(withHash: mapEntry.key.hash, data: mapEntry.value) else {
                     // TODO: validate the policy's signature
 
                     guard let doc = TPPolicyDocument.policyDoc(withHash: mapEntry.key.hash, data: mapEntry.value) else {
-                        os_log("Can't make policy document with hash %@ and data %@",
+                        os_log("Can't make policy document with hash %{public}@ and data %{public}@",
                                log: tplogDebug, type: .default, mapEntry.key.hash, mapEntry.value.base64EncodedString())
                         reply(nil, ContainerError.policyDocumentDoesNotValidate)
                         return
                     }
 
                                log: tplogDebug, type: .default, mapEntry.key.hash, mapEntry.value.base64EncodedString())
                         reply(nil, ContainerError.policyDocumentDoesNotValidate)
                         return
                     }
 
-                    guard let hash = keys[NSNumber(value: doc.policyVersion)], hash == doc.policyHash else {
-                        os_log("Requested hash %@ does not match fetched hash %@", log: tplogDebug, type: .default,
-                               keys[NSNumber(value: doc.policyVersion)] ?? "<nil>", doc.policyHash)
+                    guard let expectedVersion = (remaining.filter { $0.versionNumber == doc.version.versionNumber }.first) else {
+                        os_log("Received a policy version we didn't request: %d", log: tplogDebug, type: .default, doc.version.versionNumber)
+                        reply(nil, ContainerError.policyDocumentDoesNotValidate)
+                        return
+                    }
+
+                    guard expectedVersion.policyHash == doc.version.policyHash else {
+                        os_log("Requested hash %{public}@ does not match fetched hash %{public}@", log: tplogDebug, type: .default,
+                               expectedVersion.policyHash, doc.version.policyHash)
                         reply(nil, ContainerError.policyDocumentDoesNotValidate)
                         return
                     }
                         reply(nil, ContainerError.policyDocumentDoesNotValidate)
                         return
                     }
-                    keys[NSNumber(value: doc.policyVersion)] = nil  // Server responses should be unique, let's enforce
-                    docs[NSNumber(value: doc.policyVersion)] = [doc.policyHash, doc.protobuf.base64EncodedString()]
+
+                    remaining.remove(expectedVersion) // Server responses should be unique, let's enforce
+
+                    docs[doc.version] = doc
                     self.model.register(doc)
                 }
 
                     self.model.register(doc)
                 }
 
@@ -2660,13 +2759,14 @@ class Container: NSObject {
                     return
                 }
 
                     return
                 }
 
-                if !keys.isEmpty {
-                    let (unknownVersion, _) = keys.first!
-                    reply(nil, ContainerError.unknownPolicyVersion(unknownVersion.uint64Value))
+                // Determine if there's anything left to fetch
+                guard let unfetchedVersion = remaining.first else {
+                    // Nothing remaining? Success!
+                    reply(docs, nil)
                     return
                 }
 
                     return
                 }
 
-                reply(docs, nil)
+                reply(nil, ContainerError.unknownPolicyVersion(unfetchedVersion.versionNumber))
             }
         }
     }
             }
         }
     }
@@ -2711,10 +2811,10 @@ class Container: NSObject {
 
     /* Returns any new CKKS keys that need uploading, as well as any TLKShares necessary for those keys */
     func makeSharesForNewKeySets(ckksKeys: [CKKSKeychainBackedKeySet],
 
     /* Returns any new CKKS keys that need uploading, as well as any TLKShares necessary for those keys */
     func makeSharesForNewKeySets(ckksKeys: [CKKSKeychainBackedKeySet],
-                                tlkShares: [CKKSTLKShare],
-                                egoPeerKeys: OctagonSelfPeerKeys,
-                                egoPeerDynamicInfo: TPPeerDynamicInfo,
-                                epoch: Int) throws -> ([ViewKeys], [TLKShare]) {
+                                 tlkShares: [CKKSTLKShare],
+                                 egoPeerKeys: OctagonSelfPeerKeys,
+                                 egoPeerDynamicInfo: TPPeerDynamicInfo,
+                                 epoch: Int) throws -> ([ViewKeys], [TLKShare]) {
         let newCKKSKeys = ckksKeys.filter { $0.newUpload }
         let newViewKeys: [ViewKeys] = newCKKSKeys.map(ViewKeys.convert)
 
         let newCKKSKeys = ckksKeys.filter { $0.newUpload }
         let newViewKeys: [ViewKeys] = newCKKSKeys.map(ViewKeys.convert)
 
@@ -2730,7 +2830,7 @@ class Container: NSObject {
             do {
                 let peerIDsWithAccess = try self.model.getPeerIDsTrustedByPeer(with: egoPeerDynamicInfo,
                                                                                toAccessView: keyset.tlk.zoneID.zoneName)
             do {
                 let peerIDsWithAccess = try self.model.getPeerIDsTrustedByPeer(with: egoPeerDynamicInfo,
                                                                                toAccessView: keyset.tlk.zoneID.zoneName)
-                os_log("Planning to share %@ with peers %@", log: tplogDebug, type: .default, String(describing: keyset.tlk), peerIDsWithAccess)
+                os_log("Planning to share %@ with peers %{public}@", log: tplogDebug, type: .default, String(describing: keyset.tlk), peerIDsWithAccess)
 
                 let peers = peerIDsWithAccess.compactMap { self.model.peer(withID: $0) }
                 let viewPeerShares = try peers.map { receivingPeer in
 
                 let peers = peerIDsWithAccess.compactMap { self.model.peer(withID: $0) }
                 let viewPeerShares = try peers.map { receivingPeer in
@@ -2741,10 +2841,10 @@ class Container: NSObject {
                                                                     poisoned: 0))
                 }
 
                                                                     poisoned: 0))
                 }
 
-                peerShares = peerShares + viewPeerShares
+                peerShares += viewPeerShares
 
             } catch {
 
             } catch {
-                os_log("Unable to create TLKShares for keyset %@: %@", log: tplogDebug, type: .default, String(describing: keyset), error as CVarArg)
+                os_log("Unable to create TLKShares for keyset %@: %{public}@", log: tplogDebug, type: .default, String(describing: keyset), error as CVarArg)
             }
         }
 
             }
         }
 
@@ -2768,6 +2868,7 @@ class Container: NSObject {
 
         let newStableInfo = try self.createNewStableInfoIfNeeded(stableChanges: nil,
                                                                  egoPeerID: egoPeerID,
 
         let newStableInfo = try self.createNewStableInfoIfNeeded(stableChanges: nil,
                                                                  egoPeerID: egoPeerID,
+                                                                 existingStableInfo: stableInfo,
                                                                  dynamicInfo: dynamicInfo,
                                                                  signingKeyPair: egoPeerKeys.signingKey)
 
                                                                  dynamicInfo: dynamicInfo,
                                                                  signingKeyPair: egoPeerKeys.signingKey)
 
@@ -2787,161 +2888,180 @@ class Container: NSObject {
               ckksKeys: [CKKSKeychainBackedKeySet],
               tlkShares: [CKKSTLKShare],
               preapprovedKeys: [Data]?,
               ckksKeys: [CKKSKeychainBackedKeySet],
               tlkShares: [CKKSTLKShare],
               preapprovedKeys: [Data]?,
-              reply: @escaping (String?, [CKRecord], Error?) -> Void) {
+              reply: @escaping (String?, [CKRecord], Set<String>?, TPPolicy?, Error?) -> Void) {
         self.semaphore.wait()
         self.semaphore.wait()
-        let reply: (String?, [CKRecord], Error?) -> Void = {
-            os_log("join complete: %@", log: tplogTrace, type: .info, traceError($2))
+        let reply: (String?, [CKRecord], Set<String>?, TPPolicy?, Error?) -> Void = {
+            os_log("join complete: %{public}@", log: tplogTrace, type: .info, traceError($4))
             self.semaphore.signal()
             self.semaphore.signal()
-            reply($0, $1, $2)
+            reply($0, $1, $2, $3, $4)
         }
 
         self.fetchAndPersistChanges { error in
             guard error == nil else {
         }
 
         self.fetchAndPersistChanges { error in
             guard error == nil else {
-                reply(nil, [], error)
+                reply(nil, [], nil, nil, error)
                 return
             }
                 return
             }
-            self.moc.performAndWait {
-                guard let voucher = TPVoucher(infoWith: voucherData, sig: voucherSig) else {
-                    reply(nil, [], ContainerError.invalidVoucherOrSig)
-                    return
-                }
-                guard let sponsor = self.model.peer(withID: voucher.sponsorID) else {
-                    reply(nil, [], ContainerError.sponsorNotRegistered(voucher.sponsorID))
-                    return
+
+            // To join, you must know all policies that exist
+            let allPolicyVersions = self.model.allPolicyVersions()
+            self.fetchPolicyDocumentsWithSemaphore(versions: allPolicyVersions) { _, policyFetchError in
+                if let error = policyFetchError {
+                    os_log("join: error fetching all requested policies (continuing anyway): %{public}@", log: tplogDebug, type: .default, error as CVarArg)
                 }
 
                 }
 
-                // Fetch ego peer identity from local storage.
-                guard let egoPeerID = self.containerMO.egoPeerID,
-                    let egoPermData = self.containerMO.egoPeerPermanentInfo,
-                    let egoPermSig = self.containerMO.egoPeerPermanentInfoSig,
-                    let egoStableData = self.containerMO.egoPeerStableInfo,
-                    let egoStableSig = self.containerMO.egoPeerStableInfoSig
-                    else {
-                        reply(nil, [], ContainerError.noPreparedIdentity)
+                self.moc.performAndWait {
+                    guard let voucher = TPVoucher(infoWith: voucherData, sig: voucherSig) else {
+                        reply(nil, [], nil, nil, ContainerError.invalidVoucherOrSig)
                         return
                         return
-                }
+                    }
+                    guard let sponsor = self.model.peer(withID: voucher.sponsorID) else {
+                        reply(nil, [], nil, nil, ContainerError.sponsorNotRegistered(voucher.sponsorID))
+                        return
+                    }
 
 
-                let keyFactory = TPECPublicKeyFactory()
-                guard let selfPermanentInfo = TPPeerPermanentInfo(peerID: egoPeerID, data: egoPermData, sig: egoPermSig, keyFactory: keyFactory) else {
-                    reply(nil, [], ContainerError.invalidPermanentInfoOrSig)
-                    return
-                }
-                guard let selfStableInfo = TPPeerStableInfo(data: egoStableData, sig: egoStableSig) else {
-                    reply(nil, [], ContainerError.invalidStableInfoOrSig)
-                    return
-                }
-                guard self.onqueueMachineIDAllowedByIDMS(machineID: selfPermanentInfo.machineID) else {
-                    os_log("join: self machineID %@ not on list", log: tplogDebug, type: .debug, selfPermanentInfo.machineID)
-                    self.onqueueTTRUntrusted()
-                    reply(nil, [], ContainerError.preparedIdentityNotOnAllowedList(selfPermanentInfo.machineID))
-                    return
-                }
+                    // Fetch ego peer identity from local storage.
+                    guard let egoPeerID = self.containerMO.egoPeerID,
+                        let egoPermData = self.containerMO.egoPeerPermanentInfo,
+                        let egoPermSig = self.containerMO.egoPeerPermanentInfoSig,
+                        let egoStableData = self.containerMO.egoPeerStableInfo,
+                        let egoStableSig = self.containerMO.egoPeerStableInfoSig
+                        else {
+                            reply(nil, [], nil, nil, ContainerError.noPreparedIdentity)
+                            return
+                    }
 
 
-                loadEgoKeys(peerID: egoPeerID) { egoPeerKeys, error in
-                    guard let egoPeerKeys = egoPeerKeys else {
-                        os_log("Don't have my own peer keys; can't join: %@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "error missing")
-                        reply(nil, [], error)
+                    let keyFactory = TPECPublicKeyFactory()
+                    guard let selfPermanentInfo = TPPeerPermanentInfo(peerID: egoPeerID, data: egoPermData, sig: egoPermSig, keyFactory: keyFactory) else {
+                        reply(nil, [], nil, nil, ContainerError.invalidPermanentInfoOrSig)
+                        return
+                    }
+                    guard let selfStableInfo = TPPeerStableInfo(data: egoStableData, sig: egoStableSig) else {
+                        reply(nil, [], nil, nil, ContainerError.invalidStableInfoOrSig)
+                        return
+                    }
+                    guard self.onqueueMachineIDAllowedByIDMS(machineID: selfPermanentInfo.machineID) else {
+                        os_log("join: self machineID %{public}@ not on list", log: tplogDebug, type: .debug, selfPermanentInfo.machineID)
+                        self.onqueueTTRUntrusted()
+                        reply(nil, [], nil, nil, ContainerError.preparedIdentityNotOnAllowedList(selfPermanentInfo.machineID))
                         return
                     }
                         return
                     }
-                    self.moc.performAndWait {
-                        let peer: Peer
-                        let newDynamicInfo: TPPeerDynamicInfo
-                        do {
-                            (peer, newDynamicInfo) = try self.onqueuePreparePeerForJoining(egoPeerID: egoPeerID,
-                                                                                           peerPermanentInfo: selfPermanentInfo,
-                                                                                           stableInfo: selfStableInfo,
-                                                                                           sponsorID: sponsor.peerID,
-                                                                                           preapprovedKeys: preapprovedKeys,
-                                                                                           vouchers: [SignedVoucher.with {
-                                                                                               $0.voucher = voucher.data
-                                                                                               $0.sig = voucher.sig
-                                                                                               }, ],
-                                                                                           egoPeerKeys: egoPeerKeys)
-                        } catch {
-                            os_log("Unable to create peer for joining: %@", log: tplogDebug, type: .default, error as CVarArg)
-                            reply(nil, [], error)
-                            return
-                        }
 
 
-                        let allTLKShares: [TLKShare]
-                        let viewKeys: [ViewKeys]
-                        do {
-                            (viewKeys, allTLKShares) = try self.makeSharesForNewKeySets(ckksKeys: ckksKeys,
-                                                                                        tlkShares: tlkShares,
-                                                                                        egoPeerKeys: egoPeerKeys,
-                                                                                        egoPeerDynamicInfo: newDynamicInfo,
-                                                                                        epoch: Int(selfPermanentInfo.epoch))
-                        } catch {
-                            os_log("Unable to process keys before joining: %@", log: tplogDebug, type: .default, error as CVarArg)
-                            reply(nil, [], error)
+                    loadEgoKeys(peerID: egoPeerID) { egoPeerKeys, error in
+                        guard let egoPeerKeys = egoPeerKeys else {
+                            os_log("Don't have my own peer keys; can't join: %{public}@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "error missing")
+                            reply(nil, [], nil, nil, error)
                             return
                         }
                             return
                         }
+                        self.moc.performAndWait {
+                            let peer: Peer
+                            let newDynamicInfo: TPPeerDynamicInfo
+                            do {
+                                (peer, newDynamicInfo) = try self.onqueuePreparePeerForJoining(egoPeerID: egoPeerID,
+                                                                                               peerPermanentInfo: selfPermanentInfo,
+                                                                                               stableInfo: selfStableInfo,
+                                                                                               sponsorID: sponsor.peerID,
+                                                                                               preapprovedKeys: preapprovedKeys,
+                                                                                               vouchers: [SignedVoucher.with {
+                                                                                                $0.voucher = voucher.data
+                                                                                                $0.sig = voucher.sig
+                                                                                                }, ],
+                                                                                               egoPeerKeys: egoPeerKeys)
+                            } catch {
+                                os_log("Unable to create peer for joining: %{public}@", log: tplogDebug, type: .default, error as CVarArg)
+                                reply(nil, [], nil, nil, error)
+                                return
+                            }
 
 
-                        do {
-                            try self.model.checkIntroduction(forCandidate: selfPermanentInfo,
-                                                             stableInfo: peer.stableInfoAndSig.toStableInfo(),
-                                                             withSponsorID: sponsor.peerID)
-                        } catch {
-                            os_log("Error checking introduction: %@", log: tplogDebug, type: .default, error as CVarArg)
-                            reply(nil, [], error)
-                            return
-                        }
+                            guard let peerStableInfo = peer.stableInfoAndSig.toStableInfo() else {
+                                os_log("Unable to create new peer stable new for joining", log: tplogDebug, type: .default)
+                                reply(nil, [], nil, nil, ContainerError.invalidStableInfoOrSig)
+                                return
+                            }
 
 
-                        var bottle: Bottle
-                        do {
-                            bottle = try self.assembleBottle(egoPeerID: egoPeerID)
-                        } catch {
-                            reply(nil, [], error)
-                            return
-                        }
+                            let allTLKShares: [TLKShare]
+                            let viewKeys: [ViewKeys]
+                            do {
+                                (viewKeys, allTLKShares) = try self.makeSharesForNewKeySets(ckksKeys: ckksKeys,
+                                                                                            tlkShares: tlkShares,
+                                                                                            egoPeerKeys: egoPeerKeys,
+                                                                                            egoPeerDynamicInfo: newDynamicInfo,
+                                                                                            epoch: Int(selfPermanentInfo.epoch))
+                            } catch {
+                                os_log("Unable to process keys before joining: %{public}@", log: tplogDebug, type: .default, error as CVarArg)
+                                reply(nil, [], nil, nil, error)
+                                return
+                            }
 
 
-                        os_log("Beginning join for peer %@", log: tplogDebug, type: .default, egoPeerID)
-                        os_log("Join permanentInfo: %@", log: tplogDebug, type: .debug, egoPermData.base64EncodedString())
-                        os_log("Join permanentInfoSig: %@", log: tplogDebug, type: .debug, egoPermSig.base64EncodedString())
-                        os_log("Join stableInfo: %@", log: tplogDebug, type: .debug, peer.stableInfoAndSig.peerStableInfo.base64EncodedString())
-                        os_log("Join stableInfoSig: %@", log: tplogDebug, type: .debug, peer.stableInfoAndSig.sig.base64EncodedString())
-                        os_log("Join dynamicInfo: %@", log: tplogDebug, type: .debug, peer.dynamicInfoAndSig.peerDynamicInfo.base64EncodedString())
-                        os_log("Join dynamicInfoSig: %@", log: tplogDebug, type: .debug, peer.dynamicInfoAndSig.sig.base64EncodedString())
+                            do {
+                                try self.model.checkIntroduction(forCandidate: selfPermanentInfo,
+                                                                 stableInfo: peer.stableInfoAndSig.toStableInfo(),
+                                                                 withSponsorID: sponsor.peerID)
+                            } catch {
+                                os_log("Error checking introduction: %{public}@", log: tplogDebug, type: .default, error as CVarArg)
+                                reply(nil, [], nil, nil, error)
+                                return
+                            }
 
 
-                        os_log("Join vouchers: %@", log: tplogDebug, type: .debug, peer.vouchers.map { $0.voucher.base64EncodedString() })
-                        os_log("Join voucher signatures: %@", log: tplogDebug, type: .debug, peer.vouchers.map { $0.sig.base64EncodedString() })
+                            var bottle: Bottle
+                            do {
+                                bottle = try self.assembleBottle(egoPeerID: egoPeerID)
+                            } catch {
+                                reply(nil, [], nil, nil, error)
+                                return
+                            }
 
 
-                        os_log("Uploading %d tlk shares", log: tplogDebug, type: .default, allTLKShares.count)
+                            os_log("Beginning join for peer %{public}@", log: tplogDebug, type: .default, egoPeerID)
+                            os_log("Join permanentInfo: %{public}@", log: tplogDebug, type: .debug, egoPermData.base64EncodedString())
+                            os_log("Join permanentInfoSig: %{public}@", log: tplogDebug, type: .debug, egoPermSig.base64EncodedString())
+                            os_log("Join stableInfo: %{public}@", log: tplogDebug, type: .debug, peer.stableInfoAndSig.peerStableInfo.base64EncodedString())
+                            os_log("Join stableInfoSig: %{public}@", log: tplogDebug, type: .debug, peer.stableInfoAndSig.sig.base64EncodedString())
+                            os_log("Join dynamicInfo: %{public}@", log: tplogDebug, type: .debug, peer.dynamicInfoAndSig.peerDynamicInfo.base64EncodedString())
+                            os_log("Join dynamicInfoSig: %{public}@", log: tplogDebug, type: .debug, peer.dynamicInfoAndSig.sig.base64EncodedString())
 
 
-                        do {
-                            os_log("Join peer: %@", log: tplogDebug, type: .debug, try peer.serializedData().base64EncodedString())
-                        } catch {
-                            os_log("Join unable to encode peer: %@", log: tplogDebug, type: .debug, error as CVarArg)
-                        }
+                            os_log("Join vouchers: %{public}@", log: tplogDebug, type: .debug, peer.vouchers.map { $0.voucher.base64EncodedString() })
+                            os_log("Join voucher signatures: %{public}@", log: tplogDebug, type: .debug, peer.vouchers.map { $0.sig.base64EncodedString() })
 
 
-                        let changeToken = self.containerMO.changeToken ?? ""
-                        let request = JoinWithVoucherRequest.with {
-                            $0.changeToken = changeToken
-                            $0.peer = peer
-                            $0.bottle = bottle
-                            $0.tlkShares = allTLKShares
-                            $0.viewKeys = viewKeys
-                        }
-                        self.cuttlefish.joinWithVoucher(request) { response, error in
-                            os_log("JoinWithVoucher(%@): %@, error: %@", log: tplogDebug, "\(String(describing: request))", "\(String(describing: response))", "\(String(describing: error))")
-                            guard let response = response, error == nil else {
-                                os_log("joinWithVoucher failed: %@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "no error")
-                                reply(nil, [], error ?? ContainerError.cloudkitResponseMissing)
-                                return
+                            os_log("Uploading %d tlk shares", log: tplogDebug, type: .default, allTLKShares.count)
+
+                            do {
+                                os_log("Join peer: %{public}@", log: tplogDebug, type: .debug, try peer.serializedData().base64EncodedString())
+                            } catch {
+                                os_log("Join unable to encode peer: %{public}@", log: tplogDebug, type: .debug, error as CVarArg)
                             }
 
                             }
 
-                            self.moc.performAndWait {
-                                do {
-                                    self.containerMO.egoPeerStableInfo = peer.stableInfoAndSig.peerStableInfo
-                                    self.containerMO.egoPeerStableInfoSig = peer.stableInfoAndSig.sig
-                                    try self.onQueuePersist(changes: response.changes)
-                                    os_log("JoinWithVoucher succeeded", log: tplogDebug)
+                            let changeToken = self.containerMO.changeToken ?? ""
+                            let request = JoinWithVoucherRequest.with {
+                                $0.changeToken = changeToken
+                                $0.peer = peer
+                                $0.bottle = bottle
+                                $0.tlkShares = allTLKShares
+                                $0.viewKeys = viewKeys
+                            }
+                            self.cuttlefish.joinWithVoucher(request) { response, error in
+                                os_log("JoinWithVoucher(%{public}@): %{public}@, error: %{public}@", log: tplogDebug, "\(String(describing: request))", "\(String(describing: response))", "\(String(describing: error))")
+                                guard let response = response, error == nil else {
+                                    os_log("joinWithVoucher failed: %{public}@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "no error")
+                                    reply(nil, [], nil, nil, error ?? ContainerError.cloudkitResponseMissing)
+                                    return
+                                }
 
 
-                                    let keyHierarchyRecords = response.zoneKeyHierarchyRecords.compactMap { CKRecord($0) }
-                                    reply(egoPeerID, keyHierarchyRecords, nil)
-                                } catch {
-                                    os_log("JoinWithVoucher failed: %@", log: tplogDebug, String(describing: error))
-                                    reply(nil, [], error)
+                                self.moc.performAndWait {
+                                    do {
+                                        self.containerMO.egoPeerStableInfo = peer.stableInfoAndSig.peerStableInfo
+                                        self.containerMO.egoPeerStableInfoSig = peer.stableInfoAndSig.sig
+
+                                        let (syncingViews, policy) = try self.policyAndViewsFor(permanentInfo: selfPermanentInfo,
+                                                                                                stableInfo: peerStableInfo)
+
+                                        try self.onQueuePersist(changes: response.changes)
+                                        os_log("JoinWithVoucher succeeded", log: tplogDebug)
+
+                                        let keyHierarchyRecords = response.zoneKeyHierarchyRecords.compactMap { CKRecord($0) }
+                                        reply(egoPeerID, keyHierarchyRecords, syncingViews, policy, nil)
+                                    } catch {
+                                        os_log("JoinWithVoucher failed: %{public}@", log: tplogDebug, String(describing: error))
+                                        reply(nil, [], nil, nil, error)
+                                    }
                                 }
                             }
                         }
                                 }
                             }
                         }
@@ -2951,12 +3071,12 @@ class Container: NSObject {
         }
     }
 
         }
     }
 
-    func requestHealthCheck(requiresEscrowCheck: Bool, reply: @escaping (Bool, Bool, Bool, Error?) -> Void) {
+    func requestHealthCheck(requiresEscrowCheck: Bool, reply: @escaping (Bool, Bool, Bool, Bool, Error?) -> Void) {
         self.semaphore.wait()
         self.semaphore.wait()
-        let reply: (Bool, Bool, Bool, Error?) -> Void = {
-            os_log("health check complete: %@", log: tplogTrace, type: .info, traceError($3))
+        let reply: (Bool, Bool, Bool, Bool, Error?) -> Void = {
+            os_log("health check complete: %{public}@", log: tplogTrace, type: .info, traceError($4))
             self.semaphore.signal()
             self.semaphore.signal()
-            reply($0, $1, $2, $3)
+            reply($0, $1, $2, $3, $4)
         }
 
         os_log("requestHealthCheck requiring escrow check: %d", log: tplogDebug, type: .default, requiresEscrowCheck)
         }
 
         os_log("requestHealthCheck requiring escrow check: %d", log: tplogDebug, type: .default, requiresEscrowCheck)
@@ -2965,7 +3085,7 @@ class Container: NSObject {
             guard let egoPeerID = self.containerMO.egoPeerID else {
                 // No identity, nothing to do
                 os_log("requestHealthCheck: No identity.", log: tplogDebug, type: .default)
             guard let egoPeerID = self.containerMO.egoPeerID else {
                 // No identity, nothing to do
                 os_log("requestHealthCheck: No identity.", log: tplogDebug, type: .default)
-                reply(false, false, false, ContainerError.noPreparedIdentity)
+                reply(false, false, false, false, ContainerError.noPreparedIdentity)
                 return
             }
             let request = GetRepairActionRequest.with {
                 return
             }
             let request = GetRepairActionRequest.with {
@@ -2975,34 +3095,34 @@ class Container: NSObject {
 
             self.cuttlefish.getRepairAction(request) { response, error in
                 guard error == nil else {
 
             self.cuttlefish.getRepairAction(request) { response, error in
                 guard error == nil else {
-                    reply(false, false, false, error)
+                    reply(false, false, false, false, error)
                     return
                 }
                 guard let action = response?.repairAction else {
                     return
                 }
                 guard let action = response?.repairAction else {
-                    os_log("repair response is empty, returning false: %@", log: tplogDebug, type: .default)
-                    reply(false, false, false, nil)
+                    os_log("repair response is empty, returning false", log: tplogDebug, type: .default)
+                    reply(false, false, false, false, nil)
                     return
                 }
                 var postRepairAccount: Bool = false
                 var postRepairEscrow: Bool = false
                 var resetOctagon: Bool = false
                     return
                 }
                 var postRepairAccount: Bool = false
                 var postRepairEscrow: Bool = false
                 var resetOctagon: Bool = false
+                var leaveTrust: Bool = false
 
                 switch action {
                 case .noAction:
                     break
                 case .postRepairAccount:
                     postRepairAccount = true
 
                 switch action {
                 case .noAction:
                     break
                 case .postRepairAccount:
                     postRepairAccount = true
-                    break
                 case .postRepairEscrow:
                     postRepairEscrow = true
                 case .postRepairEscrow:
                     postRepairEscrow = true
-                    break
                 case .resetOctagon:
                     resetOctagon = true
                 case .resetOctagon:
                     resetOctagon = true
-                    break
+                case .leaveTrust:
+                    leaveTrust = true
                 case .UNRECOGNIZED:
                     break
                 }
                 case .UNRECOGNIZED:
                     break
                 }
-                reply(postRepairAccount, postRepairEscrow, resetOctagon, nil)
+                reply(postRepairAccount, postRepairEscrow, resetOctagon, leaveTrust, nil)
             }
         }
     }
             }
         }
     }
@@ -3010,16 +3130,16 @@ class Container: NSObject {
     func getSupportAppInfo(reply: @escaping (Data?, Error?) -> Void) {
         self.semaphore.wait()
         let reply: (Data?, Error?) -> Void = {
     func getSupportAppInfo(reply: @escaping (Data?, Error?) -> Void) {
         self.semaphore.wait()
         let reply: (Data?, Error?) -> Void = {
-            os_log("getSupportAppInfo complete: %@", log: tplogTrace, type: .info, traceError($1))
+            os_log("getSupportAppInfo complete: %{public}@", log: tplogTrace, type: .info, traceError($1))
             self.semaphore.signal()
             reply($0, $1)
         }
 
         self.cuttlefish.getSupportAppInfo { response, error in
             self.semaphore.signal()
             reply($0, $1)
         }
 
         self.cuttlefish.getSupportAppInfo { response, error in
-            os_log("getSupportAppInfo(): %@, error: %@", log: tplogDebug,
+            os_log("getSupportAppInfo(): %{public}@, error: %{public}@", log: tplogDebug,
                    "(\(String(describing: response))", "\(String(describing: error))")
             guard let response = response, error == nil else {
                    "(\(String(describing: response))", "\(String(describing: error))")
             guard let response = response, error == nil else {
-                os_log("getSupportAppInfo failed: %@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "no error")
+                os_log("getSupportAppInfo failed: %{public}@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "no error")
                 reply(nil, error ?? ContainerError.cloudkitResponseMissing)
                 return
             }
                 reply(nil, error ?? ContainerError.cloudkitResponseMissing)
                 return
             }
@@ -3037,67 +3157,75 @@ class Container: NSObject {
     func preflightPreapprovedJoin(reply: @escaping (Bool, Error?) -> Void) {
         self.semaphore.wait()
         let reply: (Bool, Error?) -> Void = {
     func preflightPreapprovedJoin(reply: @escaping (Bool, Error?) -> Void) {
         self.semaphore.wait()
         let reply: (Bool, Error?) -> Void = {
-            os_log("preflightPreapprovedJoin complete: %@", log: tplogTrace, type: .info, traceError($1))
+            os_log("preflightPreapprovedJoin complete: %{public}@", log: tplogTrace, type: .info, traceError($1))
             self.semaphore.signal()
             reply($0, $1)
         }
 
         self.fetchAndPersistChanges { error in
             guard error == nil else {
             self.semaphore.signal()
             reply($0, $1)
         }
 
         self.fetchAndPersistChanges { error in
             guard error == nil else {
-                os_log("preflightPreapprovedJoin unable to fetch changes: %@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "")
+                os_log("preflightPreapprovedJoin unable to fetch changes: %{public}@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "")
                 reply(false, error)
                 return
             }
 
                 reply(false, error)
                 return
             }
 
-            // We explicitly ignore the machine ID list here; we're only interested in the peer states: do they preapprove us?
+            // We need to try to have all policy versions that our peers claim to behave
+            let allPolicyVersions = self.model.allPolicyVersions()
+            self.fetchPolicyDocumentsWithSemaphore(versions: allPolicyVersions) { _, policyFetchError in
+                if let error = policyFetchError {
+                    os_log("preflightPreapprovedJoin: error fetching all requested policies (continuing anyway): %{public}@", log: tplogDebug, type: .default, error as CVarArg)
+                }
 
 
-            guard !self.model.allPeerIDs().isEmpty else {
-                // If, after fetch and handle changes, there's no peers, then we can likely establish.
-                reply(true, nil)
-                return
-            }
+                // We explicitly ignore the machine ID list here; we're only interested in the peer states: do they preapprove us?
 
 
-            guard let egoPeerID = self.containerMO.egoPeerID,
-                let egoPermData = self.containerMO.egoPeerPermanentInfo,
-                let egoPermSig = self.containerMO.egoPeerPermanentInfoSig
-                else {
-                    os_log("preflightPreapprovedJoin: no prepared identity", log: tplogDebug, type: .debug)
-                    reply(false, ContainerError.noPreparedIdentity)
+                guard !self.model.allPeerIDs().isEmpty else {
+                    // If, after fetch and handle changes, there's no peers, then we can likely establish.
+                    reply(true, nil)
                     return
                     return
-            }
+                }
 
 
-            let keyFactory = TPECPublicKeyFactory()
-            guard let egoPermanentInfo = TPPeerPermanentInfo(peerID: egoPeerID, data: egoPermData, sig: egoPermSig, keyFactory: keyFactory) else {
-                os_log("preflightPreapprovedJoin: invalid permanent info", log: tplogDebug, type: .debug)
-                reply(false, ContainerError.invalidPermanentInfoOrSig)
-                return
-            }
+                guard let egoPeerID = self.containerMO.egoPeerID,
+                    let egoPermData = self.containerMO.egoPeerPermanentInfo,
+                    let egoPermSig = self.containerMO.egoPeerPermanentInfoSig
+                    else {
+                        os_log("preflightPreapprovedJoin: no prepared identity", log: tplogDebug, type: .debug)
+                        reply(false, ContainerError.noPreparedIdentity)
+                        return
+                }
 
 
-            guard self.model.hasPotentiallyTrustedPeerPreapprovingKey(egoPermanentInfo.signingPubKey.spki()) else {
-                os_log("preflightPreapprovedJoin: no peers preapprove our key", log: tplogDebug, type: .debug)
-                reply(false, ContainerError.noPeersPreapprovePreparedIdentity)
-                return
-            }
+                let keyFactory = TPECPublicKeyFactory()
+                guard let egoPermanentInfo = TPPeerPermanentInfo(peerID: egoPeerID, data: egoPermData, sig: egoPermSig, keyFactory: keyFactory) else {
+                    os_log("preflightPreapprovedJoin: invalid permanent info", log: tplogDebug, type: .debug)
+                    reply(false, ContainerError.invalidPermanentInfoOrSig)
+                    return
+                }
 
 
-            reply(true, nil)
+                guard self.model.hasPotentiallyTrustedPeerPreapprovingKey(egoPermanentInfo.signingPubKey.spki()) else {
+                    os_log("preflightPreapprovedJoin: no peers preapprove our key", log: tplogDebug, type: .debug)
+                    reply(false, ContainerError.noPeersPreapprovePreparedIdentity)
+                    return
+                }
+
+                reply(true, nil)
+            }
         }
     }
 
     func preapprovedJoin(ckksKeys: [CKKSKeychainBackedKeySet],
                          tlkShares: [CKKSTLKShare],
                          preapprovedKeys: [Data]?,
         }
     }
 
     func preapprovedJoin(ckksKeys: [CKKSKeychainBackedKeySet],
                          tlkShares: [CKKSTLKShare],
                          preapprovedKeys: [Data]?,
-                         reply: @escaping (String?, [CKRecord], Error?) -> Void) {
+                         reply: @escaping (String?, [CKRecord], Set<String>?, TPPolicy?, Error?) -> Void) {
         self.semaphore.wait()
         self.semaphore.wait()
-        let reply: (String?, [CKRecord], Error?) -> Void = {
-            os_log("preapprovedJoin complete: %@", log: tplogTrace, type: .info, traceError($2))
+        let reply: (String?, [CKRecord], Set<String>?, TPPolicy?, Error?) -> Void = {
+            os_log("preapprovedJoin complete: %{public}@", log: tplogTrace, type: .info, traceError($4))
             self.semaphore.signal()
             self.semaphore.signal()
-            reply($0, $1, $2)
+            reply($0, $1, $2, $3, $4)
         }
 
         self.fetchAndPersistChangesIfNeeded { error in
             guard error == nil else {
         }
 
         self.fetchAndPersistChangesIfNeeded { error in
             guard error == nil else {
-                os_log("preapprovedJoin unable to fetch changes: %@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "")
-                reply(nil, [], error)
+                os_log("preapprovedJoin unable to fetch changes: %{public}@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "")
+                reply(nil, [], nil, nil, error)
                 return
             }
             self.moc.performAndWait {
                 return
             }
             self.moc.performAndWait {
@@ -3121,36 +3249,36 @@ class Container: NSObject {
                     let egoStableSig = self.containerMO.egoPeerStableInfoSig
                     else {
                         os_log("preapprovedJoin: no prepared identity", log: tplogDebug, type: .debug)
                     let egoStableSig = self.containerMO.egoPeerStableInfoSig
                     else {
                         os_log("preapprovedJoin: no prepared identity", log: tplogDebug, type: .debug)
-                        reply(nil, [], ContainerError.noPreparedIdentity)
+                        reply(nil, [], nil, nil, ContainerError.noPreparedIdentity)
                         return
                 }
 
                 let keyFactory = TPECPublicKeyFactory()
                 guard let selfPermanentInfo = TPPeerPermanentInfo(peerID: egoPeerID, data: egoPermData, sig: egoPermSig, keyFactory: keyFactory) else {
                         return
                 }
 
                 let keyFactory = TPECPublicKeyFactory()
                 guard let selfPermanentInfo = TPPeerPermanentInfo(peerID: egoPeerID, data: egoPermData, sig: egoPermSig, keyFactory: keyFactory) else {
-                    reply(nil, [], ContainerError.invalidPermanentInfoOrSig)
+                    reply(nil, [], nil, nil, ContainerError.invalidPermanentInfoOrSig)
                     return
                 }
                 guard let selfStableInfo = TPPeerStableInfo(data: egoStableData, sig: egoStableSig) else {
                     return
                 }
                 guard let selfStableInfo = TPPeerStableInfo(data: egoStableData, sig: egoStableSig) else {
-                    reply(nil, [], ContainerError.invalidStableInfoOrSig)
+                    reply(nil, [], nil, nil, ContainerError.invalidStableInfoOrSig)
                     return
                 }
 
                 guard self.onqueueMachineIDAllowedByIDMS(machineID: selfPermanentInfo.machineID) else {
                     return
                 }
 
                 guard self.onqueueMachineIDAllowedByIDMS(machineID: selfPermanentInfo.machineID) else {
-                    os_log("preapprovedJoin: self machineID %@ (me) not on list", log: tplogDebug, type: .debug, selfPermanentInfo.machineID)
+                    os_log("preapprovedJoin: self machineID %{public}@ (me) not on list", log: tplogDebug, type: .debug, selfPermanentInfo.machineID)
                     self.onqueueTTRUntrusted()
                     self.onqueueTTRUntrusted()
-                    reply(nil, [], ContainerError.preparedIdentityNotOnAllowedList(selfPermanentInfo.machineID))
+                    reply(nil, [], nil, nil, ContainerError.preparedIdentityNotOnAllowedList(selfPermanentInfo.machineID))
                     return
                 }
                 loadEgoKeys(peerID: egoPeerID) { egoPeerKeys, error in
                     guard let egoPeerKeys = egoPeerKeys else {
                         os_log("preapprovedJoin: Don't have my own keys: can't join", log: tplogDebug, type: .default, (error as CVarArg?) ?? "no error")
                     return
                 }
                 loadEgoKeys(peerID: egoPeerID) { egoPeerKeys, error in
                     guard let egoPeerKeys = egoPeerKeys else {
                         os_log("preapprovedJoin: Don't have my own keys: can't join", log: tplogDebug, type: .default, (error as CVarArg?) ?? "no error")
-                        reply(nil, [], error)
+                        reply(nil, [], nil, nil, error)
                         return
                     }
 
                     guard self.model.hasPotentiallyTrustedPeerPreapprovingKey(egoPeerKeys.signingKey.publicKey().spki()) else {
                         os_log("preapprovedJoin: no peers preapprove our key", log: tplogDebug, type: .debug)
                         return
                     }
 
                     guard self.model.hasPotentiallyTrustedPeerPreapprovingKey(egoPeerKeys.signingKey.publicKey().spki()) else {
                         os_log("preapprovedJoin: no peers preapprove our key", log: tplogDebug, type: .debug)
-                        reply(nil, [], ContainerError.noPeersPreapprovePreparedIdentity)
+                        reply(nil, [], nil, nil, ContainerError.noPeersPreapprovePreparedIdentity)
                         return
                     }
 
                         return
                     }
 
@@ -3167,8 +3295,14 @@ class Container: NSObject {
                                                                                            vouchers: [],
                                                                                            egoPeerKeys: egoPeerKeys)
                         } catch {
                                                                                            vouchers: [],
                                                                                            egoPeerKeys: egoPeerKeys)
                         } catch {
-                            os_log("Unable to create peer for joining: %@", log: tplogDebug, type: .default, error as CVarArg)
-                            reply(nil, [], error)
+                            os_log("Unable to create peer for joining: %{public}@", log: tplogDebug, type: .default, error as CVarArg)
+                            reply(nil, [], nil, nil, error)
+                            return
+                        }
+
+                        guard let peerStableInfo = peer.stableInfoAndSig.toStableInfo() else {
+                            os_log("Unable to create new peer stable new for joining", log: tplogDebug, type: .default)
+                            reply(nil, [], nil, nil, ContainerError.invalidStableInfoOrSig)
                             return
                         }
 
                             return
                         }
 
@@ -3181,8 +3315,8 @@ class Container: NSObject {
                                                                                    egoPeerDynamicInfo: newDynamicInfo,
                                                                                    epoch: Int(selfPermanentInfo.epoch))
                         } catch {
                                                                                    egoPeerDynamicInfo: newDynamicInfo,
                                                                                    epoch: Int(selfPermanentInfo.epoch))
                         } catch {
-                            os_log("Unable to process keys before joining: %@", log: tplogDebug, type: .default, error as CVarArg)
-                            reply(nil, [], error)
+                            os_log("Unable to process keys before joining: %{public}@", log: tplogDebug, type: .default, error as CVarArg)
+                            reply(nil, [], nil, nil, error)
                             return
                         }
 
                             return
                         }
 
@@ -3190,27 +3324,27 @@ class Container: NSObject {
                         do {
                             bottle = try self.assembleBottle(egoPeerID: egoPeerID)
                         } catch {
                         do {
                             bottle = try self.assembleBottle(egoPeerID: egoPeerID)
                         } catch {
-                            reply(nil, [], error)
+                            reply(nil, [], nil, nil, error)
                             return
                         }
 
                             return
                         }
 
-                        os_log("Beginning preapprovedJoin for peer %@", log: tplogDebug, type: .default, egoPeerID)
-                        os_log("preapprovedJoin permanentInfo: %@", log: tplogDebug, type: .debug, egoPermData.base64EncodedString())
-                        os_log("preapprovedJoin permanentInfoSig: %@", log: tplogDebug, type: .debug, egoPermSig.base64EncodedString())
-                        os_log("preapprovedJoin stableInfo: %@", log: tplogDebug, type: .debug, egoStableData.base64EncodedString())
-                        os_log("preapprovedJoin stableInfoSig: %@", log: tplogDebug, type: .debug, egoStableSig.base64EncodedString())
-                        os_log("preapprovedJoin dynamicInfo: %@", log: tplogDebug, type: .debug, peer.dynamicInfoAndSig.peerDynamicInfo.base64EncodedString())
-                        os_log("preapprovedJoin dynamicInfoSig: %@", log: tplogDebug, type: .debug, peer.dynamicInfoAndSig.sig.base64EncodedString())
+                        os_log("Beginning preapprovedJoin for peer %{public}@", log: tplogDebug, type: .default, egoPeerID)
+                        os_log("preapprovedJoin permanentInfo: %{public}@", log: tplogDebug, type: .debug, egoPermData.base64EncodedString())
+                        os_log("preapprovedJoin permanentInfoSig: %{public}@", log: tplogDebug, type: .debug, egoPermSig.base64EncodedString())
+                        os_log("preapprovedJoin stableInfo: %{public}@", log: tplogDebug, type: .debug, egoStableData.base64EncodedString())
+                        os_log("preapprovedJoin stableInfoSig: %{public}@", log: tplogDebug, type: .debug, egoStableSig.base64EncodedString())
+                        os_log("preapprovedJoin dynamicInfo: %{public}@", log: tplogDebug, type: .debug, peer.dynamicInfoAndSig.peerDynamicInfo.base64EncodedString())
+                        os_log("preapprovedJoin dynamicInfoSig: %{public}@", log: tplogDebug, type: .debug, peer.dynamicInfoAndSig.sig.base64EncodedString())
 
 
-                        os_log("preapprovedJoin vouchers: %@", log: tplogDebug, type: .debug, peer.vouchers.map { $0.voucher.base64EncodedString() })
-                        os_log("preapprovedJoin voucher signatures: %@", log: tplogDebug, type: .debug, peer.vouchers.map { $0.sig.base64EncodedString() })
+                        os_log("preapprovedJoin vouchers: %{public}@", log: tplogDebug, type: .debug, peer.vouchers.map { $0.voucher.base64EncodedString() })
+                        os_log("preapprovedJoin voucher signatures: %{public}@", log: tplogDebug, type: .debug, peer.vouchers.map { $0.sig.base64EncodedString() })
 
                         os_log("preapprovedJoin: uploading %d tlk shares", log: tplogDebug, type: .default, allTLKShares.count)
 
                         do {
 
                         os_log("preapprovedJoin: uploading %d tlk shares", log: tplogDebug, type: .default, allTLKShares.count)
 
                         do {
-                            os_log("preapprovedJoin peer: %@", log: tplogDebug, type: .debug, try peer.serializedData().base64EncodedString())
+                            os_log("preapprovedJoin peer: %{public}@", log: tplogDebug, type: .debug, try peer.serializedData().base64EncodedString())
                         } catch {
                         } catch {
-                            os_log("preapprovedJoin unable to encode peer: %@", log: tplogDebug, type: .debug, error as CVarArg)
+                            os_log("preapprovedJoin unable to encode peer: %{public}@", log: tplogDebug, type: .debug, error as CVarArg)
                         }
 
                         let changeToken = self.containerMO.changeToken ?? ""
                         }
 
                         let changeToken = self.containerMO.changeToken ?? ""
@@ -3222,10 +3356,10 @@ class Container: NSObject {
                             $0.viewKeys = viewKeys
                         }
                         self.cuttlefish.joinWithVoucher(request) { response, error in
                             $0.viewKeys = viewKeys
                         }
                         self.cuttlefish.joinWithVoucher(request) { response, error in
-                            os_log("preapprovedJoin(%@): %@, error: %@", log: tplogDebug, "\(String(describing: request))", "\(String(describing: response))", "\(String(describing: error))")
+                            os_log("preapprovedJoin(%{public}@): %{public}@, error: %{public}@", log: tplogDebug, "\(String(describing: request))", "\(String(describing: response))", "\(String(describing: error))")
                             guard let response = response, error == nil else {
                             guard let response = response, error == nil else {
-                                os_log("preapprovedJoin failed: %@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "no error")
-                                reply(nil, [], error ?? ContainerError.cloudkitResponseMissing)
+                                os_log("preapprovedJoin failed: %{public}@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "no error")
+                                reply(nil, [], nil, nil, error ?? ContainerError.cloudkitResponseMissing)
                                 return
                             }
 
                                 return
                             }
 
@@ -3233,14 +3367,18 @@ class Container: NSObject {
                                 do {
                                     self.containerMO.egoPeerStableInfo = peer.stableInfoAndSig.peerStableInfo
                                     self.containerMO.egoPeerStableInfoSig = peer.stableInfoAndSig.sig
                                 do {
                                     self.containerMO.egoPeerStableInfo = peer.stableInfoAndSig.peerStableInfo
                                     self.containerMO.egoPeerStableInfoSig = peer.stableInfoAndSig.sig
+
+                                    let (syncingViews, policy) = try self.policyAndViewsFor(permanentInfo: selfPermanentInfo,
+                                                                                            stableInfo: peerStableInfo)
+
                                     try self.onQueuePersist(changes: response.changes)
                                     os_log("preapprovedJoin succeeded", log: tplogDebug)
 
                                     let keyHierarchyRecords = response.zoneKeyHierarchyRecords.compactMap { CKRecord($0) }
                                     try self.onQueuePersist(changes: response.changes)
                                     os_log("preapprovedJoin succeeded", log: tplogDebug)
 
                                     let keyHierarchyRecords = response.zoneKeyHierarchyRecords.compactMap { CKRecord($0) }
-                                    reply(egoPeerID, keyHierarchyRecords, nil)
+                                    reply(egoPeerID, keyHierarchyRecords, syncingViews, policy, nil)
                                 } catch {
                                 } catch {
-                                    os_log("preapprovedJoin failed: %@", log: tplogDebug, String(describing: error))
-                                    reply(nil, [], error)
+                                    os_log("preapprovedJoin failed: %{public}@", log: tplogDebug, String(describing: error))
+                                    reply(nil, [], nil, nil, error)
                                 }
                             }
                         }
                                 }
                             }
                         }
@@ -3258,7 +3396,7 @@ class Container: NSObject {
                 reply: @escaping (TrustedPeersHelperPeerState?, Error?) -> Void) {
         self.semaphore.wait()
         let reply: (TrustedPeersHelperPeerState?, Error?) -> Void = {
                 reply: @escaping (TrustedPeersHelperPeerState?, Error?) -> Void) {
         self.semaphore.wait()
         let reply: (TrustedPeersHelperPeerState?, Error?) -> Void = {
-            os_log("update complete: %@", log: tplogTrace, type: .info, traceError($1))
+            os_log("update complete: %{public}@", log: tplogTrace, type: .info, traceError($1))
             self.semaphore.signal()
             reply($0, $1)
         }
             self.semaphore.signal()
             reply($0, $1)
         }
@@ -3268,19 +3406,17 @@ class Container: NSObject {
                                           serialNumber: serialNumber,
                                           osVersion: osVersion,
                                           policyVersion: policyVersion,
                                           serialNumber: serialNumber,
                                           osVersion: osVersion,
                                           policyVersion: policyVersion,
-                                          policySecrets: policySecrets,
-                                          recoverySigningPubKey: nil,
-                                          recoveryEncryptionPubKey: nil)
+                                          policySecrets: policySecrets)
         self.fetchChangesAndUpdateTrustIfNeeded(stableChanges: stableChanges, reply: reply)
     }
 
     func set(preapprovedKeys: [Data],
         self.fetchChangesAndUpdateTrustIfNeeded(stableChanges: stableChanges, reply: reply)
     }
 
     func set(preapprovedKeys: [Data],
-             reply: @escaping (Error?) -> Void) {
+             reply: @escaping (TrustedPeersHelperPeerState?, Error?) -> Void) {
         self.semaphore.wait()
         self.semaphore.wait()
-        let reply: (Error?) -> Void = {
-            os_log("setPreapprovedKeys complete: %@", log: tplogTrace, type: .info, traceError($0))
+        let reply: (TrustedPeersHelperPeerState?, Error?) -> Void = {
+            os_log("setPreapprovedKeys complete: %{public}@", log: tplogTrace, type: .info, traceError($1))
             self.semaphore.signal()
             self.semaphore.signal()
-            reply($0)
+            reply($0, $1)
         }
 
         self.moc.performAndWait {
         }
 
         self.moc.performAndWait {
@@ -3289,13 +3425,13 @@ class Container: NSObject {
             guard let egoPeerID = self.containerMO.egoPeerID else {
                 // No identity, nothing to do
                 os_log("setPreapprovedKeys: No identity.", log: tplogDebug, type: .default)
             guard let egoPeerID = self.containerMO.egoPeerID else {
                 // No identity, nothing to do
                 os_log("setPreapprovedKeys: No identity.", log: tplogDebug, type: .default)
-                reply(ContainerError.noPreparedIdentity)
+                reply(nil, ContainerError.noPreparedIdentity)
                 return
             }
             loadEgoKeyPair(identifier: signingKeyIdentifier(peerID: egoPeerID)) { signingKeyPair, error in
                 guard let signingKeyPair = signingKeyPair else {
                 return
             }
             loadEgoKeyPair(identifier: signingKeyIdentifier(peerID: egoPeerID)) { signingKeyPair, error in
                 guard let signingKeyPair = signingKeyPair else {
-                    os_log("setPreapprovedKeys: no signing key pair: %@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "no error")
-                    reply(error ?? ContainerError.unableToCreateKeyPair)
+                    os_log("setPreapprovedKeys: no signing key pair: %{public}@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "no error")
+                    reply(nil, error ?? ContainerError.unableToCreateKeyPair)
                     return
                 }
 
                     return
                 }
 
@@ -3309,44 +3445,37 @@ class Container: NSObject {
                                                                                  signing: signingKeyPair,
                                                                                  currentMachineIDs: self.onqueueCurrentMIDList())
                     } catch {
                                                                                  signing: signingKeyPair,
                                                                                  currentMachineIDs: self.onqueueCurrentMIDList())
                     } catch {
-                        os_log("setPreapprovedKeys: couldn't calculate dynamic info: %@", log: tplogDebug, type: .default, error as CVarArg)
-                        reply(error)
+                        os_log("setPreapprovedKeys: couldn't calculate dynamic info: %{public}@", log: tplogDebug, type: .default, error as CVarArg)
+                        reply(nil, error)
                         return
                     }
 
                         return
                     }
 
-                    os_log("setPreapprovedKeys: produced a dynamicInfo: %@", log: tplogDebug, type: .default, dynamicInfo)
+                    os_log("setPreapprovedKeys: produced a dynamicInfo: %{public}@", log: tplogDebug, type: .default, dynamicInfo)
 
                     if dynamicInfo == self.model.peer(withID: egoPeerID)?.dynamicInfo {
                         os_log("setPreapprovedKeys: no change; nothing to do.", log: tplogDebug, type: .default)
 
                     if dynamicInfo == self.model.peer(withID: egoPeerID)?.dynamicInfo {
                         os_log("setPreapprovedKeys: no change; nothing to do.", log: tplogDebug, type: .default)
-                        reply(nil)
+
+                        // Calling this will fill in the peer status
+                        self.updateTrustIfNeeded(reply: reply)
                         return
                     }
 
                         return
                     }
 
-                    os_log("setPreapprovedKeys: attempting updateTrust for %@ with: %@", log: tplogDebug, type: .default, egoPeerID, dynamicInfo)
+                    os_log("setPreapprovedKeys: attempting updateTrust for %{public}@ with: %{public}@", log: tplogDebug, type: .default, egoPeerID, dynamicInfo)
                     let request = UpdateTrustRequest.with {
                         $0.changeToken = self.containerMO.changeToken ?? ""
                         $0.peerID = egoPeerID
                         $0.dynamicInfoAndSig = SignedPeerDynamicInfo(dynamicInfo)
                     }
 
                     let request = UpdateTrustRequest.with {
                         $0.changeToken = self.containerMO.changeToken ?? ""
                         $0.peerID = egoPeerID
                         $0.dynamicInfoAndSig = SignedPeerDynamicInfo(dynamicInfo)
                     }
 
-                    self.cuttlefish.updateTrust(request) { response, error in
-                        os_log("setPreapprovedKeys(%@): %@, error: %@", log: tplogDebug, "\(String(describing: request))", "\(String(describing: response))", "\(String(describing: error))")
-                        guard let response = response, error == nil else {
-                            os_log("setPreapprovedKeys failed: %@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "no error")
-                            reply(error ?? ContainerError.cloudkitResponseMissing)
-                            return
-                        }
-                        os_log("setPreapprovedKeys: updateTrust suceeded", log: tplogDebug, type: .default)
-
-                        do {
-                            try self.persist(changes: response.changes)
-                        } catch {
-                            os_log("setPreapprovedKeys: could not persist changes: %@", log: tplogDebug, type: .default, error as CVarArg)
-                            reply(error)
+                    self.perform(updateTrust: request) { state, error in
+                        guard error == nil else {
+                            os_log("setPreapprovedKeys: failed: %{public}@", log: tplogDebug, type: .default, error as CVarArg? ?? "no error")
+                            reply(state, error)
                             return
                         }
 
                             return
                         }
 
-                        reply(nil)
+                        os_log("setPreapprovedKeys: updateTrust succeeded", log: tplogDebug, type: .default)
+                        reply(state, nil)
                     }
                 }
             }
                     }
                 }
             }
@@ -3358,7 +3487,7 @@ class Container: NSObject {
                     reply: @escaping ([CKRecord]?, Error?) -> Void) {
         self.semaphore.wait()
         let reply: ([CKRecord]?, Error?) -> Void = {
                     reply: @escaping ([CKRecord]?, Error?) -> Void) {
         self.semaphore.wait()
         let reply: ([CKRecord]?, Error?) -> Void = {
-            os_log("updateTLKs complete: %@", log: tplogTrace, type: .info, traceError($1))
+            os_log("updateTLKs complete: %{public}@", log: tplogTrace, type: .info, traceError($1))
             self.semaphore.signal()
             reply($0, $1)
         }
             self.semaphore.signal()
             reply($0, $1)
         }
@@ -3366,67 +3495,73 @@ class Container: NSObject {
         os_log("Uploading some new TLKs: %@", log: tplogDebug, type: .default, ckksKeys)
 
         self.moc.performAndWait {
         os_log("Uploading some new TLKs: %@", log: tplogDebug, type: .default, ckksKeys)
 
         self.moc.performAndWait {
-            guard let egoPeerID = self.containerMO.egoPeerID,
-                let egoPermData = self.containerMO.egoPeerPermanentInfo,
-                let egoPermSig = self.containerMO.egoPeerPermanentInfoSig
-                else {
-                    os_log("Have no self identity, can't make tlk shares", log: tplogDebug, type: .default)
-                    reply(nil, ContainerError.noPreparedIdentity)
-                    return
-            }
+            self.onqueueUpdateTLKs(ckksKeys: ckksKeys, tlkShares: tlkShares, reply: reply)
+        }
+   }
 
 
-            guard let selfPermanentInfo = TPPeerPermanentInfo(peerID: egoPeerID, data: egoPermData, sig: egoPermSig, keyFactory: TPECPublicKeyFactory()) else {
-                os_log("Couldn't parse self identity", log: tplogDebug, type: .default, ckksKeys)
-                reply(nil, ContainerError.invalidPermanentInfoOrSig)
+   func onqueueUpdateTLKs(ckksKeys: [CKKSKeychainBackedKeySet],
+                          tlkShares: [CKKSTLKShare],
+                          reply: @escaping ([CKRecord]?, Error?) -> Void) {
+        guard let egoPeerID = self.containerMO.egoPeerID,
+            let egoPermData = self.containerMO.egoPeerPermanentInfo,
+            let egoPermSig = self.containerMO.egoPeerPermanentInfoSig
+            else {
+                os_log("Have no self identity, can't make tlk shares", log: tplogDebug, type: .default)
+                reply(nil, ContainerError.noPreparedIdentity)
+                return
+        }
+
+        guard let selfPermanentInfo = TPPeerPermanentInfo(peerID: egoPeerID, data: egoPermData, sig: egoPermSig, keyFactory: TPECPublicKeyFactory()) else {
+            os_log("Couldn't parse self identity", log: tplogDebug, type: .default, ckksKeys)
+            reply(nil, ContainerError.invalidPermanentInfoOrSig)
+            return
+        }
+
+        loadEgoKeys(peerID: egoPeerID) { egoPeerKeys, error in
+            guard let egoPeerKeys = egoPeerKeys else {
+                os_log("Don't have my own peer keys; can't upload new TLKs: %{public}@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "error missing")
+                reply(nil, error)
                 return
             }
                 return
             }
+            self.moc.performAndWait {
+                guard let egoPeerDynamicInfo = self.model.getDynamicInfoForPeer(withID: egoPeerID) else {
+                    os_log("Unable to fetch dynamic info for self", log: tplogDebug, type: .default)
+                    reply(nil, ContainerError.missingDynamicInfo)
+                    return
+                }
 
 
-            loadEgoKeys(peerID: egoPeerID) { egoPeerKeys, error in
-                guard let egoPeerKeys = egoPeerKeys else {
-                    os_log("Don't have my own peer keys; can't upload new TLKs: %@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "error missing")
+                let allTLKShares: [TLKShare]
+                let viewKeys: [ViewKeys]
+                do {
+                    (viewKeys, allTLKShares) = try self.makeSharesForNewKeySets(ckksKeys: ckksKeys,
+                                                                                tlkShares: tlkShares,
+                                                                                egoPeerKeys: egoPeerKeys,
+                                                                                egoPeerDynamicInfo: egoPeerDynamicInfo,
+                                                                                epoch: Int(selfPermanentInfo.epoch))
+                } catch {
+                    os_log("Unable to process keys before uploading: %{public}@", log: tplogDebug, type: .default, error as CVarArg)
                     reply(nil, error)
                     return
                 }
                     reply(nil, error)
                     return
                 }
-                self.moc.performAndWait {
-                    guard let egoPeerDynamicInfo = self.model.getDynamicInfoForPeer(withID: egoPeerID) else {
-                        os_log("Unable to fetch dynamic info for self", log: tplogDebug, type: .default)
-                        reply(nil, ContainerError.missingDynamicInfo)
-                        return
-                    }
 
 
-                    let allTLKShares: [TLKShare]
-                    let viewKeys: [ViewKeys]
-                    do {
-                        (viewKeys, allTLKShares) = try self.makeSharesForNewKeySets(ckksKeys: ckksKeys,
-                                                                                    tlkShares: tlkShares,
-                                                                                    egoPeerKeys: egoPeerKeys,
-                                                                                    egoPeerDynamicInfo: egoPeerDynamicInfo,
-                                                                                    epoch: Int(selfPermanentInfo.epoch))
-                    } catch {
-                        os_log("Unable to process keys before uploading: %@", log: tplogDebug, type: .default, error as CVarArg)
+                let request = UpdateTrustRequest.with {
+                    $0.changeToken = self.containerMO.changeToken ?? ""
+                    $0.peerID = egoPeerID
+                    $0.tlkShares = allTLKShares
+                    $0.viewKeys = viewKeys
+                }
+
+                self.cuttlefish.updateTrust(request) { response, error in
+                    os_log("UpdateTrust(%{public}@): %{public}@, error: %{public}@", log: tplogDebug, "\(String(describing: request))", "\(String(describing: response))", "\(String(describing: error))")
+
+                    guard error == nil else {
                         reply(nil, error)
                         return
                     }
 
                         reply(nil, error)
                         return
                     }
 
-                    let request = UpdateTrustRequest.with {
-                        $0.changeToken = self.containerMO.changeToken ?? ""
-                        $0.peerID = egoPeerID
-                        $0.tlkShares = allTLKShares
-                        $0.viewKeys = viewKeys
-                    }
-
-                    self.cuttlefish.updateTrust(request) { response, error in
-                        os_log("UpdateTrust(%@): %@, error: %@", log: tplogDebug, "\(String(describing: request))", "\(String(describing: response))", "\(String(describing: error))")
-
-                        guard error == nil else {
-                            reply(nil, error)
-                            return
-                        }
-
-                        let keyHierarchyRecords = response?.zoneKeyHierarchyRecords.compactMap { CKRecord($0) } ?? []
-                        os_log("Recevied %d CKRecords back", log: tplogDebug, type: .default, keyHierarchyRecords.count)
-                        reply(keyHierarchyRecords, nil)
-                    }
+                    let keyHierarchyRecords = response?.zoneKeyHierarchyRecords.compactMap { CKRecord($0) } ?? []
+                    os_log("Recevied %d CKRecords back", log: tplogDebug, type: .default, keyHierarchyRecords.count)
+                    reply(keyHierarchyRecords, nil)
                 }
             }
         }
                 }
             }
         }
@@ -3435,7 +3570,7 @@ class Container: NSObject {
     func getState(reply: @escaping (ContainerState) -> Void) {
         self.semaphore.wait()
         let reply: (ContainerState) -> Void = {
     func getState(reply: @escaping (ContainerState) -> Void) {
         self.semaphore.wait()
         let reply: (ContainerState) -> Void = {
-            os_log("getState complete: %@", log: tplogTrace, type: .info, $0.egoPeerID ?? "<NULL>")
+            os_log("getState complete: %{public}@", log: tplogTrace, type: .info, $0.egoPeerID ?? "<NULL>")
             self.semaphore.signal()
             reply($0)
         }
             self.semaphore.signal()
             reply($0)
         }
@@ -3459,7 +3594,7 @@ class Container: NSObject {
     }
 
     // This will only fetch changes if no changes have ever been fetched before
     }
 
     // This will only fetch changes if no changes have ever been fetched before
-    private func fetchAndPersistChangesIfNeeded(reply: @escaping (Error?) -> Void) {
+    func fetchAndPersistChangesIfNeeded(reply: @escaping (Error?) -> Void) {
         self.moc.performAndWait {
             if self.containerMO.changeToken == nil {
                 self.onqueueFetchAndPersistChanges(reply: reply)
         self.moc.performAndWait {
             if self.containerMO.changeToken == nil {
                 self.onqueueFetchAndPersistChanges(reply: reply)
@@ -3479,10 +3614,10 @@ class Container: NSObject {
         let request = FetchChangesRequest.with {
             $0.changeToken = self.containerMO.changeToken ?? ""
         }
         let request = FetchChangesRequest.with {
             $0.changeToken = self.containerMO.changeToken ?? ""
         }
-        os_log("Fetching with change token: %@", log: tplogDebug, type: .default, request.changeToken.count > 0 ? request.changeToken : "empty")
+        os_log("Fetching with change token: %{public}@", log: tplogDebug, type: .default, !request.changeToken.isEmpty ? request.changeToken : "empty")
 
         self.cuttlefish.fetchChanges(request) { response, error in
 
         self.cuttlefish.fetchChanges(request) { response, error in
-            os_log("FetchChanges(%@): %@, error: %@", log: tplogDebug, "\(String(describing: request))", "\(String(describing: response))", "\(String(describing: error))")
+            os_log("FetchChanges(%{public}@): %{public}@, error: %{public}@", log: tplogDebug, "\(String(describing: request))", "\(String(describing: response))", "\(String(describing: error))")
             guard let response = response, error == nil else {
                 switch error {
                 case CuttlefishErrorMatcher(code: CuttlefishErrorCode.changeTokenExpired):
             guard let response = response, error == nil else {
                 switch error {
                 case CuttlefishErrorMatcher(code: CuttlefishErrorCode.changeTokenExpired):
@@ -3492,7 +3627,7 @@ class Container: NSObject {
                         do {
                             try self.deleteLocalCloudKitData()
                         } catch {
                         do {
                             try self.deleteLocalCloudKitData()
                         } catch {
-                            os_log("Failed to reset local data: %@", log: tplogDebug, type: .default, error as CVarArg)
+                            os_log("Failed to reset local data: %{public}@", log: tplogDebug, type: .default, error as CVarArg)
                             reply(error)
                             return
                         }
                             reply(error)
                             return
                         }
@@ -3502,10 +3637,10 @@ class Container: NSObject {
 
                     return
                 default:
 
                     return
                 default:
-                    os_log("Fetch error is an unknown error: %@", log: tplogDebug, type: .default, String(describing: error))
+                    os_log("Fetch error is an unknown error: %{public}@", log: tplogDebug, type: .default, String(describing: error))
                 }
 
                 }
 
-                os_log("Could not fetch changes: %@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "no error")
+                os_log("Could not fetch changes: %{public}@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "no error")
                 reply(error)
                 return
             }
                 reply(error)
                 return
             }
@@ -3513,7 +3648,7 @@ class Container: NSObject {
             do {
                 try self.persist(changes: response.changes)
             } catch {
             do {
                 try self.persist(changes: response.changes)
             } catch {
-                os_log("Could not persist changes: %@", log: tplogDebug, type: .default, error as CVarArg)
+                os_log("Could not persist changes: %{public}@", log: tplogDebug, type: .default, error as CVarArg)
                 reply(error)
                 return
             }
                 reply(error)
                 return
             }
@@ -3536,13 +3671,13 @@ class Container: NSObject {
         guard let oldDynamicInfo = oldDynamicInfo else {
             return true
         }
         guard let oldDynamicInfo = oldDynamicInfo else {
             return true
         }
-        if (newDynamicInfo.includedPeerIDs != oldDynamicInfo.includedPeerIDs) {
+        if newDynamicInfo.includedPeerIDs != oldDynamicInfo.includedPeerIDs {
             return true
         }
             return true
         }
-        if (newDynamicInfo.excludedPeerIDs != oldDynamicInfo.excludedPeerIDs) {
+        if newDynamicInfo.excludedPeerIDs != oldDynamicInfo.excludedPeerIDs {
             return true
         }
             return true
         }
-        if (newDynamicInfo.preapprovals != oldDynamicInfo.preapprovals) {
+        if newDynamicInfo.preapprovals != oldDynamicInfo.preapprovals {
             return true
         }
         return false
             return true
         }
         return false
@@ -3558,16 +3693,16 @@ class Container: NSObject {
     // the caller's responsibility to release it after it completes
     // (i.e. after reply is invoked).
     internal func fetchChangesAndUpdateTrustIfNeeded(stableChanges: StableChanges? = nil,
     // the caller's responsibility to release it after it completes
     // (i.e. after reply is invoked).
     internal func fetchChangesAndUpdateTrustIfNeeded(stableChanges: StableChanges? = nil,
-                                                    changesPending: Bool = false,
-                                                    reply: @escaping (TrustedPeersHelperPeerState?, Error?) -> Void) {
+                                                     peerChanges: Bool = false,
+                                                     reply: @escaping (TrustedPeersHelperPeerState?, Error?) -> Void) {
         self.fetchAndPersistChanges { error in
             if let error = error {
         self.fetchAndPersistChanges { error in
             if let error = error {
-                os_log("fetchChangesAndUpdateTrustIfNeeded: fetching failed: %@", log: tplogDebug, type: .default, error as CVarArg)
+                os_log("fetchChangesAndUpdateTrustIfNeeded: fetching failed: %{public}@", log: tplogDebug, type: .default, error as CVarArg)
                 reply(nil, error)
                 return
             }
 
                 reply(nil, error)
                 return
             }
 
-            self.updateTrustIfNeeded(stableChanges: stableChanges, changesPending: changesPending, reply: reply)
+            self.updateTrustIfNeeded(stableChanges: stableChanges, peerChanges: peerChanges, reply: reply)
         }
     }
 
         }
     }
 
@@ -3581,132 +3716,152 @@ class Container: NSObject {
     // the caller's responsibility to release it after it completes
     // (i.e. after reply is invoked).
     private func updateTrustIfNeeded(stableChanges: StableChanges? = nil,
     // the caller's responsibility to release it after it completes
     // (i.e. after reply is invoked).
     private func updateTrustIfNeeded(stableChanges: StableChanges? = nil,
-                                     changesPending: Bool = false,
+                                     peerChanges: Bool = false,
                                      reply: @escaping (TrustedPeersHelperPeerState?, Error?) -> Void) {
         self.moc.performAndWait {
             guard let egoPeerID = self.containerMO.egoPeerID else {
                 // No identity, nothing to do
                 os_log("updateTrustIfNeeded: No identity.", log: tplogDebug, type: .default)
                                      reply: @escaping (TrustedPeersHelperPeerState?, Error?) -> Void) {
         self.moc.performAndWait {
             guard let egoPeerID = self.containerMO.egoPeerID else {
                 // No identity, nothing to do
                 os_log("updateTrustIfNeeded: No identity.", log: tplogDebug, type: .default)
-                reply(TrustedPeersHelperPeerState(peerID: nil, isPreapproved: false, status: .unknown, memberChanges: changesPending, unknownMachineIDs: false, osVersion: nil), nil)
+                reply(TrustedPeersHelperPeerState(peerID: nil, isPreapproved: false, status: .unknown, memberChanges: peerChanges, unknownMachineIDs: false, osVersion: nil), nil)
                 return
             }
             loadEgoKeyPair(identifier: signingKeyIdentifier(peerID: egoPeerID)) { signingKeyPair, error in
                 guard let signingKeyPair = signingKeyPair else {
                 return
             }
             loadEgoKeyPair(identifier: signingKeyIdentifier(peerID: egoPeerID)) { signingKeyPair, error in
                 guard let signingKeyPair = signingKeyPair else {
-                    os_log("updateTrustIfNeeded: no signing key pair: %@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "no error")
-                    reply(TrustedPeersHelperPeerState(peerID: nil, isPreapproved: false, status: .unknown, memberChanges: changesPending, unknownMachineIDs: false, osVersion: nil), error)
+                    os_log("updateTrustIfNeeded: no signing key pair: %{public}@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "no error")
+                    reply(TrustedPeersHelperPeerState(peerID: nil, isPreapproved: false, status: .unknown, memberChanges: peerChanges, unknownMachineIDs: false, osVersion: nil), error)
                     return
                 }
                     return
                 }
-                guard self.model.hasPeer(withID: egoPeerID) else {
+                guard let currentSelfInModel = self.model.peer(withID: egoPeerID) else {
                     // Not in circle, nothing to do
                     let isPreapproved = self.model.hasPotentiallyTrustedPeerPreapprovingKey(signingKeyPair.publicKey().spki())
                     // Not in circle, nothing to do
                     let isPreapproved = self.model.hasPotentiallyTrustedPeerPreapprovingKey(signingKeyPair.publicKey().spki())
-                    os_log("updateTrustIfNeeded: ego peer is not in model, is %@", log: tplogDebug, type: .default, isPreapproved ? "preapproved" : "not yet preapproved")
+                    os_log("updateTrustIfNeeded: ego peer is not in model, is %{public}@", log: tplogDebug, type: .default, isPreapproved ? "preapproved" : "not yet preapproved")
                     reply(TrustedPeersHelperPeerState(peerID: egoPeerID,
                                                       isPreapproved: isPreapproved,
                                                       status: .unknown,
                     reply(TrustedPeersHelperPeerState(peerID: egoPeerID,
                                                       isPreapproved: isPreapproved,
                                                       status: .unknown,
-                                                      memberChanges: changesPending,
+                                                      memberChanges: peerChanges,
                                                       unknownMachineIDs: false,
                                                       osVersion: nil),
                           nil)
                     return
                 }
                                                       unknownMachineIDs: false,
                                                       osVersion: nil),
                           nil)
                     return
                 }
-                self.moc.performAndWait {
-                    let dynamicInfo: TPPeerDynamicInfo
-                    var stableInfo: TPPeerStableInfo?
-                    do {
-                        // FIXME We should be able to calculate the contents of dynamicInfo without the signingKeyPair,
-                        // and then only load the key if it has changed and we need to sign a new one. This would also
-                        // help make our detection of change immune from non-canonical serialization of dynamicInfo.
-                        dynamicInfo = try self.model.calculateDynamicInfoForPeer(withID: egoPeerID,
-                                                                                 addingPeerIDs: nil,
-                                                                                 removingPeerIDs: nil,
-                                                                                 preapprovedKeys: nil,
-                                                                                 signing: signingKeyPair,
-                                                                                 currentMachineIDs: self.onqueueCurrentMIDList())
 
 
-                        stableInfo = try self.createNewStableInfoIfNeeded(stableChanges: stableChanges,
-                                                                          egoPeerID: egoPeerID,
-                                                                          dynamicInfo: dynamicInfo,
-                                                                          signingKeyPair: signingKeyPair)
-                    } catch {
-                        os_log("updateTrustIfNeeded: couldn't calculate dynamic info: %@", log: tplogDebug, type: .default, error as CVarArg)
-                        reply(TrustedPeersHelperPeerState(peerID: egoPeerID,
-                                                          isPreapproved: false,
-                                                          status: self.model.statusOfPeer(withID: egoPeerID),
-                                                          memberChanges: changesPending,
-                                                          unknownMachineIDs: false,
-                                                          osVersion: nil),
-                              error)
-                        return
+                // We need to try to have all policy versions that our peers claim to behave
+                let allPolicyVersions = self.model.allPolicyVersions()
+                self.fetchPolicyDocumentsWithSemaphore(versions: allPolicyVersions) { _, policyFetchError in
+                    if let error = policyFetchError {
+                        os_log("updateTrustIfNeeded: error fetching all requested policies (continuing anyway): %{public}@", log: tplogDebug, type: .default, error as CVarArg)
                     }
 
                     }
 
-                    os_log("updateTrustIfNeeded: produced a stableInfo: %@", log: tplogDebug, type: .default, String(describing: stableInfo))
-                    os_log("updateTrustIfNeeded: produced a dynamicInfo: %@", log: tplogDebug, type: .default, dynamicInfo)
-
-                    let peer = self.model.peer(withID: egoPeerID)
-                    if (stableInfo == nil || stableInfo == peer?.stableInfo) &&
-                        dynamicInfo == peer?.dynamicInfo {
-                        os_log("updateTrustIfNeeded: complete.", log: tplogDebug, type: .default)
-                        // No change to the dynamicInfo: update the MID list now that we've reached a steady state
+                    self.moc.performAndWait {
+                        let dynamicInfo: TPPeerDynamicInfo
+                        var stableInfo: TPPeerStableInfo?
                         do {
                         do {
-                            self.onqueueUpdateMachineIDListFromModel(dynamicInfo: dynamicInfo)
-                            try self.moc.save()
+
+                            // FIXME We should be able to calculate the contents of dynamicInfo without the signingKeyPair,
+                            // and then only load the key if it has changed and we need to sign a new one. This would also
+                            // help make our detection of change immune from non-canonical serialization of dynamicInfo.
+                            dynamicInfo = try self.model.calculateDynamicInfoForPeer(withID: egoPeerID,
+                                                                                     addingPeerIDs: nil,
+                                                                                     removingPeerIDs: nil,
+                                                                                     preapprovedKeys: nil,
+                                                                                     signing: signingKeyPair,
+                                                                                     currentMachineIDs: self.onqueueCurrentMIDList())
+
+                            stableInfo = try self.createNewStableInfoIfNeeded(stableChanges: stableChanges,
+                                                                              egoPeerID: egoPeerID,
+                                                                              existingStableInfo: currentSelfInModel.stableInfo,
+                                                                              dynamicInfo: dynamicInfo,
+                                                                              signingKeyPair: signingKeyPair)
                         } catch {
                         } catch {
-                            os_log("updateTrustIfNeeded: unable to remove untrusted MachineIDs: %@", log: tplogDebug, type: .default, error as CVarArg)
+                            os_log("updateTrustIfNeeded: couldn't calculate dynamic info: %{public}@", log: tplogDebug, type: .default, error as CVarArg)
+                            reply(TrustedPeersHelperPeerState(peerID: egoPeerID,
+                                                              isPreapproved: false,
+                                                              status: self.model.statusOfPeer(withID: egoPeerID),
+                                                              memberChanges: peerChanges,
+                                                              unknownMachineIDs: false,
+                                                              osVersion: nil),
+                                  error)
+                            return
                         }
 
                         }
 
-                        reply(TrustedPeersHelperPeerState(peerID: egoPeerID,
-                                                          isPreapproved: false,
-                                                          status: self.model.statusOfPeer(withID: egoPeerID),
-                                                          memberChanges: changesPending,
-                                                          unknownMachineIDs: self.onqueueFullIDMSListWouldBeHelpful(),
-                                                          osVersion: peer?.stableInfo?.osVersion),
-                              nil)
-                        return
-                    }
-                    // Check if we change that should trigger a notification that should trigger TLKShare updates
-                    let haveChanges = changesPending || self.haveTrustMemberChanges(newDynamicInfo: dynamicInfo, oldDynamicInfo: peer?.dynamicInfo)
+                        os_log("updateTrustIfNeeded: produced a stableInfo: %{public}@", log: tplogDebug, type: .default, String(describing: stableInfo))
+                        os_log("updateTrustIfNeeded: produced a dynamicInfo: %{public}@", log: tplogDebug, type: .default, dynamicInfo)
+
+                        let peer = self.model.peer(withID: egoPeerID)
+                        if (stableInfo == nil || stableInfo == peer?.stableInfo) &&
+                            dynamicInfo == peer?.dynamicInfo {
+                            os_log("updateTrustIfNeeded: complete.", log: tplogDebug, type: .default)
+                            // No change to the dynamicInfo: update the MID list now that we've reached a steady state
+                            do {
+                                self.onqueueUpdateMachineIDListFromModel(dynamicInfo: dynamicInfo)
+                                try self.moc.save()
+                            } catch {
+                                os_log("updateTrustIfNeeded: unable to remove untrusted MachineIDs: %{public}@", log: tplogDebug, type: .default, error as CVarArg)
+                            }
 
 
-                    let signedDynamicInfo = SignedPeerDynamicInfo(dynamicInfo)
-                    os_log("updateTrustIfNeeded: attempting updateTrust for %@ with: %@", log: tplogDebug, type: .default, egoPeerID, dynamicInfo)
-                    var request = UpdateTrustRequest.with {
-                        $0.changeToken = self.containerMO.changeToken ?? ""
-                        $0.peerID = egoPeerID
-                        $0.dynamicInfoAndSig = signedDynamicInfo
-                    }
-                    if let stableInfo = stableInfo {
-                        request.stableInfoAndSig = SignedPeerStableInfo(stableInfo)
-                    }
-                    self.cuttlefish.updateTrust(request) { response, error in
-                        os_log("UpdateTrust(%@): %@, error: %@", log: tplogDebug, "\(String(describing: request))", "\(String(describing: response))", "\(String(describing: error))")
-                        guard let response = response, error == nil else {
-                            os_log("UpdateTrust failed: %@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "no error")
-                            reply(nil, error ?? ContainerError.cloudkitResponseMissing)
+                            reply(TrustedPeersHelperPeerState(peerID: egoPeerID,
+                                                              isPreapproved: false,
+                                                              status: self.model.statusOfPeer(withID: egoPeerID),
+                                                              memberChanges: peerChanges,
+                                                              unknownMachineIDs: self.onqueueFullIDMSListWouldBeHelpful(),
+                                                              osVersion: peer?.stableInfo?.osVersion),
+                                  nil)
                             return
                         }
                             return
                         }
+                        // Check if we change that should trigger a notification that should trigger TLKShare updates
+                        let havePeerChanges = peerChanges || self.haveTrustMemberChanges(newDynamicInfo: dynamicInfo, oldDynamicInfo: peer?.dynamicInfo)
 
 
-                        do {
-                            try self.persist(changes: response.changes)
-                        } catch {
-                            os_log("updateTrust failed: %@", log: tplogDebug, String(describing: error))
-                            reply(nil, error)
-                            return
+                        let signedDynamicInfo = SignedPeerDynamicInfo(dynamicInfo)
+                            os_log("updateTrustIfNeeded: attempting updateTrust for %{public}@ with: %{public}@", log: tplogDebug, type: .default, egoPeerID, dynamicInfo)
+                        var request = UpdateTrustRequest.with {
+                            $0.changeToken = self.containerMO.changeToken ?? ""
+                            $0.peerID = egoPeerID
+                            $0.dynamicInfoAndSig = signedDynamicInfo
                         }
                         }
-
-                        if response.changes.more {
-                            self.fetchChangesAndUpdateTrustIfNeeded(stableChanges: stableChanges,
-                                                                    changesPending: haveChanges,
-                                                                    reply: reply)
-                        } else {
-                            self.updateTrustIfNeeded(stableChanges: stableChanges,
-                                                     changesPending: haveChanges,
-                                                     reply: reply)
+                        if let stableInfo = stableInfo {
+                            request.stableInfoAndSig = SignedPeerStableInfo(stableInfo)
                         }
                         }
+
+                        self.perform(updateTrust: request, stableChanges: stableChanges, peerChanges: havePeerChanges, reply: reply)
                     }
                 }
             }
         }
     }
 
                     }
                 }
             }
         }
     }
 
+    private func perform(updateTrust request: UpdateTrustRequest,
+                         stableChanges: StableChanges? = nil,
+                         peerChanges: Bool = false,
+                         reply: @escaping (TrustedPeersHelperPeerState?, Error?) -> Void) {
+
+        self.cuttlefish.updateTrust(request) { response, error in
+            os_log("UpdateTrust(%{public}@): %{public}@, error: %{public}@", log: tplogDebug, "\(String(describing: request))", "\(String(describing: response))", "\(String(describing: error))")
+            guard let response = response, error == nil else {
+                os_log("UpdateTrust failed: %{public}@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "no error")
+                reply(nil, error ?? ContainerError.cloudkitResponseMissing)
+                return
+            }
+
+            do {
+                try self.persist(changes: response.changes)
+            } catch {
+                os_log("UpdateTrust failed: %{public}@", log: tplogDebug, String(describing: error))
+                reply(nil, error)
+                return
+            }
+
+            if response.changes.more {
+                self.fetchChangesAndUpdateTrustIfNeeded(stableChanges: stableChanges,
+                                                        peerChanges: peerChanges,
+                                                        reply: reply)
+            } else {
+                self.updateTrustIfNeeded(stableChanges: stableChanges,
+                                         peerChanges: peerChanges,
+                                         reply: reply)
+            }
+        }
+    }
+
     private func persist(changes: Changes) throws {
         // This is some nonsense: I can't figure out how to tell swift to throw an exception across performAndWait.
         // So, do it ourself
     private func persist(changes: Changes) throws {
         // This is some nonsense: I can't figure out how to tell swift to throw an exception across performAndWait.
         // So, do it ourself
@@ -3716,7 +3871,7 @@ class Container: NSObject {
             os_log("persist: Received %d peer differences, more: %d", log: tplogDebug, type: .default,
                    changes.differences.count,
                    changes.more)
             os_log("persist: Received %d peer differences, more: %d", log: tplogDebug, type: .default,
                    changes.differences.count,
                    changes.more)
-            os_log("persist: New change token: %@", log: tplogDebug, type: .default, changes.changeToken)
+            os_log("persist: New change token: %{public}@", log: tplogDebug, type: .default, changes.changeToken)
 
             do {
                 try self.onQueuePersist(changes: changes)
 
             do {
                 try self.onQueuePersist(changes: changes)
@@ -3736,7 +3891,7 @@ class Container: NSObject {
         self.containerMO.changeToken = changes.changeToken
         self.containerMO.moreChanges = changes.more
 
         self.containerMO.changeToken = changes.changeToken
         self.containerMO.moreChanges = changes.more
 
-        if changes.differences.count > 0 {
+        if !changes.differences.isEmpty {
             self.model.clearViableBottles()
         }
 
             self.model.clearViableBottles()
         }
 
@@ -3769,7 +3924,7 @@ class Container: NSObject {
         let signingKey = changes.recoverySigningPubKey
         let encryptionKey = changes.recoveryEncryptionPubKey
 
         let signingKey = changes.recoverySigningPubKey
         let encryptionKey = changes.recoveryEncryptionPubKey
 
-        if signingKey.count > 0 && encryptionKey.count > 0 {
+        if !signingKey.isEmpty && !encryptionKey.isEmpty {
             self.addOrUpdate(signingKey: signingKey, encryptionKey: encryptionKey)
         }
         try self.moc.save()
             self.addOrUpdate(signingKey: signingKey, encryptionKey: encryptionKey)
         }
         try self.moc.save()
@@ -3797,7 +3952,7 @@ class Container: NSObject {
             self.model = Container.loadModel(from: self.containerMO)
             try self.moc.save()
         } catch {
             self.model = Container.loadModel(from: self.containerMO)
             try self.moc.save()
         } catch {
-            os_log("Local delete failed: %@", log: tplogDebug, type: .default, error as CVarArg)
+            os_log("Local delete failed: %{public}@", log: tplogDebug, type: .default, error as CVarArg)
             throw error
         }
 
             throw error
         }
 
@@ -3808,6 +3963,9 @@ class Container: NSObject {
     private func addOrUpdate(signingKey: Data, encryptionKey: Data) {
         self.model.setRecoveryKeys(
             TPRecoveryKeyPair(signingSPKI: signingKey, encryptionSPKI: encryptionKey))
     private func addOrUpdate(signingKey: Data, encryptionKey: Data) {
         self.model.setRecoveryKeys(
             TPRecoveryKeyPair(signingSPKI: signingKey, encryptionSPKI: encryptionKey))
+
+        self.containerMO.recoveryKeySigningSPKI = signingKey
+        self.containerMO.recoveryKeyEncryptionSPKI = encryptionKey
     }
 
     // Must be on moc queue to call this.
     }
 
     // Must be on moc queue to call this.
@@ -3875,9 +4033,9 @@ class Container: NSObject {
         guard let policyDoc = self.model.policy(withVersion: policyVersion) else {
             throw ContainerError.unknownPolicyVersion(policyVersion)
         }
         guard let policyDoc = self.model.policy(withVersion: policyVersion) else {
             throw ContainerError.unknownPolicyVersion(policyVersion)
         }
-        assert(policyVersion == policyDoc.policyVersion)
-        if policyVersion == prevailingPolicyVersion {
-            assert(policyDoc.policyHash == prevailingPolicyHash)
+        assert(policyVersion == policyDoc.version.versionNumber)
+        if policyVersion == prevailingPolicyVersion.versionNumber {
+            assert(policyDoc.version.policyHash == prevailingPolicyVersion.policyHash)
         }
         return policyDoc
     }
         }
         return policyDoc
     }
@@ -3885,42 +4043,49 @@ class Container: NSObject {
     // Must be on moc queue to call this.
     private func createNewStableInfoIfNeeded(stableChanges: StableChanges?,
                                              egoPeerID: String,
     // Must be on moc queue to call this.
     private func createNewStableInfoIfNeeded(stableChanges: StableChanges?,
                                              egoPeerID: String,
+                                             existingStableInfo: TPPeerStableInfo?,
                                              dynamicInfo: TPPeerDynamicInfo,
                                              signingKeyPair: _SFECKeyPair) throws -> TPPeerStableInfo? {
         func noChange<T: Equatable>(_ change: T?, _ existing: T?) -> Bool {
             return (nil == change) || change == existing
         }
                                              dynamicInfo: TPPeerDynamicInfo,
                                              signingKeyPair: _SFECKeyPair) throws -> TPPeerStableInfo? {
         func noChange<T: Equatable>(_ change: T?, _ existing: T?) -> Bool {
             return (nil == change) || change == existing
         }
-        let existingStableInfo = self.model.peer(withID: egoPeerID)?.stableInfo
+
+        let policyOfPeers = try? self.model.policy(forPeerIDs: dynamicInfo.includedPeerIDs,
+                                                   candidatePeerID: egoPeerID,
+                                                   candidateStableInfo: existingStableInfo)
+
+        // Pick the best version of:
+        //   1. The policy version asked for by the client
+        //   2. The max of our existing policyVersion, the highest policy used by our trusted peers, and the compile-time prevailing policy version
+        let optimalPolicyVersionNumber = stableChanges?.policyVersion ??
+                        max(existingStableInfo?.bestPolicyVersion().versionNumber ?? prevailingPolicyVersion.versionNumber,
+                        policyOfPeers?.version.versionNumber ?? prevailingPolicyVersion.versionNumber,
+                        prevailingPolicyVersion.versionNumber)
+
+        // Determine which recovery key we'd like to be using, given our current idea of who to trust
+        let optimalRecoveryKey = self.model.bestRecoveryKey(for: existingStableInfo, dynamicInfo: dynamicInfo)
+
         if noChange(stableChanges?.deviceName, existingStableInfo?.deviceName) &&
             noChange(stableChanges?.serialNumber, existingStableInfo?.serialNumber) &&
             noChange(stableChanges?.osVersion, existingStableInfo?.osVersion) &&
         if noChange(stableChanges?.deviceName, existingStableInfo?.deviceName) &&
             noChange(stableChanges?.serialNumber, existingStableInfo?.serialNumber) &&
             noChange(stableChanges?.osVersion, existingStableInfo?.osVersion) &&
-            noChange(stableChanges?.policyVersion, existingStableInfo?.policyVersion) &&
+            noChange(optimalPolicyVersionNumber, existingStableInfo?.bestPolicyVersion().versionNumber) &&
             noChange(stableChanges?.policySecrets, existingStableInfo?.policySecrets) &&
             noChange(stableChanges?.policySecrets, existingStableInfo?.policySecrets) &&
-            noChange(stableChanges?.recoverySigningPubKey, existingStableInfo?.recoverySigningPublicKey) &&
-            noChange(stableChanges?.recoveryEncryptionPubKey, existingStableInfo?.recoveryEncryptionPublicKey) &&
-            self.model.doesPeerRecoveryKeyMatchPeers(egoPeerID) {
+            noChange(optimalRecoveryKey?.signingSPKI, existingStableInfo?.recoverySigningPublicKey) &&
+            noChange(optimalRecoveryKey?.encryptionSPKI, existingStableInfo?.recoveryEncryptionPublicKey) {
             return nil
         }
             return nil
         }
-        let policyHash: String?
-        if let policyVersion = stableChanges?.policyVersion {
-            let policyDoc = try self.getPolicyDoc(policyVersion)
-            policyHash = policyDoc.policyHash
-        } else {
-            policyHash = nil
-        }
 
 
-        // Determine which recovery key we'd like to be using, given our current idea of who to trust
-        let newRecoveryKeys = self.model.bestRecoveryKey(with: dynamicInfo)
+        let optimalPolicyVersion = try self.getPolicyDoc(optimalPolicyVersionNumber).version
 
 
-        return try self.model.createStableInfo(withPolicyVersion: stableChanges?.policyVersion ?? existingStableInfo?.policyVersion ?? prevailingPolicyVersion,
-                                               policyHash: policyHash ?? existingStableInfo?.policyHash ?? prevailingPolicyHash,
+        return try self.model.createStableInfo(withFrozenPolicyVersion: frozenPolicyVersion,
+                                               flexiblePolicyVersion: optimalPolicyVersion,
                                                policySecrets: stableChanges?.policySecrets ?? existingStableInfo?.policySecrets,
                                                deviceName: stableChanges?.deviceName ?? existingStableInfo?.deviceName ?? "",
                                                serialNumber: stableChanges?.serialNumber ?? existingStableInfo?.serialNumber ?? "",
                                                osVersion: stableChanges?.osVersion ?? existingStableInfo?.osVersion ?? "",
                                                signing: signingKeyPair,
                                                policySecrets: stableChanges?.policySecrets ?? existingStableInfo?.policySecrets,
                                                deviceName: stableChanges?.deviceName ?? existingStableInfo?.deviceName ?? "",
                                                serialNumber: stableChanges?.serialNumber ?? existingStableInfo?.serialNumber ?? "",
                                                osVersion: stableChanges?.osVersion ?? existingStableInfo?.osVersion ?? "",
                                                signing: signingKeyPair,
-                                               recoverySigningPubKey: newRecoveryKeys?.signingSPKI ?? existingStableInfo?.recoverySigningPublicKey,
-                                               recoveryEncryptionPubKey: newRecoveryKeys?.encryptionSPKI ?? existingStableInfo?.recoveryEncryptionPublicKey)
+                                               recoverySigningPubKey: optimalRecoveryKey?.signingSPKI,
+                                               recoveryEncryptionPubKey: optimalRecoveryKey?.encryptionSPKI)
     }
 
     private func assembleBottle(egoPeerID: String) throws -> Bottle {
     }
 
     private func assembleBottle(egoPeerID: String) throws -> Bottle {
@@ -3982,7 +4147,7 @@ class Container: NSObject {
     func reportHealth(request: ReportHealthRequest, reply: @escaping (Error?) -> Void) {
         self.semaphore.wait()
         let reply: (Error?) -> Void = {
     func reportHealth(request: ReportHealthRequest, reply: @escaping (Error?) -> Void) {
         self.semaphore.wait()
         let reply: (Error?) -> Void = {
-            os_log("reportHealth complete %@", log: tplogTrace, type: .info, traceError($0))
+            os_log("reportHealth complete %{public}@", log: tplogTrace, type: .info, traceError($0))
             self.semaphore.signal()
             reply($0)
         }
             self.semaphore.signal()
             reply($0)
         }
@@ -3997,7 +4162,7 @@ class Container: NSObject {
 
         self.moc.performAndWait {
             self.cuttlefish.reportHealth(updatedRequest) { response, error in
 
         self.moc.performAndWait {
             self.cuttlefish.reportHealth(updatedRequest) { response, error in
-                os_log("reportHealth(%@): %@, error: %@", log: tplogDebug, "\(String(describing: request))", "\(String(describing: response))", "\(String(describing: error))")
+                os_log("reportHealth(%{public}@): %{public}@, error: %{public}@", log: tplogDebug, "\(String(describing: request))", "\(String(describing: response))", "\(String(describing: error))")
                 guard error == nil else {
                     reply(error)
                     return
                 guard error == nil else {
                     reply(error)
                     return
@@ -4010,14 +4175,14 @@ class Container: NSObject {
     func pushHealthInquiry(reply: @escaping (Error?) -> Void) {
         self.semaphore.wait()
         let reply: (Error?) -> Void = {
     func pushHealthInquiry(reply: @escaping (Error?) -> Void) {
         self.semaphore.wait()
         let reply: (Error?) -> Void = {
-            os_log("reportHealth complete %@", log: tplogTrace, type: .info, traceError($0))
+            os_log("reportHealth complete %{public}@", log: tplogTrace, type: .info, traceError($0))
             self.semaphore.signal()
             reply($0)
         }
 
         self.moc.performAndWait {
             self.cuttlefish.pushHealthInquiry(PushHealthInquiryRequest()) { response, error in
             self.semaphore.signal()
             reply($0)
         }
 
         self.moc.performAndWait {
             self.cuttlefish.pushHealthInquiry(PushHealthInquiryRequest()) { response, error in
-                os_log("pushHealthInquiry(): %@, error: %@", log: tplogDebug, "\(String(describing: response))", "\(String(describing: error))")
+                os_log("pushHealthInquiry(): %{public}@, error: %{public}@", log: tplogDebug, "\(String(describing: response))", "\(String(describing: error))")
                 guard error == nil else {
                     reply(error)
                     return
                 guard error == nil else {
                     reply(error)
                     return
index 59f539dcabe1076472b0f437b3da67cfaa0bceec..73a8fdfa89491dbddc609a33a5f16c1cb6bac01a 100644 (file)
@@ -191,7 +191,7 @@ public class MyCodeConnection: CloudKitCode.Invocable {
             operation.requestCompletedBlock = requestCompletion
 
             let loggingCompletion = { (response: ResponseType?, error: Error?) -> Void in
             operation.requestCompletedBlock = requestCompletion
 
             let loggingCompletion = { (response: ResponseType?, error: Error?) -> Void in
-                os_log("%@(%@): %@, error: %@",
+                os_log("%{public}@(%{public}@): %{public}@, error: %{public}@",
                        log: tplogDebug,
                        function,
                        "\(String(describing: request))",
                        log: tplogDebug,
                        function,
                        "\(String(describing: request))",
@@ -284,4 +284,11 @@ class ContainerMap {
         let filename = name.container + "-" + name.context + ".TrustedPeersHelper.db"
         return SecCopyURLForFileInKeychainDirectory(filename as CFString) as URL
     }
         let filename = name.container + "-" + name.context + ".TrustedPeersHelper.db"
         return SecCopyURLForFileInKeychainDirectory(filename as CFString) as URL
     }
+
+    // To be called via test only
+    func removeAllContainers() {
+        queue.sync {
+            self.containers.removeAll()
+        }
+    }
 }
 }
diff --git a/keychain/TrustedPeersHelper/Container_BottledPeers.swift b/keychain/TrustedPeersHelper/Container_BottledPeers.swift
new file mode 100644 (file)
index 0000000..ff281da
--- /dev/null
@@ -0,0 +1,81 @@
+import CoreData
+import Foundation
+
+extension Container {
+    func preflightVouchWithBottle(bottleID: String,
+                                  reply: @escaping (String?, Set<String>?, TPPolicy?, Error?) -> Void) {
+        self.semaphore.wait()
+        let reply: (String?, Set<String>?, TPPolicy?, Error?) -> Void = {
+            os_log("preflightVouchWithBottle complete: %{public}@",
+                   log: tplogTrace, type: .info, traceError($3))
+            self.semaphore.signal()
+            reply($0, $1, $2, $3)
+        }
+
+        self.fetchAndPersistChangesIfNeeded { fetchError in
+            guard fetchError == nil else {
+                os_log("preflightVouchWithBottle unable to fetch current peers: %{public}@", log: tplogDebug, type: .default, (fetchError as CVarArg?) ?? "")
+                reply(nil, nil, nil, fetchError)
+                return
+            }
+
+            // Ensure we have all policy versions claimed by peers, including our sponsor
+            let allPolicyVersions = self.model.allPolicyVersions()
+            self.fetchPolicyDocumentsWithSemaphore(versions: allPolicyVersions) { _, fetchPolicyDocumentsError in
+                guard fetchPolicyDocumentsError == nil else {
+                    os_log("preflightVouchWithBottle unable to fetch policy documents: %{public}@", log: tplogDebug, type: .default, (fetchPolicyDocumentsError as CVarArg?) ?? "no error")
+                    reply(nil, nil, nil, fetchPolicyDocumentsError)
+                    return
+                }
+
+                self.moc.performAndWait {
+                    guard let egoPeerID = self.containerMO.egoPeerID,
+                        let egoPermData = self.containerMO.egoPeerPermanentInfo,
+                        let egoPermSig = self.containerMO.egoPeerPermanentInfoSig else {
+                            os_log("fetchCurrentPolicy failed to find ego peer information", log: tplogDebug, type: .error)
+                            reply(nil, nil, nil, ContainerError.noPreparedIdentity)
+                            return
+                    }
+
+                    let keyFactory = TPECPublicKeyFactory()
+                    guard let egoPermanentInfo = TPPeerPermanentInfo(peerID: egoPeerID, data: egoPermData, sig: egoPermSig, keyFactory: keyFactory) else {
+                        os_log("fetchCurrentPolicy failed to create TPPeerPermanentInfo", log: tplogDebug, type: .error)
+                        reply(nil, nil, nil, ContainerError.invalidPermanentInfoOrSig)
+                        return
+                    }
+
+                    self.onqueueFindBottle(bottleID: bottleID) { bottleMO, error in
+                        guard let bottleMO = bottleMO else {
+                            os_log("preflightVouchWithBottle found no bottle: %{public}@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "")
+                            reply(nil, nil, nil, error)
+                            return
+                        }
+
+                        guard let sponsorPeer = self.model.peer(withID: bottleMO.peerID ?? "") else {
+                            os_log("preflightVouchWithBottle found no peer to match bottle", log: tplogDebug, type: .default)
+                            reply(nil, nil, nil, ContainerError.sponsorNotRegistered(bottleMO.peerID ?? "no peer ID given"))
+                            return
+                        }
+
+                        guard let sponsorPeerStableInfo = sponsorPeer.stableInfo else {
+                            os_log("preflightVouchWithBottle sponsor peer has no stable info", log: tplogDebug, type: .default)
+                            reply(nil, nil, nil, ContainerError.sponsorNotRegistered(bottleMO.peerID ?? "no peer ID given"))
+                            return
+                        }
+
+                        do {
+                            // We need to extract the syncing policy that the remote peer would have used (if they were the type of device that we are)
+                            // So, figure out their policy version...
+                            let (views, policy) = try self.policyAndViewsFor(permanentInfo: egoPermanentInfo, stableInfo: sponsorPeerStableInfo)
+
+                            reply(bottleMO.peerID, views, policy, nil)
+                        } catch {
+                            os_log("preflightVouchWithBottle failed to fetch policy: %{public}@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "")
+                            reply(nil, nil, nil, error)
+                        }
+                    }
+                }
+            }
+        }
+    }
+}
index 0a8dd0bcaee916e7ef85ef186dc0af3656a4252f..19e76cbb217145d3b79545ab6c71323129a6e11e 100644 (file)
@@ -57,7 +57,7 @@ extension Container {
 
         // Once we run this upgrade, we will set the allowed bool to false, since it's unused.
         // Therefore, if we have a single record with "allowed" set, we haven't run the upgrade.
 
         // Once we run this upgrade, we will set the allowed bool to false, since it's unused.
         // Therefore, if we have a single record with "allowed" set, we haven't run the upgrade.
-        let runUpgrade = knownMachineMOs.filter { $0.allowed }.count > 0
+        let runUpgrade = !knownMachineMOs.filter { $0.allowed }.isEmpty
         if runUpgrade {
             knownMachineMOs.forEach { mo in
                 if mo.allowed {
         if runUpgrade {
             knownMachineMOs.forEach { mo in
                 if mo.allowed {
@@ -70,15 +70,29 @@ extension Container {
         }
     }
 
         }
     }
 
-    func setAllowedMachineIDs(_ allowedMachineIDs: Set<String>, reply: @escaping (Bool, Error?) -> Void) {
+    func enforceIDMSListChanges(knownMachines: Set<MachineMO>) -> Bool {
+        if self.containerMO.honorIDMSListChanges == "YES"{
+            return true
+        } else if self.containerMO.honorIDMSListChanges == "NO" {
+            return false
+        } else if self.containerMO.honorIDMSListChanges == "UNKNOWN" && knownMachines.isEmpty {
+            return false
+        } else if self.containerMO.honorIDMSListChanges == "UNKNOWN" && !knownMachines.isEmpty {
+            return true
+        } else {
+            return true
+        }
+    }
+
+    func setAllowedMachineIDs(_ allowedMachineIDs: Set<String>, honorIDMSListChanges: Bool, reply: @escaping (Bool, Error?) -> Void) {
         self.semaphore.wait()
         let reply: (Bool, Error?) -> Void = {
         self.semaphore.wait()
         let reply: (Bool, Error?) -> Void = {
-            os_log("setAllowedMachineIDs complete: %@", log: tplogTrace, type: .info, traceError($1))
+            os_log("setAllowedMachineIDs complete: %{public}@", log: tplogTrace, type: .info, traceError($1))
             self.semaphore.signal()
             reply($0, $1)
         }
 
             self.semaphore.signal()
             reply($0, $1)
         }
 
-        os_log("Setting allowed machine IDs: %@", log: tplogDebug, type: .default, allowedMachineIDs)
+        os_log("Setting allowed machine IDs: %{public}@", log: tplogDebug, type: .default, allowedMachineIDs)
 
         // Note: we currently ignore any machineIDs that are set in the model, but never appeared on the
         // Trusted Devices list. We should give them a grace period (1wk?) then kick them out.
 
         // Note: we currently ignore any machineIDs that are set in the model, but never appeared on the
         // Trusted Devices list. We should give them a grace period (1wk?) then kick them out.
@@ -86,20 +100,21 @@ extension Container {
         self.moc.performAndWait {
             do {
                 var differences = false
         self.moc.performAndWait {
             do {
                 var differences = false
+                self.containerMO.honorIDMSListChanges = honorIDMSListChanges ? "YES" : "NO"
 
                 var knownMachines = containerMO.machines as? Set<MachineMO> ?? Set()
                 let knownMachineIDs = Set(knownMachines.compactMap { $0.machineID })
 
                 knownMachines.forEach { machine in
                     guard let mid = machine.machineID else {
 
                 var knownMachines = containerMO.machines as? Set<MachineMO> ?? Set()
                 let knownMachineIDs = Set(knownMachines.compactMap { $0.machineID })
 
                 knownMachines.forEach { machine in
                     guard let mid = machine.machineID else {
-                        os_log("Machine has no ID: %@", log: tplogDebug, type: .default, machine)
+                        os_log("Machine has no ID: %{public}@", log: tplogDebug, type: .default, machine)
                         return
                     }
                     if allowedMachineIDs.contains(mid) {
                         if machine.status == TPMachineIDStatus.allowed.rawValue {
                         return
                     }
                     if allowedMachineIDs.contains(mid) {
                         if machine.status == TPMachineIDStatus.allowed.rawValue {
-                            os_log("Machine ID still trusted: %@", log: tplogDebug, type: .default, String(describing: machine.machineID))
+                            os_log("Machine ID still trusted: %{public}@", log: tplogDebug, type: .default, String(describing: machine.machineID))
                         } else {
                         } else {
-                            os_log("Machine ID newly retrusted: %@", log: tplogDebug, type: .default, String(describing: machine.machineID))
+                            os_log("Machine ID newly retrusted: %{public}@", log: tplogDebug, type: .default, String(describing: machine.machineID))
                             differences = true
                         }
                         machine.status = Int64(TPMachineIDStatus.allowed.rawValue)
                             differences = true
                         }
                         machine.status = Int64(TPMachineIDStatus.allowed.rawValue)
@@ -115,14 +130,14 @@ extension Container {
                             if machine.seenOnFullList {
                                 machine.status = Int64(TPMachineIDStatus.disallowed.rawValue)
                                 machine.modified = Date()
                             if machine.seenOnFullList {
                                 machine.status = Int64(TPMachineIDStatus.disallowed.rawValue)
                                 machine.modified = Date()
-                                os_log("Newly distrusted machine ID: %@", log: tplogDebug, type: .default, String(describing: machine.machineID))
+                                os_log("Newly distrusted machine ID: %{public}@", log: tplogDebug, type: .default, String(describing: machine.machineID))
                                 differences = true
 
                             } else {
                                 if machine.modifiedInPast(hours: cutoffHours) {
                                 differences = true
 
                             } else {
                                 if machine.modifiedInPast(hours: cutoffHours) {
-                                    os_log("Allowed-but-unseen machine ID isn't on full list, last modified %@, ignoring: %@", log: tplogDebug, type: .default, machine.modifiedDate(), String(describing: machine.machineID))
+                                    os_log("Allowed-but-unseen machine ID isn't on full list, last modified %{public}@, ignoring: %{public}@", log: tplogDebug, type: .default, machine.modifiedDate(), String(describing: machine.machineID))
                                 } else {
                                 } else {
-                                    os_log("Allowed-but-unseen machine ID isn't on full list, last modified %@, distrusting: %@", log: tplogDebug, type: .default, machine.modifiedDate(), String(describing: machine.machineID))
+                                    os_log("Allowed-but-unseen machine ID isn't on full list, last modified %{public}@, distrusting: %{public}@", log: tplogDebug, type: .default, machine.modifiedDate(), String(describing: machine.machineID))
                                     machine.status = Int64(TPMachineIDStatus.disallowed.rawValue)
                                     machine.modified = Date()
                                     differences = true
                                     machine.status = Int64(TPMachineIDStatus.disallowed.rawValue)
                                     machine.modified = Date()
                                     differences = true
@@ -131,9 +146,9 @@ extension Container {
 
                         } else if machine.status == TPMachineIDStatus.unknown.rawValue {
                             if machine.modifiedInPast(hours: cutoffHours) {
 
                         } else if machine.status == TPMachineIDStatus.unknown.rawValue {
                             if machine.modifiedInPast(hours: cutoffHours) {
-                                os_log("Unknown machine ID last modified %@; leaving unknown: %@", log: tplogDebug, type: .default, machine.modifiedDate(), String(describing: machine.machineID))
+                                os_log("Unknown machine ID last modified %{public}@; leaving unknown: %{public}@", log: tplogDebug, type: .default, machine.modifiedDate(), String(describing: machine.machineID))
                             } else {
                             } else {
-                                os_log("Unknown machine ID last modified %@; distrusting: %@", log: tplogDebug, type: .default, machine.modifiedDate(), String(describing: machine.machineID))
+                                os_log("Unknown machine ID last modified %{public}@; distrusting: %{public}@", log: tplogDebug, type: .default, machine.modifiedDate(), String(describing: machine.machineID))
                                 machine.status = Int64(TPMachineIDStatus.disallowed.rawValue)
                                 machine.modified = Date()
                                 differences = true
                                 machine.status = Int64(TPMachineIDStatus.disallowed.rawValue)
                                 machine.modified = Date()
                                 differences = true
@@ -144,7 +159,7 @@ extension Container {
 
                 // Do we need to create any further objects?
                 allowedMachineIDs.forEach { machineID in
 
                 // Do we need to create any further objects?
                 allowedMachineIDs.forEach { machineID in
-                    if(!knownMachineIDs.contains(machineID)) {
+                    if !knownMachineIDs.contains(machineID) {
                         // We didn't know about this machine before; it's newly trusted!
                         let machine = MachineMO(context: self.moc)
                         machine.machineID = machineID
                         // We didn't know about this machine before; it's newly trusted!
                         let machine = MachineMO(context: self.moc)
                         machine.machineID = machineID
@@ -152,7 +167,7 @@ extension Container {
                         machine.seenOnFullList = true
                         machine.modified = Date()
                         machine.status = Int64(TPMachineIDStatus.allowed.rawValue)
                         machine.seenOnFullList = true
                         machine.modified = Date()
                         machine.status = Int64(TPMachineIDStatus.allowed.rawValue)
-                        os_log("Newly trusted machine ID: %@", log: tplogDebug, type: .default, String(describing: machine.machineID))
+                        os_log("Newly trusted machine ID: %{public}@", log: tplogDebug, type: .default, String(describing: machine.machineID))
                         differences = true
 
                         self.containerMO.addToMachines(machine)
                         differences = true
 
                         self.containerMO.addToMachines(machine)
@@ -160,13 +175,12 @@ extension Container {
                     }
                 }
 
                     }
                 }
 
-                // if this account is not a demo account...
-                if knownMachines.count > 0 {
+                if self.enforceIDMSListChanges(knownMachines: knownMachines) {
                     // Are there any machine IDs in the model that aren't in the list? If so, add them as "unknown"
                     let modelMachineIDs = self.model.allMachineIDs()
                     modelMachineIDs.forEach { peerMachineID in
                         if !knownMachineIDs.contains(peerMachineID) && !allowedMachineIDs.contains(peerMachineID) {
                     // Are there any machine IDs in the model that aren't in the list? If so, add them as "unknown"
                     let modelMachineIDs = self.model.allMachineIDs()
                     modelMachineIDs.forEach { peerMachineID in
                         if !knownMachineIDs.contains(peerMachineID) && !allowedMachineIDs.contains(peerMachineID) {
-                            os_log("Peer machineID is unknown, beginning grace period: %@", log: tplogDebug, type: .default, peerMachineID)
+                            os_log("Peer machineID is unknown, beginning grace period: %{public}@", log: tplogDebug, type: .default, peerMachineID)
                             let machine = MachineMO(context: self.moc)
                             machine.machineID = peerMachineID
                             machine.container = containerMO
                             let machine = MachineMO(context: self.moc)
                             machine.machineID = peerMachineID
                             machine.container = containerMO
@@ -179,7 +193,7 @@ extension Container {
                         }
                     }
                 } else {
                         }
                     }
                 } else {
-                    os_log("Believe we're in a demo account; not starting an unknown machine ID grace period", log: tplogDebug, type: .default)
+                    os_log("Believe we're in a demo account, not enforcing IDMS list", log: tplogDebug, type: .default)
                 }
 
                 // We no longer use allowed machine IDs.
                 }
 
                 // We no longer use allowed machine IDs.
@@ -189,7 +203,7 @@ extension Container {
 
                 reply(differences, nil)
             } catch {
 
                 reply(differences, nil)
             } catch {
-                os_log("Error setting machine ID list: %@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "no error")
+                os_log("Error setting machine ID list: %{public}@", log: tplogDebug, type: .default, (error as CVarArg?) ?? "no error")
                 reply(false, error)
             }
         }
                 reply(false, error)
             }
         }
@@ -198,12 +212,12 @@ extension Container {
     func addAllow(_ machineIDs: [String], reply: @escaping (Error?) -> Void) {
         self.semaphore.wait()
         let reply: (Error?) -> Void = {
     func addAllow(_ machineIDs: [String], reply: @escaping (Error?) -> Void) {
         self.semaphore.wait()
         let reply: (Error?) -> Void = {
-            os_log("addAllow complete: %@", log: tplogTrace, type: .info, traceError($0))
+            os_log("addAllow complete: %{public}@", log: tplogTrace, type: .info, traceError($0))
             self.semaphore.signal()
             reply($0)
         }
 
             self.semaphore.signal()
             reply($0)
         }
 
-        os_log("Adding allowed machine IDs: %@", log: tplogDebug, type: .default, machineIDs)
+        os_log("Adding allowed machine IDs: %{public}@", log: tplogDebug, type: .default, machineIDs)
 
         self.moc.performAndWait {
             do {
 
         self.moc.performAndWait {
             do {
@@ -218,7 +232,7 @@ extension Container {
                             if machine.machineID == machineID {
                                 machine.status = Int64(TPMachineIDStatus.allowed.rawValue)
                                 machine.modified = Date()
                             if machine.machineID == machineID {
                                 machine.status = Int64(TPMachineIDStatus.allowed.rawValue)
                                 machine.modified = Date()
-                                os_log("Continue to trust machine ID: %@", log: tplogDebug, type: .default, String(describing: machine.machineID))
+                                os_log("Continue to trust machine ID: %{public}@", log: tplogDebug, type: .default, String(describing: machine.machineID))
                             }
                         }
 
                             }
                         }
 
@@ -229,7 +243,7 @@ extension Container {
                         machine.seenOnFullList = false
                         machine.modified = Date()
                         machine.status = Int64(TPMachineIDStatus.allowed.rawValue)
                         machine.seenOnFullList = false
                         machine.modified = Date()
                         machine.status = Int64(TPMachineIDStatus.allowed.rawValue)
-                        os_log("Newly trusted machine ID: %@", log: tplogDebug, type: .default, String(describing: machine.machineID))
+                        os_log("Newly trusted machine ID: %{public}@", log: tplogDebug, type: .default, String(describing: machine.machineID))
                         self.containerMO.addToMachines(machine)
 
                         knownMachines.insert(machine)
                         self.containerMO.addToMachines(machine)
 
                         knownMachines.insert(machine)
@@ -247,12 +261,12 @@ extension Container {
     func removeAllow(_ machineIDs: [String], reply: @escaping (Error?) -> Void) {
         self.semaphore.wait()
         let reply: (Error?) -> Void = {
     func removeAllow(_ machineIDs: [String], reply: @escaping (Error?) -> Void) {
         self.semaphore.wait()
         let reply: (Error?) -> Void = {
-            os_log("removeAllow complete: %@", log: tplogTrace, type: .info, traceError($0))
+            os_log("removeAllow complete: %{public}@", log: tplogTrace, type: .info, traceError($0))
             self.semaphore.signal()
             reply($0)
         }
 
             self.semaphore.signal()
             reply($0)
         }
 
-        os_log("Removing allowed machine IDs: %@", log: tplogDebug, type: .default, machineIDs)
+        os_log("Removing allowed machine IDs: %{public}@", log: tplogDebug, type: .default, machineIDs)
 
         self.moc.performAndWait {
             do {
 
         self.moc.performAndWait {
             do {
@@ -268,7 +282,7 @@ extension Container {
                             if machine.machineID == machineID {
                                 machine.status = Int64(TPMachineIDStatus.unknown.rawValue)
                                 machine.modified = Date.distantPast
                             if machine.machineID == machineID {
                                 machine.status = Int64(TPMachineIDStatus.unknown.rawValue)
                                 machine.modified = Date.distantPast
-                                os_log("Now suspicious of machine ID: %@", log: tplogDebug, type: .default, String(describing: machine.machineID))
+                                os_log("Now suspicious of machine ID: %{public}@", log: tplogDebug, type: .default, String(describing: machine.machineID))
                             }
                         }
 
                             }
                         }
 
@@ -278,7 +292,7 @@ extension Container {
                         machine.container = containerMO
                         machine.status = Int64(TPMachineIDStatus.unknown.rawValue)
                         machine.modified = Date.distantPast
                         machine.container = containerMO
                         machine.status = Int64(TPMachineIDStatus.unknown.rawValue)
                         machine.modified = Date.distantPast
-                        os_log("Suspicious of new machine ID: %@", log: tplogDebug, type: .default, String(describing: machine.machineID))
+                        os_log("Suspicious of new machine ID: %{public}@", log: tplogDebug, type: .default, String(describing: machine.machineID))
                         self.containerMO.addToMachines(machine)
 
                         knownMachines.insert(machine)
                         self.containerMO.addToMachines(machine)
 
                         knownMachines.insert(machine)
@@ -296,7 +310,7 @@ extension Container {
     func fetchAllowedMachineIDs(reply: @escaping (Set<String>?, Error?) -> Void) {
         self.semaphore.wait()
         let reply: (Set<String>?, Error?) -> Void = {
     func fetchAllowedMachineIDs(reply: @escaping (Set<String>?, Error?) -> Void) {
         self.semaphore.wait()
         let reply: (Set<String>?, Error?) -> Void = {
-            os_log("fetchAllowedMachineIDs complete: %@", log: tplogTrace, type: .info, traceError($1))
+            os_log("fetchAllowedMachineIDs complete: %{public}@", log: tplogTrace, type: .info, traceError($1))
             self.semaphore.signal()
             reply($0, $1)
         }
             self.semaphore.signal()
             reply($0, $1)
         }
@@ -305,34 +319,34 @@ extension Container {
 
         self.moc.performAndWait {
             let knownMachines = containerMO.machines as? Set<MachineMO> ?? Set()
 
         self.moc.performAndWait {
             let knownMachines = containerMO.machines as? Set<MachineMO> ?? Set()
-            let allowedMachineIDs = knownMachines.filter { $0.status == Int64(TPMachineIDStatus.allowed.rawValue) }.compactMap({ $0.machineID })
+            let allowedMachineIDs = knownMachines.filter { $0.status == Int64(TPMachineIDStatus.allowed.rawValue) }.compactMap { $0.machineID }
 
             reply(Set(allowedMachineIDs), nil)
         }
     }
 
     func onqueueMachineIDAllowedByIDMS(machineID: String) -> Bool {
 
             reply(Set(allowedMachineIDs), nil)
         }
     }
 
     func onqueueMachineIDAllowedByIDMS(machineID: String) -> Bool {
+
         // For Demo accounts, if the list is entirely empty, then everything is allowed
         // For Demo accounts, if the list is entirely empty, then everything is allowed
-        let machines = containerMO.machines as? Set<MachineMO> ?? Set()
-        if machines.count == 0 {
-            os_log("machineID list is empty; allowing %@", log: tplogDebug, type: .debug, machineID)
+        let knownMachines = containerMO.machines as? Set<MachineMO> ?? Set()
+
+        if !self.enforceIDMSListChanges(knownMachines: knownMachines) {
+            os_log("not enforcing idms list changes; allowing %{public}@", log: tplogDebug, type: .debug, machineID)
             return true
         }
 
         // Note: this function rejects grey devices: machineIDs that are neither allowed nor disallowed
             return true
         }
 
         // Note: this function rejects grey devices: machineIDs that are neither allowed nor disallowed
-        for mo in machines {
-            if mo.machineID == machineID {
-                if mo.status == TPMachineIDStatus.allowed.rawValue {
-                    return true
-                } else {
-                    os_log("machineID %@ not explicitly allowed: %@", log: tplogDebug, type: .debug, machineID, mo)
-                    return false
-                }
+        for mo in knownMachines where mo.machineID == machineID {
+            if mo.status == TPMachineIDStatus.allowed.rawValue {
+                return true
+            } else {
+                os_log("machineID %{public}@ not explicitly allowed: %{public}@", log: tplogDebug, type: .debug, machineID, mo)
+                return false
             }
         }
 
         // Didn't find it? reject.
             }
         }
 
         // Didn't find it? reject.
-        os_log("machineID %@ not found on list", log: tplogDebug, type: .debug, machineID)
+        os_log("machineID %{public}@ not found on list", log: tplogDebug, type: .debug, machineID)
         return false
     }
 
         return false
     }
 
@@ -358,9 +372,9 @@ extension Container {
         let trustedMachineIDs = Set(dynamicInfo.includedPeerIDs.compactMap { self.model.peer(withID: $0)?.permanentInfo.machineID })
 
         // if this account is not a demo account...
         let trustedMachineIDs = Set(dynamicInfo.includedPeerIDs.compactMap { self.model.peer(withID: $0)?.permanentInfo.machineID })
 
         // if this account is not a demo account...
-        if machines.count > 0 {
+        if self.enforceIDMSListChanges(knownMachines: machines) {
             for peerMachineID in trustedMachineIDs.subtracting(knownMachineIDs) {
             for peerMachineID in trustedMachineIDs.subtracting(knownMachineIDs) {
-                os_log("Peer machineID is unknown, beginning grace period: %@", log: tplogDebug, type: .default, peerMachineID)
+                os_log("Peer machineID is unknown, beginning grace period: %{public}@", log: tplogDebug, type: .default, peerMachineID)
                 let machine = MachineMO(context: self.moc)
                 machine.machineID = peerMachineID
                 machine.container = self.containerMO
                 let machine = MachineMO(context: self.moc)
                 machine.machineID = peerMachineID
                 machine.container = self.containerMO
@@ -371,14 +385,12 @@ extension Container {
                 self.containerMO.addToMachines(machine)
             }
         } else {
                 self.containerMO.addToMachines(machine)
             }
         } else {
-            os_log("Believe we're in a demo account; not starting an unknown machine ID timer", log: tplogDebug, type: .default)
+            os_log("Not enforcing IDMS list changes", log: tplogDebug, type: .default)
         }
 
         }
 
-        for mo in (machines) {
-            if mo.status == TPMachineIDStatus.disallowed.rawValue {
-                os_log("Dropping knowledge of machineID %@", log: tplogDebug, type: .debug, String(describing: mo.machineID))
-                self.containerMO.removeFromMachines(mo)
-            }
+        for mo in (machines) where mo.status == TPMachineIDStatus.disallowed.rawValue {
+            os_log("Dropping knowledge of machineID %{public}@", log: tplogDebug, type: .debug, String(describing: mo.machineID))
+            self.containerMO.removeFromMachines(mo)
         }
     }
 
         }
     }
 
@@ -386,9 +398,14 @@ extension Container {
     // Useful means that there's an unknown MID whose modification date is before the cutoff
     // A full list fetch would either confirm it as 'untrusted' or make it trusted again
     func onqueueFullIDMSListWouldBeHelpful() -> Bool {
     // Useful means that there's an unknown MID whose modification date is before the cutoff
     // A full list fetch would either confirm it as 'untrusted' or make it trusted again
     func onqueueFullIDMSListWouldBeHelpful() -> Bool {
+
+        if self.containerMO.honorIDMSListChanges == "UNKNOWN" {
+            return true
+        }
+
         let unknownMOs = (containerMO.machines as? Set<MachineMO> ?? Set()).filter { $0.status == TPMachineIDStatus.unknown.rawValue }
         let outdatedMOs = unknownMOs.filter { !$0.modifiedInPast(hours: cutoffHours) }
 
         let unknownMOs = (containerMO.machines as? Set<MachineMO> ?? Set()).filter { $0.status == TPMachineIDStatus.unknown.rawValue }
         let outdatedMOs = unknownMOs.filter { !$0.modifiedInPast(hours: cutoffHours) }
 
-        return outdatedMOs.count > 0
+        return !outdatedMOs.isEmpty
     }
 }
     }
 }
diff --git a/keychain/TrustedPeersHelper/Container_RecoveryKey.swift b/keychain/TrustedPeersHelper/Container_RecoveryKey.swift
new file mode 100644 (file)
index 0000000..165764a
--- /dev/null
@@ -0,0 +1,91 @@
+import CoreData
+import Foundation
+
+extension Container {
+    func preflightVouchWithRecoveryKey(recoveryKey: String,
+                                       salt: String,
+                                       reply: @escaping (String?, Set<String>?, TPPolicy?, Error?) -> Void) {
+        self.semaphore.wait()
+        let reply: (String?, Set<String>?, TPPolicy?, Error?) -> Void = {
+            os_log("preflightRecoveryKey complete: %{public}@",
+                   log: tplogTrace, type: .info, traceError($3))
+            self.semaphore.signal()
+            reply($0, $1, $2, $3)
+        }
+
+        self.fetchAndPersistChangesIfNeeded { fetchError in
+            guard fetchError == nil else {
+                os_log("preflightRecoveryKey unable to fetch current peers: %{public}@", log: tplogDebug, type: .default, (fetchError as CVarArg?) ?? "")
+                reply(nil, nil, nil, fetchError)
+                return
+            }
+
+            // Ensure we have all policy versions claimed by peers, including our sponsor
+            self.fetchPolicyDocumentsWithSemaphore(versions: self.model.allPolicyVersions()) { _, fetchPolicyDocumentsError in
+                guard fetchPolicyDocumentsError == nil else {
+                    os_log("preflightRecoveryKey unable to fetch policy documents: %{public}@", log: tplogDebug, type: .default, (fetchPolicyDocumentsError as CVarArg?) ?? "no error")
+                    reply(nil, nil, nil, fetchPolicyDocumentsError)
+                    return
+                }
+
+                self.moc.performAndWait {
+                    guard let egoPeerID = self.containerMO.egoPeerID,
+                        let egoPermData = self.containerMO.egoPeerPermanentInfo,
+                        let egoPermSig = self.containerMO.egoPeerPermanentInfoSig else {
+                        os_log("preflightRecoveryKey: no ego peer ID", log: tplogDebug, type: .default)
+                        reply(nil, nil, nil, ContainerError.noPreparedIdentity)
+                        return
+                    }
+
+                    let keyFactory = TPECPublicKeyFactory()
+                    guard let selfPermanentInfo = TPPeerPermanentInfo(peerID: egoPeerID, data: egoPermData, sig: egoPermSig, keyFactory: keyFactory) else {
+                        reply(nil, nil, nil, ContainerError.invalidPermanentInfoOrSig)
+                        return
+                    }
+
+                    var recoveryKeys: RecoveryKey
+                    do {
+                        recoveryKeys = try RecoveryKey(recoveryKeyString: recoveryKey, recoverySalt: salt)
+                    } catch {
+                        os_log("preflightRecoveryKey: failed to create recovery keys: %{public}@", log: tplogDebug, type: .default, error as CVarArg)
+                        reply(nil, nil, nil, ContainerError.failedToCreateRecoveryKey)
+                        return
+                    }
+
+                    // Dear model: if i were to use this recovery key, what peers would I end up using?
+                    guard self.model.isRecoveryKeyEnrolled() else {
+                        os_log("preflightRecoveryKey: recovery Key is not enrolled", log: tplogDebug, type: .default)
+                        reply(nil, nil, nil, ContainerError.recoveryKeysNotEnrolled)
+                        return
+                    }
+
+                    guard let sponsorPeerID = self.model.peerIDThatTrustsRecoveryKeys(TPRecoveryKeyPair(signingSPKI: recoveryKeys.peerKeys.signingKey.publicKey.keyData,
+                                                                                                        encryptionSPKI: recoveryKeys.peerKeys.encryptionKey.publicKey.keyData)) else {
+                                                                                                            os_log("preflightRecoveryKey Untrusted recovery key set", log: tplogDebug, type: .default)
+                                                                                                            reply(nil, nil, nil, ContainerError.untrustedRecoveryKeys)
+                                                                                                            return
+                    }
+
+                    guard let sponsor = self.model.peer(withID: sponsorPeerID) else {
+                        os_log("preflightRecoveryKey Failed to find peer with ID", log: tplogDebug, type: .default)
+                        reply(nil, nil, nil, ContainerError.sponsorNotRegistered(sponsorPeerID))
+                        return
+                    }
+
+                    do {
+                        let bestPolicy = try self.model.policy(forPeerIDs: sponsor.dynamicInfo?.includedPeerIDs ?? [sponsor.peerID],
+                                                               candidatePeerID: egoPeerID,
+                                                               candidateStableInfo: sponsor.stableInfo)
+
+                        let views = try bestPolicy.views(forModel: selfPermanentInfo.modelID)
+                        reply(recoveryKeys.peerKeys.peerID, views, bestPolicy, nil)
+                    } catch {
+                        os_log("preflightRecoveryKey: error fetching policy: %{public}@", log: tplogDebug, type: .default, error as CVarArg)
+                        reply(nil, nil, nil, error)
+                        return
+                    }
+                }
+            }
+        }
+    }
+}
index 04d9b902ede10d63f54ac54b08d9ec10e3a6e3b5..c10415deceb3dc9925c9fbe0dc24f023733baa8b 100644 (file)
@@ -5,7 +5,7 @@ struct CuttlefishErrorMatcher {
 }
 
 // Use a 'pattern match operator' to make pretty case statements matching Cuttlefish errors
 }
 
 // Use a 'pattern match operator' to make pretty case statements matching Cuttlefish errors
-func ~=(pattern: CuttlefishErrorMatcher, value: Error?) -> Bool {
+func ~= (pattern: CuttlefishErrorMatcher, value: Error?) -> Bool {
     guard let error = value else {
         return false
     }
     guard let error = value else {
         return false
     }
index 2cbece09f47f305200c27a09f8d6bb60baddde46..ce1c3eef9350c84414451148f8ac8009b4693fff 100644 (file)
 import Foundation
 
 struct RawPolicy {
 import Foundation
 
 struct RawPolicy {
-    let policyVersion: Int
-    let policyHash: String
+    let version: TPPolicyVersion
     let policyData: String
     let plaintextPolicy: TPPolicyDocument
 }
 
     let policyData: String
     let plaintextPolicy: TPPolicyDocument
 }
 
-let prevailingPolicyVersion: UInt64 = 5
-let prevailingPolicyHash: String = "SHA256:O/ECQlWhvNlLmlDNh2+nal/yekUC87bXpV3k+6kznSo="
+let prevailingPolicyVersion = TPPolicyVersion(version: 6, hash: "SHA256:L2Px1aYyR1tgChe8dIyTBSmCHCWEFJirZ3ELMFXz2PY=")
+
+// Some peers don't know how to handle new policies when pairing. If we're pairing with one of those,
+// we must prepare our identity using this policy.
+let frozenPolicyVersion = TPPolicyVersion(version: 5, hash: "SHA256:O/ECQlWhvNlLmlDNh2+nal/yekUC87bXpV3k+6kznSo=")
 
 func builtInPolicyDocuments() -> [TPPolicyDocument] {
 
 
 func builtInPolicyDocuments() -> [TPPolicyDocument] {
 
+    // swiftlint:disable force_try
     // These bytes are generated by tppolicy
     let rawPolicies = [
         RawPolicy(
     // These bytes are generated by tppolicy
     let rawPolicies = [
         RawPolicy(
-            policyVersion: 1,
-            policyHash: "SHA256:TLXrcQmY4ue3oP5pCX1pwsi9BF8cKfohlJBilCroeBs=",
+            version: TPPolicyVersion(version: 1, hash: "SHA256:TLXrcQmY4ue3oP5pCX1pwsi9BF8cKfohlJBilCroeBs="),
             policyData: "CAESDgoGaVBob25lEgRmdWxsEgwKBGlQYWQSBGZ1bGwSCwoDTWFjEgRmdWxsEgwKBGlNYWMSBGZ1bGwSDQoHQXBwbGVUVhICdHYSDgoFV2F0Y2gSBXdhdGNoGhEKCVBDU0VzY3JvdxIEZnVsbBoXCgRXaUZpEgRmdWxsEgJ0dhIFd2F0Y2gaGQoRU2FmYXJpQ3JlZGl0Q2FyZHMSBGZ1bGwiDAoEZnVsbBIEZnVsbCIUCgV3YXRjaBIEZnVsbBIFd2F0Y2giDgoCdHYSBGZ1bGwSAnR2",
             plaintextPolicy: try! TPPolicyDocument(version: 1,
                                                    modelToCategory: [
             policyData: "CAESDgoGaVBob25lEgRmdWxsEgwKBGlQYWQSBGZ1bGwSCwoDTWFjEgRmdWxsEgwKBGlNYWMSBGZ1bGwSDQoHQXBwbGVUVhICdHYSDgoFV2F0Y2gSBXdhdGNoGhEKCVBDU0VzY3JvdxIEZnVsbBoXCgRXaUZpEgRmdWxsEgJ0dhIFd2F0Y2gaGQoRU2FmYXJpQ3JlZGl0Q2FyZHMSBGZ1bGwiDAoEZnVsbBIEZnVsbCIUCgV3YXRjaBIEZnVsbBIFd2F0Y2giDgoCdHYSBGZ1bGwSAnR2",
             plaintextPolicy: try! TPPolicyDocument(version: 1,
                                                    modelToCategory: [
@@ -66,8 +68,7 @@ func builtInPolicyDocuments() -> [TPPolicyDocument] {
         ),
 
         RawPolicy(
         ),
 
         RawPolicy(
-            policyVersion: 2,
-            policyHash: "SHA256:ZL1WBUCyO155rHBJQeghomCCKGmfjtS0jvsK+UEvx5o=",
+            version: TPPolicyVersion(version: 2, hash: "SHA256:ZL1WBUCyO155rHBJQeghomCCKGmfjtS0jvsK+UEvx5o="),
             policyData: "CAISDgoGaUN5Y2xlEgRmdWxsEg4KBmlQaG9uZRIEZnVsbBIMCgRpUGFkEgRmdWxsEgsKA01hYxIEZnVsbBIMCgRpTWFjEgRmdWxsEg0KB0FwcGxlVFYSAnR2Eg4KBVdhdGNoEgV3YXRjaBoRCglQQ1NFc2Nyb3cSBGZ1bGwaFwoEV2lGaRIEZnVsbBICdHYSBXdhdGNoGhkKEVNhZmFyaUNyZWRpdENhcmRzEgRmdWxsIgwKBGZ1bGwSBGZ1bGwiFAoFd2F0Y2gSBGZ1bGwSBXdhdGNoIg4KAnR2EgRmdWxsEgJ0dg==",
             plaintextPolicy: try! TPPolicyDocument(version: 2,
                                                    modelToCategory: [
             policyData: "CAISDgoGaUN5Y2xlEgRmdWxsEg4KBmlQaG9uZRIEZnVsbBIMCgRpUGFkEgRmdWxsEgsKA01hYxIEZnVsbBIMCgRpTWFjEgRmdWxsEg0KB0FwcGxlVFYSAnR2Eg4KBVdhdGNoEgV3YXRjaBoRCglQQ1NFc2Nyb3cSBGZ1bGwaFwoEV2lGaRIEZnVsbBICdHYSBXdhdGNoGhkKEVNhZmFyaUNyZWRpdENhcmRzEgRmdWxsIgwKBGZ1bGwSBGZ1bGwiFAoFd2F0Y2gSBGZ1bGwSBXdhdGNoIg4KAnR2EgRmdWxsEgJ0dg==",
             plaintextPolicy: try! TPPolicyDocument(version: 2,
                                                    modelToCategory: [
@@ -94,8 +95,7 @@ func builtInPolicyDocuments() -> [TPPolicyDocument] {
                                                    hashAlgo: .SHA256)
         ),
 
                                                    hashAlgo: .SHA256)
         ),
 
-        RawPolicy(policyVersion: 3,
-                  policyHash: "SHA256:JZzazSuHXrUhiOfSgElsg6vYKpnvvEPVpciR8FewRWg=",
+        RawPolicy(version: TPPolicyVersion(version: 3, hash: "SHA256:JZzazSuHXrUhiOfSgElsg6vYKpnvvEPVpciR8FewRWg="),
                   policyData: "CAMSDgoGaVBob25lEgRmdWxsEgwKBGlQYWQSBGZ1bGwSCwoDTWFjEgRmdWxsEgwKBGlNYWMSBGZ1bGwSDQoHQXBwbGVUVhICdHYSDgoFV2F0Y2gSBXdhdGNoEhcKDkF1ZGlvQWNjZXNzb3J5EgVhdWRpbxocCg1EZXZpY2VQYWlyaW5nEgRmdWxsEgV3YXRjaBoXCghBcHBsZVBheRIEZnVsbBIFd2F0Y2gaJAoVUHJvdGVjdGVkQ2xvdWRTdG9yYWdlEgRmdWxsEgV3YXRjaBoXCghCYWNrc3RvcBIEZnVsbBIFd2F0Y2gaGQoKQXV0b1VubG9jaxIEZnVsbBIFd2F0Y2gaHwoQU2VjdXJlT2JqZWN0U3luYxIEZnVsbBIFd2F0Y2gaIAoRU2FmYXJpQ3JlZGl0Q2FyZHMSBGZ1bGwSBXdhdGNoGhMKBEhvbWUSBGZ1bGwSBXdhdGNoGh4KD1NhZmFyaVBhc3N3b3JkcxIEZnVsbBIFd2F0Y2gaGwoMQXBwbGljYXRpb25zEgRmdWxsEgV3YXRjaBoVCgZFbmdyYW0SBGZ1bGwSBXdhdGNoGi0KE0xpbWl0ZWRQZWVyc0FsbG93ZWQSBGZ1bGwSBXdhdGNoEgJ0dhIFYXVkaW8aFgoHTWFuYXRlZRIEZnVsbBIFd2F0Y2gaHgoEV2lGaRIEZnVsbBIFd2F0Y2gSAnR2EgVhdWRpbxoVCgZIZWFsdGgSBGZ1bGwSBXdhdGNoIhMKBGZ1bGwSBGZ1bGwSBXdhdGNoIhsKBWF1ZGlvEgRmdWxsEgV3YXRjaBIFYXVkaW8iFAoFd2F0Y2gSBGZ1bGwSBXdhdGNoIhUKAnR2EgRmdWxsEgV3YXRjaBICdHYyIgoWAAQiEgIEdndodAoKXkFwcGxlUGF5JBIIQXBwbGVQYXkyJgoYAAQiFAIEdndodAoMXkF1dG9VbmxvY2skEgpBdXRvVW5sb2NrMh4KFAAEIhACBHZ3aHQKCF5FbmdyYW0kEgZFbmdyYW0yHgoUAAQiEAIEdndodAoIXkhlYWx0aCQSBkhlYWx0aDIaChIABCIOAgR2d2h0CgZeSG9tZSQSBEhvbWUyIAoVAAQiEQIEdndodAoJXk1hbmF0ZWUkEgdNYW5hdGVlMjgKIQAEIh0CBHZ3aHQKFV5MaW1pdGVkUGVlcnNBbGxvd2VkJBITTGltaXRlZFBlZXJzQWxsb3dlZDJdClAAAhIeAAQiGgIEdndodAoSXkNvbnRpbnVpdHlVbmxvY2skEhUABCIRAgR2d2h0CgleSG9tZUtpdCQSFQAEIhECBHZ3aHQKCV5BcHBsZVRWJBIJTm90U3luY2VkMisKGwAEIhcCBGFncnAKD15bMC05QS1aXXsxMH1cLhIMQXBwbGljYXRpb25zMsUBCrABAAISNAABChMABCIPAgVjbGFzcwoGXmdlbnAkChsABCIXAgRhZ3JwCg9eY29tLmFwcGxlLnNiZCQSPQABChMABCIPAgVjbGFzcwoGXmtleXMkCiQABCIgAgRhZ3JwChheY29tLmFwcGxlLnNlY3VyaXR5LnNvcyQSGQAEIhUCBHZ3aHQKDV5CYWNrdXBCYWdWMCQSHAAEIhgCBHZ3aHQKEF5pQ2xvdWRJZGVudGl0eSQSEFNlY3VyZU9iamVjdFN5bmMyYwpbAAISEgAEIg4CBHZ3aHQKBl5XaUZpJBJDAAEKEwAEIg8CBWNsYXNzCgZeZ2VucCQKEwAEIg8CBGFncnAKB15hcHBsZSQKFQAEIhECBHN2Y2UKCV5BaXJQb3J0JBIEV2lGaTLbAgrBAgACEhkABCIVAgR2d2h0Cg1eUENTQ2xvdWRLaXQkEhcABCITAgR2d2h0CgteUENTRXNjcm93JBIUAAQiEAIEdndodAoIXlBDU0ZERSQSGQAEIhUCBHZ3aHQKDV5QQ1NGZWxkc3BhciQSGQAEIhUCBHZ3aHQKDV5QQ1NNYWlsRHJvcCQSGgAEIhYCBHZ3aHQKDl5QQ1NNYXN0ZXJLZXkkEhYABCISAgR2d2h0CgpeUENTTm90ZXMkEhcABCITAgR2d2h0CgteUENTUGhvdG9zJBIYAAQiFAIEdndodAoMXlBDU1NoYXJpbmckEh0ABCIZAgR2d2h0ChFeUENTaUNsb3VkQmFja3VwJBIcAAQiGAIEdndodAoQXlBDU2lDbG91ZERyaXZlJBIZAAQiFQIEdndodAoNXlBDU2lNZXNzYWdlJBIVUHJvdGVjdGVkQ2xvdWRTdG9yYWdlMkAKKwAEIicCBGFncnAKH15jb20uYXBwbGUuc2FmYXJpLmNyZWRpdC1jYXJkcyQSEVNhZmFyaUNyZWRpdENhcmRzMjQKIQAEIh0CBGFncnAKFV5jb20uYXBwbGUuY2ZuZXR3b3JrJBIPU2FmYXJpUGFzc3dvcmRzMm0KXAACEh4ABCIaAgR2d2h0ChJeQWNjZXNzb3J5UGFpcmluZyQSGgAEIhYCBHZ3aHQKDl5OYW5vUmVnaXN0cnkkEhwABCIYAgR2d2h0ChBeV2F0Y2hNaWdyYXRpb24kEg1EZXZpY2VQYWlyaW5nMi0KIQAEIh0CBGFncnAKFV5jb20uYXBwbGUuY2ZuZXR3b3JrJBIIQmFja3N0b3A=",
                   plaintextPolicy: try! TPPolicyDocument(version: 3,
                                              modelToCategory: [
                   policyData: "CAMSDgoGaVBob25lEgRmdWxsEgwKBGlQYWQSBGZ1bGwSCwoDTWFjEgRmdWxsEgwKBGlNYWMSBGZ1bGwSDQoHQXBwbGVUVhICdHYSDgoFV2F0Y2gSBXdhdGNoEhcKDkF1ZGlvQWNjZXNzb3J5EgVhdWRpbxocCg1EZXZpY2VQYWlyaW5nEgRmdWxsEgV3YXRjaBoXCghBcHBsZVBheRIEZnVsbBIFd2F0Y2gaJAoVUHJvdGVjdGVkQ2xvdWRTdG9yYWdlEgRmdWxsEgV3YXRjaBoXCghCYWNrc3RvcBIEZnVsbBIFd2F0Y2gaGQoKQXV0b1VubG9jaxIEZnVsbBIFd2F0Y2gaHwoQU2VjdXJlT2JqZWN0U3luYxIEZnVsbBIFd2F0Y2gaIAoRU2FmYXJpQ3JlZGl0Q2FyZHMSBGZ1bGwSBXdhdGNoGhMKBEhvbWUSBGZ1bGwSBXdhdGNoGh4KD1NhZmFyaVBhc3N3b3JkcxIEZnVsbBIFd2F0Y2gaGwoMQXBwbGljYXRpb25zEgRmdWxsEgV3YXRjaBoVCgZFbmdyYW0SBGZ1bGwSBXdhdGNoGi0KE0xpbWl0ZWRQZWVyc0FsbG93ZWQSBGZ1bGwSBXdhdGNoEgJ0dhIFYXVkaW8aFgoHTWFuYXRlZRIEZnVsbBIFd2F0Y2gaHgoEV2lGaRIEZnVsbBIFd2F0Y2gSAnR2EgVhdWRpbxoVCgZIZWFsdGgSBGZ1bGwSBXdhdGNoIhMKBGZ1bGwSBGZ1bGwSBXdhdGNoIhsKBWF1ZGlvEgRmdWxsEgV3YXRjaBIFYXVkaW8iFAoFd2F0Y2gSBGZ1bGwSBXdhdGNoIhUKAnR2EgRmdWxsEgV3YXRjaBICdHYyIgoWAAQiEgIEdndodAoKXkFwcGxlUGF5JBIIQXBwbGVQYXkyJgoYAAQiFAIEdndodAoMXkF1dG9VbmxvY2skEgpBdXRvVW5sb2NrMh4KFAAEIhACBHZ3aHQKCF5FbmdyYW0kEgZFbmdyYW0yHgoUAAQiEAIEdndodAoIXkhlYWx0aCQSBkhlYWx0aDIaChIABCIOAgR2d2h0CgZeSG9tZSQSBEhvbWUyIAoVAAQiEQIEdndodAoJXk1hbmF0ZWUkEgdNYW5hdGVlMjgKIQAEIh0CBHZ3aHQKFV5MaW1pdGVkUGVlcnNBbGxvd2VkJBITTGltaXRlZFBlZXJzQWxsb3dlZDJdClAAAhIeAAQiGgIEdndodAoSXkNvbnRpbnVpdHlVbmxvY2skEhUABCIRAgR2d2h0CgleSG9tZUtpdCQSFQAEIhECBHZ3aHQKCV5BcHBsZVRWJBIJTm90U3luY2VkMisKGwAEIhcCBGFncnAKD15bMC05QS1aXXsxMH1cLhIMQXBwbGljYXRpb25zMsUBCrABAAISNAABChMABCIPAgVjbGFzcwoGXmdlbnAkChsABCIXAgRhZ3JwCg9eY29tLmFwcGxlLnNiZCQSPQABChMABCIPAgVjbGFzcwoGXmtleXMkCiQABCIgAgRhZ3JwChheY29tLmFwcGxlLnNlY3VyaXR5LnNvcyQSGQAEIhUCBHZ3aHQKDV5CYWNrdXBCYWdWMCQSHAAEIhgCBHZ3aHQKEF5pQ2xvdWRJZGVudGl0eSQSEFNlY3VyZU9iamVjdFN5bmMyYwpbAAISEgAEIg4CBHZ3aHQKBl5XaUZpJBJDAAEKEwAEIg8CBWNsYXNzCgZeZ2VucCQKEwAEIg8CBGFncnAKB15hcHBsZSQKFQAEIhECBHN2Y2UKCV5BaXJQb3J0JBIEV2lGaTLbAgrBAgACEhkABCIVAgR2d2h0Cg1eUENTQ2xvdWRLaXQkEhcABCITAgR2d2h0CgteUENTRXNjcm93JBIUAAQiEAIEdndodAoIXlBDU0ZERSQSGQAEIhUCBHZ3aHQKDV5QQ1NGZWxkc3BhciQSGQAEIhUCBHZ3aHQKDV5QQ1NNYWlsRHJvcCQSGgAEIhYCBHZ3aHQKDl5QQ1NNYXN0ZXJLZXkkEhYABCISAgR2d2h0CgpeUENTTm90ZXMkEhcABCITAgR2d2h0CgteUENTUGhvdG9zJBIYAAQiFAIEdndodAoMXlBDU1NoYXJpbmckEh0ABCIZAgR2d2h0ChFeUENTaUNsb3VkQmFja3VwJBIcAAQiGAIEdndodAoQXlBDU2lDbG91ZERyaXZlJBIZAAQiFQIEdndodAoNXlBDU2lNZXNzYWdlJBIVUHJvdGVjdGVkQ2xvdWRTdG9yYWdlMkAKKwAEIicCBGFncnAKH15jb20uYXBwbGUuc2FmYXJpLmNyZWRpdC1jYXJkcyQSEVNhZmFyaUNyZWRpdENhcmRzMjQKIQAEIh0CBGFncnAKFV5jb20uYXBwbGUuY2ZuZXR3b3JrJBIPU2FmYXJpUGFzc3dvcmRzMm0KXAACEh4ABCIaAgR2d2h0ChJeQWNjZXNzb3J5UGFpcmluZyQSGgAEIhYCBHZ3aHQKDl5OYW5vUmVnaXN0cnkkEhwABCIYAgR2d2h0ChBeV2F0Y2hNaWdyYXRpb24kEg1EZXZpY2VQYWlyaW5nMi0KIQAEIh0CBGFncnAKFV5jb20uYXBwbGUuY2ZuZXR3b3JrJBIIQmFja3N0b3A=",
                   plaintextPolicy: try! TPPolicyDocument(version: 3,
                                              modelToCategory: [
@@ -210,8 +210,7 @@ func builtInPolicyDocuments() -> [TPPolicyDocument] {
                                              ],
                                              hashAlgo: .SHA256)
             ),
                                              ],
                                              hashAlgo: .SHA256)
             ),
-        RawPolicy(policyVersion: 4,
-                  policyHash: "SHA256:Tjdu5QrWGvKWMx7k3VWFrEWSsBDPZAwCql9ybDkvFs8=",
+        RawPolicy(version: TPPolicyVersion(version: 4, hash: "SHA256:Tjdu5QrWGvKWMx7k3VWFrEWSsBDPZAwCql9ybDkvFs8="),
                   policyData: "CAQSDgoGaVBob25lEgRmdWxsEgwKBGlQYWQSBGZ1bGwSCwoDTWFjEgRmdWxsEgwKBGlNYWMSBGZ1bGwSDQoHQXBwbGVUVhICdHYSDgoFV2F0Y2gSBXdhdGNoEhcKDkF1ZGlvQWNjZXNzb3J5EgVhdWRpbxoTCgRIb21lEgRmdWxsEgV3YXRjaBobCgxBcHBsaWNhdGlvbnMSBGZ1bGwSBXdhdGNoGh4KBFdpRmkSBGZ1bGwSBXdhdGNoEgJ0dhIFYXVkaW8aGQoKQXV0b1VubG9jaxIEZnVsbBIFd2F0Y2gaFwoIQXBwbGVQYXkSBGZ1bGwSBXdhdGNoGhUKBkhlYWx0aBIEZnVsbBIFd2F0Y2gaFgoHTWFuYXRlZRIEZnVsbBIFd2F0Y2gaLQoTTGltaXRlZFBlZXJzQWxsb3dlZBIEZnVsbBIFd2F0Y2gSAnR2EgVhdWRpbxokChVQcm90ZWN0ZWRDbG91ZFN0b3JhZ2USBGZ1bGwSBXdhdGNoGhgKCVBhc3N3b3JkcxIEZnVsbBIFd2F0Y2gaHAoNRGV2aWNlUGFpcmluZxIEZnVsbBIFd2F0Y2gaHwoQU2VjdXJlT2JqZWN0U3luYxIEZnVsbBIFd2F0Y2gaFQoGRW5ncmFtEgRmdWxsEgV3YXRjaBoaCgtDcmVkaXRDYXJkcxIEZnVsbBIFd2F0Y2giGwoFYXVkaW8SBGZ1bGwSBXdhdGNoEgVhdWRpbyITCgRmdWxsEgRmdWxsEgV3YXRjaCIUCgV3YXRjaBIEZnVsbBIFd2F0Y2giFQoCdHYSBGZ1bGwSBXdhdGNoEgJ0djIiChYABCISAgR2d2h0CgpeQXBwbGVQYXkkEghBcHBsZVBheTImChgABCIUAgR2d2h0CgxeQXV0b1VubG9jayQSCkF1dG9VbmxvY2syHgoUAAQiEAIEdndodAoIXkVuZ3JhbSQSBkVuZ3JhbTIeChQABCIQAgR2d2h0CgheSGVhbHRoJBIGSGVhbHRoMhoKEgAEIg4CBHZ3aHQKBl5Ib21lJBIESG9tZTIgChUABCIRAgR2d2h0CgleTWFuYXRlZSQSB01hbmF0ZWUyOAohAAQiHQIEdndodAoVXkxpbWl0ZWRQZWVyc0FsbG93ZWQkEhNMaW1pdGVkUGVlcnNBbGxvd2VkMl0KUAACEh4ABCIaAgR2d2h0ChJeQ29udGludWl0eVVubG9jayQSFQAEIhECBHZ3aHQKCV5Ib21lS2l0JBIVAAQiEQIEdndodAoJXkFwcGxlVFYkEglOb3RTeW5jZWQyKwobAAQiFwIEYWdycAoPXlswLTlBLVpdezEwfVwuEgxBcHBsaWNhdGlvbnMyxQEKsAEAAhI0AAEKEwAEIg8CBWNsYXNzCgZeZ2VucCQKGwAEIhcCBGFncnAKD15jb20uYXBwbGUuc2JkJBI9AAEKEwAEIg8CBWNsYXNzCgZea2V5cyQKJAAEIiACBGFncnAKGF5jb20uYXBwbGUuc2VjdXJpdHkuc29zJBIZAAQiFQIEdndodAoNXkJhY2t1cEJhZ1YwJBIcAAQiGAIEdndodAoQXmlDbG91ZElkZW50aXR5JBIQU2VjdXJlT2JqZWN0U3luYzJjClsAAhISAAQiDgIEdndodAoGXldpRmkkEkMAAQoTAAQiDwIFY2xhc3MKBl5nZW5wJAoTAAQiDwIEYWdycAoHXmFwcGxlJAoVAAQiEQIEc3ZjZQoJXkFpclBvcnQkEgRXaUZpMucCCs0CAAISGgAEIhYCBHZ3aHQKDl5QQ1MtQ2xvdWRLaXQkEhgABCIUAgR2d2h0CgxeUENTLUVzY3JvdyQSFQAEIhECBHZ3aHQKCV5QQ1MtRkRFJBIaAAQiFgIEdndodAoOXlBDUy1GZWxkc3BhciQSGgAEIhYCBHZ3aHQKDl5QQ1MtTWFpbERyb3AkEhsABCIXAgR2d2h0Cg9eUENTLU1hc3RlcktleSQSFwAEIhMCBHZ3aHQKC15QQ1MtTm90ZXMkEhgABCIUAgR2d2h0CgxeUENTLVBob3RvcyQSGQAEIhUCBHZ3aHQKDV5QQ1MtU2hhcmluZyQSHgAEIhoCBHZ3aHQKEl5QQ1MtaUNsb3VkQmFja3VwJBIdAAQiGQIEdndodAoRXlBDUy1pQ2xvdWREcml2ZSQSGgAEIhYCBHZ3aHQKDl5QQ1MtaU1lc3NhZ2UkEhVQcm90ZWN0ZWRDbG91ZFN0b3JhZ2UyOgorAAQiJwIEYWdycAofXmNvbS5hcHBsZS5zYWZhcmkuY3JlZGl0LWNhcmRzJBILQ3JlZGl0Q2FyZHMyLgohAAQiHQIEYWdycAoVXmNvbS5hcHBsZS5jZm5ldHdvcmskEglQYXNzd29yZHMybQpcAAISHgAEIhoCBHZ3aHQKEl5BY2Nlc3NvcnlQYWlyaW5nJBIaAAQiFgIEdndodAoOXk5hbm9SZWdpc3RyeSQSHAAEIhgCBHZ3aHQKEF5XYXRjaE1pZ3JhdGlvbiQSDURldmljZVBhaXJpbmc=",
                   plaintextPolicy: try! TPPolicyDocument(version: 4,
                                              modelToCategory: [
                   policyData: "CAQSDgoGaVBob25lEgRmdWxsEgwKBGlQYWQSBGZ1bGwSCwoDTWFjEgRmdWxsEgwKBGlNYWMSBGZ1bGwSDQoHQXBwbGVUVhICdHYSDgoFV2F0Y2gSBXdhdGNoEhcKDkF1ZGlvQWNjZXNzb3J5EgVhdWRpbxoTCgRIb21lEgRmdWxsEgV3YXRjaBobCgxBcHBsaWNhdGlvbnMSBGZ1bGwSBXdhdGNoGh4KBFdpRmkSBGZ1bGwSBXdhdGNoEgJ0dhIFYXVkaW8aGQoKQXV0b1VubG9jaxIEZnVsbBIFd2F0Y2gaFwoIQXBwbGVQYXkSBGZ1bGwSBXdhdGNoGhUKBkhlYWx0aBIEZnVsbBIFd2F0Y2gaFgoHTWFuYXRlZRIEZnVsbBIFd2F0Y2gaLQoTTGltaXRlZFBlZXJzQWxsb3dlZBIEZnVsbBIFd2F0Y2gSAnR2EgVhdWRpbxokChVQcm90ZWN0ZWRDbG91ZFN0b3JhZ2USBGZ1bGwSBXdhdGNoGhgKCVBhc3N3b3JkcxIEZnVsbBIFd2F0Y2gaHAoNRGV2aWNlUGFpcmluZxIEZnVsbBIFd2F0Y2gaHwoQU2VjdXJlT2JqZWN0U3luYxIEZnVsbBIFd2F0Y2gaFQoGRW5ncmFtEgRmdWxsEgV3YXRjaBoaCgtDcmVkaXRDYXJkcxIEZnVsbBIFd2F0Y2giGwoFYXVkaW8SBGZ1bGwSBXdhdGNoEgVhdWRpbyITCgRmdWxsEgRmdWxsEgV3YXRjaCIUCgV3YXRjaBIEZnVsbBIFd2F0Y2giFQoCdHYSBGZ1bGwSBXdhdGNoEgJ0djIiChYABCISAgR2d2h0CgpeQXBwbGVQYXkkEghBcHBsZVBheTImChgABCIUAgR2d2h0CgxeQXV0b1VubG9jayQSCkF1dG9VbmxvY2syHgoUAAQiEAIEdndodAoIXkVuZ3JhbSQSBkVuZ3JhbTIeChQABCIQAgR2d2h0CgheSGVhbHRoJBIGSGVhbHRoMhoKEgAEIg4CBHZ3aHQKBl5Ib21lJBIESG9tZTIgChUABCIRAgR2d2h0CgleTWFuYXRlZSQSB01hbmF0ZWUyOAohAAQiHQIEdndodAoVXkxpbWl0ZWRQZWVyc0FsbG93ZWQkEhNMaW1pdGVkUGVlcnNBbGxvd2VkMl0KUAACEh4ABCIaAgR2d2h0ChJeQ29udGludWl0eVVubG9jayQSFQAEIhECBHZ3aHQKCV5Ib21lS2l0JBIVAAQiEQIEdndodAoJXkFwcGxlVFYkEglOb3RTeW5jZWQyKwobAAQiFwIEYWdycAoPXlswLTlBLVpdezEwfVwuEgxBcHBsaWNhdGlvbnMyxQEKsAEAAhI0AAEKEwAEIg8CBWNsYXNzCgZeZ2VucCQKGwAEIhcCBGFncnAKD15jb20uYXBwbGUuc2JkJBI9AAEKEwAEIg8CBWNsYXNzCgZea2V5cyQKJAAEIiACBGFncnAKGF5jb20uYXBwbGUuc2VjdXJpdHkuc29zJBIZAAQiFQIEdndodAoNXkJhY2t1cEJhZ1YwJBIcAAQiGAIEdndodAoQXmlDbG91ZElkZW50aXR5JBIQU2VjdXJlT2JqZWN0U3luYzJjClsAAhISAAQiDgIEdndodAoGXldpRmkkEkMAAQoTAAQiDwIFY2xhc3MKBl5nZW5wJAoTAAQiDwIEYWdycAoHXmFwcGxlJAoVAAQiEQIEc3ZjZQoJXkFpclBvcnQkEgRXaUZpMucCCs0CAAISGgAEIhYCBHZ3aHQKDl5QQ1MtQ2xvdWRLaXQkEhgABCIUAgR2d2h0CgxeUENTLUVzY3JvdyQSFQAEIhECBHZ3aHQKCV5QQ1MtRkRFJBIaAAQiFgIEdndodAoOXlBDUy1GZWxkc3BhciQSGgAEIhYCBHZ3aHQKDl5QQ1MtTWFpbERyb3AkEhsABCIXAgR2d2h0Cg9eUENTLU1hc3RlcktleSQSFwAEIhMCBHZ3aHQKC15QQ1MtTm90ZXMkEhgABCIUAgR2d2h0CgxeUENTLVBob3RvcyQSGQAEIhUCBHZ3aHQKDV5QQ1MtU2hhcmluZyQSHgAEIhoCBHZ3aHQKEl5QQ1MtaUNsb3VkQmFja3VwJBIdAAQiGQIEdndodAoRXlBDUy1pQ2xvdWREcml2ZSQSGgAEIhYCBHZ3aHQKDl5QQ1MtaU1lc3NhZ2UkEhVQcm90ZWN0ZWRDbG91ZFN0b3JhZ2UyOgorAAQiJwIEYWdycAofXmNvbS5hcHBsZS5zYWZhcmkuY3JlZGl0LWNhcmRzJBILQ3JlZGl0Q2FyZHMyLgohAAQiHQIEYWdycAoVXmNvbS5hcHBsZS5jZm5ldHdvcmskEglQYXNzd29yZHMybQpcAAISHgAEIhoCBHZ3aHQKEl5BY2Nlc3NvcnlQYWlyaW5nJBIaAAQiFgIEdndodAoOXk5hbm9SZWdpc3RyeSQSHAAEIhgCBHZ3aHQKEF5XYXRjaE1pZ3JhdGlvbiQSDURldmljZVBhaXJpbmc=",
                   plaintextPolicy: try! TPPolicyDocument(version: 4,
                                              modelToCategory: [
@@ -322,8 +321,7 @@ func builtInPolicyDocuments() -> [TPPolicyDocument] {
                                              hashAlgo: .SHA256)
             ),
 
                                              hashAlgo: .SHA256)
             ),
 
-        RawPolicy(policyVersion: 5,
-                  policyHash: "SHA256:O/ECQlWhvNlLmlDNh2+nal/yekUC87bXpV3k+6kznSo=",
+        RawPolicy(version: TPPolicyVersion(version: 5, hash: "SHA256:O/ECQlWhvNlLmlDNh2+nal/yekUC87bXpV3k+6kznSo="),
                   policyData: "CAUSDgoGaVBob25lEgRmdWxsEgwKBGlQYWQSBGZ1bGwSDAoEaVBvZBIEZnVsbBILCgNNYWMSBGZ1bGwSDAoEaU1hYxIEZnVsbBINCgdBcHBsZVRWEgJ0dhIOCgVXYXRjaBIFd2F0Y2gSFwoOQXVkaW9BY2Nlc3NvcnkSBWF1ZGlvGhsKDEFwcGxpY2F0aW9ucxIEZnVsbBIFd2F0Y2gaHwoQU2VjdXJlT2JqZWN0U3luYxIEZnVsbBIFd2F0Y2gaHAoNRGV2aWNlUGFpcmluZxIEZnVsbBIFd2F0Y2gaGgoLQ3JlZGl0Q2FyZHMSBGZ1bGwSBXdhdGNoGhUKBkhlYWx0aBIEZnVsbBIFd2F0Y2gaLQoTTGltaXRlZFBlZXJzQWxsb3dlZBIEZnVsbBIFd2F0Y2gSAnR2EgVhdWRpbxokChVQcm90ZWN0ZWRDbG91ZFN0b3JhZ2USBGZ1bGwSBXdhdGNoGhcKCEFwcGxlUGF5EgRmdWxsEgV3YXRjaBoZCgpBdXRvVW5sb2NrEgRmdWxsEgV3YXRjaBoWCgdNYW5hdGVlEgRmdWxsEgV3YXRjaBoYCglQYXNzd29yZHMSBGZ1bGwSBXdhdGNoGhUKBkVuZ3JhbRIEZnVsbBIFd2F0Y2gaHgoEV2lGaRIEZnVsbBIFd2F0Y2gSAnR2EgVhdWRpbxoTCgRIb21lEgRmdWxsEgV3YXRjaCIbCgVhdWRpbxIEZnVsbBIFd2F0Y2gSBWF1ZGlvIhMKBGZ1bGwSBGZ1bGwSBXdhdGNoIhUKAnR2EgRmdWxsEgV3YXRjaBICdHYiFAoFd2F0Y2gSBGZ1bGwSBXdhdGNoMiIKFgAEIhICBHZ3aHQKCl5BcHBsZVBheSQSCEFwcGxlUGF5MiYKGAAEIhQCBHZ3aHQKDF5BdXRvVW5sb2NrJBIKQXV0b1VubG9jazIeChQABCIQAgR2d2h0CgheRW5ncmFtJBIGRW5ncmFtMh4KFAAEIhACBHZ3aHQKCF5IZWFsdGgkEgZIZWFsdGgyGgoSAAQiDgIEdndodAoGXkhvbWUkEgRIb21lMiAKFQAEIhECBHZ3aHQKCV5NYW5hdGVlJBIHTWFuYXRlZTI4CiEABCIdAgR2d2h0ChVeTGltaXRlZFBlZXJzQWxsb3dlZCQSE0xpbWl0ZWRQZWVyc0FsbG93ZWQyXQpQAAISHgAEIhoCBHZ3aHQKEl5Db250aW51aXR5VW5sb2NrJBIVAAQiEQIEdndodAoJXkhvbWVLaXQkEhUABCIRAgR2d2h0CgleQXBwbGVUViQSCU5vdFN5bmNlZDIrChsABCIXAgRhZ3JwCg9eWzAtOUEtWl17MTB9XC4SDEFwcGxpY2F0aW9uczLFAQqwAQACEjQAAQoTAAQiDwIFY2xhc3MKBl5nZW5wJAobAAQiFwIEYWdycAoPXmNvbS5hcHBsZS5zYmQkEj0AAQoTAAQiDwIFY2xhc3MKBl5rZXlzJAokAAQiIAIEYWdycAoYXmNvbS5hcHBsZS5zZWN1cml0eS5zb3MkEhkABCIVAgR2d2h0Cg1eQmFja3VwQmFnVjAkEhwABCIYAgR2d2h0ChBeaUNsb3VkSWRlbnRpdHkkEhBTZWN1cmVPYmplY3RTeW5jMmMKWwACEhIABCIOAgR2d2h0CgZeV2lGaSQSQwABChMABCIPAgVjbGFzcwoGXmdlbnAkChMABCIPAgRhZ3JwCgdeYXBwbGUkChUABCIRAgRzdmNlCgleQWlyUG9ydCQSBFdpRmkynQMKgwMAAhIYAAQiFAIEdndodAoMXlBDUy1CYWNrdXAkEhoABCIWAgR2d2h0Cg5eUENTLUNsb3VkS2l0JBIYAAQiFAIEdndodAoMXlBDUy1Fc2Nyb3ckEhUABCIRAgR2d2h0CgleUENTLUZERSQSGgAEIhYCBHZ3aHQKDl5QQ1MtRmVsZHNwYXIkEhoABCIWAgR2d2h0Cg5eUENTLU1haWxEcm9wJBIaAAQiFgIEdndodAoOXlBDUy1NYWlsZHJvcCQSGwAEIhcCBHZ3aHQKD15QQ1MtTWFzdGVyS2V5JBIXAAQiEwIEdndodAoLXlBDUy1Ob3RlcyQSGAAEIhQCBHZ3aHQKDF5QQ1MtUGhvdG9zJBIZAAQiFQIEdndodAoNXlBDUy1TaGFyaW5nJBIeAAQiGgIEdndodAoSXlBDUy1pQ2xvdWRCYWNrdXAkEh0ABCIZAgR2d2h0ChFeUENTLWlDbG91ZERyaXZlJBIaAAQiFgIEdndodAoOXlBDUy1pTWVzc2FnZSQSFVByb3RlY3RlZENsb3VkU3RvcmFnZTI6CisABCInAgRhZ3JwCh9eY29tLmFwcGxlLnNhZmFyaS5jcmVkaXQtY2FyZHMkEgtDcmVkaXRDYXJkczIuCiEABCIdAgRhZ3JwChVeY29tLmFwcGxlLmNmbmV0d29yayQSCVBhc3N3b3JkczJtClwAAhIeAAQiGgIEdndodAoSXkFjY2Vzc29yeVBhaXJpbmckEhoABCIWAgR2d2h0Cg5eTmFub1JlZ2lzdHJ5JBIcAAQiGAIEdndodAoQXldhdGNoTWlncmF0aW9uJBINRGV2aWNlUGFpcmluZzIOCgIABhIIQmFja3N0b3A=",
                   plaintextPolicy: try! TPPolicyDocument(version: 5,
                                                          modelToCategory: [
                   policyData: "CAUSDgoGaVBob25lEgRmdWxsEgwKBGlQYWQSBGZ1bGwSDAoEaVBvZBIEZnVsbBILCgNNYWMSBGZ1bGwSDAoEaU1hYxIEZnVsbBINCgdBcHBsZVRWEgJ0dhIOCgVXYXRjaBIFd2F0Y2gSFwoOQXVkaW9BY2Nlc3NvcnkSBWF1ZGlvGhsKDEFwcGxpY2F0aW9ucxIEZnVsbBIFd2F0Y2gaHwoQU2VjdXJlT2JqZWN0U3luYxIEZnVsbBIFd2F0Y2gaHAoNRGV2aWNlUGFpcmluZxIEZnVsbBIFd2F0Y2gaGgoLQ3JlZGl0Q2FyZHMSBGZ1bGwSBXdhdGNoGhUKBkhlYWx0aBIEZnVsbBIFd2F0Y2gaLQoTTGltaXRlZFBlZXJzQWxsb3dlZBIEZnVsbBIFd2F0Y2gSAnR2EgVhdWRpbxokChVQcm90ZWN0ZWRDbG91ZFN0b3JhZ2USBGZ1bGwSBXdhdGNoGhcKCEFwcGxlUGF5EgRmdWxsEgV3YXRjaBoZCgpBdXRvVW5sb2NrEgRmdWxsEgV3YXRjaBoWCgdNYW5hdGVlEgRmdWxsEgV3YXRjaBoYCglQYXNzd29yZHMSBGZ1bGwSBXdhdGNoGhUKBkVuZ3JhbRIEZnVsbBIFd2F0Y2gaHgoEV2lGaRIEZnVsbBIFd2F0Y2gSAnR2EgVhdWRpbxoTCgRIb21lEgRmdWxsEgV3YXRjaCIbCgVhdWRpbxIEZnVsbBIFd2F0Y2gSBWF1ZGlvIhMKBGZ1bGwSBGZ1bGwSBXdhdGNoIhUKAnR2EgRmdWxsEgV3YXRjaBICdHYiFAoFd2F0Y2gSBGZ1bGwSBXdhdGNoMiIKFgAEIhICBHZ3aHQKCl5BcHBsZVBheSQSCEFwcGxlUGF5MiYKGAAEIhQCBHZ3aHQKDF5BdXRvVW5sb2NrJBIKQXV0b1VubG9jazIeChQABCIQAgR2d2h0CgheRW5ncmFtJBIGRW5ncmFtMh4KFAAEIhACBHZ3aHQKCF5IZWFsdGgkEgZIZWFsdGgyGgoSAAQiDgIEdndodAoGXkhvbWUkEgRIb21lMiAKFQAEIhECBHZ3aHQKCV5NYW5hdGVlJBIHTWFuYXRlZTI4CiEABCIdAgR2d2h0ChVeTGltaXRlZFBlZXJzQWxsb3dlZCQSE0xpbWl0ZWRQZWVyc0FsbG93ZWQyXQpQAAISHgAEIhoCBHZ3aHQKEl5Db250aW51aXR5VW5sb2NrJBIVAAQiEQIEdndodAoJXkhvbWVLaXQkEhUABCIRAgR2d2h0CgleQXBwbGVUViQSCU5vdFN5bmNlZDIrChsABCIXAgRhZ3JwCg9eWzAtOUEtWl17MTB9XC4SDEFwcGxpY2F0aW9uczLFAQqwAQACEjQAAQoTAAQiDwIFY2xhc3MKBl5nZW5wJAobAAQiFwIEYWdycAoPXmNvbS5hcHBsZS5zYmQkEj0AAQoTAAQiDwIFY2xhc3MKBl5rZXlzJAokAAQiIAIEYWdycAoYXmNvbS5hcHBsZS5zZWN1cml0eS5zb3MkEhkABCIVAgR2d2h0Cg1eQmFja3VwQmFnVjAkEhwABCIYAgR2d2h0ChBeaUNsb3VkSWRlbnRpdHkkEhBTZWN1cmVPYmplY3RTeW5jMmMKWwACEhIABCIOAgR2d2h0CgZeV2lGaSQSQwABChMABCIPAgVjbGFzcwoGXmdlbnAkChMABCIPAgRhZ3JwCgdeYXBwbGUkChUABCIRAgRzdmNlCgleQWlyUG9ydCQSBFdpRmkynQMKgwMAAhIYAAQiFAIEdndodAoMXlBDUy1CYWNrdXAkEhoABCIWAgR2d2h0Cg5eUENTLUNsb3VkS2l0JBIYAAQiFAIEdndodAoMXlBDUy1Fc2Nyb3ckEhUABCIRAgR2d2h0CgleUENTLUZERSQSGgAEIhYCBHZ3aHQKDl5QQ1MtRmVsZHNwYXIkEhoABCIWAgR2d2h0Cg5eUENTLU1haWxEcm9wJBIaAAQiFgIEdndodAoOXlBDUy1NYWlsZHJvcCQSGwAEIhcCBHZ3aHQKD15QQ1MtTWFzdGVyS2V5JBIXAAQiEwIEdndodAoLXlBDUy1Ob3RlcyQSGAAEIhQCBHZ3aHQKDF5QQ1MtUGhvdG9zJBIZAAQiFQIEdndodAoNXlBDUy1TaGFyaW5nJBIeAAQiGgIEdndodAoSXlBDUy1pQ2xvdWRCYWNrdXAkEh0ABCIZAgR2d2h0ChFeUENTLWlDbG91ZERyaXZlJBIaAAQiFgIEdndodAoOXlBDUy1pTWVzc2FnZSQSFVByb3RlY3RlZENsb3VkU3RvcmFnZTI6CisABCInAgRhZ3JwCh9eY29tLmFwcGxlLnNhZmFyaS5jcmVkaXQtY2FyZHMkEgtDcmVkaXRDYXJkczIuCiEABCIdAgRhZ3JwChVeY29tLmFwcGxlLmNmbmV0d29yayQSCVBhc3N3b3JkczJtClwAAhIeAAQiGgIEdndodAoSXkFjY2Vzc29yeVBhaXJpbmckEhoABCIWAgR2d2h0Cg5eTmFub1JlZ2lzdHJ5JBIcAAQiGAIEdndodAoQXldhdGNoTWlncmF0aW9uJBINRGV2aWNlUGFpcmluZzIOCgIABhIIQmFja3N0b3A=",
                   plaintextPolicy: try! TPPolicyDocument(version: 5,
                                                          modelToCategory: [
@@ -439,16 +437,142 @@ func builtInPolicyDocuments() -> [TPPolicyDocument] {
                     ],
                                                          hashAlgo: .SHA256)
         ),
                     ],
                                                          hashAlgo: .SHA256)
         ),
+
+        RawPolicy(version: TPPolicyVersion(version: 6, hash: "SHA256:L2Px1aYyR1tgChe8dIyTBSmCHCWEFJirZ3ELMFXz2PY="),
+                  policyData: "CAYSDgoGaVBob25lEgRmdWxsEgwKBGlQYWQSBGZ1bGwSDAoEaVBvZBIEZnVsbBILCgNNYWMSBGZ1bGwSDAoEaU1hYxIEZnVsbBINCgdBcHBsZVRWEgJ0dhIOCgVXYXRjaBIFd2F0Y2gSFwoOQXVkaW9BY2Nlc3NvcnkSBWF1ZGlvGhoKC0NyZWRpdENhcmRzEgRmdWxsEgV3YXRjaBofChBTZWN1cmVPYmplY3RTeW5jEgRmdWxsEgV3YXRjaBoVCgZIZWFsdGgSBGZ1bGwSBXdhdGNoGhkKCkF1dG9VbmxvY2sSBGZ1bGwSBXdhdGNoGh4KBFdpRmkSBGZ1bGwSBXdhdGNoEgJ0dhIFYXVkaW8aHgoESG9tZRIEZnVsbBIFd2F0Y2gSAnR2EgVhdWRpbxokChVQcm90ZWN0ZWRDbG91ZFN0b3JhZ2USBGZ1bGwSBXdhdGNoGi0KE0xpbWl0ZWRQZWVyc0FsbG93ZWQSBGZ1bGwSBXdhdGNoEgJ0dhIFYXVkaW8aGAoJUGFzc3dvcmRzEgRmdWxsEgV3YXRjaBobCgxBcHBsaWNhdGlvbnMSBGZ1bGwSBXdhdGNoGhwKDURldmljZVBhaXJpbmcSBGZ1bGwSBXdhdGNoGhcKCEFwcGxlUGF5EgRmdWxsEgV3YXRjaBoWCgdNYW5hdGVlEgRmdWxsEgV3YXRjaBoVCgZFbmdyYW0SBGZ1bGwSBXdhdGNoIhsKBWF1ZGlvEgRmdWxsEgV3YXRjaBIFYXVkaW8iFQoCdHYSBGZ1bGwSBXdhdGNoEgJ0diIUCgV3YXRjaBIEZnVsbBIFd2F0Y2giEwoEZnVsbBIEZnVsbBIFd2F0Y2gyIgoWAAQiEgIEdndodAoKXkFwcGxlUGF5JBIIQXBwbGVQYXkyJgoYAAQiFAIEdndodAoMXkF1dG9VbmxvY2skEgpBdXRvVW5sb2NrMh4KFAAEIhACBHZ3aHQKCF5FbmdyYW0kEgZFbmdyYW0yHgoUAAQiEAIEdndodAoIXkhlYWx0aCQSBkhlYWx0aDIaChIABCIOAgR2d2h0CgZeSG9tZSQSBEhvbWUyIAoVAAQiEQIEdndodAoJXk1hbmF0ZWUkEgdNYW5hdGVlMjgKIQAEIh0CBHZ3aHQKFV5MaW1pdGVkUGVlcnNBbGxvd2VkJBITTGltaXRlZFBlZXJzQWxsb3dlZDJdClAAAhIeAAQiGgIEdndodAoSXkNvbnRpbnVpdHlVbmxvY2skEhUABCIRAgR2d2h0CgleSG9tZUtpdCQSFQAEIhECBHZ3aHQKCV5BcHBsZVRWJBIJTm90U3luY2VkMisKGwAEIhcCBGFncnAKD15bMC05QS1aXXsxMH1cLhIMQXBwbGljYXRpb25zMsoBCrUBAAISNgABChMABCIPAgVjbGFzcwoGXmdlbnAkCh0ABCIZAgRhZ3JwChFeY29tXC5hcHBsZVwuc2JkJBJAAAEKEwAEIg8CBWNsYXNzCgZea2V5cyQKJwAEIiMCBGFncnAKG15jb21cLmFwcGxlXC5zZWN1cml0eVwuc29zJBIZAAQiFQIEdndodAoNXkJhY2t1cEJhZ1YwJBIcAAQiGAIEdndodAoQXmlDbG91ZElkZW50aXR5JBIQU2VjdXJlT2JqZWN0U3luYzJjClsAAhISAAQiDgIEdndodAoGXldpRmkkEkMAAQoTAAQiDwIFY2xhc3MKBl5nZW5wJAoTAAQiDwIEYWdycAoHXmFwcGxlJAoVAAQiEQIEc3ZjZQoJXkFpclBvcnQkEgRXaUZpMp0DCoMDAAISGAAEIhQCBHZ3aHQKDF5QQ1MtQmFja3VwJBIaAAQiFgIEdndodAoOXlBDUy1DbG91ZEtpdCQSGAAEIhQCBHZ3aHQKDF5QQ1MtRXNjcm93JBIVAAQiEQIEdndodAoJXlBDUy1GREUkEhoABCIWAgR2d2h0Cg5eUENTLUZlbGRzcGFyJBIaAAQiFgIEdndodAoOXlBDUy1NYWlsRHJvcCQSGgAEIhYCBHZ3aHQKDl5QQ1MtTWFpbGRyb3AkEhsABCIXAgR2d2h0Cg9eUENTLU1hc3RlcktleSQSFwAEIhMCBHZ3aHQKC15QQ1MtTm90ZXMkEhgABCIUAgR2d2h0CgxeUENTLVBob3RvcyQSGQAEIhUCBHZ3aHQKDV5QQ1MtU2hhcmluZyQSHgAEIhoCBHZ3aHQKEl5QQ1MtaUNsb3VkQmFja3VwJBIdAAQiGQIEdndodAoRXlBDUy1pQ2xvdWREcml2ZSQSGgAEIhYCBHZ3aHQKDl5QQ1MtaU1lc3NhZ2UkEhVQcm90ZWN0ZWRDbG91ZFN0b3JhZ2UyPQouAAQiKgIEYWdycAoiXmNvbVwuYXBwbGVcLnNhZmFyaVwuY3JlZGl0LWNhcmRzJBILQ3JlZGl0Q2FyZHMyMAojAAQiHwIEYWdycAoXXmNvbVwuYXBwbGVcLmNmbmV0d29yayQSCVBhc3N3b3JkczJtClwAAhIeAAQiGgIEdndodAoSXkFjY2Vzc29yeVBhaXJpbmckEhoABCIWAgR2d2h0Cg5eTmFub1JlZ2lzdHJ5JBIcAAQiGAIEdndodAoQXldhdGNoTWlncmF0aW9uJBINRGV2aWNlUGFpcmluZzIOCgIABhIIQmFja3N0b3A=",
+                  plaintextPolicy: try! TPPolicyDocument(version: 6,
+                                                         modelToCategory: [
+                                                            ["prefix": "iPhone", "category": "full"],
+                                                            ["prefix": "iPad", "category": "full"],
+                                                            ["prefix": "iPod", "category": "full"],
+                                                            ["prefix": "Mac", "category": "full"],
+                                                            ["prefix": "iMac", "category": "full"],
+                                                            ["prefix": "AppleTV", "category": "tv"],
+                                                            ["prefix": "Watch", "category": "watch"],
+                                                            ["prefix": "AudioAccessory", "category": "audio"],
+                    ],
+                                                         categoriesByView: [
+                                                            "AutoUnlock": ["full", "watch"],
+                                                            "ApplePay": ["full", "watch"],
+                                                            "Engram": ["full", "watch"],
+                                                            "Health": ["full", "watch"],
+                                                            "Home": ["full", "watch", "tv", "audio"],
+                                                            "LimitedPeersAllowed": ["full", "watch", "tv", "audio"],
+                                                            "Manatee": ["full", "watch"],
+                                                            "Applications": ["full", "watch"],
+                                                            "SecureObjectSync": ["full", "watch"],
+                                                            "WiFi": ["full", "watch", "tv", "audio"],
+                                                            "ProtectedCloudStorage": ["full", "watch"],
+                                                            "CreditCards": ["full", "watch"],
+                                                            "Passwords": ["full", "watch"],
+                                                            "DevicePairing": ["full", "watch"],
+                    ],
+                                                         introducersByCategory: [
+                                                            "full": ["full", "watch"],
+                                                            "watch": ["full", "watch"],
+                                                            "tv": ["full", "watch", "tv"],
+                                                            "audio": ["full", "watch", "audio"],
+                    ],
+                                                         redactions: [:],
+                                                         keyViewMapping: [
+                                                            TPPBPolicyKeyViewMapping(view: "ApplePay", matchingRule: TPDictionaryMatchingRule.fieldMatch("vwht", fieldRegex: "^ApplePay$")),
+                                                            TPPBPolicyKeyViewMapping(view: "AutoUnlock", matchingRule: TPDictionaryMatchingRule.fieldMatch("vwht", fieldRegex: "^AutoUnlock$")),
+                                                            TPPBPolicyKeyViewMapping(view: "Engram", matchingRule: TPDictionaryMatchingRule.fieldMatch("vwht", fieldRegex: "^Engram$")),
+                                                            TPPBPolicyKeyViewMapping(view: "Health", matchingRule: TPDictionaryMatchingRule.fieldMatch("vwht", fieldRegex: "^Health$")),
+                                                            TPPBPolicyKeyViewMapping(view: "Home", matchingRule: TPDictionaryMatchingRule.fieldMatch("vwht", fieldRegex: "^Home$")),
+                                                            TPPBPolicyKeyViewMapping(view: "Manatee", matchingRule: TPDictionaryMatchingRule.fieldMatch("vwht", fieldRegex: "^Manatee$")),
+                                                            TPPBPolicyKeyViewMapping(view: "LimitedPeersAllowed", matchingRule: TPDictionaryMatchingRule.fieldMatch("vwht", fieldRegex: "^LimitedPeersAllowed$")),
+
+                                                            // These items will not be synced by Octagon
+                                                            TPPBPolicyKeyViewMapping(view: "NotSynced", matchingRule:
+                                                                TPDictionaryMatchingRule.orMatch([
+                                                                    TPDictionaryMatchingRule.fieldMatch("vwht", fieldRegex: "^ContinuityUnlock$"),
+                                                                    TPDictionaryMatchingRule.fieldMatch("vwht", fieldRegex: "^HomeKit$"),
+                                                                    TPDictionaryMatchingRule.fieldMatch("vwht", fieldRegex: "^AppleTV$"),
+                                                                    ])),
+
+                                                            TPPBPolicyKeyViewMapping(view: "Applications", matchingRule:
+                                                                TPDictionaryMatchingRule.fieldMatch("agrp", fieldRegex: "^[0-9A-Z]{10}\\.")),
+
+                                                            TPPBPolicyKeyViewMapping(view: "SecureObjectSync", matchingRule:
+                                                                TPDictionaryMatchingRule.orMatch([
+                                                                    TPDictionaryMatchingRule.andMatch([
+                                                                        TPDictionaryMatchingRule.fieldMatch("class", fieldRegex: "^genp$"),
+                                                                        TPDictionaryMatchingRule.fieldMatch("agrp", fieldRegex: "^com\\.apple\\.sbd$"),
+                                                                        ]),
+                                                                    TPDictionaryMatchingRule.andMatch([
+                                                                        TPDictionaryMatchingRule.fieldMatch("class", fieldRegex: "^keys$"),
+                                                                        TPDictionaryMatchingRule.fieldMatch("agrp", fieldRegex: "^com\\.apple\\.security\\.sos$"),
+                                                                        ]),
+                                                                    TPDictionaryMatchingRule.fieldMatch("vwht", fieldRegex: "^BackupBagV0$"),
+                                                                    TPDictionaryMatchingRule.fieldMatch("vwht", fieldRegex: "^iCloudIdentity$"),
+                                                                    ])),
+
+                                                            TPPBPolicyKeyViewMapping(view: "WiFi", matchingRule:
+                                                                TPDictionaryMatchingRule.orMatch([
+                                                                    TPDictionaryMatchingRule.fieldMatch("vwht", fieldRegex: "^WiFi$"),
+                                                                    TPDictionaryMatchingRule.andMatch([
+                                                                        TPDictionaryMatchingRule.fieldMatch("class", fieldRegex: "^genp$"),
+                                                                        TPDictionaryMatchingRule.fieldMatch("agrp", fieldRegex: "^apple$"),
+                                                                        TPDictionaryMatchingRule.fieldMatch("svce", fieldRegex: "^AirPort$"),
+                                                                        ]),
+                                                                    ])),
+
+                                                            TPPBPolicyKeyViewMapping(view: "ProtectedCloudStorage", matchingRule:
+                                                                TPDictionaryMatchingRule.orMatch([
+                                                                    TPDictionaryMatchingRule.fieldMatch("vwht", fieldRegex: "^PCS-Backup$"),
+                                                                    TPDictionaryMatchingRule.fieldMatch("vwht", fieldRegex: "^PCS-CloudKit$"),
+                                                                    TPDictionaryMatchingRule.fieldMatch("vwht", fieldRegex: "^PCS-Escrow$"),
+                                                                    TPDictionaryMatchingRule.fieldMatch("vwht", fieldRegex: "^PCS-FDE$"),
+                                                                    TPDictionaryMatchingRule.fieldMatch("vwht", fieldRegex: "^PCS-Feldspar$"),
+                                                                    TPDictionaryMatchingRule.fieldMatch("vwht", fieldRegex: "^PCS-MailDrop$"),
+                                                                    TPDictionaryMatchingRule.fieldMatch("vwht", fieldRegex: "^PCS-Maildrop$"),
+                                                                    TPDictionaryMatchingRule.fieldMatch("vwht", fieldRegex: "^PCS-MasterKey$"),
+                                                                    TPDictionaryMatchingRule.fieldMatch("vwht", fieldRegex: "^PCS-Notes$"),
+                                                                    TPDictionaryMatchingRule.fieldMatch("vwht", fieldRegex: "^PCS-Photos$"),
+                                                                    TPDictionaryMatchingRule.fieldMatch("vwht", fieldRegex: "^PCS-Sharing$"),
+                                                                    TPDictionaryMatchingRule.fieldMatch("vwht", fieldRegex: "^PCS-iCloudBackup$"),
+                                                                    TPDictionaryMatchingRule.fieldMatch("vwht", fieldRegex: "^PCS-iCloudDrive$"),
+                                                                    TPDictionaryMatchingRule.fieldMatch("vwht", fieldRegex: "^PCS-iMessage$"),
+                                                                    ])),
+
+                                                            TPPBPolicyKeyViewMapping(view: "CreditCards",
+                                                                                     matchingRule: TPDictionaryMatchingRule.fieldMatch("agrp", fieldRegex: "^com\\.apple\\.safari\\.credit-cards$")),
+
+                                                            TPPBPolicyKeyViewMapping(view: "Passwords",
+                                                                                     matchingRule: TPDictionaryMatchingRule.fieldMatch("agrp", fieldRegex: "^com\\.apple\\.cfnetwork$")),
+
+                                                            TPPBPolicyKeyViewMapping(view: "DevicePairing", matchingRule:
+                                                                TPDictionaryMatchingRule.orMatch([
+                                                                    TPDictionaryMatchingRule.fieldMatch("vwht", fieldRegex: "^AccessoryPairing$"),
+                                                                    TPDictionaryMatchingRule.fieldMatch("vwht", fieldRegex: "^NanoRegistry$"),
+                                                                    TPDictionaryMatchingRule.fieldMatch("vwht", fieldRegex: "^WatchMigration$"),
+                                                                    ])),
+
+                                                            TPPBPolicyKeyViewMapping(view: "Backstop", matchingRule:
+                                                                TPDictionaryMatchingRule.trueMatch()),
+                    ],
+                                                         hashAlgo: .SHA256)
+        ),
             ]
             ]
+    // swiftlint:enable force_try
 
 
-    assert(rawPolicies.filter { prevailingPolicyVersion == $0.policyVersion }.count == 1)
+    assert(rawPolicies.filter { prevailingPolicyVersion.versionNumber == $0.version.versionNumber }.count == 1)
 
     return rawPolicies.map { raw in
         let data = Data(base64Encoded: raw.policyData)!
 
     return rawPolicies.map { raw in
         let data = Data(base64Encoded: raw.policyData)!
-        let doc = TPPolicyDocument.policyDoc(withHash: raw.policyHash, data: data)!
-        assert(doc.policyVersion == raw.policyVersion)
-        if raw.policyVersion == prevailingPolicyVersion {
-           assert(prevailingPolicyHash == raw.policyHash)
+        let doc = TPPolicyDocument.policyDoc(withHash: raw.version.policyHash, data: data)!
+
+        if(!doc.isEqual(to: raw.plaintextPolicy)) {
+            let bodyData = raw.plaintextPolicy.protobuf
+            let bodyBase64 = bodyData.base64EncodedString()
+            let hash = TPHashBuilder.hash(with: .SHA256, of: bodyData)
+            os_log("raw policy doesn't match encoded bytes, new hash would be: %{public}@ new data: %{public}@", log: tplogDebug, hash, bodyBase64)
+        }
+
+        assert(doc.version.versionNumber == raw.version.versionNumber)
+        if raw.version.versionNumber == prevailingPolicyVersion.versionNumber {
+            assert(prevailingPolicyVersion.policyHash == raw.version.policyHash)
         }
         assert(doc.isEqual(to: raw.plaintextPolicy))
         return doc
         }
         assert(doc.isEqual(to: raw.plaintextPolicy))
         return doc
index 1ab70461d4d560995036f0b764b5e299eac806e6..5499b200bca7feb5ae8cd0e62f96ac8d9e4f18ed 100644 (file)
@@ -27,7 +27,7 @@ import SecurityFoundation
 let OT_RECOVERY_SIGNING_HKDF_SIZE = 56
 let OT_RECOVERY_ENCRYPTION_HKDF_SIZE = 56
 
 let OT_RECOVERY_SIGNING_HKDF_SIZE = 56
 let OT_RECOVERY_ENCRYPTION_HKDF_SIZE = 56
 
-enum recoveryKeyType: Int {
+enum RecoveryKeyType: Int {
     case kOTRecoveryKeySigning = 1
     case kOTRecoveryKeyEncryption = 2
 }
     case kOTRecoveryKeySigning = 1
     case kOTRecoveryKeyEncryption = 2
 }
@@ -43,10 +43,10 @@ class RecoveryKeySet: NSObject {
         self.secret = secret
         self.recoverySalt = recoverySalt
 
         self.secret = secret
         self.recoverySalt = recoverySalt
 
-        let encryptionKeyData = try RecoveryKeySet.generateRecoveryKey(keyType: recoveryKeyType.kOTRecoveryKeyEncryption, masterSecret: secret, recoverySalt: recoverySalt)
+        let encryptionKeyData = try RecoveryKeySet.generateRecoveryKey(keyType: RecoveryKeyType.kOTRecoveryKeyEncryption, masterSecret: secret, recoverySalt: recoverySalt)
         self.encryptionKey = _SFECKeyPair.init(secKey: try RecoveryKeySet.createSecKey(keyData: encryptionKeyData))
 
         self.encryptionKey = _SFECKeyPair.init(secKey: try RecoveryKeySet.createSecKey(keyData: encryptionKeyData))
 
-        let signingKeyData = try RecoveryKeySet.generateRecoveryKey(keyType: recoveryKeyType.kOTRecoveryKeySigning, masterSecret: secret, recoverySalt: recoverySalt)
+        let signingKeyData = try RecoveryKeySet.generateRecoveryKey(keyType: RecoveryKeyType.kOTRecoveryKeySigning, masterSecret: secret, recoverySalt: recoverySalt)
         self.signingKey = _SFECKeyPair.init(secKey: try RecoveryKeySet.createSecKey(keyData: signingKeyData))
 
         let RecoverySigningPubKeyHash = try RecoveryKeySet.hashRecoveryedSigningPublicKey(keyData: self.signingKey.publicKey().spki())
         self.signingKey = _SFECKeyPair.init(secKey: try RecoveryKeySet.createSecKey(keyData: signingKeyData))
 
         let RecoverySigningPubKeyHash = try RecoveryKeySet.hashRecoveryedSigningPublicKey(keyData: self.signingKey.publicKey().spki())
@@ -58,27 +58,25 @@ class RecoveryKeySet: NSObject {
         return  SecRKCreateRecoveryKeyString(nil) as String
     }
 
         return  SecRKCreateRecoveryKeyString(nil) as String
     }
 
-    class func generateRecoveryKey(keyType: recoveryKeyType, masterSecret: Data, recoverySalt: String) throws -> (Data) {
+    class func generateRecoveryKey(keyType: RecoveryKeyType, masterSecret: Data, recoverySalt: String) throws -> (Data) {
         var keyLength: Int
         var info: Data
         var derivedKey: Data
         var finalKey = Data()
 
         switch keyType {
         var keyLength: Int
         var info: Data
         var derivedKey: Data
         var finalKey = Data()
 
         switch keyType {
-        case recoveryKeyType.kOTRecoveryKeyEncryption:
+        case RecoveryKeyType.kOTRecoveryKeyEncryption:
             keyLength = OT_RECOVERY_ENCRYPTION_HKDF_SIZE
 
             let infoString = Array("Recovery Encryption Private Key".utf8)
             info = Data(bytes: infoString, count: infoString.count)
 
             keyLength = OT_RECOVERY_ENCRYPTION_HKDF_SIZE
 
             let infoString = Array("Recovery Encryption Private Key".utf8)
             info = Data(bytes: infoString, count: infoString.count)
 
-            break
-        case recoveryKeyType.kOTRecoveryKeySigning:
+        case RecoveryKeyType.kOTRecoveryKeySigning:
             keyLength = OT_RECOVERY_SIGNING_HKDF_SIZE
 
             let infoString = Array("Recovery Signing Private Key".utf8)
             info = Data(bytes: infoString, count: infoString.count)
 
             keyLength = OT_RECOVERY_SIGNING_HKDF_SIZE
 
             let infoString = Array("Recovery Signing Private Key".utf8)
             info = Data(bytes: infoString, count: infoString.count)
 
-            break
         }
 
         guard let cp = ccec_cp_384() else {
         }
 
         guard let cp = ccec_cp_384() else {
@@ -108,7 +106,7 @@ class RecoveryKeySet: NSObject {
                             throw RecoveryKeySetError.corecryptoKeyGeneration(corecryptoError: status)
                         }
 
                             throw RecoveryKeySetError.corecryptoKeyGeneration(corecryptoError: status)
                         }
 
-                        if(keyType == recoveryKeyType.kOTRecoveryKeyEncryption || keyType == recoveryKeyType.kOTRecoveryKeySigning) {
+                        if keyType == RecoveryKeyType.kOTRecoveryKeyEncryption || keyType == RecoveryKeyType.kOTRecoveryKeySigning {
                             status = ccec_generate_key_deterministic(cp,
                                                                      derivedKeyBytes.count, derivedKeyBytes.bindMemory(to: UInt8.self).baseAddress!,
                                                                      ccDRBGGetRngState(),
                             status = ccec_generate_key_deterministic(cp,
                                                                      derivedKeyBytes.count, derivedKeyBytes.bindMemory(to: UInt8.self).baseAddress!,
                                                                      ccDRBGGetRngState(),
@@ -143,7 +141,7 @@ class RecoveryKeySet: NSObject {
         return key
     }
 
         return key
     }
 
-    class func setKeyMaterialInKeychain(query: Dictionary<CFString, Any>) throws -> (Bool) {
+    class func setKeyMaterialInKeychain(query: [CFString: Any]) throws -> (Bool) {
         var result = false
 
         var results: CFTypeRef?
         var result = false
 
         var results: CFTypeRef?
@@ -152,7 +150,7 @@ class RecoveryKeySet: NSObject {
         if status == errSecSuccess {
             result = true
         } else if status == errSecDuplicateItem {
         if status == errSecSuccess {
             result = true
         } else if status == errSecDuplicateItem {
-            var updateQuery: Dictionary<CFString, Any> = query
+            var updateQuery: [CFString: Any] = query
             updateQuery[kSecClass] = nil
 
             status = SecItemUpdate(query as CFDictionary, updateQuery as CFDictionary)
             updateQuery[kSecClass] = nil
 
             status = SecItemUpdate(query as CFDictionary, updateQuery as CFDictionary)
@@ -213,8 +211,8 @@ class RecoveryKeySet: NSObject {
         return try RecoveryKeySet.setKeyMaterialInKeychain(query: query)
     }
 
         return try RecoveryKeySet.setKeyMaterialInKeychain(query: query)
     }
 
-    class func retrieveRecoveryKeysFromKeychain(label: String) throws -> [Dictionary <CFString, Any>]? {
-        var keySet: [Dictionary<CFString, Any>]?
+    class func retrieveRecoveryKeysFromKeychain(label: String) throws -> [ [CFString: Any]]? {
+        var keySet: [[CFString: Any]]?
 
         let query: [CFString: Any] = [
             kSecClass: kSecClassKey,
 
         let query: [CFString: Any] = [
             kSecClass: kSecClassKey,
@@ -234,10 +232,10 @@ class RecoveryKeySet: NSObject {
         }
 
         if result != nil {
         }
 
         if result != nil {
-            if let dictionaryArray = result as? [Dictionary<CFString, Any>] {
+            if let dictionaryArray = result as? [[CFString: Any]] {
                 keySet = dictionaryArray
             } else {
                 keySet = dictionaryArray
             } else {
-                if let dictionary = result as? Dictionary<CFString, Any> {
+                if let dictionary = result as? [CFString: Any] {
                     keySet = [dictionary]
                 } else {
                     keySet = nil
                     keySet = [dictionary]
                 } else {
                     keySet = nil
index 2529be279598ecb954d066774da9b1ed56374e8f..e670dbde94098c247980bedb8da9484eee06cf0a 100644 (file)
@@ -13,7 +13,9 @@ class SetValueTransformer: ValueTransformer {
 
     override func transformedValue(_ value: Any?) -> Any? {
         do {
 
     override func transformedValue(_ value: Any?) -> Any? {
         do {
-            guard let value = value else { return nil }
+            guard let value = value else {
+                return nil
+            }
             return try NSKeyedArchiver.archivedData(withRootObject: value, requiringSecureCoding: true)
         } catch {
             os_log("Failed to serialize a Set: %@", log: tplogDebug, type: .default, error as CVarArg)
             return try NSKeyedArchiver.archivedData(withRootObject: value, requiringSecureCoding: true)
         } catch {
             os_log("Failed to serialize a Set: %@", log: tplogDebug, type: .default, error as CVarArg)
@@ -23,8 +25,12 @@ class SetValueTransformer: ValueTransformer {
 
     override func reverseTransformedValue(_ value: Any?) -> Any? {
         do {
 
     override func reverseTransformedValue(_ value: Any?) -> Any? {
         do {
-            guard let dataOp = value as? Data? else { return nil }
-            guard let data = dataOp else { return nil }
+            guard let dataOp = value as? Data? else {
+                return nil
+            }
+            guard let data = dataOp else {
+                return nil
+            }
 
             let unarchiver = try NSKeyedUnarchiver(forReadingFrom: data)
             return unarchiver.decodeObject(of: [NSSet.self], forKey: NSKeyedArchiveRootObjectKey)
 
             let unarchiver = try NSKeyedUnarchiver(forReadingFrom: data)
             return unarchiver.decodeObject(of: [NSSet.self], forKey: NSKeyedArchiveRootObjectKey)
index 34e02638b8f9587eb2d1f4e8efea6e61e68709bf..83d8766c0dc8d42c737c88d8184031e90847be25 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
 <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
-<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="14886.2" systemVersion="19A541" minimumToolsVersion="Automatic" sourceLanguage="Swift" userDefinedModelVersionIdentifier="">
+<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="17026" systemVersion="19E219" minimumToolsVersion="Automatic" sourceLanguage="Swift" userDefinedModelVersionIdentifier="">
     <entity name="Bottle" representedClassName="BottleMO" syncable="YES" codeGenerationType="class">
         <attribute name="bottleID" optional="YES" attributeType="String"/>
         <attribute name="contents" optional="YES" attributeType="Binary"/>
     <entity name="Bottle" representedClassName="BottleMO" syncable="YES" codeGenerationType="class">
         <attribute name="bottleID" optional="YES" attributeType="String"/>
         <attribute name="contents" optional="YES" attributeType="Binary"/>
         <attribute name="egoPeerPermanentInfoSig" optional="YES" attributeType="Binary"/>
         <attribute name="egoPeerStableInfo" optional="YES" attributeType="Binary"/>
         <attribute name="egoPeerStableInfoSig" optional="YES" attributeType="Binary"/>
         <attribute name="egoPeerPermanentInfoSig" optional="YES" attributeType="Binary"/>
         <attribute name="egoPeerStableInfo" optional="YES" attributeType="Binary"/>
         <attribute name="egoPeerStableInfoSig" optional="YES" attributeType="Binary"/>
+        <attribute name="honorIDMSListChanges" optional="YES" attributeType="String" defaultValueString="UNKNOWN"/>
         <attribute name="moreChanges" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
         <attribute name="name" optional="YES" attributeType="String"/>
         <attribute name="moreChanges" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
         <attribute name="name" optional="YES" attributeType="String"/>
+        <attribute name="recoveryKeyEncryptionSPKI" optional="YES" attributeType="Binary"/>
+        <attribute name="recoveryKeySigningSPKI" optional="YES" attributeType="Binary"/>
         <relationship name="bottles" optional="YES" toMany="YES" deletionRule="Nullify" destinationEntity="Bottle" inverseName="container" inverseEntity="Bottle"/>
         <relationship name="machines" optional="YES" toMany="YES" deletionRule="Nullify" destinationEntity="Machine" inverseName="container" inverseEntity="Machine"/>
         <relationship name="peers" optional="YES" toMany="YES" deletionRule="Cascade" destinationEntity="Peer" inverseName="container" inverseEntity="Peer"/>
         <relationship name="bottles" optional="YES" toMany="YES" deletionRule="Nullify" destinationEntity="Bottle" inverseName="container" inverseEntity="Bottle"/>
         <relationship name="machines" optional="YES" toMany="YES" deletionRule="Nullify" destinationEntity="Machine" inverseName="container" inverseEntity="Machine"/>
         <relationship name="peers" optional="YES" toMany="YES" deletionRule="Cascade" destinationEntity="Peer" inverseName="container" inverseEntity="Peer"/>
     </entity>
     <elements>
         <element name="Bottle" positionX="-549" positionY="-234" width="128" height="150"/>
     </entity>
     <elements>
         <element name="Bottle" positionX="-549" positionY="-234" width="128" height="150"/>
-        <element name="Container" positionX="-758" positionY="-261" width="128" height="253"/>
+        <element name="Container" positionX="-758" positionY="-261" width="128" height="268"/>
         <element name="Machine" positionX="-549" positionY="-234" width="128" height="133"/>
         <element name="Peer" positionX="-414" positionY="-387" width="128" height="210"/>
         <element name="Policy" positionX="-414" positionY="54" width="128" height="105"/>
         <element name="RecoveryVoucher" positionX="-65" positionY="-297" width="128" height="105"/>
         <element name="Voucher" positionX="-65" positionY="-171" width="128" height="90"/>
     </elements>
         <element name="Machine" positionX="-549" positionY="-234" width="128" height="133"/>
         <element name="Peer" positionX="-414" positionY="-387" width="128" height="210"/>
         <element name="Policy" positionX="-414" positionY="54" width="128" height="105"/>
         <element name="RecoveryVoucher" positionX="-65" positionY="-297" width="128" height="105"/>
         <element name="Voucher" positionX="-65" positionY="-171" width="128" height="90"/>
     </elements>
-</model>
\ No newline at end of file
+</model>
index 197d419b45a32cbddf45517a75437ebf904b0e53..0f37bed20f8510afabe30b86985894c73dc56069 100644 (file)
@@ -129,6 +129,7 @@ NS_ASSUME_NONNULL_BEGIN
 - (void)setAllowedMachineIDsWithContainer:(NSString *)container
                                   context:(NSString *)context
                         allowedMachineIDs:(NSSet<NSString*> *)allowedMachineIDs
 - (void)setAllowedMachineIDsWithContainer:(NSString *)container
                                   context:(NSString *)context
                         allowedMachineIDs:(NSSet<NSString*> *)allowedMachineIDs
+                            honorIDMSListChanges:(BOOL)honorIDMSListChanges
                                     reply:(void (^)(BOOL listDifferences, NSError * _Nullable error))reply;
 
 - (void)addAllowedMachineIDsWithContainer:(NSString *)container
                                     reply:(void (^)(BOOL listDifferences, NSError * _Nullable error))reply;
 
 - (void)addAllowedMachineIDsWithContainer:(NSString *)container
@@ -160,7 +161,7 @@ NS_ASSUME_NONNULL_BEGIN
                   deviceName:(nullable NSString*)deviceName
                 serialNumber:(NSString *)serialNumber
                    osVersion:(NSString *)osVersion
                   deviceName:(nullable NSString*)deviceName
                 serialNumber:(NSString *)serialNumber
                    osVersion:(NSString *)osVersion
-               policyVersion:(nullable NSNumber *)policyVersion
+               policyVersion:(nullable TPPolicyVersion *)policyVersion
                policySecrets:(nullable NSDictionary<NSString*,NSData*> *)policySecrets
  signingPrivKeyPersistentRef:(nullable NSData *)spkPr
      encPrivKeyPersistentRef:(nullable NSData*)epkPr
                policySecrets:(nullable NSDictionary<NSString*,NSData*> *)policySecrets
  signingPrivKeyPersistentRef:(nullable NSData *)spkPr
      encPrivKeyPersistentRef:(nullable NSData*)epkPr
@@ -169,6 +170,8 @@ NS_ASSUME_NONNULL_BEGIN
                                        NSData * _Nullable permanentInfoSig,
                                        NSData * _Nullable stableInfo,
                                        NSData * _Nullable stableInfoSig,
                                        NSData * _Nullable permanentInfoSig,
                                        NSData * _Nullable stableInfo,
                                        NSData * _Nullable stableInfoSig,
+                                       NSSet<NSString*>* _Nullable syncingViewList,
+                                       TPPolicy* _Nullable syncingPolicy,
                                        NSError * _Nullable error))reply;
 
 // If there already are existing CKKSViews, please pass in their key sets anyway.
                                        NSError * _Nullable error))reply;
 
 // If there already are existing CKKSViews, please pass in their key sets anyway.
@@ -196,12 +199,15 @@ NS_ASSUME_NONNULL_BEGIN
                                      NSData * _Nullable voucherSig,
                                      NSError * _Nullable error))reply;
 
                                      NSData * _Nullable voucherSig,
                                      NSError * _Nullable error))reply;
 
-// Preflighting a vouch will return the peer ID associated with the bottle you will be recovering.
+// Preflighting a vouch will return the peer ID associated with the bottle you will be recovering, as well as
+// the syncing policy used by that peer, and,
 // You can then use that peer ID to filter the tlkshares provided to vouchWithBottle.
 - (void)preflightVouchWithBottleWithContainer:(NSString *)container
                                       context:(NSString *)context
                                      bottleID:(NSString*)bottleID
                                         reply:(void (^)(NSString* _Nullable peerID,
 // You can then use that peer ID to filter the tlkshares provided to vouchWithBottle.
 - (void)preflightVouchWithBottleWithContainer:(NSString *)container
                                       context:(NSString *)context
                                      bottleID:(NSString*)bottleID
                                         reply:(void (^)(NSString* _Nullable peerID,
+                                                        NSSet<NSString*>* _Nullable peerSyncingViewList,
+                                                        TPPolicy * _Nullable peerSyncingPolicy,
                                                         NSError * _Nullable error))reply;
 
 // Returns a voucher for our own identity, created by the identity inside this bottle
                                                         NSError * _Nullable error))reply;
 
 // Returns a voucher for our own identity, created by the identity inside this bottle
@@ -213,8 +219,21 @@ NS_ASSUME_NONNULL_BEGIN
                            tlkShares:(NSArray<CKKSTLKShare*> *)tlkShares
                                reply:(void (^)(NSData * _Nullable voucher,
                                                NSData * _Nullable voucherSig,
                            tlkShares:(NSArray<CKKSTLKShare*> *)tlkShares
                                reply:(void (^)(NSData * _Nullable voucher,
                                                NSData * _Nullable voucherSig,
+                                               int64_t uniqueTLKsRecovered,
+                                               int64_t totalTLKSharesRecovered,
                                                NSError * _Nullable error))reply;
 
                                                NSError * _Nullable error))reply;
 
+// Preflighting a vouch will return the RK ID, view list and policy associated with the RK you will be recovering.
+// You can then use that peer ID to filter the tlkshares provided to vouchWithRecoveryKey.
+- (void)preflightVouchWithRecoveryKeyWithContainer:(NSString*)container
+                                           context:(NSString*)context
+                                       recoveryKey:(NSString*)recoveryKey
+                                              salt:(NSString*)salt
+                                             reply:(void (^)(NSString* _Nullable recoveryKeyID,
+                                                             NSSet<NSString*>* _Nullable peerSyncingViewList,
+                                                             TPPolicy * _Nullable peerSyncingPolicy,
+                                                             NSError * _Nullable error))reply;
+
 // Returns a voucher for our own identity, using recovery key
 - (void)vouchWithRecoveryKeyWithContainer:(NSString *)container
                                   context:(NSString *)context
 // Returns a voucher for our own identity, using recovery key
 - (void)vouchWithRecoveryKeyWithContainer:(NSString *)container
                                   context:(NSString *)context
@@ -237,6 +256,8 @@ NS_ASSUME_NONNULL_BEGIN
           preapprovedKeys:(NSArray<NSData*> *)preapprovedKeys
                     reply:(void (^)(NSString * _Nullable peerID,
                                     NSArray<CKRecord*>* _Nullable keyHierarchyRecords,
           preapprovedKeys:(NSArray<NSData*> *)preapprovedKeys
                     reply:(void (^)(NSString * _Nullable peerID,
                                     NSArray<CKRecord*>* _Nullable keyHierarchyRecords,
+                                    NSSet<NSString*>* _Nullable syncingViewList,
+                                    TPPolicy* _Nullable syncingPolicy,
                                     NSError * _Nullable error))reply;
 
 // Preflighting a preapproved join suggests whether or not you expect to succeed in an immediate preapprovedJoin() call
                                     NSError * _Nullable error))reply;
 
 // Preflighting a preapproved join suggests whether or not you expect to succeed in an immediate preapprovedJoin() call
@@ -258,6 +279,8 @@ NS_ASSUME_NONNULL_BEGIN
                             preapprovedKeys:(NSArray<NSData*> *)preapprovedKeys
                                       reply:(void (^)(NSString * _Nullable peerID,
                                                       NSArray<CKRecord*>* _Nullable keyHierarchyRecords,
                             preapprovedKeys:(NSArray<NSData*> *)preapprovedKeys
                                       reply:(void (^)(NSString * _Nullable peerID,
                                                       NSArray<CKRecord*>* _Nullable keyHierarchyRecords,
+                                                      NSSet<NSString*>* _Nullable syncingViewList,
+                                                      TPPolicy* _Nullable syncingPolicy,
                                                       NSError * _Nullable error))reply;
 
 // TODO: if the new policy causes someone to lose access to a view, how should this API work?
                                                       NSError * _Nullable error))reply;
 
 // TODO: if the new policy causes someone to lose access to a view, how should this API work?
@@ -273,7 +296,7 @@ NS_ASSUME_NONNULL_BEGIN
 - (void)setPreapprovedKeysWithContainer:(NSString *)container
                                 context:(NSString *)context
                         preapprovedKeys:(NSArray<NSData*> *)preapprovedKeys
 - (void)setPreapprovedKeysWithContainer:(NSString *)container
                                 context:(NSString *)context
                         preapprovedKeys:(NSArray<NSData*> *)preapprovedKeys
-                                  reply:(void (^)(NSError * _Nullable error))reply;
+                                  reply:(void (^)(TrustedPeersHelperPeerState* _Nullable peerState, NSError * _Nullable error))reply;
 
 /* Rather thin pass-through for uploading new TLKs (for zones which may have disappeared) */
 - (void)updateTLKsWithContainer:(NSString *)container
 
 /* Rather thin pass-through for uploading new TLKs (for zones which may have disappeared) */
 - (void)updateTLKsWithContainer:(NSString *)container
@@ -293,19 +316,18 @@ NS_ASSUME_NONNULL_BEGIN
                                                    NSData* _Nullable signingPublicKey,
                                                    NSError* _Nullable error))reply;
 
                                                    NSData* _Nullable signingPublicKey,
                                                    NSError* _Nullable error))reply;
 
-// The argument contains N [version:hash] keys,
-// the reply block contains 0<=N [version:[hash, data]] entries.
 - (void)fetchPolicyDocumentsWithContainer:(NSString*)container
                                   context:(NSString*)context
 - (void)fetchPolicyDocumentsWithContainer:(NSString*)container
                                   context:(NSString*)context
-                                     keys:(NSDictionary<NSNumber*,NSString*>*)keys
-                                    reply:(void (^)(NSDictionary<NSNumber*,NSArray<NSString*>*>* _Nullable entries,
+                                 versions:(NSSet<TPPolicyVersion*>*)versions
+                                    reply:(void (^)(NSDictionary<TPPolicyVersion*, NSData*>* _Nullable entries,
                                                     NSError * _Nullable error))reply;
 
                                                     NSError * _Nullable error))reply;
 
-// Fetch the policy for current peer.
-- (void)fetchPolicyWithContainer:(NSString*)container
-                         context:(NSString*)context
-                         reply:(void (^)(TPPolicy * _Nullable policy,
-                                         NSError * _Nullable error))reply;
+// Fetch the policy and view list for current peer.
+- (void)fetchCurrentPolicyWithContainer:(NSString*)container
+                                context:(NSString*)context
+                                  reply:(void (^)(NSSet<NSString*>* _Nullable syncingViewList,
+                                                  TPPolicy * _Nullable syncingPolicy,
+                                                  NSError * _Nullable error))reply;
 
 - (void)validatePeersWithContainer:(NSString *)container
                            context:(NSString *)context
 
 - (void)validatePeersWithContainer:(NSString *)container
                            context:(NSString *)context
@@ -336,15 +358,10 @@ NS_ASSUME_NONNULL_BEGIN
                                context:(NSString *)context
                                  reply:(void (^)(NSError* _Nullable error))reply;
 
                                context:(NSString *)context
                                  reply:(void (^)(NSError* _Nullable error))reply;
 
-- (void)getViewsWithContainer:(NSString *)container
-                      context:(NSString *)context
-                     inViews:(NSArray<NSString*>*)inViews
-                     reply:(void (^)(NSArray<NSString*>* _Nullable, NSError* _Nullable))reply;
-
 - (void)requestHealthCheckWithContainer:(NSString *)container
                                 context:(NSString *)context
                     requiresEscrowCheck:(BOOL)requiresEscrowCheck
 - (void)requestHealthCheckWithContainer:(NSString *)container
                                 context:(NSString *)context
                     requiresEscrowCheck:(BOOL)requiresEscrowCheck
-                                  reply:(void (^)(BOOL postRepairCFU, BOOL postEscrowCFU, BOOL resetOctagon, NSError* _Nullable))reply;
+                                  reply:(void (^)(BOOL postRepairCFU, BOOL postEscrowCFU, BOOL resetOctagon, BOOL leaveTrust, NSError* _Nullable))reply;
 
 - (void)getSupportAppInfoWithContainer:(NSString *)container
                                context:(NSString *)context
 
 - (void)getSupportAppInfoWithContainer:(NSString *)container
                                context:(NSString *)context
index 6603111379090d2d5cdcb5beacfff0826c49d64c..fb2f1557e1c331a54c1da269a496b85019ca8a4c 100644 (file)
@@ -95,9 +95,9 @@ NSXPCInterface* TrustedPeersHelperSetupProtocol(NSXPCInterface* interface)
                                                                      tlkShares:
                                                                      reply:) argumentIndex:4 ofReply:NO];
 
                                                                      tlkShares:
                                                                      reply:) argumentIndex:4 ofReply:NO];
 
-        [interface setClasses:[NSSet setWithObject:[TPPolicy class]] forSelector:@selector(fetchPolicyWithContainer:
+        [interface setClasses:[NSSet setWithObject:[TPPolicy class]] forSelector:@selector(fetchCurrentPolicyWithContainer:
                                                                                            context:
                                                                                            context:
-                                                                                           reply:) argumentIndex:0 ofReply:YES];
+                                                                                           reply:) argumentIndex:1 ofReply:YES];
 
         [interface setClasses:trustedPeersHelperPeerState forSelector:@selector(updateWithContainer:
                                                                                 context:
 
         [interface setClasses:trustedPeersHelperPeerState forSelector:@selector(updateWithContainer:
                                                                                 context:
index 7111e5b917ae95e42fe125cee14b3793817daa93..9bfa5fb666d17d0897158c518473aa867b590487 100644 (file)
@@ -30,23 +30,23 @@ class ServiceDelegate: NSObject, NSXPCListenerDelegate {
     func listener(_ listener: NSXPCListener, shouldAcceptNewConnection newConnection: NSXPCConnection) -> Bool {
         let tphEntitlement = "com.apple.private.trustedpeershelper.client"
 
     func listener(_ listener: NSXPCListener, shouldAcceptNewConnection newConnection: NSXPCConnection) -> Bool {
         let tphEntitlement = "com.apple.private.trustedpeershelper.client"
 
-        os_log("Received a new client: %@", log: tplogDebug, type: .default, newConnection)
+        os_log("Received a new client: %{public}@", log: tplogDebug, type: .default, newConnection)
         switch newConnection.value(forEntitlement: tphEntitlement) {
         case 1 as Int:
         switch newConnection.value(forEntitlement: tphEntitlement) {
         case 1 as Int:
-            os_log("client has entitlement '%@'", log: tplogDebug, type: .default, tphEntitlement)
+            os_log("client has entitlement '%{public}@'", log: tplogDebug, type: .default, tphEntitlement)
         case true as Bool:
         case true as Bool:
-            os_log("client has entitlement '%@'", log: tplogDebug, type: .default, tphEntitlement)
+            os_log("client has entitlement '%{public}@'", log: tplogDebug, type: .default, tphEntitlement)
 
         case let someInt as Int:
 
         case let someInt as Int:
-            os_log("client(%@) has wrong integer value for '%@' (%d), rejecting", log: tplogDebug, type: .default, newConnection, tphEntitlement, someInt)
+            os_log("client(%{public}@) has wrong integer value for '%{public}@' (%d), rejecting", log: tplogDebug, type: .default, newConnection, tphEntitlement, someInt)
             return false
 
         case let someBool as Bool:
             return false
 
         case let someBool as Bool:
-            os_log("client(%@) has wrong boolean value for '%@' (%d), rejecting", log: tplogDebug, type: .default, newConnection, tphEntitlement, someBool)
+            os_log("client(%{public}@) has wrong boolean value for '%{public}@' (%d), rejecting", log: tplogDebug, type: .default, newConnection, tphEntitlement, someBool)
             return false
 
         default:
             return false
 
         default:
-            os_log("client(%@) is missing entitlement '%@', rejecting", log: tplogDebug, type: .default, newConnection, tphEntitlement)
+            os_log("client(%{public}@) is missing entitlement '%{public}@', rejecting", log: tplogDebug, type: .default, newConnection, tphEntitlement)
             return false
         }
 
             return false
         }
 
diff --git a/keychain/TrustedPeersHelperUnitTests/.swiftlint.yml b/keychain/TrustedPeersHelperUnitTests/.swiftlint.yml
new file mode 100644 (file)
index 0000000..9ddd0d1
--- /dev/null
@@ -0,0 +1,3 @@
+disabled_rules:
+    - force_cast
+    - force_try
index f3b3eb19a5c82ba4a1f2dcaf7953690ee10a3f20..655bfdf8cab1bde89a6a3874315d47a7736d1246 100644 (file)
@@ -52,13 +52,15 @@ extension Container {
                      deviceName: String = "test device name",
                      serialNumber: String = "456",
                      osVersion: String = "123",
                      deviceName: String = "test device name",
                      serialNumber: String = "456",
                      osVersion: String = "123",
-                     policyVersion: UInt64? = nil,
+                     policyVersion: TPPolicyVersion? = nil,
                      policySecrets: [String: Data]? = nil,
                      signingPrivateKeyPersistentRef: Data? = nil,
                      encryptionPrivateKeyPersistentRef: Data? = nil
                      policySecrets: [String: Data]? = nil,
                      signingPrivateKeyPersistentRef: Data? = nil,
                      encryptionPrivateKeyPersistentRef: Data? = nil
-        ) -> (String?, Data?, Data?, Data?, Data?, Error?) {
+        ) -> (String?, Data?, Data?, Data?, Data?, Set<String>?, TPPolicy?, Error?) {
         let expectation = XCTestExpectation(description: "prepare replied")
         var reta: String?, retb: Data?, retc: Data?, retd: Data?, rete: Data?, reterr: Error?
         let expectation = XCTestExpectation(description: "prepare replied")
         var reta: String?, retb: Data?, retc: Data?, retd: Data?, rete: Data?, reterr: Error?
+        var retviews: Set<String>?
+        var retpolicy: TPPolicy?
         self.prepare(epoch: epoch,
                      machineID: machineID,
                      bottleSalt: bottleSalt,
         self.prepare(epoch: epoch,
                      machineID: machineID,
                      bottleSalt: bottleSalt,
@@ -71,17 +73,19 @@ extension Container {
                      policySecrets: policySecrets,
                      signingPrivateKeyPersistentRef: signingPrivateKeyPersistentRef,
                      encryptionPrivateKeyPersistentRef: encryptionPrivateKeyPersistentRef
                      policySecrets: policySecrets,
                      signingPrivateKeyPersistentRef: signingPrivateKeyPersistentRef,
                      encryptionPrivateKeyPersistentRef: encryptionPrivateKeyPersistentRef
-        ) { a, b, c, d, e, err in
+        ) { a, b, c, d, e, f, g, err in
             reta = a
             retb = b
             retc = c
             retd = d
             rete = e
             reta = a
             retb = b
             retc = c
             retd = d
             rete = e
+            retviews = f
+            retpolicy = g
             reterr = err
             expectation.fulfill()
         }
         test.wait(for: [expectation], timeout: 10.0)
             reterr = err
             expectation.fulfill()
         }
         test.wait(for: [expectation], timeout: 10.0)
-        return (reta, retb, retc, retd, rete, reterr)
+        return (reta, retb, retc, retd, rete, retviews, retpolicy, reterr)
     }
 
     func establishSync(test: XCTestCase,
     }
 
     func establishSync(test: XCTestCase,
@@ -126,29 +130,34 @@ extension Container {
         return (reta, retb, reterr)
     }
 
         return (reta, retb, reterr)
     }
 
-    func preflightVouchWithBottleSync(test: XCTestCase, bottleID: String) -> (String?, Error?) {
+    func preflightVouchWithBottleSync(test: XCTestCase, bottleID: String) -> (String?, Set<String>?, TPPolicy?, Error?) {
         let expectation = XCTestExpectation(description: "preflightVouchWithBottle replied")
         var reta: String?, reterr: Error?
         let expectation = XCTestExpectation(description: "preflightVouchWithBottle replied")
         var reta: String?, reterr: Error?
-        self.preflightVouchWithBottle(bottleID: bottleID) { a, err in
+        var retviews: Set<String>?, retpolicy: TPPolicy?
+        self.preflightVouchWithBottle(bottleID: bottleID) { a, views, policy, err in
             reta = a
             reta = a
+            retviews = views
+            retpolicy = policy
             reterr = err
             expectation.fulfill()
         }
         test.wait(for: [expectation], timeout: 10.0)
             reterr = err
             expectation.fulfill()
         }
         test.wait(for: [expectation], timeout: 10.0)
-        return (reta, reterr)
+        return (reta, retviews, retpolicy, reterr)
     }
 
     }
 
-    func vouchWithBottleSync(test: XCTestCase, b: String, entropy: Data, bottleSalt: String, tlkShares: [CKKSTLKShare]) -> (Data?, Data?, Error?) {
+    func vouchWithBottleSync(test: XCTestCase, b: String, entropy: Data, bottleSalt: String, tlkShares: [CKKSTLKShare]) -> (Data?, Data?, Int64, Int64, Error?) {
         let expectation = XCTestExpectation(description: "vouchWithBottle replied")
         let expectation = XCTestExpectation(description: "vouchWithBottle replied")
-        var reta: Data?, retb: Data?, reterr: Error?
-        self.vouchWithBottle(bottleID: b, entropy: entropy, bottleSalt: bottleSalt, tlkShares: tlkShares) { a, b, err in
+        var reta: Data?, retb: Data?, retc: Int64 = 0, retd: Int64 = 0, reterr: Error?
+        self.vouchWithBottle(bottleID: b, entropy: entropy, bottleSalt: bottleSalt, tlkShares: tlkShares) { a, b, c, d, err in
             reta = a
             retb = b
             reta = a
             retb = b
+            retc = c
+            retd = d
             reterr = err
             expectation.fulfill()
         }
         test.wait(for: [expectation], timeout: 10.0)
             reterr = err
             expectation.fulfill()
         }
         test.wait(for: [expectation], timeout: 10.0)
-        return (reta, retb, reterr)
+        return (reta, retb, retc, retd, reterr)
     }
 
     func joinSync(test: XCTestCase,
     }
 
     func joinSync(test: XCTestCase,
@@ -156,41 +165,48 @@ extension Container {
                   voucherSig: Data,
                   ckksKeys: [CKKSKeychainBackedKeySet],
                   tlkShares: [CKKSTLKShare],
                   voucherSig: Data,
                   ckksKeys: [CKKSKeychainBackedKeySet],
                   tlkShares: [CKKSTLKShare],
-                  preapprovedKeys: [Data]? = nil) -> (String?, [CKRecord]?, Error?) {
+                  preapprovedKeys: [Data]? = nil) -> (String?, [CKRecord]?, Set<String>?, TPPolicy?, Error?) {
         let expectation = XCTestExpectation(description: "join replied")
         var reta: String?, retkhr: [CKRecord]?, reterr: Error?
         let expectation = XCTestExpectation(description: "join replied")
         var reta: String?, retkhr: [CKRecord]?, reterr: Error?
+        var retviews: Set<String>?, retpolicy: TPPolicy?
         self.join(voucherData: voucherData,
                   voucherSig: voucherSig,
                   ckksKeys: ckksKeys,
                   tlkShares: tlkShares,
         self.join(voucherData: voucherData,
                   voucherSig: voucherSig,
                   ckksKeys: ckksKeys,
                   tlkShares: tlkShares,
-                  preapprovedKeys: preapprovedKeys) { a, khr, err in
+                  preapprovedKeys: preapprovedKeys) { a, khr, views, policy, err in
                     reta = a
                     retkhr = khr
                     reta = a
                     retkhr = khr
+                    retviews = views
+                    retpolicy = policy
                     reterr = err
                     expectation.fulfill()
         }
         test.wait(for: [expectation], timeout: 10.0)
                     reterr = err
                     expectation.fulfill()
         }
         test.wait(for: [expectation], timeout: 10.0)
-        return (reta, retkhr, reterr)
+        return (reta, retkhr, retviews, retpolicy, reterr)
     }
 
     func preapprovedJoinSync(test: XCTestCase,
                              ckksKeys: [CKKSKeychainBackedKeySet],
                              tlkShares: [CKKSTLKShare],
     }
 
     func preapprovedJoinSync(test: XCTestCase,
                              ckksKeys: [CKKSKeychainBackedKeySet],
                              tlkShares: [CKKSTLKShare],
-                             preapprovedKeys: [Data]? = nil) -> (String?, [CKRecord]?, Error?) {
+                             preapprovedKeys: [Data]? = nil) -> (String?, [CKRecord]?, Set<String>?, TPPolicy?, Error?) {
         let expectation = XCTestExpectation(description: "preapprovedjoin replied")
         var reta: String?
         var retkhr: [CKRecord]?
         let expectation = XCTestExpectation(description: "preapprovedjoin replied")
         var reta: String?
         var retkhr: [CKRecord]?
+        var retviews: Set<String>?
+        var retpolicy: TPPolicy?
         var reterr: Error?
         self.preapprovedJoin(ckksKeys: ckksKeys,
                              tlkShares: tlkShares,
         var reterr: Error?
         self.preapprovedJoin(ckksKeys: ckksKeys,
                              tlkShares: tlkShares,
-                             preapprovedKeys: preapprovedKeys) { a, khr, err in
+                             preapprovedKeys: preapprovedKeys) { a, khr, views, policy, err in
                                 reta = a
                                 retkhr = khr
                                 reta = a
                                 retkhr = khr
+                                retviews = views
+                                retpolicy = policy
                                 reterr = err
                                 expectation.fulfill()
         }
         test.wait(for: [expectation], timeout: 10.0)
                                 reterr = err
                                 expectation.fulfill()
         }
         test.wait(for: [expectation], timeout: 10.0)
-        return (reta, retkhr, reterr)
+        return (reta, retkhr, retviews, retpolicy, reterr)
     }
 
     func updateSync(test: XCTestCase,
     }
 
     func updateSync(test: XCTestCase,
@@ -215,10 +231,11 @@ extension Container {
         return (retstate, reterr)
     }
 
         return (retstate, reterr)
     }
 
-    func setAllowedMachineIDsSync(test: XCTestCase, allowedMachineIDs: Set<String>, listDifference: Bool = true) -> (Error?) {
+    func setAllowedMachineIDsSync(test: XCTestCase, allowedMachineIDs: Set<String>, accountIsDemo: Bool, listDifference: Bool = true) -> (Error?) {
         let expectation = XCTestExpectation(description: "setAllowedMachineIDs replied")
         var reterr: Error?
         let expectation = XCTestExpectation(description: "setAllowedMachineIDs replied")
         var reterr: Error?
-        self.setAllowedMachineIDs(allowedMachineIDs) { differences, err in
+        let honorIDMSListChanges = accountIsDemo ? false : true
+        self.setAllowedMachineIDs(allowedMachineIDs, honorIDMSListChanges: honorIDMSListChanges) { differences, err in
             XCTAssertEqual(differences, listDifference, "Reported list difference should match expectation")
             reterr = err
             expectation.fulfill()
             XCTAssertEqual(differences, listDifference, "Reported list difference should match expectation")
             reterr = err
             expectation.fulfill()
@@ -253,7 +270,7 @@ extension Container {
         let expectation = XCTestExpectation(description: "fetchMIDList replied")
         var retlist: Set<String>?
         var reterr: Error?
         let expectation = XCTestExpectation(description: "fetchMIDList replied")
         var retlist: Set<String>?
         var reterr: Error?
-        self.fetchAllowedMachineIDs() { list, err in
+        self.fetchAllowedMachineIDs { list, err in
             retlist = list
             reterr = err
             expectation.fulfill()
             retlist = list
             reterr = err
             expectation.fulfill()
@@ -352,10 +369,10 @@ extension Container {
     }
 
     func fetchPolicyDocumentsSync(test: XCTestCase,
     }
 
     func fetchPolicyDocumentsSync(test: XCTestCase,
-                                  keys: [NSNumber: String]) -> ([NSNumber: [String]]?, Error?) {
+                                  versions: Set<TPPolicyVersion>) -> ([TPPolicyVersion: Data]?, Error?) {
         let expectation = XCTestExpectation(description: "fetchPolicyDocuments replied")
         let expectation = XCTestExpectation(description: "fetchPolicyDocuments replied")
-        var reta: [NSNumber: [String]]?, reterr: Error?
-        self.fetchPolicyDocuments(keys: keys) { a, err in
+        var reta: [TPPolicyVersion: Data]?, reterr: Error?
+        self.fetchPolicyDocuments(versions: versions) { a, err in
             reta = a
             reterr = err
             expectation.fulfill()
             reta = a
             reterr = err
             expectation.fulfill()
@@ -383,22 +400,24 @@ extension Container {
         return (retentropy, retbottleID, retspki, reterror)
     }
 
         return (retentropy, retbottleID, retspki, reterror)
     }
 
-    func requestHealthCheckSync(requiresEscrowCheck: Bool, test: XCTestCase) -> (Bool, Bool, Bool, Error?) {
+    func requestHealthCheckSync(requiresEscrowCheck: Bool, test: XCTestCase) -> (Bool, Bool, Bool, Bool, Error?) {
         let expectation = XCTestExpectation(description: "requestHealthCheck replied")
         var retrepairaccount: Bool = false
         var retrepairescrow: Bool = false
         var retresetoctagon: Bool = false
         let expectation = XCTestExpectation(description: "requestHealthCheck replied")
         var retrepairaccount: Bool = false
         var retrepairescrow: Bool = false
         var retresetoctagon: Bool = false
+        var retleavetrust: Bool = false
         var reterror: Error?
 
         var reterror: Error?
 
-        self.requestHealthCheck(requiresEscrowCheck: requiresEscrowCheck) { repairAccount, repairEscrow, resetOctagon, error in
+        self.requestHealthCheck(requiresEscrowCheck: requiresEscrowCheck) { repairAccount, repairEscrow, resetOctagon, leaveTrust, error in
             retrepairaccount = repairAccount
             retrepairescrow = repairEscrow
             retresetoctagon = resetOctagon
             retrepairaccount = repairAccount
             retrepairescrow = repairEscrow
             retresetoctagon = resetOctagon
+            retleavetrust = leaveTrust
             reterror = error
 
             expectation.fulfill()
         }
         test.wait(for: [expectation], timeout: 10.0)
             reterror = error
 
             expectation.fulfill()
         }
         test.wait(for: [expectation], timeout: 10.0)
-        return (retrepairaccount, retrepairescrow, retresetoctagon, reterror)
+        return (retrepairaccount, retrepairescrow, retresetoctagon, retleavetrust, reterror)
     }
 }
     }
 }
index fbef651a4af2372f25a0908b8344566e5ba51773..23267a012df547dd3028458aa653894ae9568019 100644 (file)
@@ -66,16 +66,19 @@ struct FakeCuttlefishAssertion: CustomStringConvertible {
     }
 }
 
     }
 }
 
-@objc class FakeCuttlefishNotify: NSObject {
+@objc
+class FakeCuttlefishNotify: NSObject {
     let pushes: (Data) -> Void
     let containerName: String
     let pushes: (Data) -> Void
     let containerName: String
-    @objc init(_ containerName: String, pushes: @escaping (Data) -> Void) {
+    @objc
+    init(_ containerName: String, pushes: @escaping (Data) -> Void) {
         self.containerName = containerName
         self.pushes = pushes
     }
 
         self.containerName = containerName
         self.pushes = pushes
     }
 
-    @objc public func notify(_ function: String) throws {
-        let notification: [String: Dictionary<String, Any>] = [
+    @objc
+    public func notify(_ function: String) throws {
+        let notification: [String: [String: Any]] = [
             "aps": ["content-available": 1],
             "cf": [
                 "f": function,
             "aps": ["content-available": 1],
             "cf": [
                 "f": function,
@@ -99,7 +102,7 @@ extension ViewKey {
 
         record[SecCKRecordWrappedKeyKey] = self.wrappedkeyBase64
 
 
         record[SecCKRecordWrappedKeyKey] = self.wrappedkeyBase64
 
-        switch(self.keyclass) {
+        switch self.keyclass {
         case .tlk:
             record[SecCKRecordKeyClassKey] = "tlk"
         case .classA:
         case .tlk:
             record[SecCKRecordKeyClassKey] = "tlk"
         case .classA:
@@ -110,7 +113,7 @@ extension ViewKey {
             abort()
         }
 
             abort()
         }
 
-        if self.parentkeyUuid.count > 0 {
+        if !self.parentkeyUuid.isEmpty {
             // TODO: no idea how to tell it about the 'verify' action
             record[SecCKRecordParentKeyRefKey] = CKRecord.Reference(recordID: CKRecord.ID(__recordName: self.parentkeyUuid, zoneID: zoneID), action: .none)
         }
             // TODO: no idea how to tell it about the 'verify' action
             record[SecCKRecordParentKeyRefKey] = CKRecord.Reference(recordID: CKRecord.ID(__recordName: self.parentkeyUuid, zoneID: zoneID), action: .none)
         }
@@ -120,7 +123,7 @@ extension ViewKey {
 
     func fakeKeyPointer(zoneID: CKRecordZone.ID) -> CKRecord {
         let recordName: String
 
     func fakeKeyPointer(zoneID: CKRecordZone.ID) -> CKRecord {
         let recordName: String
-        switch(self.keyclass) {
+        switch self.keyclass {
         case .tlk:
             recordName = "tlk"
         case .classA:
         case .tlk:
             recordName = "tlk"
         case .classA:
@@ -199,6 +202,7 @@ class FakeCuttlefishServer: CuttlefishAPIAsync {
     var returnRepairAccountResponse: Bool = false
     var returnRepairEscrowResponse: Bool = false
     var returnResetOctagonResponse: Bool = false
     var returnRepairAccountResponse: Bool = false
     var returnRepairEscrowResponse: Bool = false
     var returnResetOctagonResponse: Bool = false
+    var returnLeaveTrustResponse: Bool = false
     var returnRepairErrorResponse: Error?
     var fetchChangesCalledCount: Int = 0
 
     var returnRepairErrorResponse: Error?
     var fetchChangesCalledCount: Int = 0
 
@@ -211,6 +215,10 @@ class FakeCuttlefishServer: CuttlefishAPIAsync {
     var healthListener: ((GetRepairActionRequest) -> NSError?)?
     var fetchViableBottlesListener: ((FetchViableBottlesRequest) -> NSError?)?
     var resetListener: ((ResetRequest) -> NSError?)?
     var healthListener: ((GetRepairActionRequest) -> NSError?)?
     var fetchViableBottlesListener: ((FetchViableBottlesRequest) -> NSError?)?
     var resetListener: ((ResetRequest) -> NSError?)?
+    var setRecoveryKeyListener: ((SetRecoveryKeyRequest) -> NSError?)?
+
+    // Any policies in here will be returned by FetchPolicy before any inbuilt policies
+    var policyOverlay: [TPPolicyDocument] = []
 
     var fetchViableBottlesDontReturnBottleWithID: String?
 
 
     var fetchViableBottlesDontReturnBottleWithID: String?
 
@@ -259,7 +267,7 @@ class FakeCuttlefishServer: CuttlefishAPIAsync {
         return Changes.with { changes in
             changes.changeToken = self.currentChangeToken
 
         return Changes.with { changes in
             changes.changeToken = self.currentChangeToken
 
-            changes.differences = self.state.peersByID.compactMap({ (key: String, value: Peer) -> PeerDifference? in
+            changes.differences = self.state.peersByID.compactMap { (key: String, value: Peer) -> PeerDifference? in
                 let old = snapshot.peersByID[key]
                 if old == nil {
                     return PeerDifference.with {
                 let old = snapshot.peersByID[key]
                 if old == nil {
                     return PeerDifference.with {
@@ -272,7 +280,7 @@ class FakeCuttlefishServer: CuttlefishAPIAsync {
                 } else {
                     return nil
                 }
                 } else {
                     return nil
                 }
-            })
+            }
             snapshot.peersByID.forEach { (key: String, _: Peer) in
                 if nil == self.state.peersByID[key] {
                     changes.differences.append(PeerDifference.with {
             snapshot.peersByID.forEach { (key: String, _: Peer) in
                 if nil == self.state.peersByID[key] {
                     changes.differences.append(PeerDifference.with {
@@ -562,6 +570,15 @@ class FakeCuttlefishServer: CuttlefishAPIAsync {
 
     func setRecoveryKey(_ request: SetRecoveryKeyRequest, completion: @escaping (SetRecoveryKeyResponse?, Error?) -> Void) {
         print("FakeCuttlefish: setRecoveryKey called")
 
     func setRecoveryKey(_ request: SetRecoveryKeyRequest, completion: @escaping (SetRecoveryKeyResponse?, Error?) -> Void) {
         print("FakeCuttlefish: setRecoveryKey called")
+
+        if let listener = self.setRecoveryKeyListener {
+            let operationError = listener(request)
+            guard operationError == nil else {
+                completion(nil, operationError)
+                return
+            }
+        }
+
         guard let snapshot = self.snapshotsByChangeToken[request.changeToken] else {
             completion(nil, FakeCuttlefishError.unknownChangeToken)
             return
         guard let snapshot = self.snapshotsByChangeToken[request.changeToken] else {
             completion(nil, FakeCuttlefishError.unknownChangeToken)
             return
@@ -648,8 +665,15 @@ class FakeCuttlefishServer: CuttlefishAPIAsync {
         var response = FetchPolicyDocumentsResponse()
 
         let policies = builtInPolicyDocuments()
         var response = FetchPolicyDocumentsResponse()
 
         let policies = builtInPolicyDocuments()
-        let dummyPolicies = Dictionary(uniqueKeysWithValues: policies.map({ ($0.policyVersion, ($0.policyHash, $0.protobuf)) }))
+        let dummyPolicies = Dictionary(uniqueKeysWithValues: policies.map { ($0.version.versionNumber, ($0.version.policyHash, $0.protobuf)) })
+        let overlayPolicies = Dictionary(uniqueKeysWithValues: self.policyOverlay.map { ($0.version.versionNumber, ($0.version.policyHash, $0.protobuf)) })
+
         for key in request.keys {
         for key in request.keys {
+            if let (hash, data) = overlayPolicies[key.version], hash == key.hash {
+                response.entries.append(PolicyDocumentMapEntry.with { $0.key = key; $0.value = data })
+                continue
+            }
+
             guard let (hash, data) = dummyPolicies[key.version] else {
                 continue
             }
             guard let (hash, data) = dummyPolicies[key.version] else {
                 continue
             }
@@ -703,6 +727,11 @@ class FakeCuttlefishServer: CuttlefishAPIAsync {
                 $0.repairAction = .resetOctagon
             }
             completion(response, nil)
                 $0.repairAction = .resetOctagon
             }
             completion(response, nil)
+        } else if returnLeaveTrustResponse {
+            let response = GetRepairActionResponse.with {
+                $0.repairAction = .leaveTrust
+            }
+            completion(response, nil)
         } else if self.returnNoActionResponse {
             let response = GetRepairActionResponse.with {
                 $0.repairAction = .noAction
         } else if self.returnNoActionResponse {
             let response = GetRepairActionResponse.with {
                 $0.repairAction = .noAction
@@ -718,6 +747,10 @@ class FakeCuttlefishServer: CuttlefishAPIAsync {
         }
     }
 
         }
     }
 
+    func getClubCertificates(_: GetClubCertificatesRequest, completion: @escaping (GetClubCertificatesResponse?, Error?) -> Void) {
+        completion(GetClubCertificatesResponse(), nil)
+    }
+
     func getSupportAppInfo(_: GetSupportAppInfoRequest, completion: @escaping (GetSupportAppInfoResponse?, Error?) -> Void) {
         completion(GetSupportAppInfoResponse(), nil)
     }
     func getSupportAppInfo(_: GetSupportAppInfoRequest, completion: @escaping (GetSupportAppInfoResponse?, Error?) -> Void) {
         completion(GetSupportAppInfoResponse(), nil)
     }
index 5d8d2c33ed0b72d4cec0c81478c0e0a7efe55c08..8289a40e72c175389b8198c45e34efaade23f85b 100644 (file)
@@ -15,7 +15,7 @@ enum Handler {
     case updateTrust((UpdateTrustRequest, @escaping (UpdateTrustResponse?, Error?) -> Void) -> Void)
     case setRecoveryKey((SetRecoveryKeyRequest, @escaping (SetRecoveryKeyResponse?, Error?) -> Void) -> Void)
     case fetchChanges((FetchChangesRequest, @escaping (FetchChangesResponse?, Error?) -> Void) -> Void)
     case updateTrust((UpdateTrustRequest, @escaping (UpdateTrustResponse?, Error?) -> Void) -> Void)
     case setRecoveryKey((SetRecoveryKeyRequest, @escaping (SetRecoveryKeyResponse?, Error?) -> Void) -> Void)
     case fetchChanges((FetchChangesRequest, @escaping (FetchChangesResponse?, Error?) -> Void) -> Void)
-    case fetchViableBottles((FetchViableBottlesRequest, @escaping (FetchViableBottlesResponse?, Error?) -> Void) ->Void)
+    case fetchViableBottles((FetchViableBottlesRequest, @escaping (FetchViableBottlesResponse?, Error?) -> Void) -> Void)
     case fetchPolicyDocuments((FetchPolicyDocumentsRequest,
         @escaping (FetchPolicyDocumentsResponse?, Error?) -> Void) -> Void)
 }
     case fetchPolicyDocuments((FetchPolicyDocumentsRequest,
         @escaping (FetchPolicyDocumentsResponse?, Error?) -> Void) -> Void)
 }
@@ -169,5 +169,7 @@ class MockCuttlefishAPIAsyncClient: CuttlefishAPIAsync {
     func getSupportAppInfo(_: GetSupportAppInfoRequest, completion: @escaping (GetSupportAppInfoResponse?, Error?) -> Void) {
         completion(GetSupportAppInfoResponse(), nil)
     }
     func getSupportAppInfo(_: GetSupportAppInfoRequest, completion: @escaping (GetSupportAppInfoResponse?, Error?) -> Void) {
         completion(GetSupportAppInfoResponse(), nil)
     }
-
+    func getClubCertificates(_: GetClubCertificatesRequest, completion: @escaping (GetClubCertificatesResponse?, Error?) -> Void) {
+        completion(GetClubCertificatesResponse(), nil)
+    }
 }
 }
index 7d7ecae1ac710c6bc43bb3bc34305d2d9d07b0be..c5f2b3f766198b5cbce28b824de734e3e275fb1b 100644 (file)
@@ -157,18 +157,19 @@ class TrustedPeersHelperUnitTests: XCTestCase {
 
     func establish(reload: Bool,
                    store: NSPersistentStoreDescription) throws -> (Container, String) {
 
     func establish(reload: Bool,
                    store: NSPersistentStoreDescription) throws -> (Container, String) {
-        return try self.establish(reload: reload, contextID: OTDefaultContext, store: store)
+        return try self.establish(reload: reload, contextID: OTDefaultContext, accountIsDemo: false, store: store)
     }
 
     func establish(reload: Bool,
                    contextID: String,
                    allowedMachineIDs: Set<String> = Set(["aaa", "bbb", "ccc"]),
     }
 
     func establish(reload: Bool,
                    contextID: String,
                    allowedMachineIDs: Set<String> = Set(["aaa", "bbb", "ccc"]),
+                   accountIsDemo: Bool,
                    store: NSPersistentStoreDescription) throws -> (Container, String) {
         var container = try Container(name: ContainerName(container: "test", context: contextID), persistentStoreDescription: store, cuttlefish: cuttlefish)
 
                    store: NSPersistentStoreDescription) throws -> (Container, String) {
         var container = try Container(name: ContainerName(container: "test", context: contextID), persistentStoreDescription: store, cuttlefish: cuttlefish)
 
-        XCTAssertNil(container.setAllowedMachineIDsSync(test: self, allowedMachineIDs: allowedMachineIDs, listDifference: allowedMachineIDs.count > 0), "should be able to set allowed machine IDs")
+        XCTAssertNil(container.setAllowedMachineIDsSync(test: self, allowedMachineIDs: allowedMachineIDs, accountIsDemo: accountIsDemo, listDifference: !allowedMachineIDs.isEmpty), "should be able to set allowed machine IDs")
 
 
-        let (peerID, permanentInfo, permanentInfoSig, _, _, error) = container.prepareSync(test: self, epoch: 1, machineID: "aaa", bottleSalt: "123456789", bottleID: UUID().uuidString, modelID: "iPhone1,1")
+        let (peerID, permanentInfo, permanentInfoSig, _, _, _, _, error) = container.prepareSync(test: self, epoch: 1, machineID: "aaa", bottleSalt: "123456789", bottleID: UUID().uuidString, modelID: "iPhone1,1")
         do {
             let state = container.getStateSync(test: self)
             XCTAssertFalse( state.bottles.filter { $0.peerID == peerID } .isEmpty, "should have a bottle for peer")
         do {
             let state = container.getStateSync(test: self)
             XCTAssertFalse( state.bottles.filter { $0.peerID == peerID } .isEmpty, "should have a bottle for peer")
@@ -183,11 +184,11 @@ class TrustedPeersHelperUnitTests: XCTestCase {
 
         _ = container.dumpSync(test: self)
 
 
         _ = container.dumpSync(test: self)
 
-        if (reload) {
+        if reload {
             do {
                 container = try Container(name: ContainerName(container: "test", context: contextID), persistentStoreDescription: store, cuttlefish: cuttlefish)
             } catch {
             do {
                 container = try Container(name: ContainerName(container: "test", context: contextID), persistentStoreDescription: store, cuttlefish: cuttlefish)
             } catch {
-                XCTFail()
+                XCTFail("Creating container errored: \(error)")
             }
         }
 
             }
         }
 
@@ -216,7 +217,7 @@ class TrustedPeersHelperUnitTests: XCTestCase {
         let description = tmpStoreDescription(name: "container.db")
         let container = try Container(name: ContainerName(container: "test", context: OTDefaultContext), persistentStoreDescription: description, cuttlefish: cuttlefish)
 
         let description = tmpStoreDescription(name: "container.db")
         let container = try Container(name: ContainerName(container: "test", context: OTDefaultContext), persistentStoreDescription: description, cuttlefish: cuttlefish)
 
-        let (peerID, permanentInfo, permanentInfoSig, _, _, error) = container.prepareSync(test: self, epoch: 1, machineID: "aaa", bottleSalt: "123456789", bottleID: UUID().uuidString, modelID: "iPhone1,1")
+        let (peerID, permanentInfo, permanentInfoSig, _, _, _, _, error) = container.prepareSync(test: self, epoch: 1, machineID: "aaa", bottleSalt: "123456789", bottleID: UUID().uuidString, modelID: "iPhone1,1")
         do {
             let state = container.getStateSync(test: self)
             XCTAssertFalse( state.bottles.filter { $0.peerID == peerID } .isEmpty, "should have a bottle for peer")
         do {
             let state = container.getStateSync(test: self)
             XCTAssertFalse( state.bottles.filter { $0.peerID == peerID } .isEmpty, "should have a bottle for peer")
@@ -232,7 +233,7 @@ class TrustedPeersHelperUnitTests: XCTestCase {
         // Note that an empty machine ID list means "all are allowed", so an establish now will succeed
 
         // Now set up a machine ID list that positively does not have our peer
         // Note that an empty machine ID list means "all are allowed", so an establish now will succeed
 
         // Now set up a machine ID list that positively does not have our peer
-        XCTAssertNil(container.setAllowedMachineIDsSync(test: self, allowedMachineIDs: ["aaa"]), "should be able to set allowed machine IDs")
+        XCTAssertNil(container.setAllowedMachineIDsSync(test: self, allowedMachineIDs: ["aaa"], accountIsDemo: false), "should be able to set allowed machine IDs")
 
         let (peerID3, _, error3) = container.establishSync(test: self, ckksKeys: [], tlkShares: [], preapprovedKeys: [])
         XCTAssertNotNil(peerID3, "Should get a peer when you establish a now allow-listed peer")
 
         let (peerID3, _, error3) = container.establishSync(test: self, ckksKeys: [], tlkShares: [], preapprovedKeys: [])
         XCTAssertNotNil(peerID3, "Should get a peer when you establish a now allow-listed peer")
@@ -243,15 +244,16 @@ class TrustedPeersHelperUnitTests: XCTestCase {
                        containerID: String,
                        machineID: String,
                        machineIDs: Set<String>,
                        containerID: String,
                        machineID: String,
                        machineIDs: Set<String>,
+                       accountIsDemo: Bool,
                        store: NSPersistentStoreDescription) throws -> (Container, String) {
         let c = try Container(name: ContainerName(container: containerID, context: OTDefaultContext),
                               persistentStoreDescription: store,
                               cuttlefish: cuttlefish)
 
                        store: NSPersistentStoreDescription) throws -> (Container, String) {
         let c = try Container(name: ContainerName(container: containerID, context: OTDefaultContext),
                               persistentStoreDescription: store,
                               cuttlefish: cuttlefish)
 
-        XCTAssertNil(c.setAllowedMachineIDsSync(test: self, allowedMachineIDs: machineIDs, listDifference: machineIDs.count > 0), "Should be able to set machine IDs")
+        XCTAssertNil(c.setAllowedMachineIDsSync(test: self, allowedMachineIDs: machineIDs, accountIsDemo: accountIsDemo, listDifference: !machineIDs.isEmpty), "Should be able to set machine IDs")
 
         print("preparing \(containerID)")
 
         print("preparing \(containerID)")
-        let (peerID, permanentInfo, permanentInfoSig, stableInfo, stableInfoSig, error) =
+        let (peerID, permanentInfo, permanentInfoSig, stableInfo, stableInfoSig, _, _, error) =
             c.prepareSync(test: self, epoch: 1, machineID: machineID, bottleSalt: "123456789", bottleID: UUID().uuidString, modelID: "iPhone1,1")
         XCTAssertNil(error)
         XCTAssertNotNil(peerID)
             c.prepareSync(test: self, epoch: 1, machineID: machineID, bottleSalt: "123456789", bottleID: UUID().uuidString, modelID: "iPhone1,1")
         XCTAssertNil(error)
         XCTAssertNotNil(peerID)
@@ -280,11 +282,11 @@ class TrustedPeersHelperUnitTests: XCTestCase {
             assertTLKShareFor(peerID: peerID!, keyUUID: self.manateeKeySet.tlk.uuid, zoneID: CKRecordZone.ID(zoneName: "Manatee"))
 
             print("\(containerID) joins")
             assertTLKShareFor(peerID: peerID!, keyUUID: self.manateeKeySet.tlk.uuid, zoneID: CKRecordZone.ID(zoneName: "Manatee"))
 
             print("\(containerID) joins")
-            let (joinedPeerID, _, joinError) = c.joinSync(test: self,
-                                                       voucherData: voucherData!,
-                                                       voucherSig: voucherSig!,
-                                                       ckksKeys: [],
-                                                       tlkShares: [])
+            let (joinedPeerID, _, _, _, joinError) = c.joinSync(test: self,
+                                                                voucherData: voucherData!,
+                                                                voucherSig: voucherSig!,
+                                                                ckksKeys: [],
+                                                                tlkShares: [])
             XCTAssertNil(joinError)
             XCTAssertEqual(joinedPeerID, peerID!)
         }
             XCTAssertNil(joinError)
             XCTAssertEqual(joinedPeerID, peerID!)
         }
@@ -299,13 +301,17 @@ class TrustedPeersHelperUnitTests: XCTestCase {
         let containerC = try Container(name: ContainerName(container: "c", context: OTDefaultContext), persistentStoreDescription: description, cuttlefish: cuttlefish)
 
         let machineIDs = Set(["aaa", "bbb", "ccc"])
         let containerC = try Container(name: ContainerName(container: "c", context: OTDefaultContext), persistentStoreDescription: description, cuttlefish: cuttlefish)
 
         let machineIDs = Set(["aaa", "bbb", "ccc"])
-        XCTAssertNil(containerA.setAllowedMachineIDsSync(test: self, allowedMachineIDs: machineIDs))
-        XCTAssertNil(containerB.setAllowedMachineIDsSync(test: self, allowedMachineIDs: machineIDs))
-        XCTAssertNil(containerC.setAllowedMachineIDsSync(test: self, allowedMachineIDs: machineIDs))
+        XCTAssertNil(containerA.setAllowedMachineIDsSync(test: self, allowedMachineIDs: machineIDs, accountIsDemo: false))
+        XCTAssertNil(containerB.setAllowedMachineIDsSync(test: self, allowedMachineIDs: machineIDs, accountIsDemo: false))
+        XCTAssertNil(containerC.setAllowedMachineIDsSync(test: self, allowedMachineIDs: machineIDs, accountIsDemo: false))
 
         print("preparing A")
 
         print("preparing A")
-        let (aPeerID, aPermanentInfo, aPermanentInfoSig, _, _, error) =
+        let (aPeerID, aPermanentInfo, aPermanentInfoSig, _, _, aViewList, aPolicy, error) =
             containerA.prepareSync(test: self, epoch: 1, machineID: "aaa", bottleSalt: "123456789", bottleID: UUID().uuidString, modelID: "iPhone1,1")
             containerA.prepareSync(test: self, epoch: 1, machineID: "aaa", bottleSalt: "123456789", bottleID: UUID().uuidString, modelID: "iPhone1,1")
+        XCTAssertNotNil(aViewList, "Should have a view list coming back from a successful prepare")
+        XCTAssertNotNil(aPolicy, "Should have a policy coming back from a successful prepare")
+        XCTAssertEqual(aPolicy?.version, prevailingPolicyVersion, "Policy coming back from prepare() should be prevailing policy version")
+
         do {
             let state = containerA.getStateSync(test: self)
             XCTAssertFalse( state.bottles.filter { $0.peerID == aPeerID } .isEmpty, "should have a bottle for peer")
         do {
             let state = containerA.getStateSync(test: self)
             XCTAssertFalse( state.bottles.filter { $0.peerID == aPeerID } .isEmpty, "should have a bottle for peer")
@@ -326,7 +332,7 @@ class TrustedPeersHelperUnitTests: XCTestCase {
         }
 
         print("preparing B")
         }
 
         print("preparing B")
-        let (bPeerID, bPermanentInfo, bPermanentInfoSig, bStableInfo, bStableInfoSig, error2) =
+        let (bPeerID, bPermanentInfo, bPermanentInfoSig, bStableInfo, bStableInfoSig, _, _, error2) =
             containerB.prepareSync(test: self, epoch: 1, machineID: "bbb", bottleSalt: "123456789", bottleID: UUID().uuidString, modelID: "iPhone1,1")
         do {
             let state = containerB.getStateSync(test: self)
             containerB.prepareSync(test: self, epoch: 1, machineID: "bbb", bottleSalt: "123456789", bottleID: UUID().uuidString, modelID: "iPhone1,1")
         do {
             let state = containerB.getStateSync(test: self)
@@ -386,11 +392,11 @@ class TrustedPeersHelperUnitTests: XCTestCase {
             assertTLKShareFor(peerID: bPeerID!, keyUUID: self.manateeKeySet.tlk.uuid, zoneID: CKRecordZone.ID(zoneName: "Manatee"))
 
             print("B joins")
             assertTLKShareFor(peerID: bPeerID!, keyUUID: self.manateeKeySet.tlk.uuid, zoneID: CKRecordZone.ID(zoneName: "Manatee"))
 
             print("B joins")
-            let (peerID, _, error) = containerB.joinSync(test: self,
-                                                         voucherData: voucherData!,
-                                                         voucherSig: voucherSig!,
-                                                         ckksKeys: [],
-                                                         tlkShares: [])
+            let (peerID, _, _, _, error) = containerB.joinSync(test: self,
+                                                               voucherData: voucherData!,
+                                                               voucherSig: voucherSig!,
+                                                               ckksKeys: [],
+                                                               tlkShares: [])
             XCTAssertNil(error)
             XCTAssertEqual(peerID, bPeerID!)
         }
             XCTAssertNil(error)
             XCTAssertEqual(peerID, bPeerID!)
         }
@@ -400,7 +406,7 @@ class TrustedPeersHelperUnitTests: XCTestCase {
         _ = containerC.dumpSync(test: self)
 
         print("preparing C")
         _ = containerC.dumpSync(test: self)
 
         print("preparing C")
-        let (cPeerID, cPermanentInfo, cPermanentInfoSig, cStableInfo, cStableInfoSig, error4) =
+        let (cPeerID, cPermanentInfo, cPermanentInfoSig, cStableInfo, cStableInfoSig, _, _, error4) =
             containerC.prepareSync(test: self, epoch: 1, machineID: "ccc", bottleSalt: "123456789", bottleID: UUID().uuidString, modelID: "iPhone1,1")
         do {
             let state = containerC.getStateSync(test: self)
             containerC.prepareSync(test: self, epoch: 1, machineID: "ccc", bottleSalt: "123456789", bottleID: UUID().uuidString, modelID: "iPhone1,1")
         do {
             let state = containerC.getStateSync(test: self)
@@ -438,11 +444,11 @@ class TrustedPeersHelperUnitTests: XCTestCase {
             assertTLKShareFor(peerID: cPeerID!, keyUUID: self.manateeKeySet.tlk.uuid, zoneID: CKRecordZone.ID(zoneName: "Manatee"))
 
             print("C joins")
             assertTLKShareFor(peerID: cPeerID!, keyUUID: self.manateeKeySet.tlk.uuid, zoneID: CKRecordZone.ID(zoneName: "Manatee"))
 
             print("C joins")
-            let (peerID, _, error2) = containerC.joinSync(test: self,
-                                                          voucherData: voucherData!,
-                                                          voucherSig: voucherSig!,
-                                                          ckksKeys: [self.manateeKeySet, provisionalEngramKeySet],
-                                                          tlkShares: [])
+            let (peerID, _, _, _, error2) = containerC.joinSync(test: self,
+                                                                voucherData: voucherData!,
+                                                                voucherSig: voucherSig!,
+                                                                ckksKeys: [self.manateeKeySet, provisionalEngramKeySet],
+                                                                tlkShares: [])
             XCTAssertNil(error2)
             XCTAssertEqual(peerID, cPeerID!)
 
             XCTAssertNil(error2)
             XCTAssertEqual(peerID, cPeerID!)
 
@@ -473,7 +479,7 @@ class TrustedPeersHelperUnitTests: XCTestCase {
         let containerA = try Container(name: ContainerName(container: "a", context: OTDefaultContext), persistentStoreDescription: description, cuttlefish: cuttlefish)
         let containerB = try Container(name: ContainerName(container: "b", context: OTDefaultContext), persistentStoreDescription: description, cuttlefish: cuttlefish)
 
         let containerA = try Container(name: ContainerName(container: "a", context: OTDefaultContext), persistentStoreDescription: description, cuttlefish: cuttlefish)
         let containerB = try Container(name: ContainerName(container: "b", context: OTDefaultContext), persistentStoreDescription: description, cuttlefish: cuttlefish)
 
-        let (peerID, permanentInfo, permanentInfoSig, _, _, error) = containerA.prepareSync(test: self, epoch: 1, machineID: "aaa", bottleSalt: "123456789", bottleID: UUID().uuidString, modelID: "iPhone1,1")
+        let (peerID, permanentInfo, permanentInfoSig, _, _, _, _, error) = containerA.prepareSync(test: self, epoch: 1, machineID: "aaa", bottleSalt: "123456789", bottleID: UUID().uuidString, modelID: "iPhone1,1")
         do {
             let state = containerA.getStateSync(test: self)
             XCTAssertFalse( state.bottles.filter { $0.peerID == peerID } .isEmpty, "should have a bottle for peer")
         do {
             let state = containerA.getStateSync(test: self)
             XCTAssertFalse( state.bottles.filter { $0.peerID == peerID } .isEmpty, "should have a bottle for peer")
@@ -486,14 +492,14 @@ class TrustedPeersHelperUnitTests: XCTestCase {
         XCTAssertNotNil(permanentInfo, "Should have a permanent info after preparing A")
         XCTAssertNotNil(permanentInfoSig, "Should have a signature after preparing A")
 
         XCTAssertNotNil(permanentInfo, "Should have a permanent info after preparing A")
         XCTAssertNotNil(permanentInfoSig, "Should have a signature after preparing A")
 
-        XCTAssertNil(containerA.setAllowedMachineIDsSync(test: self, allowedMachineIDs: ["aaa"]), "should be able to set allowed machine IDs")
+        XCTAssertNil(containerA.setAllowedMachineIDsSync(test: self, allowedMachineIDs: ["aaa"], accountIsDemo: false), "should be able to set allowed machine IDs")
 
         let (peerID2, _, error2) = containerA.establishSync(test: self, ckksKeys: [], tlkShares: [], preapprovedKeys: [])
         XCTAssertNotNil(peerID2, "Should get a peer when you establish a now allow-listed peer")
         XCTAssertNil(error2, "Should not get an error when you establish a now allow-listed peer")
 
         print("preparing B")
 
         let (peerID2, _, error2) = containerA.establishSync(test: self, ckksKeys: [], tlkShares: [], preapprovedKeys: [])
         XCTAssertNotNil(peerID2, "Should get a peer when you establish a now allow-listed peer")
         XCTAssertNil(error2, "Should not get an error when you establish a now allow-listed peer")
 
         print("preparing B")
-        let (bPeerID, bPermanentInfo, bPermanentInfoSig, bStableInfo, bStableInfoSig, errorPrepareB) =
+        let (bPeerID, bPermanentInfo, bPermanentInfoSig, bStableInfo, bStableInfoSig, _, _, errorPrepareB) =
             containerB.prepareSync(test: self, epoch: 1, machineID: "bbb", bottleSalt: "123456789", bottleID: UUID().uuidString, modelID: "iPhone1,1")
         do {
             let state = containerA.getStateSync(test: self)
             containerB.prepareSync(test: self, epoch: 1, machineID: "bbb", bottleSalt: "123456789", bottleID: UUID().uuidString, modelID: "iPhone1,1")
         do {
             let state = containerA.getStateSync(test: self)
@@ -507,7 +513,7 @@ class TrustedPeersHelperUnitTests: XCTestCase {
         XCTAssertNotNil(bPermanentInfo, "Should have a permanent info after preparing B")
         XCTAssertNotNil(bPermanentInfoSig, "Should have a signature after preparing B")
 
         XCTAssertNotNil(bPermanentInfo, "Should have a permanent info after preparing B")
         XCTAssertNotNil(bPermanentInfoSig, "Should have a signature after preparing B")
 
-        XCTAssertNil(containerB.setAllowedMachineIDsSync(test: self, allowedMachineIDs: ["aaa"]), "should be able to set allowed machine IDs on container B")
+        XCTAssertNil(containerB.setAllowedMachineIDsSync(test: self, allowedMachineIDs: ["aaa"], accountIsDemo: false), "should be able to set allowed machine IDs on container B")
 
         do {
             print("A vouches for B")
 
         do {
             print("A vouches for B")
@@ -524,11 +530,11 @@ class TrustedPeersHelperUnitTests: XCTestCase {
             XCTAssertNotNil(voucherSig, "Should have a signature from A")
 
             print("B joins")
             XCTAssertNotNil(voucherSig, "Should have a signature from A")
 
             print("B joins")
-            let (peerID, _, error) = containerB.joinSync(test: self,
-                                                         voucherData: voucherData!,
-                                                         voucherSig: voucherSig!,
-                                                         ckksKeys: [],
-                                                         tlkShares: [])
+            let (peerID, _, _, _, error) = containerB.joinSync(test: self,
+                                                               voucherData: voucherData!,
+                                                               voucherSig: voucherSig!,
+                                                               ckksKeys: [],
+                                                               tlkShares: [])
             XCTAssertNotNil(error, "Should have an error joining with an unapproved machine ID")
             XCTAssertNil(peerID, "Should not receive a peer ID joining with an unapproved machine ID")
         }
             XCTAssertNotNil(error, "Should have an error joining with an unapproved machine ID")
             XCTAssertNil(peerID, "Should not receive a peer ID joining with an unapproved machine ID")
         }
@@ -539,10 +545,10 @@ class TrustedPeersHelperUnitTests: XCTestCase {
         let containerA = try Container(name: ContainerName(container: "a", context: OTDefaultContext), persistentStoreDescription: description, cuttlefish: cuttlefish)
 
         let machineIDs = Set(["aaa"])
         let containerA = try Container(name: ContainerName(container: "a", context: OTDefaultContext), persistentStoreDescription: description, cuttlefish: cuttlefish)
 
         let machineIDs = Set(["aaa"])
-        XCTAssertNil(containerA.setAllowedMachineIDsSync(test: self, allowedMachineIDs: machineIDs))
+        XCTAssertNil(containerA.setAllowedMachineIDsSync(test: self, allowedMachineIDs: machineIDs, accountIsDemo: false))
 
         print("preparing A")
 
         print("preparing A")
-        let (aPeerID, aPermanentInfo, aPermanentInfoSig, _, _, error) = containerA.prepareSync(test: self, epoch: 1, machineID: "aaa", bottleSalt: "123456789", bottleID: UUID().uuidString, modelID: "iPhone1,1")
+        let (aPeerID, aPermanentInfo, aPermanentInfoSig, _, _, _, _, error) = containerA.prepareSync(test: self, epoch: 1, machineID: "aaa", bottleSalt: "123456789", bottleID: UUID().uuidString, modelID: "iPhone1,1")
         do {
             let state = containerA.getStateSync(test: self)
             XCTAssertFalse( state.bottles.filter { $0.peerID == aPeerID } .isEmpty, "should have a bottle for peer")
         do {
             let state = containerA.getStateSync(test: self)
             XCTAssertFalse( state.bottles.filter { $0.peerID == aPeerID } .isEmpty, "should have a bottle for peer")
@@ -573,7 +579,7 @@ class TrustedPeersHelperUnitTests: XCTestCase {
             let (dict, error) = containerA.dumpSync(test: self)
             XCTAssertNil(error)
             XCTAssertNotNil(dict)
             let (dict, error) = containerA.dumpSync(test: self)
             XCTAssertNil(error)
             XCTAssertNotNil(dict)
-            let peers: Array<Any> = dict!["peers"] as! Array<Any>
+            let peers: [Any] = dict!["peers"] as! [Any]
             XCTAssertEqual(0, peers.count)
         }
     }
             XCTAssertEqual(0, peers.count)
         }
     }
@@ -583,9 +589,9 @@ class TrustedPeersHelperUnitTests: XCTestCase {
         let containerA = try Container(name: ContainerName(container: "a", context: OTDefaultContext), persistentStoreDescription: description, cuttlefish: cuttlefish)
 
         let machineIDs = Set(["aaa"])
         let containerA = try Container(name: ContainerName(container: "a", context: OTDefaultContext), persistentStoreDescription: description, cuttlefish: cuttlefish)
 
         let machineIDs = Set(["aaa"])
-        XCTAssertNil(containerA.setAllowedMachineIDsSync(test: self, allowedMachineIDs: machineIDs))
+        XCTAssertNil(containerA.setAllowedMachineIDsSync(test: self, allowedMachineIDs: machineIDs, accountIsDemo: false))
 
 
-        let (aPeerID, aPermanentInfo, aPermanentInfoSig, _, _, error) = containerA.prepareSync(test: self, epoch: 1, machineID: "aaa", bottleSalt: "123456789", bottleID: UUID().uuidString, modelID: "iPhone1,1")
+        let (aPeerID, aPermanentInfo, aPermanentInfoSig, _, _, _, _, error) = containerA.prepareSync(test: self, epoch: 1, machineID: "aaa", bottleSalt: "123456789", bottleID: UUID().uuidString, modelID: "iPhone1,1")
         do {
             let state = containerA.getStateSync(test: self)
             XCTAssertFalse( state.bottles.filter { $0.peerID == aPeerID } .isEmpty, "should have a bottle for peer")
         do {
             let state = containerA.getStateSync(test: self)
             XCTAssertFalse( state.bottles.filter { $0.peerID == aPeerID } .isEmpty, "should have a bottle for peer")
@@ -634,26 +640,26 @@ class TrustedPeersHelperUnitTests: XCTestCase {
         let containerB = try Container(name: ContainerName(container: "b", context: OTDefaultContext), persistentStoreDescription: description, cuttlefish: cuttlefish)
         let containerC = try Container(name: ContainerName(container: "c", context: OTDefaultContext), persistentStoreDescription: description, cuttlefish: cuttlefish)
 
         let containerB = try Container(name: ContainerName(container: "b", context: OTDefaultContext), persistentStoreDescription: description, cuttlefish: cuttlefish)
         let containerC = try Container(name: ContainerName(container: "c", context: OTDefaultContext), persistentStoreDescription: description, cuttlefish: cuttlefish)
 
-        XCTAssertNil(containerA.setAllowedMachineIDsSync(test: self, allowedMachineIDs: Set(["aaa", "bbb", "ccc"])))
-        XCTAssertNil(containerB.setAllowedMachineIDsSync(test: self, allowedMachineIDs: Set(["aaa", "bbb", "ccc"])))
-        XCTAssertNil(containerC.setAllowedMachineIDsSync(test: self, allowedMachineIDs: Set(["aaa", "bbb", "ccc"])))
+        XCTAssertNil(containerA.setAllowedMachineIDsSync(test: self, allowedMachineIDs: Set(["aaa", "bbb", "ccc"]), accountIsDemo: false))
+        XCTAssertNil(containerB.setAllowedMachineIDsSync(test: self, allowedMachineIDs: Set(["aaa", "bbb", "ccc"]), accountIsDemo: false))
+        XCTAssertNil(containerC.setAllowedMachineIDsSync(test: self, allowedMachineIDs: Set(["aaa", "bbb", "ccc"]), accountIsDemo: false))
 
         print("preparing")
 
         print("preparing")
-        let (peerID, _, _, _, _, _) = containerA.prepareSync(test: self, epoch: 1, machineID: "aaa", bottleSalt: "123456789", bottleID: UUID().uuidString, modelID: "iPhone1,1")
+        let (peerID, _, _, _, _, _, _, _) = containerA.prepareSync(test: self, epoch: 1, machineID: "aaa", bottleSalt: "123456789", bottleID: UUID().uuidString, modelID: "iPhone1,1")
         do {
             let state = containerA.getStateSync(test: self)
             XCTAssertFalse( state.bottles.filter { $0.peerID == peerID } .isEmpty, "should have a bottle for peer")
             let secret = containerA.loadSecretSync(test: self, label: peerID!)
             XCTAssertNotNil(secret, "secret should not be nil")
         }
         do {
             let state = containerA.getStateSync(test: self)
             XCTAssertFalse( state.bottles.filter { $0.peerID == peerID } .isEmpty, "should have a bottle for peer")
             let secret = containerA.loadSecretSync(test: self, label: peerID!)
             XCTAssertNotNil(secret, "secret should not be nil")
         }
-        let (bPeerID, bPermanentInfo, bPermanentInfoSig, bStableInfo, bStableInfoSig, _) = containerB.prepareSync(test: self, epoch: 1, machineID: "bbb", bottleSalt: "123456789", bottleID: UUID().uuidString, modelID: "iPhone1,1")
+        let (bPeerID, bPermanentInfo, bPermanentInfoSig, bStableInfo, bStableInfoSig, _, _, _) = containerB.prepareSync(test: self, epoch: 1, machineID: "bbb", bottleSalt: "123456789", bottleID: UUID().uuidString, modelID: "iPhone1,1")
         do {
             let state = containerB.getStateSync(test: self)
             XCTAssertFalse( state.bottles.filter { $0.peerID == bPeerID } .isEmpty, "should have a bottle for peer")
             let secret = containerB.loadSecretSync(test: self, label: bPeerID!)
             XCTAssertNotNil(secret, "secret should not be nil")
         }
         do {
             let state = containerB.getStateSync(test: self)
             XCTAssertFalse( state.bottles.filter { $0.peerID == bPeerID } .isEmpty, "should have a bottle for peer")
             let secret = containerB.loadSecretSync(test: self, label: bPeerID!)
             XCTAssertNotNil(secret, "secret should not be nil")
         }
-        let (cPeerID, cPermanentInfo, cPermanentInfoSig, cStableInfo, cStableInfoSig, _) = containerC.prepareSync(test: self, epoch: 1, machineID: "ccc", bottleSalt: "123456789", bottleID: UUID().uuidString, modelID: "iPhone1,1")
+        let (cPeerID, cPermanentInfo, cPermanentInfoSig, cStableInfo, cStableInfoSig, _, _, _) = containerC.prepareSync(test: self, epoch: 1, machineID: "ccc", bottleSalt: "123456789", bottleID: UUID().uuidString, modelID: "iPhone1,1")
         do {
             let state = containerC.getStateSync(test: self)
             XCTAssertFalse( state.bottles.filter { $0.peerID == cPeerID } .isEmpty, "should have a bottle for peer")
         do {
             let state = containerC.getStateSync(test: self)
             XCTAssertFalse( state.bottles.filter { $0.peerID == cPeerID } .isEmpty, "should have a bottle for peer")
@@ -743,53 +749,62 @@ class TrustedPeersHelperUnitTests: XCTestCase {
     func testFetchPolicyDocuments() throws {
 
         // 1 is known locally via builtin, 3 is not but is known to cuttlefish
     func testFetchPolicyDocuments() throws {
 
         // 1 is known locally via builtin, 3 is not but is known to cuttlefish
-        let policies =
-            [
-                1: ("SHA256:TLXrcQmY4ue3oP5pCX1pwsi9BF8cKfohlJBilCroeBs=",
-                    "CAESDgoGaVBob25lEgRmdWxsEgwKBGlQYWQSBGZ1bGwSCwoDTWFjEgRmdWxsEgwKBGlNYWMSBGZ1bGwSDQoHQXBwbGVUVhICdHYS" +
-                        "DgoFV2F0Y2gSBXdhdGNoGhEKCVBDU0VzY3JvdxIEZnVsbBoXCgRXaUZpEgRmdWxsEgJ0dhIFd2F0Y2gaGQoRU2FmYXJpQ3JlZGl0" +
-                    "Q2FyZHMSBGZ1bGwiDAoEZnVsbBIEZnVsbCIUCgV3YXRjaBIEZnVsbBIFd2F0Y2giDgoCdHYSBGZ1bGwSAnR2"),
-                3: ("SHA256:JZzazSuHXrUhiOfSgElsg6vYKpnvvEPVpciR8FewRWg=",
-                     "CAMSDgoGaVBob25lEgRmdWxsEgwKBGlQYWQSBGZ1bGwSCwoDTWFjEgRmdWxsEgwKBGlNYWMSBGZ1bGwSDQoHQXBwbGVUVhICdHYSDgoFV2F0Y2gSBXdhdGNoEhcKDkF1ZGlvQWNjZXNzb3J5EgVhdWRpbxocCg1EZXZpY2VQYWlyaW5nEgRmdWxsEgV3YXRjaBoXCghBcHBsZVBheRIEZnVsbBIFd2F0Y2gaJAoVUHJvdGVjdGVkQ2xvdWRTdG9yYWdlEgRmdWxsEgV3YXRjaBoXCghCYWNrc3RvcBIEZnVsbBIFd2F0Y2gaGQoKQXV0b1VubG9jaxIEZnVsbBIFd2F0Y2gaHwoQU2VjdXJlT2JqZWN0U3luYxIEZnVsbBIFd2F0Y2gaIAoRU2FmYXJpQ3JlZGl0Q2FyZHMSBGZ1bGwSBXdhdGNoGhMKBEhvbWUSBGZ1bGwSBXdhdGNoGh4KD1NhZmFyaVBhc3N3b3JkcxIEZnVsbBIFd2F0Y2gaGwoMQXBwbGljYXRpb25zEgRmdWxsEgV3YXRjaBoVCgZFbmdyYW0SBGZ1bGwSBXdhdGNoGi0KE0xpbWl0ZWRQZWVyc0FsbG93ZWQSBGZ1bGwSBXdhdGNoEgJ0dhIFYXVkaW8aFgoHTWFuYXRlZRIEZnVsbBIFd2F0Y2gaHgoEV2lGaRIEZnVsbBIFd2F0Y2gSAnR2EgVhdWRpbxoVCgZIZWFsdGgSBGZ1bGwSBXdhdGNoIhMKBGZ1bGwSBGZ1bGwSBXdhdGNoIhsKBWF1ZGlvEgRmdWxsEgV3YXRjaBIFYXVkaW8iFAoFd2F0Y2gSBGZ1bGwSBXdhdGNoIhUKAnR2EgRmdWxsEgV3YXRjaBICdHYyIgoWAAQiEgIEdndodAoKXkFwcGxlUGF5JBIIQXBwbGVQYXkyJgoYAAQiFAIEdndodAoMXkF1dG9VbmxvY2skEgpBdXRvVW5sb2NrMh4KFAAEIhACBHZ3aHQKCF5FbmdyYW0kEgZFbmdyYW0yHgoUAAQiEAIEdndodAoIXkhlYWx0aCQSBkhlYWx0aDIaChIABCIOAgR2d2h0CgZeSG9tZSQSBEhvbWUyIAoVAAQiEQIEdndodAoJXk1hbmF0ZWUkEgdNYW5hdGVlMjgKIQAEIh0CBHZ3aHQKFV5MaW1pdGVkUGVlcnNBbGxvd2VkJBITTGltaXRlZFBlZXJzQWxsb3dlZDJdClAAAhIeAAQiGgIEdndodAoSXkNvbnRpbnVpdHlVbmxvY2skEhUABCIRAgR2d2h0CgleSG9tZUtpdCQSFQAEIhECBHZ3aHQKCV5BcHBsZVRWJBIJTm90U3luY2VkMisKGwAEIhcCBGFncnAKD15bMC05QS1aXXsxMH1cLhIMQXBwbGljYXRpb25zMsUBCrABAAISNAABChMABCIPAgVjbGFzcwoGXmdlbnAkChsABCIXAgRhZ3JwCg9eY29tLmFwcGxlLnNiZCQSPQABChMABCIPAgVjbGFzcwoGXmtleXMkCiQABCIgAgRhZ3JwChheY29tLmFwcGxlLnNlY3VyaXR5LnNvcyQSGQAEIhUCBHZ3aHQKDV5CYWNrdXBCYWdWMCQSHAAEIhgCBHZ3aHQKEF5pQ2xvdWRJZGVudGl0eSQSEFNlY3VyZU9iamVjdFN5bmMyYwpbAAISEgAEIg4CBHZ3aHQKBl5XaUZpJBJDAAEKEwAEIg8CBWNsYXNzCgZeZ2VucCQKEwAEIg8CBGFncnAKB15hcHBsZSQKFQAEIhECBHN2Y2UKCV5BaXJQb3J0JBIEV2lGaTLbAgrBAgACEhkABCIVAgR2d2h0Cg1eUENTQ2xvdWRLaXQkEhcABCITAgR2d2h0CgteUENTRXNjcm93JBIUAAQiEAIEdndodAoIXlBDU0ZERSQSGQAEIhUCBHZ3aHQKDV5QQ1NGZWxkc3BhciQSGQAEIhUCBHZ3aHQKDV5QQ1NNYWlsRHJvcCQSGgAEIhYCBHZ3aHQKDl5QQ1NNYXN0ZXJLZXkkEhYABCISAgR2d2h0CgpeUENTTm90ZXMkEhcABCITAgR2d2h0CgteUENTUGhvdG9zJBIYAAQiFAIEdndodAoMXlBDU1NoYXJpbmckEh0ABCIZAgR2d2h0ChFeUENTaUNsb3VkQmFja3VwJBIcAAQiGAIEdndodAoQXlBDU2lDbG91ZERyaXZlJBIZAAQiFQIEdndodAoNXlBDU2lNZXNzYWdlJBIVUHJvdGVjdGVkQ2xvdWRTdG9yYWdlMkAKKwAEIicCBGFncnAKH15jb20uYXBwbGUuc2FmYXJpLmNyZWRpdC1jYXJkcyQSEVNhZmFyaUNyZWRpdENhcmRzMjQKIQAEIh0CBGFncnAKFV5jb20uYXBwbGUuY2ZuZXR3b3JrJBIPU2FmYXJpUGFzc3dvcmRzMm0KXAACEh4ABCIaAgR2d2h0ChJeQWNjZXNzb3J5UGFpcmluZyQSGgAEIhYCBHZ3aHQKDl5OYW5vUmVnaXN0cnkkEhwABCIYAgR2d2h0ChBeV2F0Y2hNaWdyYXRpb24kEg1EZXZpY2VQYWlyaW5nMi0KIQAEIh0CBGFncnAKFV5jb20uYXBwbGUuY2ZuZXR3b3JrJBIIQmFja3N0b3A="),
-                ]
-        let (request1, data1) = policies[1]!
-        let (request3, data3) = policies[3]!
+
+        let missingTuple = TPPolicyVersion(version: 900, hash: "not a hash")
+
+        let policy1Tuple = TPPolicyVersion(version: 1, hash: "SHA256:TLXrcQmY4ue3oP5pCX1pwsi9BF8cKfohlJBilCroeBs=")
+        let policy1Data = Data(base64Encoded: "CAESDgoGaVBob25lEgRmdWxsEgwKBGlQYWQSBGZ1bGwSCwoDTWFjEgRmdWxsEgwKBGlNYWMSBGZ1bGwSDQoHQXBwbGVUVhICdHYS" +
+                                              "DgoFV2F0Y2gSBXdhdGNoGhEKCVBDU0VzY3JvdxIEZnVsbBoXCgRXaUZpEgRmdWxsEgJ0dhIFd2F0Y2gaGQoRU2FmYXJpQ3JlZGl0" +
+                                              "Q2FyZHMSBGZ1bGwiDAoEZnVsbBIEZnVsbCIUCgV3YXRjaBIEZnVsbBIFd2F0Y2giDgoCdHYSBGZ1bGwSAnR2")!
+
+        let policy3Tuple = TPPolicyVersion(version: 3, hash: "SHA256:JZzazSuHXrUhiOfSgElsg6vYKpnvvEPVpciR8FewRWg=")
+        let policy3Data = Data(base64Encoded: "CAMSDgoGaVBob25lEgRmdWxsEgwKBGlQYWQSBGZ1bGwSCwoDTWFjEgRmdWxsEgwKBGlNYWMSBGZ1bGwSDQoHQXBwbGVUVhICdHYSDgoFV2F0Y2gSBXdhdGNoEhcKDkF1ZGlvQWNjZXNzb3J5EgVhdWRpbxocCg1EZXZpY2VQYWlyaW5nEgRmdWxsEgV3YXRjaBoXCghBcHBsZVBheRIEZnVsbBIFd2F0Y2gaJAoVUHJvdGVjdGVkQ2xvdWRTdG9yYWdlEgRmdWxsEgV3YXRjaBoXCghCYWNrc3RvcBIEZnVsbBIFd2F0Y2gaGQoKQXV0b1VubG9jaxIEZnVsbBIFd2F0Y2gaHwoQU2VjdXJlT2JqZWN0U3luYxIEZnVsbBIFd2F0Y2gaIAoRU2FmYXJpQ3JlZGl0Q2FyZHMSBGZ1bGwSBXdhdGNoGhMKBEhvbWUSBGZ1bGwSBXdhdGNoGh4KD1NhZmFyaVBhc3N3b3JkcxIEZnVsbBIFd2F0Y2gaGwoMQXBwbGljYXRpb25zEgRmdWxsEgV3YXRjaBoVCgZFbmdyYW0SBGZ1bGwSBXdhdGNoGi0KE0xpbWl0ZWRQZWVyc0FsbG93ZWQSBGZ1bGwSBXdhdGNoEgJ0dhIFYXVkaW8aFgoHTWFuYXRlZRIEZnVsbBIFd2F0Y2gaHgoEV2lGaRIEZnVsbBIFd2F0Y2gSAnR2EgVhdWRpbxoVCgZIZWFsdGgSBGZ1bGwSBXdhdGNoIhMKBGZ1bGwSBGZ1bGwSBXdhdGNoIhsKBWF1ZGlvEgRmdWxsEgV3YXRjaBIFYXVkaW8iFAoFd2F0Y2gSBGZ1bGwSBXdhdGNoIhUKAnR2EgRmdWxsEgV3YXRjaBICdHYyIgoWAAQiEgIEdndodAoKXkFwcGxlUGF5JBIIQXBwbGVQYXkyJgoYAAQiFAIEdndodAoMXkF1dG9VbmxvY2skEgpBdXRvVW5sb2NrMh4KFAAEIhACBHZ3aHQKCF5FbmdyYW0kEgZFbmdyYW0yHgoUAAQiEAIEdndodAoIXkhlYWx0aCQSBkhlYWx0aDIaChIABCIOAgR2d2h0CgZeSG9tZSQSBEhvbWUyIAoVAAQiEQIEdndodAoJXk1hbmF0ZWUkEgdNYW5hdGVlMjgKIQAEIh0CBHZ3aHQKFV5MaW1pdGVkUGVlcnNBbGxvd2VkJBITTGltaXRlZFBlZXJzQWxsb3dlZDJdClAAAhIeAAQiGgIEdndodAoSXkNvbnRpbnVpdHlVbmxvY2skEhUABCIRAgR2d2h0CgleSG9tZUtpdCQSFQAEIhECBHZ3aHQKCV5BcHBsZVRWJBIJTm90U3luY2VkMisKGwAEIhcCBGFncnAKD15bMC05QS1aXXsxMH1cLhIMQXBwbGljYXRpb25zMsUBCrABAAISNAABChMABCIPAgVjbGFzcwoGXmdlbnAkChsABCIXAgRhZ3JwCg9eY29tLmFwcGxlLnNiZCQSPQABChMABCIPAgVjbGFzcwoGXmtleXMkCiQABCIgAgRhZ3JwChheY29tLmFwcGxlLnNlY3VyaXR5LnNvcyQSGQAEIhUCBHZ3aHQKDV5CYWNrdXBCYWdWMCQSHAAEIhgCBHZ3aHQKEF5pQ2xvdWRJZGVudGl0eSQSEFNlY3VyZU9iamVjdFN5bmMyYwpbAAISEgAEIg4CBHZ3aHQKBl5XaUZpJBJDAAEKEwAEIg8CBWNsYXNzCgZeZ2VucCQKEwAEIg8CBGFncnAKB15hcHBsZSQKFQAEIhECBHN2Y2UKCV5BaXJQb3J0JBIEV2lGaTLbAgrBAgACEhkABCIVAgR2d2h0Cg1eUENTQ2xvdWRLaXQkEhcABCITAgR2d2h0CgteUENTRXNjcm93JBIUAAQiEAIEdndodAoIXlBDU0ZERSQSGQAEIhUCBHZ3aHQKDV5QQ1NGZWxkc3BhciQSGQAEIhUCBHZ3aHQKDV5QQ1NNYWlsRHJvcCQSGgAEIhYCBHZ3aHQKDl5QQ1NNYXN0ZXJLZXkkEhYABCISAgR2d2h0CgpeUENTTm90ZXMkEhcABCITAgR2d2h0CgteUENTUGhvdG9zJBIYAAQiFAIEdndodAoMXlBDU1NoYXJpbmckEh0ABCIZAgR2d2h0ChFeUENTaUNsb3VkQmFja3VwJBIcAAQiGAIEdndodAoQXlBDU2lDbG91ZERyaXZlJBIZAAQiFQIEdndodAoNXlBDU2lNZXNzYWdlJBIVUHJvdGVjdGVkQ2xvdWRTdG9yYWdlMkAKKwAEIicCBGFncnAKH15jb20uYXBwbGUuc2FmYXJpLmNyZWRpdC1jYXJkcyQSEVNhZmFyaUNyZWRpdENhcmRzMjQKIQAEIh0CBGFncnAKFV5jb20uYXBwbGUuY2ZuZXR3b3JrJBIPU2FmYXJpUGFzc3dvcmRzMm0KXAACEh4ABCIaAgR2d2h0ChJeQWNjZXNzb3J5UGFpcmluZyQSGgAEIhYCBHZ3aHQKDl5OYW5vUmVnaXN0cnkkEhwABCIYAgR2d2h0ChBeV2F0Y2hNaWdyYXRpb24kEg1EZXZpY2VQYWlyaW5nMi0KIQAEIh0CBGFncnAKFV5jb20uYXBwbGUuY2ZuZXR3b3JrJBIIQmFja3N0b3A=")!
 
         let description = tmpStoreDescription(name: "container.db")
         let container = try Container(name: ContainerName(container: "a", context: OTDefaultContext), persistentStoreDescription: description, cuttlefish: cuttlefish)
 
         // nothing
 
         let description = tmpStoreDescription(name: "container.db")
         let container = try Container(name: ContainerName(container: "a", context: OTDefaultContext), persistentStoreDescription: description, cuttlefish: cuttlefish)
 
         // nothing
-        let (response1, error1) = container.fetchPolicyDocumentsSync(test: self, keys: [:])
+        let (response1, error1) = container.fetchPolicyDocumentsSync(test: self, versions: [])
         XCTAssertNil(error1, "No error querying for an empty list")
         XCTAssertEqual(response1, [:], "Received empty dictionary")
 
         // local stuff
         XCTAssertNil(error1, "No error querying for an empty list")
         XCTAssertEqual(response1, [:], "Received empty dictionary")
 
         // local stuff
-        let (response2, error2) = container.fetchPolicyDocumentsSync(test: self, keys: [1: request1])
-        XCTAssertNil(error2, "No error getting locally known policy document")
-        XCTAssertEqual(response2?.count, 1, "Got one response for request for one locally known policy")
-        XCTAssertEqual(response2?[1]?[0], request1, "retrieved hash matches request hash")
-        XCTAssertEqual(response2?[1]?[1], data1, "retrieved data matches known data")
+        do {
+            let (response2, error2) = container.fetchPolicyDocumentsSync(test: self, versions: Set([policy1Tuple]))
+            XCTAssertNil(error2, "No error getting locally known policy document")
+            XCTAssertEqual(response2?.count, 1, "Got one response for request for one locally known policy")
+            XCTAssert(response2?.keys.contains(policy1Tuple) ?? false, "Should have retrieved request for policy1")
+            XCTAssertEqual(response2?[policy1Tuple], policy1Data, "retrieved data matches known data")
+        }
 
         // fetch remote
 
         // fetch remote
-        let (response3, error3) = container.fetchPolicyDocumentsSync(test: self, keys: [1: request1, 3: request3])
-        XCTAssertNil(error3, "No error fetching local + remote policy")
-        XCTAssertEqual(response3?.count, 2, "Got two responses for local+remote policy request")
-        XCTAssertEqual(response3?[1]?[0], request1, "retrieved hash matches local request hash")
-        XCTAssertEqual(response3?[1]?[1], data1, "retrieved data matches local known data")
-        XCTAssertEqual(response3?[3]?[0], request3, "retrieved hash matches remote request hash")
-        XCTAssertEqual(response3?[3]?[1], data3, "retrieved data matches remote known data")
+        do {
+            let (response3, error3) = container.fetchPolicyDocumentsSync(test: self, versions: [policy1Tuple, policy3Tuple])
+            XCTAssertNil(error3, "No error fetching local + remote policy")
+            XCTAssertEqual(response3?.count, 2, "Got two responses for local+remote policy request")
+
+            XCTAssert(response3?.keys.contains(policy1Tuple) ?? false, "Should have retrieved request for policy1")
+            XCTAssertEqual(response3?[policy1Tuple], policy1Data, "retrieved data matches known data")
+
+            XCTAssert(response3?.keys.contains(policy3Tuple) ?? false, "Should have retrieved request for policy3")
+            XCTAssertEqual(response3?[policy3Tuple], policy3Data, "retrieved data matches known data")
+        }
 
         // invalid version
 
         // invalid version
-        let (response4, error4) = container.fetchPolicyDocumentsSync(test: self, keys: [9000: "not a hash"])
-        XCTAssertNil(response4, "No response for wrong [version: hash] combination")
-        XCTAssertNotNil(error4, "Expected error fetching invalid policy version")
+        do {
+            let (response4, error4) = container.fetchPolicyDocumentsSync(test: self, versions: Set([missingTuple]))
+            XCTAssertNil(response4, "No response for wrong [version: hash] combination")
+            XCTAssertNotNil(error4, "Expected error fetching invalid policy version")
+        }
 
         // valid + invalid
 
         // valid + invalid
-        let (response5, error5) = container.fetchPolicyDocumentsSync(test: self, keys: [9000: "not a hash",
-                                                                                        1: request1,
-                                                                                        3: request3, ])
-        XCTAssertNil(response5, "No response for valid + unknown [version: hash] combination")
-        XCTAssertNotNil(error5, "Expected error fetching valid + invalid policy version")
+        do {
+            let (response5, error5) = container.fetchPolicyDocumentsSync(test: self, versions: Set([missingTuple,
+                                                                                                    policy1Tuple,
+                                                                                                    policy3Tuple, ]))
+            XCTAssertNil(response5, "No response for valid + unknown [version: hash] combination")
+            XCTAssertNotNil(error5, "Expected error fetching valid + invalid policy version")
+        }
     }
 
     func testEscrowKeys() throws {
     }
 
     func testEscrowKeys() throws {
@@ -834,25 +849,25 @@ class TrustedPeersHelperUnitTests: XCTestCase {
         let secret = secretString.data(using: .utf8)
 
         do {
         let secret = secretString.data(using: .utf8)
 
         do {
-            let testv1 = try EscrowKeys.generateEscrowKey(keyType: escrowKeyType.kOTEscrowKeySigning, masterSecret: secret!, bottleSalt: testDSID)
+            let testv1 = try EscrowKeys.generateEscrowKey(keyType: EscrowKeyType.kOTEscrowKeySigning, masterSecret: secret!, bottleSalt: testDSID)
             XCTAssertEqual(testv1, signingKey_384, "signing keys should match")
 
             XCTAssertEqual(testv1, signingKey_384, "signing keys should match")
 
-            let testv2 = try EscrowKeys.generateEscrowKey(keyType: escrowKeyType.kOTEscrowKeyEncryption, masterSecret: secret!, bottleSalt: testDSID)
+            let testv2 = try EscrowKeys.generateEscrowKey(keyType: EscrowKeyType.kOTEscrowKeyEncryption, masterSecret: secret!, bottleSalt: testDSID)
             XCTAssertEqual(testv2, encryptionKey_384, "encryption keys should match")
 
             XCTAssertEqual(testv2, encryptionKey_384, "encryption keys should match")
 
-            let testv3 = try EscrowKeys.generateEscrowKey(keyType: escrowKeyType.kOTEscrowKeySymmetric, masterSecret: secret!, bottleSalt: testDSID)
+            let testv3 = try EscrowKeys.generateEscrowKey(keyType: EscrowKeyType.kOTEscrowKeySymmetric, masterSecret: secret!, bottleSalt: testDSID)
             XCTAssertEqual(testv3, symmetricKey_384, "symmetric keys should match")
 
             let newSecretString = "I'm f secretI'm a secretI'm a secretI'm a secretI'm a secretI'm a secret"
             let newSecret = newSecretString.data(using: .utf8)
 
             XCTAssertEqual(testv3, symmetricKey_384, "symmetric keys should match")
 
             let newSecretString = "I'm f secretI'm a secretI'm a secretI'm a secretI'm a secretI'm a secret"
             let newSecret = newSecretString.data(using: .utf8)
 
-            let testv4 = try EscrowKeys.generateEscrowKey(keyType: escrowKeyType.kOTEscrowKeySigning, masterSecret: newSecret!, bottleSalt: testDSID)
+            let testv4 = try EscrowKeys.generateEscrowKey(keyType: EscrowKeyType.kOTEscrowKeySigning, masterSecret: newSecret!, bottleSalt: testDSID)
             XCTAssertNotEqual(testv4, signingKey_384, "signing keys should not match")
 
             XCTAssertNotEqual(testv4, signingKey_384, "signing keys should not match")
 
-            let testv5 = try EscrowKeys.generateEscrowKey(keyType: escrowKeyType.kOTEscrowKeyEncryption, masterSecret: newSecret!, bottleSalt: testDSID)
+            let testv5 = try EscrowKeys.generateEscrowKey(keyType: EscrowKeyType.kOTEscrowKeyEncryption, masterSecret: newSecret!, bottleSalt: testDSID)
             XCTAssertNotEqual(testv5, encryptionKey_384, "encryption keys should not match")
 
             XCTAssertNotEqual(testv5, encryptionKey_384, "encryption keys should not match")
 
-            let testv6 = try EscrowKeys.generateEscrowKey(keyType: escrowKeyType.kOTEscrowKeySymmetric, masterSecret: newSecret!, bottleSalt: testDSID)
+            let testv6 = try EscrowKeys.generateEscrowKey(keyType: EscrowKeyType.kOTEscrowKeySymmetric, masterSecret: newSecret!, bottleSalt: testDSID)
             XCTAssertNotEqual(testv6, symmetricKey_384, "symmetric keys should not match")
         } catch {
             XCTFail("error testing escrow key test vectors \(error)")
             XCTAssertNotEqual(testv6, symmetricKey_384, "symmetric keys should not match")
         } catch {
             XCTFail("error testing escrow key test vectors \(error)")
@@ -865,19 +880,19 @@ class TrustedPeersHelperUnitTests: XCTestCase {
         let secret = secretString.data(using: .utf8)
 
         do {
         let secret = secretString.data(using: .utf8)
 
         do {
-            let testv1 = try RecoveryKeySet.generateRecoveryKey(keyType: recoveryKeyType.kOTRecoveryKeySigning, masterSecret: secret!, recoverySalt: testDSID)
+            let testv1 = try RecoveryKeySet.generateRecoveryKey(keyType: RecoveryKeyType.kOTRecoveryKeySigning, masterSecret: secret!, recoverySalt: testDSID)
             XCTAssertEqual(testv1, recovery_signingKey_384, "signing keys should match")
 
             XCTAssertEqual(testv1, recovery_signingKey_384, "signing keys should match")
 
-            let testv2 = try RecoveryKeySet.generateRecoveryKey(keyType: recoveryKeyType.kOTRecoveryKeyEncryption, masterSecret: secret!, recoverySalt: testDSID)
+            let testv2 = try RecoveryKeySet.generateRecoveryKey(keyType: RecoveryKeyType.kOTRecoveryKeyEncryption, masterSecret: secret!, recoverySalt: testDSID)
             XCTAssertEqual(testv2, recovery_encryptionKey_384, "encryption keys should match")
 
             let newSecretString = "I'm f secretI'm a secretI'm a secretI'm a secretI'm a secretI'm a secret"
             let newSecret = newSecretString.data(using: .utf8)
 
             XCTAssertEqual(testv2, recovery_encryptionKey_384, "encryption keys should match")
 
             let newSecretString = "I'm f secretI'm a secretI'm a secretI'm a secretI'm a secretI'm a secret"
             let newSecret = newSecretString.data(using: .utf8)
 
-            let testv4 = try RecoveryKeySet.generateRecoveryKey(keyType: recoveryKeyType.kOTRecoveryKeySigning, masterSecret: newSecret!, recoverySalt: testDSID)
+            let testv4 = try RecoveryKeySet.generateRecoveryKey(keyType: RecoveryKeyType.kOTRecoveryKeySigning, masterSecret: newSecret!, recoverySalt: testDSID)
             XCTAssertNotEqual(testv4, recovery_signingKey_384, "signing keys should not match")
 
             XCTAssertNotEqual(testv4, recovery_signingKey_384, "signing keys should not match")
 
-            let testv5 = try RecoveryKeySet.generateRecoveryKey(keyType: recoveryKeyType.kOTRecoveryKeyEncryption, masterSecret: newSecret!, recoverySalt: testDSID)
+            let testv5 = try RecoveryKeySet.generateRecoveryKey(keyType: RecoveryKeyType.kOTRecoveryKeyEncryption, masterSecret: newSecret!, recoverySalt: testDSID)
             XCTAssertNotEqual(testv5, recovery_encryptionKey_384, "encryption keys should not match")
         } catch {
             XCTFail("error testing RecoveryKey test vectors \(error)")
             XCTAssertNotEqual(testv5, recovery_encryptionKey_384, "encryption keys should not match")
         } catch {
             XCTFail("error testing RecoveryKey test vectors \(error)")
@@ -892,11 +907,11 @@ class TrustedPeersHelperUnitTests: XCTestCase {
         let containerB = try Container(name: ContainerName(container: "b", context: OTDefaultContext), persistentStoreDescription: description, cuttlefish: cuttlefish)
 
         let machineIDs = Set(["aaa", "bbb"])
         let containerB = try Container(name: ContainerName(container: "b", context: OTDefaultContext), persistentStoreDescription: description, cuttlefish: cuttlefish)
 
         let machineIDs = Set(["aaa", "bbb"])
-        XCTAssertNil(containerA.setAllowedMachineIDsSync(test: self, allowedMachineIDs: machineIDs))
-        XCTAssertNil(containerB.setAllowedMachineIDsSync(test: self, allowedMachineIDs: machineIDs))
+        XCTAssertNil(containerA.setAllowedMachineIDsSync(test: self, allowedMachineIDs: machineIDs, accountIsDemo: false))
+        XCTAssertNil(containerB.setAllowedMachineIDsSync(test: self, allowedMachineIDs: machineIDs, accountIsDemo: false))
 
         print("preparing A")
 
         print("preparing A")
-        let (aPeerID, aPermanentInfo, aPermanentInfoSig, _, _, error) =
+        let (aPeerID, aPermanentInfo, aPermanentInfoSig, _, _, _, _, error) =
             containerA.prepareSync(test: self, epoch: 1, machineID: "aaa", bottleSalt: "123456789", bottleID: UUID().uuidString, modelID: "iPhone1,1")
         do {
             var state = containerA.getStateSync(test: self)
             containerA.prepareSync(test: self, epoch: 1, machineID: "aaa", bottleSalt: "123456789", bottleID: UUID().uuidString, modelID: "iPhone1,1")
         do {
             var state = containerA.getStateSync(test: self)
@@ -932,7 +947,7 @@ class TrustedPeersHelperUnitTests: XCTestCase {
         _ = containerB.updateSync(test: self)
 
         print("preparing B")
         _ = containerB.updateSync(test: self)
 
         print("preparing B")
-        let (bPeerID, _, _, _, _, error2) =
+        let (bPeerID, _, _, _, _, _, _, error2) =
             containerB.prepareSync(test: self, epoch: 1, machineID: "bbb", bottleSalt: "123456789", bottleID: UUID().uuidString, modelID: "iPhone1,1")
         do {
             let state = containerB.getStateSync(test: self)
             containerB.prepareSync(test: self, epoch: 1, machineID: "bbb", bottleSalt: "123456789", bottleID: UUID().uuidString, modelID: "iPhone1,1")
         do {
             let state = containerB.getStateSync(test: self)
@@ -946,11 +961,13 @@ class TrustedPeersHelperUnitTests: XCTestCase {
         do {
             print("B prepares to join via bottle")
 
         do {
             print("B prepares to join via bottle")
 
-            let (bottlePeerID, errorPreflight) = containerB.preflightVouchWithBottleSync(test: self, bottleID: bottleA.bottleID!)
+            let (bottlePeerID, views, policy, errorPreflight) = containerB.preflightVouchWithBottleSync(test: self, bottleID: bottleA.bottleID!)
             XCTAssertNil(errorPreflight, "Should be no error preflighting a vouch with bottle")
             XCTAssertEqual(bottlePeerID, aPeerID, "Bottle should be for peer A")
             XCTAssertNil(errorPreflight, "Should be no error preflighting a vouch with bottle")
             XCTAssertEqual(bottlePeerID, aPeerID, "Bottle should be for peer A")
+            XCTAssertNotNil(views, "Should have a set of views to restore")
+            XCTAssertNotNil(policy, "Should have a policy")
 
 
-            let (voucherData, voucherSig, error3) = containerB.vouchWithBottleSync(test: self, b: bottleA.bottleID!, entropy: entropy, bottleSalt: "123456789", tlkShares: [])
+            let (voucherData, voucherSig, _, _, error3) = containerB.vouchWithBottleSync(test: self, b: bottleA.bottleID!, entropy: entropy, bottleSalt: "123456789", tlkShares: [])
 
             XCTAssertNil(error3)
             XCTAssertNotNil(voucherData)
 
             XCTAssertNil(error3)
             XCTAssertNotNil(voucherData)
@@ -960,7 +977,7 @@ class TrustedPeersHelperUnitTests: XCTestCase {
             assertNoTLKShareFor(peerID: bPeerID!, keyUUID: self.manateeKeySet.tlk.uuid, zoneID: CKRecordZone.ID(zoneName: "Manatee"))
 
             print("B joins")
             assertNoTLKShareFor(peerID: bPeerID!, keyUUID: self.manateeKeySet.tlk.uuid, zoneID: CKRecordZone.ID(zoneName: "Manatee"))
 
             print("B joins")
-            let (peerID, _, error) = containerB.joinSync(test: self, voucherData: voucherData!, voucherSig: voucherSig!, ckksKeys: [self.manateeKeySet], tlkShares: [])
+            let (peerID, _, _, _, error) = containerB.joinSync(test: self, voucherData: voucherData!, voucherSig: voucherSig!, ckksKeys: [self.manateeKeySet], tlkShares: [])
             XCTAssertNil(error)
             XCTAssertEqual(peerID, bPeerID!)
 
             XCTAssertNil(error)
             XCTAssertEqual(peerID, bPeerID!)
 
@@ -977,11 +994,11 @@ class TrustedPeersHelperUnitTests: XCTestCase {
         let containerB = try Container(name: ContainerName(container: "b", context: OTDefaultContext), persistentStoreDescription: description, cuttlefish: cuttlefish)
 
         let machineIDs = Set(["aaa", "bbb"])
         let containerB = try Container(name: ContainerName(container: "b", context: OTDefaultContext), persistentStoreDescription: description, cuttlefish: cuttlefish)
 
         let machineIDs = Set(["aaa", "bbb"])
-        XCTAssertNil(containerA.setAllowedMachineIDsSync(test: self, allowedMachineIDs: machineIDs))
-        XCTAssertNil(containerB.setAllowedMachineIDsSync(test: self, allowedMachineIDs: machineIDs))
+        XCTAssertNil(containerA.setAllowedMachineIDsSync(test: self, allowedMachineIDs: machineIDs, accountIsDemo: false))
+        XCTAssertNil(containerB.setAllowedMachineIDsSync(test: self, allowedMachineIDs: machineIDs, accountIsDemo: false))
 
         print("preparing A")
 
         print("preparing A")
-        let (aPeerID, aPermanentInfo, aPermanentInfoSig, _, _, error) =
+        let (aPeerID, aPermanentInfo, aPermanentInfoSig, _, _, _, _, error) =
             containerA.prepareSync(test: self, epoch: 1, machineID: "aaa", bottleSalt: "", bottleID: UUID().uuidString, modelID: "iPhone1,1")
         do {
             var state = containerA.getStateSync(test: self)
             containerA.prepareSync(test: self, epoch: 1, machineID: "aaa", bottleSalt: "", bottleID: UUID().uuidString, modelID: "iPhone1,1")
         do {
             var state = containerA.getStateSync(test: self)
@@ -1017,7 +1034,7 @@ class TrustedPeersHelperUnitTests: XCTestCase {
         _ = containerB.updateSync(test: self)
 
         print("preparing B")
         _ = containerB.updateSync(test: self)
 
         print("preparing B")
-        let (bPeerID, _, _, _, _, error2) =
+        let (bPeerID, _, _, _, _, _, _, error2) =
             containerB.prepareSync(test: self, epoch: 1, machineID: "bbb", bottleSalt: "123456789", bottleID: UUID().uuidString, modelID: "iPhone1,1")
         do {
             let state = containerB.getStateSync(test: self)
             containerB.prepareSync(test: self, epoch: 1, machineID: "bbb", bottleSalt: "123456789", bottleID: UUID().uuidString, modelID: "iPhone1,1")
         do {
             let state = containerB.getStateSync(test: self)
@@ -1031,11 +1048,13 @@ class TrustedPeersHelperUnitTests: XCTestCase {
         do {
             print("B prepares to join via bottle")
 
         do {
             print("B prepares to join via bottle")
 
-            let (bottlePeerID, errorPreflight) = containerB.preflightVouchWithBottleSync(test: self, bottleID: bottleA.bottleID!)
+            let (bottlePeerID, views, policy, errorPreflight) = containerB.preflightVouchWithBottleSync(test: self, bottleID: bottleA.bottleID!)
             XCTAssertNil(errorPreflight, "Should be no error preflighting a vouch with bottle")
             XCTAssertEqual(bottlePeerID, aPeerID, "Bottle should be for peer A")
             XCTAssertNil(errorPreflight, "Should be no error preflighting a vouch with bottle")
             XCTAssertEqual(bottlePeerID, aPeerID, "Bottle should be for peer A")
+            XCTAssertNotNil(views, "Should have a set of views to restore")
+            XCTAssertNotNil(policy, "Should have a policy")
 
 
-            let (voucherData, voucherSig, error3) = containerB.vouchWithBottleSync(test: self, b: bottleA.bottleID!, entropy: entropy, bottleSalt: "123456789", tlkShares: [])
+            let (voucherData, voucherSig, _, _, error3) = containerB.vouchWithBottleSync(test: self, b: bottleA.bottleID!, entropy: entropy, bottleSalt: "123456789", tlkShares: [])
 
             XCTAssertNil(error3)
             XCTAssertNotNil(voucherData)
 
             XCTAssertNil(error3)
             XCTAssertNotNil(voucherData)
@@ -1045,7 +1064,7 @@ class TrustedPeersHelperUnitTests: XCTestCase {
             assertNoTLKShareFor(peerID: bPeerID!, keyUUID: self.manateeKeySet.tlk.uuid, zoneID: CKRecordZone.ID(zoneName: "Manatee"))
 
             print("B joins")
             assertNoTLKShareFor(peerID: bPeerID!, keyUUID: self.manateeKeySet.tlk.uuid, zoneID: CKRecordZone.ID(zoneName: "Manatee"))
 
             print("B joins")
-            let (peerID, _, error) = containerB.joinSync(test: self, voucherData: voucherData!, voucherSig: voucherSig!, ckksKeys: [self.manateeKeySet], tlkShares: [])
+            let (peerID, _, _, _, error) = containerB.joinSync(test: self, voucherData: voucherData!, voucherSig: voucherSig!, ckksKeys: [self.manateeKeySet], tlkShares: [])
             XCTAssertNil(error)
             XCTAssertEqual(peerID, bPeerID!)
 
             XCTAssertNil(error)
             XCTAssertEqual(peerID, bPeerID!)
 
@@ -1061,11 +1080,11 @@ class TrustedPeersHelperUnitTests: XCTestCase {
         let containerB = try Container(name: ContainerName(container: "b", context: OTDefaultContext), persistentStoreDescription: description, cuttlefish: cuttlefish)
 
         let machineIDs = Set(["aaa", "bbb"])
         let containerB = try Container(name: ContainerName(container: "b", context: OTDefaultContext), persistentStoreDescription: description, cuttlefish: cuttlefish)
 
         let machineIDs = Set(["aaa", "bbb"])
-        XCTAssertNil(containerA.setAllowedMachineIDsSync(test: self, allowedMachineIDs: machineIDs))
-        XCTAssertNil(containerB.setAllowedMachineIDsSync(test: self, allowedMachineIDs: machineIDs))
+        XCTAssertNil(containerA.setAllowedMachineIDsSync(test: self, allowedMachineIDs: machineIDs, accountIsDemo: false))
+        XCTAssertNil(containerB.setAllowedMachineIDsSync(test: self, allowedMachineIDs: machineIDs, accountIsDemo: false))
 
         print("preparing A")
 
         print("preparing A")
-        let (aPeerID, aPermanentInfo, aPermanentInfoSig, _, _, error) =
+        let (aPeerID, aPermanentInfo, aPermanentInfoSig, _, _, _, _, error) =
             containerA.prepareSync(test: self, epoch: 1, machineID: "aaa", bottleSalt: "123456789", bottleID: UUID().uuidString, modelID: "iPhone1,1")
         do {
             let state = containerA.getStateSync(test: self)
             containerA.prepareSync(test: self, epoch: 1, machineID: "aaa", bottleSalt: "123456789", bottleID: UUID().uuidString, modelID: "iPhone1,1")
         do {
             let state = containerA.getStateSync(test: self)
@@ -1097,7 +1116,7 @@ class TrustedPeersHelperUnitTests: XCTestCase {
         _ = containerB.updateSync(test: self)
 
         print("preparing B")
         _ = containerB.updateSync(test: self)
 
         print("preparing B")
-        let (bPeerID, _, _, _, _, error2) =
+        let (bPeerID, _, _, _, _, _, _, error2) =
             containerB.prepareSync(test: self, epoch: 1, machineID: "bbb", bottleSalt: "123456789", bottleID: UUID().uuidString, modelID: "iPhone1,1")
         do {
             let state = containerB.getStateSync(test: self)
             containerB.prepareSync(test: self, epoch: 1, machineID: "bbb", bottleSalt: "123456789", bottleID: UUID().uuidString, modelID: "iPhone1,1")
         do {
             let state = containerB.getStateSync(test: self)
@@ -1111,11 +1130,13 @@ class TrustedPeersHelperUnitTests: XCTestCase {
         do {
             print("B joins via bottle")
 
         do {
             print("B joins via bottle")
 
-            let (bottlePeerID, errorPreflight) = containerB.preflightVouchWithBottleSync(test: self, bottleID: "wrong escrow record")
+            let (bottlePeerID, views, policy, errorPreflight) = containerB.preflightVouchWithBottleSync(test: self, bottleID: "wrong escrow record")
             XCTAssertNotNil(errorPreflight, "Should be an error preflighting bottle that doesn't exist")
             XCTAssertNil(bottlePeerID, "peerID should be nil for no bottle")
             XCTAssertNotNil(errorPreflight, "Should be an error preflighting bottle that doesn't exist")
             XCTAssertNil(bottlePeerID, "peerID should be nil for no bottle")
+            XCTAssertNil(views, "Should not have a set of views to restore")
+            XCTAssertNil(policy, "Should not have a policy")
 
 
-            let (voucherData, voucherSig, error3) = containerB.vouchWithBottleSync(test: self, b: "wrong escrow record", entropy: entropy, bottleSalt: "123456789", tlkShares: [])
+            let (voucherData, voucherSig, _, _, error3) = containerB.vouchWithBottleSync(test: self, b: "wrong escrow record", entropy: entropy, bottleSalt: "123456789", tlkShares: [])
 
             XCTAssertNotNil(error3)
             XCTAssertNil(voucherData)
 
             XCTAssertNotNil(error3)
             XCTAssertNil(voucherData)
@@ -1131,11 +1152,11 @@ class TrustedPeersHelperUnitTests: XCTestCase {
         let containerB = try Container(name: ContainerName(container: "b", context: OTDefaultContext), persistentStoreDescription: description, cuttlefish: cuttlefish)
 
         let machineIDs = Set(["aaa", "bbb"])
         let containerB = try Container(name: ContainerName(container: "b", context: OTDefaultContext), persistentStoreDescription: description, cuttlefish: cuttlefish)
 
         let machineIDs = Set(["aaa", "bbb"])
-        XCTAssertNil(containerA.setAllowedMachineIDsSync(test: self, allowedMachineIDs: machineIDs))
-        XCTAssertNil(containerB.setAllowedMachineIDsSync(test: self, allowedMachineIDs: machineIDs))
+        XCTAssertNil(containerA.setAllowedMachineIDsSync(test: self, allowedMachineIDs: machineIDs, accountIsDemo: false))
+        XCTAssertNil(containerB.setAllowedMachineIDsSync(test: self, allowedMachineIDs: machineIDs, accountIsDemo: false))
 
         print("preparing A")
 
         print("preparing A")
-        let (aPeerID, aPermanentInfo, aPermanentInfoSig, _, _, error) =
+        let (aPeerID, aPermanentInfo, aPermanentInfoSig, _, _, _, _, error) =
             containerA.prepareSync(test: self, epoch: 1, machineID: "aaa", bottleSalt: "123456789", bottleID: UUID().uuidString, modelID: "iPhone1,1")
         do {
             let state = containerA.getStateSync(test: self)
             containerA.prepareSync(test: self, epoch: 1, machineID: "aaa", bottleSalt: "123456789", bottleID: UUID().uuidString, modelID: "iPhone1,1")
         do {
             let state = containerA.getStateSync(test: self)
@@ -1165,7 +1186,7 @@ class TrustedPeersHelperUnitTests: XCTestCase {
         }
 
         print("preparing B")
         }
 
         print("preparing B")
-        let (bPeerID, _, _, _, _, error2) =
+        let (bPeerID, _, _, _, _, _, _, error2) =
             containerB.prepareSync(test: self, epoch: 1, machineID: "bbb", bottleSalt: "123456789", bottleID: UUID().uuidString, modelID: "iPhone1,1")
         do {
             var state = containerB.getStateSync(test: self)
             containerB.prepareSync(test: self, epoch: 1, machineID: "bbb", bottleSalt: "123456789", bottleID: UUID().uuidString, modelID: "iPhone1,1")
         do {
             var state = containerB.getStateSync(test: self)
@@ -1180,11 +1201,13 @@ class TrustedPeersHelperUnitTests: XCTestCase {
         do {
             print("B joins via bottle")
 
         do {
             print("B joins via bottle")
 
-            let (bottlePeerID, errorPreflight) = containerB.preflightVouchWithBottleSync(test: self, bottleID: bottleB.bottleID!)
-            XCTAssertNil(errorPreflight, "Should be no error preflighting a vouch with bottle")
-            XCTAssertEqual(bottlePeerID, bPeerID, "Bottle should be for peer B")
+            let (bottlePeerID, views, policy, errorPreflight) = containerB.preflightVouchWithBottleSync(test: self, bottleID: bottleB.bottleID!)
+            XCTAssertNotNil(errorPreflight, "Should be an error preflighting bottle that doesn't correspond to a peer")
+            XCTAssertNil(bottlePeerID, "Should have no peer for invalid bottle")
+            XCTAssertNil(views, "Should not have a set of views to restore")
+            XCTAssertNil(policy, "Should not have a policy")
 
 
-            let (voucherData, voucherSig, error3) = containerB.vouchWithBottleSync(test: self, b: bottleB.bottleID!, entropy: entropy, bottleSalt: "123456789", tlkShares: [])
+            let (voucherData, voucherSig, _, _, error3) = containerB.vouchWithBottleSync(test: self, b: bottleB.bottleID!, entropy: entropy, bottleSalt: "123456789", tlkShares: [])
 
             XCTAssertNotNil(error3)
             XCTAssertNil(voucherData)
 
             XCTAssertNotNil(error3)
             XCTAssertNil(voucherData)
@@ -1200,11 +1223,11 @@ class TrustedPeersHelperUnitTests: XCTestCase {
         let containerB = try Container(name: ContainerName(container: "b", context: OTDefaultContext), persistentStoreDescription: description, cuttlefish: cuttlefish)
 
         let machineIDs = Set(["aaa", "bbb"])
         let containerB = try Container(name: ContainerName(container: "b", context: OTDefaultContext), persistentStoreDescription: description, cuttlefish: cuttlefish)
 
         let machineIDs = Set(["aaa", "bbb"])
-        XCTAssertNil(containerA.setAllowedMachineIDsSync(test: self, allowedMachineIDs: machineIDs))
-        XCTAssertNil(containerB.setAllowedMachineIDsSync(test: self, allowedMachineIDs: machineIDs))
+        XCTAssertNil(containerA.setAllowedMachineIDsSync(test: self, allowedMachineIDs: machineIDs, accountIsDemo: false))
+        XCTAssertNil(containerB.setAllowedMachineIDsSync(test: self, allowedMachineIDs: machineIDs, accountIsDemo: false))
 
         print("preparing A")
 
         print("preparing A")
-        let (aPeerID, aPermanentInfo, aPermanentInfoSig, _, _, error) =
+        let (aPeerID, aPermanentInfo, aPermanentInfoSig, _, _, _, _, error) =
             containerA.prepareSync(test: self, epoch: 1, machineID: "aaa", bottleSalt: "123456789", bottleID: UUID().uuidString, modelID: "iPhone1,1")
         do {
             var state = containerA.getStateSync(test: self)
             containerA.prepareSync(test: self, epoch: 1, machineID: "aaa", bottleSalt: "123456789", bottleID: UUID().uuidString, modelID: "iPhone1,1")
         do {
             var state = containerA.getStateSync(test: self)
@@ -1237,7 +1260,7 @@ class TrustedPeersHelperUnitTests: XCTestCase {
         _ = containerB.updateSync(test: self)
 
         print("preparing B")
         _ = containerB.updateSync(test: self)
 
         print("preparing B")
-        let (bPeerID, _, _, _, _, error2) =
+        let (bPeerID, _, _, _, _, _, _, error2) =
             containerB.prepareSync(test: self, epoch: 1, machineID: "bbb", bottleSalt: "123456789", bottleID: UUID().uuidString, modelID: "iPhone1,1")
         do {
             let state = containerB.getStateSync(test: self)
             containerB.prepareSync(test: self, epoch: 1, machineID: "bbb", bottleSalt: "123456789", bottleID: UUID().uuidString, modelID: "iPhone1,1")
         do {
             let state = containerB.getStateSync(test: self)
@@ -1251,11 +1274,13 @@ class TrustedPeersHelperUnitTests: XCTestCase {
         do {
             print("B joins via bottle")
 
         do {
             print("B joins via bottle")
 
-            let (bottlePeerID, errorPreflight) = containerB.preflightVouchWithBottleSync(test: self, bottleID: bottleA.bottleID!)
+            let (bottlePeerID, views, policy, errorPreflight) = containerB.preflightVouchWithBottleSync(test: self, bottleID: bottleA.bottleID!)
             XCTAssertNil(errorPreflight, "Should be no error preflighting a vouch with bottle")
             XCTAssertEqual(bottlePeerID, aPeerID, "Bottle should be for peer A")
             XCTAssertNil(errorPreflight, "Should be no error preflighting a vouch with bottle")
             XCTAssertEqual(bottlePeerID, aPeerID, "Bottle should be for peer A")
+            XCTAssertNotNil(views, "Should have a set of views to restore")
+            XCTAssertNotNil(policy, "Should have a policy")
 
 
-            let (voucherData, voucherSig, error3) = containerB.vouchWithBottleSync(test: self, b: bottleA.bottleID!, entropy: entropy, bottleSalt: "987654321", tlkShares: [])
+            let (voucherData, voucherSig, _, _, error3) = containerB.vouchWithBottleSync(test: self, b: bottleA.bottleID!, entropy: entropy, bottleSalt: "987654321", tlkShares: [])
 
             XCTAssertNotNil(error3)
             XCTAssertNil(voucherData)
 
             XCTAssertNotNil(error3)
             XCTAssertNil(voucherData)
@@ -1270,11 +1295,11 @@ class TrustedPeersHelperUnitTests: XCTestCase {
         let containerB = try Container(name: ContainerName(container: "b", context: OTDefaultContext), persistentStoreDescription: description, cuttlefish: cuttlefish)
 
         let machineIDs = Set(["aaa", "bbb"])
         let containerB = try Container(name: ContainerName(container: "b", context: OTDefaultContext), persistentStoreDescription: description, cuttlefish: cuttlefish)
 
         let machineIDs = Set(["aaa", "bbb"])
-        XCTAssertNil(containerA.setAllowedMachineIDsSync(test: self, allowedMachineIDs: machineIDs))
-        XCTAssertNil(containerB.setAllowedMachineIDsSync(test: self, allowedMachineIDs: machineIDs))
+        XCTAssertNil(containerA.setAllowedMachineIDsSync(test: self, allowedMachineIDs: machineIDs, accountIsDemo: false))
+        XCTAssertNil(containerB.setAllowedMachineIDsSync(test: self, allowedMachineIDs: machineIDs, accountIsDemo: false))
 
         print("preparing A")
 
         print("preparing A")
-        let (aPeerID, aPermanentInfo, aPermanentInfoSig, _, _, error) =
+        let (aPeerID, aPermanentInfo, aPermanentInfoSig, _, _, _, _, error) =
             containerA.prepareSync(test: self, epoch: 1, machineID: "aaa", bottleSalt: "123456789", bottleID: UUID().uuidString, modelID: "iPhone1,1")
         do {
             var state = containerA.getStateSync(test: self)
             containerA.prepareSync(test: self, epoch: 1, machineID: "aaa", bottleSalt: "123456789", bottleID: UUID().uuidString, modelID: "iPhone1,1")
         do {
             var state = containerA.getStateSync(test: self)
@@ -1306,7 +1331,7 @@ class TrustedPeersHelperUnitTests: XCTestCase {
         _ = containerB.updateSync(test: self)
 
         print("preparing B")
         _ = containerB.updateSync(test: self)
 
         print("preparing B")
-        let (bPeerID, _, _, _, _, error2) =
+        let (bPeerID, _, _, _, _, _, _, error2) =
             containerB.prepareSync(test: self, epoch: 1, machineID: "bbb", bottleSalt: "123456789", bottleID: UUID().uuidString, modelID: "iPhone1,1")
         do {
             let state = containerB.getStateSync(test: self)
             containerB.prepareSync(test: self, epoch: 1, machineID: "bbb", bottleSalt: "123456789", bottleID: UUID().uuidString, modelID: "iPhone1,1")
         do {
             let state = containerB.getStateSync(test: self)
@@ -1320,11 +1345,13 @@ class TrustedPeersHelperUnitTests: XCTestCase {
         do {
             print("B joins via bottle")
 
         do {
             print("B joins via bottle")
 
-            let (bottlePeerID, errorPreflight) = containerB.preflightVouchWithBottleSync(test: self, bottleID: bottleA.bottleID!)
+            let (bottlePeerID, views, policy, errorPreflight) = containerB.preflightVouchWithBottleSync(test: self, bottleID: bottleA.bottleID!)
             XCTAssertNil(errorPreflight, "Should be no error preflighting a vouch with bottle")
             XCTAssertEqual(bottlePeerID, aPeerID, "Bottle should be for peer A")
             XCTAssertNil(errorPreflight, "Should be no error preflighting a vouch with bottle")
             XCTAssertEqual(bottlePeerID, aPeerID, "Bottle should be for peer A")
+            XCTAssertNotNil(views, "Should have a set of views to restore")
+            XCTAssertNotNil(policy, "Should have a policy")
 
 
-            let (voucherData, voucherSig, error3) = containerB.vouchWithBottleSync(test: self, b: bottleA.bottleID!, entropy: Data(count: Int(OTMasterSecretLength)), bottleSalt: "123456789", tlkShares: [])
+            let (voucherData, voucherSig, _, _, error3) = containerB.vouchWithBottleSync(test: self, b: bottleA.bottleID!, entropy: Data(count: Int(OTMasterSecretLength)), bottleSalt: "123456789", tlkShares: [])
 
             XCTAssertNotNil(error3)
             XCTAssertNil(voucherData)
 
             XCTAssertNotNil(error3)
             XCTAssertNil(voucherData)
@@ -1340,11 +1367,11 @@ class TrustedPeersHelperUnitTests: XCTestCase {
         let containerB = try Container(name: ContainerName(container: "b", context: OTDefaultContext), persistentStoreDescription: description, cuttlefish: cuttlefish)
 
         let machineIDs = Set(["aaa", "bbb"])
         let containerB = try Container(name: ContainerName(container: "b", context: OTDefaultContext), persistentStoreDescription: description, cuttlefish: cuttlefish)
 
         let machineIDs = Set(["aaa", "bbb"])
-        XCTAssertNil(containerA.setAllowedMachineIDsSync(test: self, allowedMachineIDs: machineIDs))
-        XCTAssertNil(containerB.setAllowedMachineIDsSync(test: self, allowedMachineIDs: machineIDs))
+        XCTAssertNil(containerA.setAllowedMachineIDsSync(test: self, allowedMachineIDs: machineIDs, accountIsDemo: false))
+        XCTAssertNil(containerB.setAllowedMachineIDsSync(test: self, allowedMachineIDs: machineIDs, accountIsDemo: false))
 
         print("preparing A")
 
         print("preparing A")
-        let (aPeerID, aPermanentInfo, aPermanentInfoSig, _, _, error) =
+        let (aPeerID, aPermanentInfo, aPermanentInfoSig, _, _, _, _, error) =
             containerA.prepareSync(test: self, epoch: 1, machineID: "aaa", bottleSalt: "123456789", bottleID: UUID().uuidString, modelID: "iPhone1,1")
         do {
             var state = containerA.getStateSync(test: self)
             containerA.prepareSync(test: self, epoch: 1, machineID: "aaa", bottleSalt: "123456789", bottleID: UUID().uuidString, modelID: "iPhone1,1")
         do {
             var state = containerA.getStateSync(test: self)
@@ -1375,7 +1402,7 @@ class TrustedPeersHelperUnitTests: XCTestCase {
         }
 
         print("preparing B")
         }
 
         print("preparing B")
-        let (bPeerID, _, _, _, _, error2) =
+        let (bPeerID, _, _, _, _, _, _, error2) =
             containerB.prepareSync(test: self, epoch: 1, machineID: "bbb", bottleSalt: "123456789", bottleID: UUID().uuidString, modelID: "iPhone1,1")
         do {
             let state = containerB.getStateSync(test: self)
             containerB.prepareSync(test: self, epoch: 1, machineID: "bbb", bottleSalt: "123456789", bottleID: UUID().uuidString, modelID: "iPhone1,1")
         do {
             let state = containerB.getStateSync(test: self)
@@ -1391,13 +1418,15 @@ class TrustedPeersHelperUnitTests: XCTestCase {
 
             self.cuttlefish.fetchViableBottlesError.append(FakeCuttlefishServer.makeCloudKitCuttlefishError(code: .changeTokenExpired))
 
 
             self.cuttlefish.fetchViableBottlesError.append(FakeCuttlefishServer.makeCloudKitCuttlefishError(code: .changeTokenExpired))
 
-            let (bottlePeerID, errorPreflight) = containerB.preflightVouchWithBottleSync(test: self, bottleID: bottleA.bottleID!)
+            let (bottlePeerID, views, policy, errorPreflight) = containerB.preflightVouchWithBottleSync(test: self, bottleID: bottleA.bottleID!)
             XCTAssertNotNil(errorPreflight, "Should be an error preflighting a vouch with bottle with a fetch error")
             XCTAssertNil(bottlePeerID, "peerID should be nil")
             XCTAssertNotNil(errorPreflight, "Should be an error preflighting a vouch with bottle with a fetch error")
             XCTAssertNil(bottlePeerID, "peerID should be nil")
+            XCTAssertNil(views, "Should not have a set of views to restore")
+            XCTAssertNil(policy, "Should not have a policy")
 
             self.cuttlefish.fetchViableBottlesError.append(FakeCuttlefishServer.makeCloudKitCuttlefishError(code: .changeTokenExpired))
 
 
             self.cuttlefish.fetchViableBottlesError.append(FakeCuttlefishServer.makeCloudKitCuttlefishError(code: .changeTokenExpired))
 
-            let (voucherData, voucherSig, error3) = containerB.vouchWithBottleSync(test: self, b: bottleA.bottleID!, entropy: entropy, bottleSalt: "123456789", tlkShares: [])
+            let (voucherData, voucherSig, _, _, error3) = containerB.vouchWithBottleSync(test: self, b: bottleA.bottleID!, entropy: entropy, bottleSalt: "123456789", tlkShares: [])
 
             XCTAssertNotNil(error3)
             XCTAssertNil(voucherData)
 
             XCTAssertNotNil(error3)
             XCTAssertNil(voucherData)
@@ -1411,19 +1440,23 @@ class TrustedPeersHelperUnitTests: XCTestCase {
         let containerB = try Container(name: ContainerName(container: "b", context: OTDefaultContext), persistentStoreDescription: description, cuttlefish: cuttlefish)
 
         let machineIDs = Set(["aaa", "bbb"])
         let containerB = try Container(name: ContainerName(container: "b", context: OTDefaultContext), persistentStoreDescription: description, cuttlefish: cuttlefish)
 
         let machineIDs = Set(["aaa", "bbb"])
-        XCTAssertNil(containerA.setAllowedMachineIDsSync(test: self, allowedMachineIDs: machineIDs))
-        XCTAssertNil(containerB.setAllowedMachineIDsSync(test: self, allowedMachineIDs: machineIDs))
+        XCTAssertNil(containerA.setAllowedMachineIDsSync(test: self, allowedMachineIDs: machineIDs, accountIsDemo: false))
+        XCTAssertNil(containerB.setAllowedMachineIDsSync(test: self, allowedMachineIDs: machineIDs, accountIsDemo: false))
 
         print("preparing A")
 
         print("preparing A")
-        let (aPeerID, aPermanentInfo, aPermanentInfoSig, _, _, error) =
+        let (aPeerID, aPermanentInfo, aPermanentInfoSig, _, _, aViewList, aPolicy, error) =
             containerA.prepareSync(test: self, epoch: 1, machineID: "aaa", bottleSalt: "123456789", bottleID: UUID().uuidString, modelID: "iPhone1,1")
         XCTAssertNil(error)
         XCTAssertNotNil(aPeerID)
         XCTAssertNotNil(aPermanentInfo)
         XCTAssertNotNil(aPermanentInfoSig)
 
             containerA.prepareSync(test: self, epoch: 1, machineID: "aaa", bottleSalt: "123456789", bottleID: UUID().uuidString, modelID: "iPhone1,1")
         XCTAssertNil(error)
         XCTAssertNotNil(aPeerID)
         XCTAssertNotNil(aPermanentInfo)
         XCTAssertNotNil(aPermanentInfoSig)
 
+        XCTAssertNotNil(aViewList, "Should have a view list coming back from a successful prepare")
+        XCTAssertNotNil(aPolicy, "Should have a policy coming back from a successful prepare")
+        XCTAssertEqual(aPolicy?.version, prevailingPolicyVersion, "Policy coming back from prepare() should be prevailing policy version")
+
         print("preparing B")
         print("preparing B")
-        let (bPeerID, bPermanentInfo, bPermanentInfoSig, _, _, error2) =
+        let (bPeerID, bPermanentInfo, bPermanentInfoSig, _, _, _, _, error2) =
             containerB.prepareSync(test: self, epoch: 1, machineID: "bbb", bottleSalt: "123456789", bottleID: UUID().uuidString, modelID: "iPhone1,1")
         do {
             let state = containerB.getStateSync(test: self)
             containerB.prepareSync(test: self, epoch: 1, machineID: "bbb", bottleSalt: "123456789", bottleID: UUID().uuidString, modelID: "iPhone1,1")
         do {
             let state = containerB.getStateSync(test: self)
@@ -1454,9 +1487,11 @@ class TrustedPeersHelperUnitTests: XCTestCase {
             assertNoTLKShareFor(peerID: bPeerID!, keyUUID: self.manateeKeySet.tlk.uuid, zoneID: CKRecordZone.ID(zoneName: "Manatee"))
 
             print("B joins by preapproval, and uploads all TLKShares that it has")
             assertNoTLKShareFor(peerID: bPeerID!, keyUUID: self.manateeKeySet.tlk.uuid, zoneID: CKRecordZone.ID(zoneName: "Manatee"))
 
             print("B joins by preapproval, and uploads all TLKShares that it has")
-            let (bJoinedPeerID, _, bJoinedError) = containerB.preapprovedJoinSync(test: self, ckksKeys: [self.manateeKeySet], tlkShares: [])
+            let (bJoinedPeerID, _, views, policy, bJoinedError) = containerB.preapprovedJoinSync(test: self, ckksKeys: [self.manateeKeySet], tlkShares: [])
             XCTAssertNil(bJoinedError, "Should be no error joining by preapproval")
             XCTAssertNotNil(bJoinedPeerID, "Should have a peer ID out of join")
             XCTAssertNil(bJoinedError, "Should be no error joining by preapproval")
             XCTAssertNotNil(bJoinedPeerID, "Should have a peer ID out of join")
+            XCTAssertNotNil(views, "should have a list of views to use")
+            XCTAssertNotNil(policy, "Should have a policy back from preapprovedjoin")
 
             assertTLKShareFor(peerID: bPeerID!, keyUUID: self.manateeKeySet.tlk.uuid, zoneID: CKRecordZone.ID(zoneName: "Manatee"))
         }
 
             assertTLKShareFor(peerID: bPeerID!, keyUUID: self.manateeKeySet.tlk.uuid, zoneID: CKRecordZone.ID(zoneName: "Manatee"))
         }
@@ -1480,13 +1515,13 @@ class TrustedPeersHelperUnitTests: XCTestCase {
         let (c2, peerID2) = try joinByVoucher(sponsor: c,
                                               containerID: "second",
                                               machineID: "bbb",
         let (c2, peerID2) = try joinByVoucher(sponsor: c,
                                               containerID: "second",
                                               machineID: "bbb",
-                                              machineIDs: ["aaa", "bbb", "ccc"],
+                                              machineIDs: ["aaa", "bbb", "ccc"], accountIsDemo: false,
                                               store: store)
 
         let (c3, peerID3) = try joinByVoucher(sponsor: c,
                                               containerID: "third",
                                               machineID: "ccc",
                                               store: store)
 
         let (c3, peerID3) = try joinByVoucher(sponsor: c,
                                               containerID: "third",
                                               machineID: "ccc",
-                                              machineIDs: ["aaa", "bbb", "ccc"],
+                                              machineIDs: ["aaa", "bbb", "ccc"], accountIsDemo: false,
                                               store: store)
 
         let (_, cUpdateError) = c.updateSync(test: self)
                                               store: store)
 
         let (_, cUpdateError) = c.updateSync(test: self)
@@ -1519,7 +1554,7 @@ class TrustedPeersHelperUnitTests: XCTestCase {
 
         // But all that goes away, and a new peer establishes
         self.cuttlefish.state = FakeCuttlefishServer.State()
 
         // But all that goes away, and a new peer establishes
         self.cuttlefish.state = FakeCuttlefishServer.State()
-        let (_, peerID2) = try establish(reload: false, contextID: "second", store: tmpStoreDescription(name: "container-peer2.db"))
+        let (_, peerID2) = try establish(reload: false, contextID: "second", accountIsDemo: false, store: tmpStoreDescription(name: "container-peer2.db"))
 
         // And the first container fetches again, which should succeed
         self.cuttlefish.nextFetchErrors.append(FakeCuttlefishServer.makeCloudKitCuttlefishError(code: .changeTokenExpired))
 
         // And the first container fetches again, which should succeed
         self.cuttlefish.nextFetchErrors.append(FakeCuttlefishServer.makeCloudKitCuttlefishError(code: .changeTokenExpired))
@@ -1680,10 +1715,10 @@ class TrustedPeersHelperUnitTests: XCTestCase {
                                  cuttlefish: self.cuttlefish)
 
         let machineIDs = Set(["aaa", "bbb", "ccc"])
                                  cuttlefish: self.cuttlefish)
 
         let machineIDs = Set(["aaa", "bbb", "ccc"])
-        XCTAssertNil(c.setAllowedMachineIDsSync(test: self, allowedMachineIDs: machineIDs))
+        XCTAssertNil(c.setAllowedMachineIDsSync(test: self, allowedMachineIDs: machineIDs, accountIsDemo: false))
 
         print("preparing peer A")
 
         print("preparing peer A")
-        let (aPeerID, aPermanentInfo, aPermanentInfoSig, _, _, error) =
+        let (aPeerID, aPermanentInfo, aPermanentInfoSig, _, _, _, _, error) =
             c.prepareSync(test: self, epoch: 1, machineID: "aaa", bottleSalt: "123456789", bottleID: UUID().uuidString, modelID: "iPhone1,1")
         do {
             let state = c.getStateSync(test: self)
             c.prepareSync(test: self, epoch: 1, machineID: "aaa", bottleSalt: "123456789", bottleID: UUID().uuidString, modelID: "iPhone1,1")
         do {
             let state = c.getStateSync(test: self)
@@ -1736,10 +1771,10 @@ class TrustedPeersHelperUnitTests: XCTestCase {
                               cuttlefish: self.cuttlefish)
 
         let machineIDs = Set(["aaa", "bbb", "ccc"])
                               cuttlefish: self.cuttlefish)
 
         let machineIDs = Set(["aaa", "bbb", "ccc"])
-        XCTAssertNil(c.setAllowedMachineIDsSync(test: self, allowedMachineIDs: machineIDs))
+        XCTAssertNil(c.setAllowedMachineIDsSync(test: self, allowedMachineIDs: machineIDs, accountIsDemo: false))
 
         print("preparing peer A")
 
         print("preparing peer A")
-        let (aPeerID, aPermanentInfo, aPermanentInfoSig, _, _, error) =
+        let (aPeerID, aPermanentInfo, aPermanentInfoSig, _, _, _, _, error) =
             c.prepareSync(test: self, epoch: 1, machineID: "aaa", bottleSalt: "123456789", bottleID: UUID().uuidString, modelID: "iPhone1,1")
         do {
             let state = c.getStateSync(test: self)
             c.prepareSync(test: self, epoch: 1, machineID: "aaa", bottleSalt: "123456789", bottleID: UUID().uuidString, modelID: "iPhone1,1")
         do {
             let state = c.getStateSync(test: self)
@@ -1759,10 +1794,11 @@ class TrustedPeersHelperUnitTests: XCTestCase {
             XCTAssertNil(error)
             XCTAssertNotNil(peerID)
         }
             XCTAssertNil(error)
             XCTAssertNotNil(peerID)
         }
-        let (repairAccount, repairEscrow, resetOctagon, healthError) = c.requestHealthCheckSync(requiresEscrowCheck: true, test: self)
+        let (repairAccount, repairEscrow, resetOctagon, leaveTrust, healthError) = c.requestHealthCheckSync(requiresEscrowCheck: true, test: self)
         XCTAssertEqual(repairAccount, false, "")
         XCTAssertEqual(repairEscrow, false, "")
         XCTAssertEqual(resetOctagon, false, "")
         XCTAssertEqual(repairAccount, false, "")
         XCTAssertEqual(repairEscrow, false, "")
         XCTAssertEqual(resetOctagon, false, "")
+        XCTAssertEqual(leaveTrust, false, "")
         XCTAssertNil(healthError)
     }
 
         XCTAssertNil(healthError)
     }
 
@@ -1774,11 +1810,11 @@ class TrustedPeersHelperUnitTests: XCTestCase {
         let containerB = try Container(name: ContainerName(container: "b", context: OTDefaultContext), persistentStoreDescription: description, cuttlefish: cuttlefish)
 
         let machineIDs = Set(["aaa", "bbb"])
         let containerB = try Container(name: ContainerName(container: "b", context: OTDefaultContext), persistentStoreDescription: description, cuttlefish: cuttlefish)
 
         let machineIDs = Set(["aaa", "bbb"])
-        XCTAssertNil(containerA.setAllowedMachineIDsSync(test: self, allowedMachineIDs: machineIDs))
-        XCTAssertNil(containerB.setAllowedMachineIDsSync(test: self, allowedMachineIDs: machineIDs))
+        XCTAssertNil(containerA.setAllowedMachineIDsSync(test: self, allowedMachineIDs: machineIDs, accountIsDemo: false))
+        XCTAssertNil(containerB.setAllowedMachineIDsSync(test: self, allowedMachineIDs: machineIDs, accountIsDemo: false))
 
         print("preparing A")
 
         print("preparing A")
-        let (aPeerID, aPermanentInfo, aPermanentInfoSig, _, _, error) =
+        let (aPeerID, aPermanentInfo, aPermanentInfoSig, _, _, _, _, error) =
             containerA.prepareSync(test: self, epoch: 1, machineID: "aaa", bottleSalt: "123456789", bottleID: UUID().uuidString, modelID: "iPhone1,1")
         do {
             var state = containerA.getStateSync(test: self)
             containerA.prepareSync(test: self, epoch: 1, machineID: "aaa", bottleSalt: "123456789", bottleID: UUID().uuidString, modelID: "iPhone1,1")
         do {
             var state = containerA.getStateSync(test: self)
@@ -1814,7 +1850,7 @@ class TrustedPeersHelperUnitTests: XCTestCase {
         _ = containerB.updateSync(test: self)
 
         print("preparing B")
         _ = containerB.updateSync(test: self)
 
         print("preparing B")
-        let (bPeerID, _, _, _, _, error2) =
+        let (bPeerID, _, _, _, _, _, _, error2) =
             containerB.prepareSync(test: self, epoch: 1, machineID: "bbb", bottleSalt: "123456789", bottleID: UUID().uuidString, modelID: "iPhone1,1")
         do {
             let state = containerB.getStateSync(test: self)
             containerB.prepareSync(test: self, epoch: 1, machineID: "bbb", bottleSalt: "123456789", bottleID: UUID().uuidString, modelID: "iPhone1,1")
         do {
             let state = containerB.getStateSync(test: self)
@@ -1828,11 +1864,13 @@ class TrustedPeersHelperUnitTests: XCTestCase {
         do {
             print("B prepares to join via bottle")
 
         do {
             print("B prepares to join via bottle")
 
-            let (bottlePeerID, errorPreflight) = containerB.preflightVouchWithBottleSync(test: self, bottleID: bottleA.bottleID!)
+            let (bottlePeerID, views, policy, errorPreflight) = containerB.preflightVouchWithBottleSync(test: self, bottleID: bottleA.bottleID!)
             XCTAssertNil(errorPreflight, "Should be no error preflighting a vouch with bottle")
             XCTAssertEqual(bottlePeerID, aPeerID, "Bottle should be for peer A")
             XCTAssertNil(errorPreflight, "Should be no error preflighting a vouch with bottle")
             XCTAssertEqual(bottlePeerID, aPeerID, "Bottle should be for peer A")
+            XCTAssertNotNil(views, "Should have a set of views to restore")
+            XCTAssertNotNil(policy, "Should have a policy")
 
 
-            let (voucherData, voucherSig, error3) = containerB.vouchWithBottleSync(test: self, b: bottleA.bottleID!, entropy: entropy, bottleSalt: "123456789", tlkShares: [])
+            let (voucherData, voucherSig, _, _, error3) = containerB.vouchWithBottleSync(test: self, b: bottleA.bottleID!, entropy: entropy, bottleSalt: "123456789", tlkShares: [])
 
             XCTAssertNil(error3)
             XCTAssertNotNil(voucherData)
 
             XCTAssertNil(error3)
             XCTAssertNotNil(voucherData)
@@ -1844,7 +1882,7 @@ class TrustedPeersHelperUnitTests: XCTestCase {
             assertNoTLKShareFor(peerID: bPeerID!, keyUUID: self.manateeKeySet.tlk.uuid, zoneID: CKRecordZone.ID(zoneName: "Manatee"))
 
             print("B joins")
             assertNoTLKShareFor(peerID: bPeerID!, keyUUID: self.manateeKeySet.tlk.uuid, zoneID: CKRecordZone.ID(zoneName: "Manatee"))
 
             print("B joins")
-            let (peerID, _, error) = containerB.joinSync(test: self, voucherData: voucherData!, voucherSig: voucherSig!, ckksKeys: [self.manateeKeySet], tlkShares: [])
+            let (peerID, _, _, _, error) = containerB.joinSync(test: self, voucherData: voucherData!, voucherSig: voucherSig!, ckksKeys: [self.manateeKeySet], tlkShares: [])
             XCTAssertNotNil(error)
             XCTAssertNil(peerID)
         }
             XCTAssertNotNil(error)
             XCTAssertNil(peerID)
         }
@@ -1856,11 +1894,11 @@ class TrustedPeersHelperUnitTests: XCTestCase {
         let containerB = try Container(name: ContainerName(container: "b", context: OTDefaultContext), persistentStoreDescription: description, cuttlefish: cuttlefish)
 
         let machineIDs = Set(["aaa", "bbb"])
         let containerB = try Container(name: ContainerName(container: "b", context: OTDefaultContext), persistentStoreDescription: description, cuttlefish: cuttlefish)
 
         let machineIDs = Set(["aaa", "bbb"])
-        XCTAssertNil(containerA.setAllowedMachineIDsSync(test: self, allowedMachineIDs: machineIDs))
-        XCTAssertNil(containerB.setAllowedMachineIDsSync(test: self, allowedMachineIDs: machineIDs))
+        XCTAssertNil(containerA.setAllowedMachineIDsSync(test: self, allowedMachineIDs: machineIDs, accountIsDemo: false))
+        XCTAssertNil(containerB.setAllowedMachineIDsSync(test: self, allowedMachineIDs: machineIDs, accountIsDemo: false))
 
         print("preparing peer A")
 
         print("preparing peer A")
-        let (aPeerID, aPermanentInfo, aPermanentInfoSig, aStableInfo, aStableInfoSig, error) =
+        let (aPeerID, aPermanentInfo, aPermanentInfoSig, aStableInfo, aStableInfoSig, _, _, error) =
             containerA.prepareSync(test: self, epoch: 1, machineID: "aaa", bottleSalt: "123456789", bottleID: UUID().uuidString, modelID: "iPhone1,1")
         do {
             let state = containerA.getStateSync(test: self)
             containerA.prepareSync(test: self, epoch: 1, machineID: "aaa", bottleSalt: "123456789", bottleID: UUID().uuidString, modelID: "iPhone1,1")
         do {
             let state = containerA.getStateSync(test: self)
@@ -1884,7 +1922,7 @@ class TrustedPeersHelperUnitTests: XCTestCase {
         }
 
         print("preparing B")
         }
 
         print("preparing B")
-        let (bPeerID, bPermanentInfo, bPermanentInfoSig, bStableInfo, bStableInfoSig, error2) =
+        let (bPeerID, bPermanentInfo, bPermanentInfoSig, bStableInfo, bStableInfoSig, _, _, error2) =
             containerB.prepareSync(test: self, epoch: 1, machineID: "bbb", bottleSalt: "123456789", bottleID: UUID().uuidString, modelID: "iPhone1,1")
         do {
             let state = containerB.getStateSync(test: self)
             containerB.prepareSync(test: self, epoch: 1, machineID: "bbb", bottleSalt: "123456789", bottleID: UUID().uuidString, modelID: "iPhone1,1")
         do {
             let state = containerB.getStateSync(test: self)
@@ -1944,11 +1982,11 @@ class TrustedPeersHelperUnitTests: XCTestCase {
             assertTLKShareFor(peerID: bPeerID!, keyUUID: self.manateeKeySet.tlk.uuid, zoneID: CKRecordZone.ID(zoneName: "Manatee"))
 
             print("B joins")
             assertTLKShareFor(peerID: bPeerID!, keyUUID: self.manateeKeySet.tlk.uuid, zoneID: CKRecordZone.ID(zoneName: "Manatee"))
 
             print("B joins")
-            let (peerID, _, error) = containerB.joinSync(test: self,
-                                                         voucherData: voucherData!,
-                                                         voucherSig: voucherSig!,
-                                                         ckksKeys: [],
-                                                         tlkShares: [])
+            let (peerID, _, _, _, error) = containerB.joinSync(test: self,
+                                                               voucherData: voucherData!,
+                                                               voucherSig: voucherSig!,
+                                                               ckksKeys: [],
+                                                               tlkShares: [])
             XCTAssertNil(error)
             XCTAssertEqual(peerID, bPeerID!)
         }
             XCTAssertNil(error)
             XCTAssertEqual(peerID, bPeerID!)
         }
@@ -2018,7 +2056,6 @@ class TrustedPeersHelperUnitTests: XCTestCase {
         XCTAssertEqual(midList.machineIDs(in: .disallowed), disallowedMachineIDs, "List of disallowed machine IDs should match")
         XCTAssertEqual(midList.machineIDs(in: .unknown), unknownMachineIDs, "List of unknown machine IDs should match")
 
         XCTAssertEqual(midList.machineIDs(in: .disallowed), disallowedMachineIDs, "List of disallowed machine IDs should match")
         XCTAssertEqual(midList.machineIDs(in: .unknown), unknownMachineIDs, "List of unknown machine IDs should match")
 
-
         let (fetchedAllowList, fetchErr) = container.fetchAllowedMachineIDsSync(test: self)
         XCTAssertNil(fetchErr, "Should be no error fetching the allowed list")
         XCTAssertEqual(fetchedAllowList, allowedMachineIDs, "A fetched list of allowed machine IDs should match the loaded list")
         let (fetchedAllowList, fetchErr) = container.fetchAllowedMachineIDsSync(test: self)
         XCTAssertNil(fetchErr, "Should be no error fetching the allowed list")
         XCTAssertEqual(fetchedAllowList, allowedMachineIDs, "A fetched list of allowed machine IDs should match the loaded list")
@@ -2036,7 +2073,7 @@ class TrustedPeersHelperUnitTests: XCTestCase {
         let description = tmpStoreDescription(name: "container.db")
         let container = try Container(name: ContainerName(container: "test", context: OTDefaultContext), persistentStoreDescription: description, cuttlefish: cuttlefish)
 
         let description = tmpStoreDescription(name: "container.db")
         let container = try Container(name: ContainerName(container: "test", context: OTDefaultContext), persistentStoreDescription: description, cuttlefish: cuttlefish)
 
-        let (peerID, permanentInfo, permanentInfoSig, _, _, error) = container.prepareSync(test: self, epoch: 1, machineID: "aaa", bottleSalt: "123456789", bottleID: UUID().uuidString, modelID: "iPhone1,1")
+        let (peerID, permanentInfo, permanentInfoSig, _, _, _, _, error) = container.prepareSync(test: self, epoch: 1, machineID: "aaa", bottleSalt: "123456789", bottleID: UUID().uuidString, modelID: "iPhone1,1")
 
         XCTAssertNil(error)
         XCTAssertNotNil(peerID)
 
         XCTAssertNil(error)
         XCTAssertNotNil(peerID)
@@ -2045,11 +2082,11 @@ class TrustedPeersHelperUnitTests: XCTestCase {
 
         try self.assert(container: container, allowedMachineIDs: [], disallowedMachineIDs: [], persistentStore: description, cuttlefish: self.cuttlefish)
 
 
         try self.assert(container: container, allowedMachineIDs: [], disallowedMachineIDs: [], persistentStore: description, cuttlefish: self.cuttlefish)
 
-        XCTAssertNil(container.setAllowedMachineIDsSync(test: self, allowedMachineIDs: ["aaa", "bbb", "ccc"]), "should be able to set allowed machine IDs")
+        XCTAssertNil(container.setAllowedMachineIDsSync(test: self, allowedMachineIDs: ["aaa", "bbb", "ccc"], accountIsDemo: false), "should be able to set allowed machine IDs")
         try self.assert(container: container, allowedMachineIDs: Set(["aaa", "bbb", "ccc"]), disallowedMachineIDs: [], persistentStore: description, cuttlefish: self.cuttlefish)
         XCTAssertFalse(container.onqueueFullIDMSListWouldBeHelpful(), "Container shouldn't think it could use an IDMS list set")
 
         try self.assert(container: container, allowedMachineIDs: Set(["aaa", "bbb", "ccc"]), disallowedMachineIDs: [], persistentStore: description, cuttlefish: self.cuttlefish)
         XCTAssertFalse(container.onqueueFullIDMSListWouldBeHelpful(), "Container shouldn't think it could use an IDMS list set")
 
-        XCTAssertNil(container.setAllowedMachineIDsSync(test: self, allowedMachineIDs: ["aaa", "bbb"]), "should be able to set allowed machine IDs")
+        XCTAssertNil(container.setAllowedMachineIDsSync(test: self, allowedMachineIDs: ["aaa", "bbb"], accountIsDemo: false), "should be able to set allowed machine IDs")
         try self.assert(container: container, allowedMachineIDs: Set(["aaa", "bbb"]), disallowedMachineIDs: Set(["ccc"]), persistentStore: description, cuttlefish: self.cuttlefish)
         XCTAssertFalse(container.onqueueFullIDMSListWouldBeHelpful(), "Container shouldn't think it could use an IDMS list set")
 
         try self.assert(container: container, allowedMachineIDs: Set(["aaa", "bbb"]), disallowedMachineIDs: Set(["ccc"]), persistentStore: description, cuttlefish: self.cuttlefish)
         XCTAssertFalse(container.onqueueFullIDMSListWouldBeHelpful(), "Container shouldn't think it could use an IDMS list set")
 
@@ -2063,22 +2100,22 @@ class TrustedPeersHelperUnitTests: XCTestCase {
         XCTAssertTrue(container.onqueueFullIDMSListWouldBeHelpful(), "Container should think it could use an IDMS list set: there's machine IDs pending removal")
 
         // once they're unknown, a full list set will make them disallowed
         XCTAssertTrue(container.onqueueFullIDMSListWouldBeHelpful(), "Container should think it could use an IDMS list set: there's machine IDs pending removal")
 
         // once they're unknown, a full list set will make them disallowed
-        XCTAssertNil(container.setAllowedMachineIDsSync(test: self, allowedMachineIDs: ["aaa", "zzz", "kkk"]), "should be able to set allowed machine IDs")
+        XCTAssertNil(container.setAllowedMachineIDsSync(test: self, allowedMachineIDs: ["aaa", "zzz", "kkk"], accountIsDemo: false), "should be able to set allowed machine IDs")
         try self.assert(container: container, allowedMachineIDs: Set(["aaa", "zzz", "kkk"]), disallowedMachineIDs: Set(["bbb", "ccc", "fff"]), persistentStore: description, cuttlefish: self.cuttlefish)
         XCTAssertFalse(container.onqueueFullIDMSListWouldBeHelpful(), "Container shouldn't think it could use an IDMS list set")
 
         // Resetting the list to what it is doesn't change the list
         try self.assert(container: container, allowedMachineIDs: Set(["aaa", "zzz", "kkk"]), disallowedMachineIDs: Set(["bbb", "ccc", "fff"]), persistentStore: description, cuttlefish: self.cuttlefish)
         XCTAssertFalse(container.onqueueFullIDMSListWouldBeHelpful(), "Container shouldn't think it could use an IDMS list set")
 
         // Resetting the list to what it is doesn't change the list
-        XCTAssertNil(container.setAllowedMachineIDsSync(test: self, allowedMachineIDs: ["aaa", "zzz", "kkk"], listDifference: false), "should be able to set allowed machine IDs")
+        XCTAssertNil(container.setAllowedMachineIDsSync(test: self, allowedMachineIDs: ["aaa", "zzz", "kkk"], accountIsDemo: false, listDifference: false), "should be able to set allowed machine IDs")
         try self.assert(container: container, allowedMachineIDs: Set(["aaa", "zzz", "kkk"]), disallowedMachineIDs: Set(["bbb", "ccc", "fff"]), persistentStore: description, cuttlefish: self.cuttlefish)
         XCTAssertFalse(container.onqueueFullIDMSListWouldBeHelpful(), "Container shouldn't think it could use an IDMS list set")
 
         // But changing it to something completely new does
         try self.assert(container: container, allowedMachineIDs: Set(["aaa", "zzz", "kkk"]), disallowedMachineIDs: Set(["bbb", "ccc", "fff"]), persistentStore: description, cuttlefish: self.cuttlefish)
         XCTAssertFalse(container.onqueueFullIDMSListWouldBeHelpful(), "Container shouldn't think it could use an IDMS list set")
 
         // But changing it to something completely new does
-        XCTAssertNil(container.setAllowedMachineIDsSync(test: self, allowedMachineIDs: ["xxx", "mmm"]), "should be able to set allowed machine IDs")
+        XCTAssertNil(container.setAllowedMachineIDsSync(test: self, allowedMachineIDs: ["xxx", "mmm"], accountIsDemo: false), "should be able to set allowed machine IDs")
         try self.assert(container: container, allowedMachineIDs: Set(["xxx", "mmm"]), disallowedMachineIDs: Set(["aaa", "zzz", "kkk", "bbb", "ccc", "fff"]), persistentStore: description, cuttlefish: self.cuttlefish)
         XCTAssertFalse(container.onqueueFullIDMSListWouldBeHelpful(), "Container shouldn't think it could use an IDMS list set")
 
         // And, readding a previously disallowed machine ID works too
         try self.assert(container: container, allowedMachineIDs: Set(["xxx", "mmm"]), disallowedMachineIDs: Set(["aaa", "zzz", "kkk", "bbb", "ccc", "fff"]), persistentStore: description, cuttlefish: self.cuttlefish)
         XCTAssertFalse(container.onqueueFullIDMSListWouldBeHelpful(), "Container shouldn't think it could use an IDMS list set")
 
         // And, readding a previously disallowed machine ID works too
-        XCTAssertNil(container.setAllowedMachineIDsSync(test: self, allowedMachineIDs: ["xxx", "mmm", "aaa"]), "should be able to set allowed machine IDs")
+        XCTAssertNil(container.setAllowedMachineIDsSync(test: self, allowedMachineIDs: ["xxx", "mmm", "aaa"], accountIsDemo: false), "should be able to set allowed machine IDs")
         try self.assert(container: container, allowedMachineIDs: Set(["xxx", "mmm", "aaa"]), disallowedMachineIDs: Set(["zzz", "kkk", "bbb", "ccc", "fff"]), persistentStore: description, cuttlefish: self.cuttlefish)
         XCTAssertFalse(container.onqueueFullIDMSListWouldBeHelpful(), "Container shouldn't think it could use an IDMS list set")
 
         try self.assert(container: container, allowedMachineIDs: Set(["xxx", "mmm", "aaa"]), disallowedMachineIDs: Set(["zzz", "kkk", "bbb", "ccc", "fff"]), persistentStore: description, cuttlefish: self.cuttlefish)
         XCTAssertFalse(container.onqueueFullIDMSListWouldBeHelpful(), "Container shouldn't think it could use an IDMS list set")
 
@@ -2105,7 +2142,7 @@ class TrustedPeersHelperUnitTests: XCTestCase {
 
         try self.assert(container: container, allowedMachineIDs: [], disallowedMachineIDs: [], persistentStore: description, cuttlefish: self.cuttlefish)
 
 
         try self.assert(container: container, allowedMachineIDs: [], disallowedMachineIDs: [], persistentStore: description, cuttlefish: self.cuttlefish)
 
-        XCTAssertNil(container.setAllowedMachineIDsSync(test: self, allowedMachineIDs: ["aaa", "bbb", "ccc"], listDifference: true), "should be able to set allowed machine IDs")
+        XCTAssertNil(container.setAllowedMachineIDsSync(test: self, allowedMachineIDs: ["aaa", "bbb", "ccc"], accountIsDemo: false, listDifference: true), "should be able to set allowed machine IDs")
         try self.assert(container: container, allowedMachineIDs: Set(["aaa", "bbb", "ccc"]), disallowedMachineIDs: [], persistentStore: description, cuttlefish: self.cuttlefish)
         XCTAssertFalse(container.onqueueFullIDMSListWouldBeHelpful(), "Container shouldn't think it could use an IDMS list set")
 
         try self.assert(container: container, allowedMachineIDs: Set(["aaa", "bbb", "ccc"]), disallowedMachineIDs: [], persistentStore: description, cuttlefish: self.cuttlefish)
         XCTAssertFalse(container.onqueueFullIDMSListWouldBeHelpful(), "Container shouldn't think it could use an IDMS list set")
 
@@ -2115,7 +2152,7 @@ class TrustedPeersHelperUnitTests: XCTestCase {
         XCTAssertFalse(container.onqueueFullIDMSListWouldBeHelpful(), "Container shouldn't think it could use an IDMS list set")
 
         // But, the next time we ask IDMS, they still haven't made it to the full list, and in fact, C has disappeared.
         XCTAssertFalse(container.onqueueFullIDMSListWouldBeHelpful(), "Container shouldn't think it could use an IDMS list set")
 
         // But, the next time we ask IDMS, they still haven't made it to the full list, and in fact, C has disappeared.
-        XCTAssertNil(container.setAllowedMachineIDsSync(test: self, allowedMachineIDs: ["aaa", "bbb"], listDifference: true), "should be able to set allowed machine IDs")
+        XCTAssertNil(container.setAllowedMachineIDsSync(test: self, allowedMachineIDs: ["aaa", "bbb"], accountIsDemo: false, listDifference: true), "should be able to set allowed machine IDs")
         try self.assert(container: container, allowedMachineIDs: Set(["aaa", "bbb", "ddd", "eee"]), disallowedMachineIDs: Set(["ccc"]), persistentStore: description, cuttlefish: self.cuttlefish)
         XCTAssertFalse(container.onqueueFullIDMSListWouldBeHelpful(), "Container shouldn't think it could use an IDMS list set")
 
         try self.assert(container: container, allowedMachineIDs: Set(["aaa", "bbb", "ddd", "eee"]), disallowedMachineIDs: Set(["ccc"]), persistentStore: description, cuttlefish: self.cuttlefish)
         XCTAssertFalse(container.onqueueFullIDMSListWouldBeHelpful(), "Container shouldn't think it could use an IDMS list set")
 
@@ -2125,23 +2162,23 @@ class TrustedPeersHelperUnitTests: XCTestCase {
         XCTAssertTrue(container.onqueueFullIDMSListWouldBeHelpful(), "Container should think it could use an IDMS list set: there's machine IDs pending removal")
 
         // and a list set after the remove confirms the removal
         XCTAssertTrue(container.onqueueFullIDMSListWouldBeHelpful(), "Container should think it could use an IDMS list set: there's machine IDs pending removal")
 
         // and a list set after the remove confirms the removal
-        XCTAssertNil(container.setAllowedMachineIDsSync(test: self, allowedMachineIDs: ["aaa", "bbb"], listDifference: true), "should be able to set allowed machine IDs")
+        XCTAssertNil(container.setAllowedMachineIDsSync(test: self, allowedMachineIDs: ["aaa", "bbb"], accountIsDemo: false, listDifference: true), "should be able to set allowed machine IDs")
         try self.assert(container: container, allowedMachineIDs: Set(["aaa", "bbb", "ddd"]), disallowedMachineIDs: Set(["ccc", "eee"]), persistentStore: description, cuttlefish: self.cuttlefish)
         XCTAssertFalse(container.onqueueFullIDMSListWouldBeHelpful(), "Container shouldn't think it could use an IDMS list set")
 
         // Then a new list set includes D! Hurray IDMS. Note that this is not a "list change", because the list doesn't actually change
         try self.assert(container: container, allowedMachineIDs: Set(["aaa", "bbb", "ddd"]), disallowedMachineIDs: Set(["ccc", "eee"]), persistentStore: description, cuttlefish: self.cuttlefish)
         XCTAssertFalse(container.onqueueFullIDMSListWouldBeHelpful(), "Container shouldn't think it could use an IDMS list set")
 
         // Then a new list set includes D! Hurray IDMS. Note that this is not a "list change", because the list doesn't actually change
-        XCTAssertNil(container.setAllowedMachineIDsSync(test: self, allowedMachineIDs: ["aaa", "bbb", "ddd"], listDifference: false), "should be able to set allowed machine IDs")
+        XCTAssertNil(container.setAllowedMachineIDsSync(test: self, allowedMachineIDs: ["aaa", "bbb", "ddd"], accountIsDemo: false, listDifference: false), "should be able to set allowed machine IDs")
         try self.assert(container: container, allowedMachineIDs: Set(["aaa", "bbb", "ddd"]), disallowedMachineIDs: Set(["ccc", "eee"]), persistentStore: description, cuttlefish: self.cuttlefish)
         XCTAssertFalse(container.onqueueFullIDMSListWouldBeHelpful(), "Container shouldn't think it could use an IDMS list set")
 
         // And another list set no longer includes D, so it should now be disallowed
         try self.assert(container: container, allowedMachineIDs: Set(["aaa", "bbb", "ddd"]), disallowedMachineIDs: Set(["ccc", "eee"]), persistentStore: description, cuttlefish: self.cuttlefish)
         XCTAssertFalse(container.onqueueFullIDMSListWouldBeHelpful(), "Container shouldn't think it could use an IDMS list set")
 
         // And another list set no longer includes D, so it should now be disallowed
-        XCTAssertNil(container.setAllowedMachineIDsSync(test: self, allowedMachineIDs: ["aaa", "bbb"], listDifference: true), "should be able to set allowed machine IDs")
+        XCTAssertNil(container.setAllowedMachineIDsSync(test: self, allowedMachineIDs: ["aaa", "bbb"], accountIsDemo: false, listDifference: true), "should be able to set allowed machine IDs")
         try self.assert(container: container, allowedMachineIDs: Set(["aaa", "bbb"]), disallowedMachineIDs: Set(["ccc", "ddd", "eee"]), persistentStore: description, cuttlefish: self.cuttlefish)
         XCTAssertFalse(container.onqueueFullIDMSListWouldBeHelpful(), "Container shouldn't think it could use an IDMS list set")
 
         // And just to check the 48 hour boundary...
         XCTAssertNil(container.addAllowedMachineIDsSync(test: self, machineIDs: ["xxx"]), "should be able to receive an add push")
         try self.assert(container: container, allowedMachineIDs: Set(["aaa", "bbb"]), disallowedMachineIDs: Set(["ccc", "ddd", "eee"]), persistentStore: description, cuttlefish: self.cuttlefish)
         XCTAssertFalse(container.onqueueFullIDMSListWouldBeHelpful(), "Container shouldn't think it could use an IDMS list set")
 
         // And just to check the 48 hour boundary...
         XCTAssertNil(container.addAllowedMachineIDsSync(test: self, machineIDs: ["xxx"]), "should be able to receive an add push")
-        XCTAssertNil(container.setAllowedMachineIDsSync(test: self, allowedMachineIDs: ["aaa", "bbb"], listDifference: false), "should be able to set allowed machine IDs")
+        XCTAssertNil(container.setAllowedMachineIDsSync(test: self, allowedMachineIDs: ["aaa", "bbb"], accountIsDemo: false, listDifference: false), "should be able to set allowed machine IDs")
         try self.assert(container: container, allowedMachineIDs: Set(["aaa", "bbb", "xxx"]), disallowedMachineIDs: Set(["ccc", "ddd", "eee"]), persistentStore: description, cuttlefish: self.cuttlefish)
 
         container.moc.performAndWait {
         try self.assert(container: container, allowedMachineIDs: Set(["aaa", "bbb", "xxx"]), disallowedMachineIDs: Set(["ccc", "ddd", "eee"]), persistentStore: description, cuttlefish: self.cuttlefish)
 
         container.moc.performAndWait {
@@ -2159,7 +2196,7 @@ class TrustedPeersHelperUnitTests: XCTestCase {
         XCTAssertFalse(container.onqueueFullIDMSListWouldBeHelpful(), "Container shouldn't think it could use an IDMS list set")
 
         // Setting the list again should kick out X, since it was 'added' too long ago
         XCTAssertFalse(container.onqueueFullIDMSListWouldBeHelpful(), "Container shouldn't think it could use an IDMS list set")
 
         // Setting the list again should kick out X, since it was 'added' too long ago
-        XCTAssertNil(container.setAllowedMachineIDsSync(test: self, allowedMachineIDs: ["aaa", "bbb"], listDifference: true), "should be able to set allowed machine IDs")
+        XCTAssertNil(container.setAllowedMachineIDsSync(test: self, allowedMachineIDs: ["aaa", "bbb"], accountIsDemo: false, listDifference: true), "should be able to set allowed machine IDs")
         try self.assert(container: container, allowedMachineIDs: Set(["aaa", "bbb"]), disallowedMachineIDs: Set(["ccc", "ddd", "eee", "xxx"]), persistentStore: description, cuttlefish: self.cuttlefish)
     }
 
         try self.assert(container: container, allowedMachineIDs: Set(["aaa", "bbb"]), disallowedMachineIDs: Set(["ccc", "ddd", "eee", "xxx"]), persistentStore: description, cuttlefish: self.cuttlefish)
     }
 
@@ -2188,7 +2225,7 @@ class TrustedPeersHelperUnitTests: XCTestCase {
         // Now TPH boots up with a preexisting model
         let container = try Container(name: containerName, persistentStoreDescription: description, cuttlefish: cuttlefish)
 
         // Now TPH boots up with a preexisting model
         let container = try Container(name: containerName, persistentStoreDescription: description, cuttlefish: cuttlefish)
 
-        let (peerID, permanentInfo, permanentInfoSig, _, _, error) = container.prepareSync(test: self, epoch: 1, machineID: "aaa", bottleSalt: "123456789", bottleID: UUID().uuidString, modelID: "iPhone1,1")
+        let (peerID, permanentInfo, permanentInfoSig, _, _, _, _, error) = container.prepareSync(test: self, epoch: 1, machineID: "aaa", bottleSalt: "123456789", bottleID: UUID().uuidString, modelID: "iPhone1,1")
 
         XCTAssertNil(error)
         XCTAssertNotNil(peerID)
 
         XCTAssertNil(error)
         XCTAssertNotNil(peerID)
@@ -2198,7 +2235,7 @@ class TrustedPeersHelperUnitTests: XCTestCase {
         try self.assert(container: container, allowedMachineIDs: Set(["aaa", "bbb", "ccc"]), disallowedMachineIDs: [], persistentStore: description, cuttlefish: self.cuttlefish)
 
         // Setting a new list should work fine
         try self.assert(container: container, allowedMachineIDs: Set(["aaa", "bbb", "ccc"]), disallowedMachineIDs: [], persistentStore: description, cuttlefish: self.cuttlefish)
 
         // Setting a new list should work fine
-        XCTAssertNil(container.setAllowedMachineIDsSync(test: self, allowedMachineIDs: ["aaa", "bbb", "ddd"]), "should be able to set allowed machine IDs")
+        XCTAssertNil(container.setAllowedMachineIDsSync(test: self, allowedMachineIDs: ["aaa", "bbb", "ddd"], accountIsDemo: false), "should be able to set allowed machine IDs")
         try self.assert(container: container, allowedMachineIDs: Set(["aaa", "bbb", "ddd"]), disallowedMachineIDs: ["ccc"], persistentStore: description, cuttlefish: self.cuttlefish)
 
         XCTAssertEqual(container.containerMO.allowedMachineIDs, Set<String>() as NSSet, "Set of allowed machine IDs should now be empty")
         try self.assert(container: container, allowedMachineIDs: Set(["aaa", "bbb", "ddd"]), disallowedMachineIDs: ["ccc"], persistentStore: description, cuttlefish: self.cuttlefish)
 
         XCTAssertEqual(container.containerMO.allowedMachineIDs, Set<String>() as NSSet, "Set of allowed machine IDs should now be empty")
@@ -2242,7 +2279,7 @@ class TrustedPeersHelperUnitTests: XCTestCase {
         try self.assert(container: container, allowedMachineIDs: Set(["aaa"]), disallowedMachineIDs: ["bbb"], persistentStore: description, cuttlefish: self.cuttlefish)
 
         // Setting a new list should work fine
         try self.assert(container: container, allowedMachineIDs: Set(["aaa"]), disallowedMachineIDs: ["bbb"], persistentStore: description, cuttlefish: self.cuttlefish)
 
         // Setting a new list should work fine
-        XCTAssertNil(container.setAllowedMachineIDsSync(test: self, allowedMachineIDs: ["aaa", "ddd"]), "should be able to set allowed machine IDs")
+        XCTAssertNil(container.setAllowedMachineIDsSync(test: self, allowedMachineIDs: ["aaa", "ddd"], accountIsDemo: false), "should be able to set allowed machine IDs")
         try self.assert(container: container, allowedMachineIDs: Set(["aaa", "ddd"]), disallowedMachineIDs: ["bbb"], persistentStore: description, cuttlefish: self.cuttlefish)
 
         XCTAssertEqual(container.containerMO.allowedMachineIDs, Set<String>() as NSSet, "Set of allowed machine IDs should now be empty")
         try self.assert(container: container, allowedMachineIDs: Set(["aaa", "ddd"]), disallowedMachineIDs: ["bbb"], persistentStore: description, cuttlefish: self.cuttlefish)
 
         XCTAssertEqual(container.containerMO.allowedMachineIDs, Set<String>() as NSSet, "Set of allowed machine IDs should now be empty")
@@ -2263,7 +2300,7 @@ class TrustedPeersHelperUnitTests: XCTestCase {
         }
 
         // and set the machine ID list to something that doesn't include 'aaa'
         }
 
         // and set the machine ID list to something that doesn't include 'aaa'
-        XCTAssertNil(container.setAllowedMachineIDsSync(test: self, allowedMachineIDs: ["bbb", "ccc"], listDifference: true), "should be able to set allowed machine IDs")
+        XCTAssertNil(container.setAllowedMachineIDsSync(test: self, allowedMachineIDs: ["bbb", "ccc"], accountIsDemo: false, listDifference: true), "should be able to set allowed machine IDs")
         try self.assert(container: container, allowedMachineIDs: Set(["bbb", "ccc"]), disallowedMachineIDs: [], unknownMachineIDs: Set(["aaa"]), persistentStore: description, cuttlefish: self.cuttlefish)
         XCTAssertFalse(container.onqueueFullIDMSListWouldBeHelpful(), "Container shouldn't think it could use an IDMS list set")
 
         try self.assert(container: container, allowedMachineIDs: Set(["bbb", "ccc"]), disallowedMachineIDs: [], unknownMachineIDs: Set(["aaa"]), persistentStore: description, cuttlefish: self.cuttlefish)
         XCTAssertFalse(container.onqueueFullIDMSListWouldBeHelpful(), "Container shouldn't think it could use an IDMS list set")
 
@@ -2286,7 +2323,7 @@ class TrustedPeersHelperUnitTests: XCTestCase {
         XCTAssertFalse(container.onqueueFullIDMSListWouldBeHelpful(), "Container shouldn't think it could use an IDMS list set")
 
         // Setting it again is fine...
         XCTAssertFalse(container.onqueueFullIDMSListWouldBeHelpful(), "Container shouldn't think it could use an IDMS list set")
 
         // Setting it again is fine...
-        XCTAssertNil(container.setAllowedMachineIDsSync(test: self, allowedMachineIDs: ["bbb", "ccc"], listDifference: false), "should be able to set allowed machine IDs")
+        XCTAssertNil(container.setAllowedMachineIDsSync(test: self, allowedMachineIDs: ["bbb", "ccc"], accountIsDemo: false, listDifference: false), "should be able to set allowed machine IDs")
         try self.assert(container: container, allowedMachineIDs: Set(["bbb", "ccc"]), disallowedMachineIDs: [], unknownMachineIDs: Set(["aaa"]), persistentStore: description, cuttlefish: self.cuttlefish)
 
         // And doesn't reset the modified date on the record
         try self.assert(container: container, allowedMachineIDs: Set(["bbb", "ccc"]), disallowedMachineIDs: [], unknownMachineIDs: Set(["aaa"]), persistentStore: description, cuttlefish: self.cuttlefish)
 
         // And doesn't reset the modified date on the record
@@ -2304,53 +2341,80 @@ class TrustedPeersHelperUnitTests: XCTestCase {
         }
 
         // And can be promoted to 'allowed'
         }
 
         // And can be promoted to 'allowed'
-        XCTAssertNil(container.setAllowedMachineIDsSync(test: self, allowedMachineIDs: ["aaa", "bbb"], listDifference: true), "should be able to set allowed machine IDs")
+        XCTAssertNil(container.setAllowedMachineIDsSync(test: self, allowedMachineIDs: ["aaa", "bbb"], accountIsDemo: false, listDifference: true), "should be able to set allowed machine IDs")
         try self.assert(container: container, allowedMachineIDs: Set(["aaa", "bbb"]), disallowedMachineIDs: ["ccc"], persistentStore: description, cuttlefish: self.cuttlefish)
         XCTAssertFalse(container.onqueueFullIDMSListWouldBeHelpful(), "Container shouldn't think it could use an IDMS list set")
     }
 
     func testMachineIDListSetDisallowedOldUnknownMachineIDs() throws {
         let description = tmpStoreDescription(name: "container.db")
         try self.assert(container: container, allowedMachineIDs: Set(["aaa", "bbb"]), disallowedMachineIDs: ["ccc"], persistentStore: description, cuttlefish: self.cuttlefish)
         XCTAssertFalse(container.onqueueFullIDMSListWouldBeHelpful(), "Container shouldn't think it could use an IDMS list set")
     }
 
     func testMachineIDListSetDisallowedOldUnknownMachineIDs() throws {
         let description = tmpStoreDescription(name: "container.db")
-        let (container, _) = try establish(reload: false, contextID: OTDefaultContext, allowedMachineIDs: Set(), store: description)
+        var (container, peerID1) = try establish(reload: false, contextID: OTDefaultContext, allowedMachineIDs: Set(["aaa"]), accountIsDemo: false, store: description)
 
 
-        // and set the machine ID list to something that doesn't include 'aaa'
-        XCTAssertNil(container.setAllowedMachineIDsSync(test: self, allowedMachineIDs: ["bbb", "ccc"], listDifference: true), "should be able to set allowed machine IDs")
-        try self.assert(container: container, allowedMachineIDs: Set(["bbb", "ccc"]), disallowedMachineIDs: [], unknownMachineIDs: Set(["aaa"]), persistentStore: description, cuttlefish: self.cuttlefish)
+        // and set the machine ID list to something that doesn't include 'ddd'
+        XCTAssertNil(container.setAllowedMachineIDsSync(test: self, allowedMachineIDs: ["aaa", "bbb", "ccc"], accountIsDemo: false, listDifference: true), "should be able to set allowed machine IDs")
+        try self.assert(container: container, allowedMachineIDs: Set(["aaa", "bbb", "ccc"]), disallowedMachineIDs: [], unknownMachineIDs: Set(), persistentStore: description, cuttlefish: self.cuttlefish)
         XCTAssertFalse(container.onqueueFullIDMSListWouldBeHelpful(), "Container shouldn't think it could use an IDMS list set")
         XCTAssertFalse(container.onqueueFullIDMSListWouldBeHelpful(), "Container shouldn't think it could use an IDMS list set")
+        XCTAssertEqual(container.containerMO.honorIDMSListChanges, "YES", "honorIDMSListChanges should be YES")
 
 
-        // But an entry for "aaa" should exist, as a peer in the model claims it as their MID
+        let unknownMachineID = "ddd"
+        let (_, peerID3) = try self.joinByVoucher(sponsor: container,
+                                                  containerID: "second",
+                                                  machineID: unknownMachineID,
+                                                  machineIDs: Set([unknownMachineID, "aaa", "bbb", "ccc"]), accountIsDemo: false,
+                                                  store: description)
+
+        // And the first container accepts the join...
+        let (_, cUpdateError) = container.updateSync(test: self)
+        XCTAssertNil(cUpdateError, "Should be able to update first container")
+        assertTrusts(context: container, peerIDs: [peerID1, peerID3])
+
+        try self.assert(container: container, allowedMachineIDs: Set(["aaa", "bbb", "ccc"]), disallowedMachineIDs: [], unknownMachineIDs: Set([unknownMachineID]), persistentStore: description, cuttlefish: self.cuttlefish)
+
+        XCTAssertFalse(container.onqueueFullIDMSListWouldBeHelpful(), "Container shouldn't think it could use an IDMS list set")
+        XCTAssertEqual(container.containerMO.honorIDMSListChanges, "YES", "honorIDMSListChanges should be YES")
+
+        // But an entry for "ddd" should exist, as a peer in the model claims it as their MID
         container.moc.performAndWait {
             let knownMachineMOs = container.containerMO.machines as? Set<MachineMO> ?? Set()
 
         container.moc.performAndWait {
             let knownMachineMOs = container.containerMO.machines as? Set<MachineMO> ?? Set()
 
-            let aaaMOs = knownMachineMOs.filter { $0.machineID == "aaa" }
-            XCTAssertEqual(aaaMOs.count, 1, "Should have one machine MO for aaa")
+            let unknownMOs = knownMachineMOs.filter { $0.machineID == unknownMachineID }
+            XCTAssertEqual(unknownMOs.count, 1, "Should have one machine MO for ddd")
 
 
-            let aaaMO = aaaMOs.first!
-            XCTAssertEqual(aaaMO.status, Int64(TPMachineIDStatus.unknown.rawValue), "Status of aaa MO should be 'unknown'")
-            XCTAssertFalse(aaaMO.allowed, "allowed should no longer be a used field")
+            let dddMO = unknownMOs.first!
+            XCTAssertEqual(dddMO.status, Int64(TPMachineIDStatus.unknown.rawValue), "Status of ddd MO should be 'unknown'")
+            XCTAssertFalse(dddMO.allowed, "allowed should no longer be a used field")
 
 
-            // Pretend that aaa was added 49 hours ago
-            aaaMO.modified = Date(timeIntervalSinceNow: -60 * 60 * 49)
+            // Pretend that ddd was added 49 hours ago
+            dddMO.modified = Date(timeIntervalSinceNow: -60 * 60 * 49)
             try! container.moc.save()
         }
 
             try! container.moc.save()
         }
 
+        //reload container
+        do {
+            container = try Container(name: ContainerName(container: "test", context: OTDefaultContext), persistentStoreDescription: description, cuttlefish: cuttlefish)
+            XCTAssertEqual(container.containerMO.honorIDMSListChanges, "YES", "honorIDMSListChanges should be YES")
+
+        } catch {
+            XCTFail("Creating container errored: \(error)")
+        }
         XCTAssertTrue(container.onqueueFullIDMSListWouldBeHelpful(), "Container should think it could use an IDMS list set: there's machine IDs pending removal")
 
         XCTAssertTrue(container.onqueueFullIDMSListWouldBeHelpful(), "Container should think it could use an IDMS list set: there's machine IDs pending removal")
 
-        // And, setting the list again should disallow aaa, since it is so old
-        // Note that this _should_ return a list difference, since A is promoted to disallowed
-        XCTAssertNil(container.setAllowedMachineIDsSync(test: self, allowedMachineIDs: ["bbb", "ccc"], listDifference: true), "should be able to set allowed machine IDs")
-        try self.assert(container: container, allowedMachineIDs: Set(["bbb", "ccc"]), disallowedMachineIDs: ["aaa"], persistentStore: description, cuttlefish: self.cuttlefish)
+        // And, setting the list again should disallow ddd, since it is so old
+        // Note that this _should_ return a list difference, since D is promoted to disallowed
+        XCTAssertNil(container.setAllowedMachineIDsSync(test: self, allowedMachineIDs: ["aaa", "bbb", "ccc"], accountIsDemo: false, listDifference: true), "should be able to set allowed machine IDs")
+        try self.assert(container: container, allowedMachineIDs: Set(["aaa", "bbb", "ccc"]), disallowedMachineIDs: [unknownMachineID], persistentStore: description, cuttlefish: self.cuttlefish)
         XCTAssertFalse(container.onqueueFullIDMSListWouldBeHelpful(), "Container shouldn't think it could use an IDMS list set")
 
         // Setting ths list again has no change
         XCTAssertFalse(container.onqueueFullIDMSListWouldBeHelpful(), "Container shouldn't think it could use an IDMS list set")
 
         // Setting ths list again has no change
-        XCTAssertNil(container.setAllowedMachineIDsSync(test: self, allowedMachineIDs: ["bbb", "ccc"], listDifference: false), "should be able to set allowed machine IDs")
-        try self.assert(container: container, allowedMachineIDs: Set(["bbb", "ccc"]), disallowedMachineIDs: ["aaa"], persistentStore: description, cuttlefish: self.cuttlefish)
+        XCTAssertNil(container.setAllowedMachineIDsSync(test: self, allowedMachineIDs: ["aaa", "bbb", "ccc"], accountIsDemo: false, listDifference: false), "should be able to set allowed machine IDs")
+        try self.assert(container: container, allowedMachineIDs: Set(["aaa", "bbb", "ccc"]), disallowedMachineIDs: [unknownMachineID], persistentStore: description, cuttlefish: self.cuttlefish)
         XCTAssertFalse(container.onqueueFullIDMSListWouldBeHelpful(), "Container shouldn't think it could use an IDMS list set")
 
         XCTAssertFalse(container.onqueueFullIDMSListWouldBeHelpful(), "Container shouldn't think it could use an IDMS list set")
 
-        // But A can appear again, no problem.
-        XCTAssertNil(container.setAllowedMachineIDsSync(test: self, allowedMachineIDs: ["aaa", "bbb", "ccc"], listDifference: true), "should be able to set allowed machine IDs")
-        try self.assert(container: container, allowedMachineIDs: Set(["aaa", "bbb", "ccc"]), disallowedMachineIDs: [], persistentStore: description, cuttlefish: self.cuttlefish)
+        // But D can appear again, no problem.
+        XCTAssertNil(container.setAllowedMachineIDsSync(test: self, allowedMachineIDs: ["aaa", "bbb", "ccc", "ddd"], accountIsDemo: false, listDifference: true), "should be able to set allowed machine IDs")
+        try self.assert(container: container, allowedMachineIDs: Set(["aaa", "bbb", "ccc", "ddd"]), disallowedMachineIDs: [], persistentStore: description, cuttlefish: self.cuttlefish)
         XCTAssertFalse(container.onqueueFullIDMSListWouldBeHelpful(), "Container shouldn't think it could use an IDMS list set")
         XCTAssertFalse(container.onqueueFullIDMSListWouldBeHelpful(), "Container shouldn't think it could use an IDMS list set")
+        XCTAssertEqual(container.containerMO.honorIDMSListChanges, "YES", "honorIDMSListChanges should be YES")
     }
 
     func testMachineIDListHandlingWithPeers() throws {
     }
 
     func testMachineIDListHandlingWithPeers() throws {
@@ -2363,7 +2427,7 @@ class TrustedPeersHelperUnitTests: XCTestCase {
         let (_, peerID2) = try self.joinByVoucher(sponsor: container,
                                                   containerID: "second",
                                                   machineID: unknownMachineID,
         let (_, peerID2) = try self.joinByVoucher(sponsor: container,
                                                   containerID: "second",
                                                   machineID: unknownMachineID,
-                                                  machineIDs: Set([unknownMachineID, "aaa", "bbb", "ccc"]),
+                                                  machineIDs: Set([unknownMachineID, "aaa", "bbb", "ccc"]), accountIsDemo: false,
                                                   store: description)
 
         // And the first container accepts the join...
                                                   store: description)
 
         // And the first container accepts the join...
@@ -2377,7 +2441,7 @@ class TrustedPeersHelperUnitTests: XCTestCase {
     func testMachineIDListHandlingInDemoAccounts() throws {
         // Demo accounts have no machine IDs in their lists
         let description = tmpStoreDescription(name: "container.db")
     func testMachineIDListHandlingInDemoAccounts() throws {
         // Demo accounts have no machine IDs in their lists
         let description = tmpStoreDescription(name: "container.db")
-        let (container, peerID1) = try establish(reload: false, contextID: OTDefaultContext, allowedMachineIDs: Set(), store: description)
+        var (container, peerID1) = try establish(reload: false, contextID: OTDefaultContext, allowedMachineIDs: Set(), accountIsDemo: true, store: description)
 
         // And so we just don't write down any MIDs
         try self.assert(container: container, allowedMachineIDs: Set([]), disallowedMachineIDs: [], unknownMachineIDs: Set([]), persistentStore: description, cuttlefish: self.cuttlefish)
 
         // And so we just don't write down any MIDs
         try self.assert(container: container, allowedMachineIDs: Set([]), disallowedMachineIDs: [], unknownMachineIDs: Set([]), persistentStore: description, cuttlefish: self.cuttlefish)
@@ -2388,9 +2452,12 @@ class TrustedPeersHelperUnitTests: XCTestCase {
                                                    containerID: "second",
                                                    machineID: unknownMachineID,
                                                    machineIDs: Set(),
                                                    containerID: "second",
                                                    machineID: unknownMachineID,
                                                    machineIDs: Set(),
+                                                   accountIsDemo: true,
                                                    store: description)
         try self.assert(container: c2, allowedMachineIDs: Set([]), disallowedMachineIDs: [], unknownMachineIDs: Set([]), persistentStore: description, cuttlefish: self.cuttlefish)
 
                                                    store: description)
         try self.assert(container: c2, allowedMachineIDs: Set([]), disallowedMachineIDs: [], unknownMachineIDs: Set([]), persistentStore: description, cuttlefish: self.cuttlefish)
 
+        c2.containerMO.honorIDMSListChanges = "NO"
+
         // And the first container accepts the join...
         let (_, cUpdateError) = container.updateSync(test: self)
         XCTAssertNil(cUpdateError, "Should be able to update first container")
         // And the first container accepts the join...
         let (_, cUpdateError) = container.updateSync(test: self)
         XCTAssertNil(cUpdateError, "Should be able to update first container")
@@ -2399,11 +2466,21 @@ class TrustedPeersHelperUnitTests: XCTestCase {
         // And still has nothing in its list...
         try self.assert(container: container, allowedMachineIDs: Set([]), disallowedMachineIDs: [], unknownMachineIDs: Set([]), persistentStore: description, cuttlefish: self.cuttlefish)
 
         // And still has nothing in its list...
         try self.assert(container: container, allowedMachineIDs: Set([]), disallowedMachineIDs: [], unknownMachineIDs: Set([]), persistentStore: description, cuttlefish: self.cuttlefish)
 
+        //reload container
+        do {
+            container = try Container(name: ContainerName(container: "test", context: OTDefaultContext), persistentStoreDescription: description, cuttlefish: cuttlefish)
+            XCTAssertEqual(container.containerMO.honorIDMSListChanges, "NO", "honorIDMSListChanges should be NO")
+
+        } catch {
+            XCTFail("Creating container errored: \(error)")
+        }
+
         // Even after a full list set
         // Even after a full list set
-        XCTAssertNil(container.setAllowedMachineIDsSync(test: self, allowedMachineIDs: [], listDifference: false), "should be able to set allowed machine IDs")
+        XCTAssertNil(container.setAllowedMachineIDsSync(test: self, allowedMachineIDs: [], accountIsDemo: true, listDifference: false), "should be able to set allowed machine IDs")
         try self.assert(container: container, allowedMachineIDs: Set([]), disallowedMachineIDs: [], unknownMachineIDs: Set([]), persistentStore: description, cuttlefish: self.cuttlefish)
 
         XCTAssertFalse(container.onqueueFullIDMSListWouldBeHelpful(), "Container shouldn't think it could use an IDMS list set")
         try self.assert(container: container, allowedMachineIDs: Set([]), disallowedMachineIDs: [], unknownMachineIDs: Set([]), persistentStore: description, cuttlefish: self.cuttlefish)
 
         XCTAssertFalse(container.onqueueFullIDMSListWouldBeHelpful(), "Container shouldn't think it could use an IDMS list set")
+        XCTAssertEqual(container.containerMO.honorIDMSListChanges, "NO", "honorIDMSListChanges should be NO")
     }
 
     func testContainerAndModelConsistency() throws {
     }
 
     func testContainerAndModelConsistency() throws {
@@ -2411,7 +2488,7 @@ class TrustedPeersHelperUnitTests: XCTestCase {
         let preTestContainerName = ContainerName(container: "testToCreatePrepareData", context: "context")
         let description = tmpStoreDescription(name: "container.db")
         let containerTest = try Container(name: preTestContainerName, persistentStoreDescription: description, cuttlefish: cuttlefish)
         let preTestContainerName = ContainerName(container: "testToCreatePrepareData", context: "context")
         let description = tmpStoreDescription(name: "container.db")
         let containerTest = try Container(name: preTestContainerName, persistentStoreDescription: description, cuttlefish: cuttlefish)
-        let (peerID, permanentInfo, permanentInfoSig, stableInfo, stableInfoSig, error) = containerTest.prepareSync(test: self, epoch: 1, machineID: "aaa", bottleSalt: "123456789", bottleID: UUID().uuidString, modelID: "iPhone1,1")
+        let (peerID, permanentInfo, permanentInfoSig, stableInfo, stableInfoSig, _, _, error) = containerTest.prepareSync(test: self, epoch: 1, machineID: "aaa", bottleSalt: "123456789", bottleID: UUID().uuidString, modelID: "iPhone1,1")
         XCTAssertNil(error)
         XCTAssertNotNil(peerID)
         XCTAssertNotNil(permanentInfo)
         XCTAssertNil(error)
         XCTAssertNotNil(peerID)
         XCTAssertNotNil(permanentInfo)
@@ -2443,8 +2520,8 @@ class TrustedPeersHelperUnitTests: XCTestCase {
             do {
                 let peerKeys: OctagonSelfPeerKeys = try loadEgoKeysSync(peerID: containerMO.egoPeerID!)
                 let info3 = TPPeerStableInfo(clock: containerEgoStableInfo!.clock + 2,
             do {
                 let peerKeys: OctagonSelfPeerKeys = try loadEgoKeysSync(peerID: containerMO.egoPeerID!)
                 let info3 = TPPeerStableInfo(clock: containerEgoStableInfo!.clock + 2,
-                                                               policyVersion: containerEgoStableInfo!.policyVersion,
-                                                               policyHash: containerEgoStableInfo!.policyHash,
+                                             frozenPolicyVersion: containerEgoStableInfo!.frozenPolicyVersion,
+                                             flexiblePolicyVersion: containerEgoStableInfo!.flexiblePolicyVersion!,
                                                                policySecrets: containerEgoStableInfo!.policySecrets,
                                                                deviceName: containerEgoStableInfo!.deviceName,
                                                                serialNumber: containerEgoStableInfo!.serialNumber,
                                                                policySecrets: containerEgoStableInfo!.policySecrets,
                                                                deviceName: containerEgoStableInfo!.deviceName,
                                                                serialNumber: containerEgoStableInfo!.serialNumber,
@@ -2518,4 +2595,328 @@ class TrustedPeersHelperUnitTests: XCTestCase {
         let e4 = NSError(domain: CKErrorDomain, code: CKError.serverRejectedRequest.rawValue, userInfo: [NSUnderlyingErrorKey: int4])
         XCTAssertTrue(RetryingInvocable.retryableError(error: e4))
     }
         let e4 = NSError(domain: CKErrorDomain, code: CKError.serverRejectedRequest.rawValue, userInfo: [NSUnderlyingErrorKey: int4])
         XCTAssertTrue(RetryingInvocable.retryableError(error: e4))
     }
+
+    func testEstablishWithEnforceIDMSListNotSetBehavior() throws {
+        let contextID = "OTDefaultContext"
+        let description = tmpStoreDescription(name: "container.db")
+
+        var container = try Container(name: ContainerName(container: "test", context: contextID), persistentStoreDescription: description, cuttlefish: cuttlefish)
+        XCTAssertEqual(container.containerMO.honorIDMSListChanges, "UNKNOWN", "honorIDMSListChanges should be unknown")
+
+        let (peerID, permanentInfo, permanentInfoSig, _, _, _, _, error) = container.prepareSync(test: self, epoch: 1, machineID: "aaa", bottleSalt: "123456789", bottleID: UUID().uuidString, modelID: "iPhone1,1")
+        do {
+            let state = container.getStateSync(test: self)
+            XCTAssertFalse( state.bottles.filter { $0.peerID == peerID } .isEmpty, "should have a bottle for peer")
+            let secret = container.loadSecretSync(test: self, label: peerID!)
+            XCTAssertNotNil(secret, "secret should not be nil")
+            XCTAssertNil(error, "error should be nil")
+        }
+        XCTAssertNotNil(peerID)
+        XCTAssertNotNil(permanentInfo)
+        XCTAssertNotNil(permanentInfoSig)
+        XCTAssertNil(error)
+
+        _ = container.dumpSync(test: self)
+
+        do {
+            container = try Container(name: ContainerName(container: "test", context: contextID), persistentStoreDescription: description, cuttlefish: cuttlefish)
+        } catch {
+            XCTFail("Creating container errored: \(error)")
+        }
+
+        let (peerID2, _, error2) = container.establishSync(test: self, ckksKeys: [self.manateeKeySet], tlkShares: [], preapprovedKeys: [])
+        XCTAssertNil(error2)
+        XCTAssertNotNil(peerID2)
+
+        _ = container.dumpSync(test: self)
+        XCTAssertEqual(container.containerMO.honorIDMSListChanges, "UNKNOWN", "honorIDMSListChanges should be unknown")
+    }
+
+    func testJoinWithEnforceIDMSListNotSetBehavior() throws {
+        let description = tmpStoreDescription(name: "container.db")
+        let containerA = try Container(name: ContainerName(container: "a", context: OTDefaultContext), persistentStoreDescription: description, cuttlefish: cuttlefish)
+        let containerB = try Container(name: ContainerName(container: "b", context: OTDefaultContext), persistentStoreDescription: description, cuttlefish: cuttlefish)
+        let containerC = try Container(name: ContainerName(container: "c", context: OTDefaultContext), persistentStoreDescription: description, cuttlefish: cuttlefish)
+
+        XCTAssertEqual(containerA.containerMO.honorIDMSListChanges, "UNKNOWN", "honorIDMSListChanges should be unknown")
+        XCTAssertEqual(containerB.containerMO.honorIDMSListChanges, "UNKNOWN", "honorIDMSListChanges should be unknown")
+        XCTAssertEqual(containerC.containerMO.honorIDMSListChanges, "UNKNOWN", "honorIDMSListChanges should be unknown")
+
+        print("preparing A")
+        let (aPeerID, aPermanentInfo, aPermanentInfoSig, _, _, aViewList, aPolicy, error) =
+            containerA.prepareSync(test: self, epoch: 1, machineID: "aaa", bottleSalt: "123456789", bottleID: UUID().uuidString, modelID: "iPhone1,1")
+        XCTAssertNotNil(aViewList, "Should have a view list coming back from a successful prepare")
+        XCTAssertNotNil(aPolicy, "Should have a policy coming back from a successful prepare")
+        XCTAssertEqual(aPolicy?.version, prevailingPolicyVersion, "Policy coming back from prepare() should be prevailing policy version")
+
+        do {
+            let state = containerA.getStateSync(test: self)
+            XCTAssertFalse( state.bottles.filter { $0.peerID == aPeerID } .isEmpty, "should have a bottle for peer")
+            let secret = containerA.loadSecretSync(test: self, label: aPeerID!)
+            XCTAssertNotNil(secret, "secret should not be nil")
+            XCTAssertNil(error, "error should be nil")
+        }
+        XCTAssertNil(error)
+        XCTAssertNotNil(aPeerID)
+        XCTAssertNotNil(aPermanentInfo)
+        XCTAssertNotNil(aPermanentInfoSig)
+
+        print("establishing A")
+        do {
+            let (peerID, _, error) = containerA.establishSync(test: self, ckksKeys: [], tlkShares: [], preapprovedKeys: [])
+            XCTAssertNil(error)
+            XCTAssertNotNil(peerID)
+        }
+
+        print("preparing B")
+        let (bPeerID, bPermanentInfo, bPermanentInfoSig, bStableInfo, bStableInfoSig, _, _, error2) =
+            containerB.prepareSync(test: self, epoch: 1, machineID: "bbb", bottleSalt: "123456789", bottleID: UUID().uuidString, modelID: "iPhone1,1")
+        do {
+            let state = containerB.getStateSync(test: self)
+            XCTAssertFalse( state.bottles.filter { $0.peerID == bPeerID } .isEmpty, "should have a bottle for peer")
+            let secret = containerB.loadSecretSync(test: self, label: bPeerID!)
+            XCTAssertNotNil(secret, "secret should not be nil")
+            XCTAssertNil(error, "error should be nil")
+        }
+        XCTAssertNil(error2)
+        XCTAssertNotNil(bPeerID)
+        XCTAssertNotNil(bPermanentInfo)
+        XCTAssertNotNil(bPermanentInfoSig)
+
+        do {
+            assertNoTLKShareFor(peerID: bPeerID!, keyUUID: self.manateeKeySet.tlk.uuid, zoneID: CKRecordZone.ID(zoneName: "Manatee"))
+            print("A vouches for B, but doesn't provide any TLKShares")
+            let (_, _, errorVouchingWithoutTLKs) =
+                containerA.vouchSync(test: self,
+                                     peerID: bPeerID!,
+                                     permanentInfo: bPermanentInfo!,
+                                     permanentInfoSig: bPermanentInfoSig!,
+                                     stableInfo: bStableInfo!,
+                                     stableInfoSig: bStableInfoSig!,
+                                     ckksKeys: [])
+            XCTAssertNil(errorVouchingWithoutTLKs, "Should be no error vouching without uploading TLKShares")
+            assertNoTLKShareFor(peerID: bPeerID!, keyUUID: self.manateeKeySet.tlk.uuid, zoneID: CKRecordZone.ID(zoneName: "Manatee"))
+
+            print("A vouches for B, but doesn't only has provisional TLKs at the time")
+            let provisionalManateeKeySet = try self.makeFakeKeyHierarchy(zoneID: CKRecordZone.ID(zoneName: "Manatee"))
+            provisionalManateeKeySet.newUpload = true
+
+            let (_, _, errorVouchingWithProvisionalTLKs) =
+                containerA.vouchSync(test: self,
+                                     peerID: bPeerID!,
+                                     permanentInfo: bPermanentInfo!,
+                                     permanentInfoSig: bPermanentInfoSig!,
+                                     stableInfo: bStableInfo!,
+                                     stableInfoSig: bStableInfoSig!,
+                                     ckksKeys: [provisionalManateeKeySet])
+            XCTAssertNil(errorVouchingWithProvisionalTLKs, "Should be no error vouching without uploading TLKShares for a non-existent key")
+            assertNoTLKShareFor(peerID: bPeerID!, keyUUID: self.manateeKeySet.tlk.uuid, zoneID: CKRecordZone.ID(zoneName: "Manatee"))
+
+            print("A vouches for B")
+            let (voucherData, voucherSig, error3) =
+                containerA.vouchSync(test: self,
+                                     peerID: bPeerID!,
+                                     permanentInfo: bPermanentInfo!,
+                                     permanentInfoSig: bPermanentInfoSig!,
+                                     stableInfo: bStableInfo!,
+                                     stableInfoSig: bStableInfoSig!,
+                                     ckksKeys: [self.manateeKeySet])
+            XCTAssertNil(error3)
+            XCTAssertNotNil(voucherData)
+            XCTAssertNotNil(voucherSig)
+
+            // As part of the vouch, A should have uploaded a tlkshare for B
+            assertTLKShareFor(peerID: bPeerID!, keyUUID: self.manateeKeySet.tlk.uuid, zoneID: CKRecordZone.ID(zoneName: "Manatee"))
+
+            print("B joins")
+            let (peerID, _, _, _, error) = containerB.joinSync(test: self,
+                                                               voucherData: voucherData!,
+                                                               voucherSig: voucherSig!,
+                                                               ckksKeys: [],
+                                                               tlkShares: [])
+            XCTAssertNil(error)
+            XCTAssertEqual(peerID, bPeerID!)
+        }
+
+        _ = containerA.dumpSync(test: self)
+        _ = containerB.dumpSync(test: self)
+        _ = containerC.dumpSync(test: self)
+
+        print("preparing C")
+        let (cPeerID, cPermanentInfo, cPermanentInfoSig, cStableInfo, cStableInfoSig, _, _, error4) =
+            containerC.prepareSync(test: self, epoch: 1, machineID: "ccc", bottleSalt: "123456789", bottleID: UUID().uuidString, modelID: "iPhone1,1")
+        do {
+            let state = containerC.getStateSync(test: self)
+            XCTAssertFalse( state.bottles.filter { $0.peerID == cPeerID } .isEmpty, "should have a bottle for peer")
+            let secret = containerC.loadSecretSync(test: self, label: cPeerID!)
+            XCTAssertNotNil(secret, "secret should not be nil")
+            XCTAssertNil(error, "error should be nil")
+        }
+        XCTAssertNil(error4)
+        XCTAssertNotNil(cPeerID)
+        XCTAssertNotNil(cPermanentInfo)
+        XCTAssertNotNil(cPermanentInfoSig)
+
+        do {
+            // C, when it joins, will create a new CKKS zone. It should also upload TLKShares for A and B.
+            let provisionalEngramKeySet = try self.makeFakeKeyHierarchy(zoneID: CKRecordZone.ID(zoneName: "Engram"))
+            provisionalEngramKeySet.newUpload = true
+
+            assertNoTLKShareFor(peerID: cPeerID!, keyUUID: self.manateeKeySet.tlk.uuid, zoneID: CKRecordZone.ID(zoneName: "Manatee"))
+            assertNoTLKShareFor(peerID: cPeerID!, keyUUID: provisionalEngramKeySet.tlk.uuid, zoneID: CKRecordZone.ID(zoneName: "Engram"))
+
+            print("B vouches for C")
+            let (voucherData, voucherSig, error) =
+                containerB.vouchSync(test: self,
+                                     peerID: cPeerID!,
+                                     permanentInfo: cPermanentInfo!,
+                                     permanentInfoSig: cPermanentInfoSig!,
+                                     stableInfo: cStableInfo!,
+                                     stableInfoSig: cStableInfoSig!,
+                                     ckksKeys: [self.manateeKeySet])
+            XCTAssertNil(error)
+            XCTAssertNotNil(voucherData)
+            XCTAssertNotNil(voucherSig)
+
+            assertTLKShareFor(peerID: cPeerID!, keyUUID: self.manateeKeySet.tlk.uuid, zoneID: CKRecordZone.ID(zoneName: "Manatee"))
+
+            print("C joins")
+            let (peerID, _, _, _, error2) = containerC.joinSync(test: self,
+                                                                voucherData: voucherData!,
+                                                                voucherSig: voucherSig!,
+                                                                ckksKeys: [self.manateeKeySet, provisionalEngramKeySet],
+                                                                tlkShares: [])
+            XCTAssertNil(error2)
+            XCTAssertEqual(peerID, cPeerID!)
+
+            assertTLKShareFor(peerID: cPeerID!, keyUUID: provisionalEngramKeySet.tlk.uuid, zoneID: CKRecordZone.ID(zoneName: "Engram"))
+            assertTLKShareFor(peerID: aPeerID!, keyUUID: provisionalEngramKeySet.tlk.uuid, zoneID: CKRecordZone.ID(zoneName: "Engram"))
+            assertTLKShareFor(peerID: bPeerID!, keyUUID: provisionalEngramKeySet.tlk.uuid, zoneID: CKRecordZone.ID(zoneName: "Engram"))
+        }
+
+        print("A updates")
+        do {
+            let (_, error) = containerA.updateSync(test: self)
+            XCTAssertNil(error)
+        }
+
+        do {
+            let state = containerA.getStateSync(test: self)
+            let a = state.peers[aPeerID!]!
+            XCTAssertTrue(a.dynamicInfo!.includedPeerIDs.contains(cPeerID!))
+        }
+
+        _ = containerA.dumpSync(test: self)
+        _ = containerB.dumpSync(test: self)
+        _ = containerC.dumpSync(test: self)
+
+        XCTAssertEqual(containerA.containerMO.honorIDMSListChanges, "UNKNOWN", "honorIDMSListChanges should be unknown")
+        XCTAssertEqual(containerB.containerMO.honorIDMSListChanges, "UNKNOWN", "honorIDMSListChanges should be unknown")
+        XCTAssertEqual(containerC.containerMO.honorIDMSListChanges, "UNKNOWN", "honorIDMSListChanges should be unknown")
+    }
+
+    func testPreApprovedJoinWithEnforceIDMSListNotSetBehavior() throws {
+        let description = tmpStoreDescription(name: "container.db")
+        let containerA = try Container(name: ContainerName(container: "a", context: OTDefaultContext), persistentStoreDescription: description, cuttlefish: cuttlefish)
+        let containerB = try Container(name: ContainerName(container: "b", context: OTDefaultContext), persistentStoreDescription: description, cuttlefish: cuttlefish)
+
+        XCTAssertEqual(containerA.containerMO.honorIDMSListChanges, "UNKNOWN", "honorIDMSListChanges should be unknown")
+        XCTAssertEqual(containerB.containerMO.honorIDMSListChanges, "UNKNOWN", "honorIDMSListChanges should be unknown")
+
+        print("preparing A")
+        let (aPeerID, aPermanentInfo, aPermanentInfoSig, _, _, aViewList, aPolicy, error) =
+            containerA.prepareSync(test: self, epoch: 1, machineID: "aaa", bottleSalt: "123456789", bottleID: UUID().uuidString, modelID: "iPhone1,1")
+        XCTAssertNil(error)
+        XCTAssertNotNil(aPeerID)
+        XCTAssertNotNil(aPermanentInfo)
+        XCTAssertNotNil(aPermanentInfoSig)
+
+        XCTAssertNotNil(aViewList, "Should have a view list coming back from a successful prepare")
+        XCTAssertNotNil(aPolicy, "Should have a policy coming back from a successful prepare")
+        XCTAssertEqual(aPolicy?.version, prevailingPolicyVersion, "Policy coming back from prepare() should be prevailing policy version")
+
+        print("preparing B")
+        let (bPeerID, bPermanentInfo, bPermanentInfoSig, _, _, _, _, error2) =
+            containerB.prepareSync(test: self, epoch: 1, machineID: "bbb", bottleSalt: "123456789", bottleID: UUID().uuidString, modelID: "iPhone1,1")
+        do {
+            let state = containerB.getStateSync(test: self)
+            XCTAssertFalse( state.bottles.filter { $0.peerID == bPeerID } .isEmpty, "should have a bottle for peer")
+            let secret = containerB.loadSecretSync(test: self, label: bPeerID!)
+            XCTAssertNotNil(secret, "secret should not be nil")
+            XCTAssertNil(error, "error should be nil")
+        }
+        XCTAssertNil(error2)
+        XCTAssertNotNil(bPeerID)
+        XCTAssertNotNil(bPermanentInfo)
+        XCTAssertNotNil(bPermanentInfoSig)
+
+        // Now, A establishes preapproving B
+        // Note: secd is responsible for passing in TLKShares to these preapproved keys in sosTLKShares
+
+        let bPermanentInfoParsed = TPPeerPermanentInfo(peerID: bPeerID!, data: bPermanentInfo!, sig: bPermanentInfoSig!, keyFactory: TPECPublicKeyFactory())
+        XCTAssertNotNil(bPermanentInfoParsed, "Should have parsed B's permanent info")
+
+        print("establishing A")
+        do {
+            let (peerID, _, error) = containerA.establishSync(test: self, ckksKeys: [self.manateeKeySet], tlkShares: [], preapprovedKeys: [bPermanentInfoParsed!.signingPubKey.spki()])
+            XCTAssertNil(error)
+            XCTAssertNotNil(peerID)
+        }
+
+        do {
+            assertNoTLKShareFor(peerID: bPeerID!, keyUUID: self.manateeKeySet.tlk.uuid, zoneID: CKRecordZone.ID(zoneName: "Manatee"))
+
+            print("B joins by preapproval, and uploads all TLKShares that it has")
+            let (bJoinedPeerID, _, views, policy, bJoinedError) = containerB.preapprovedJoinSync(test: self, ckksKeys: [self.manateeKeySet], tlkShares: [])
+            XCTAssertNil(bJoinedError, "Should be no error joining by preapproval")
+            XCTAssertNotNil(bJoinedPeerID, "Should have a peer ID out of join")
+            XCTAssertNotNil(views, "should have a list of views to use")
+            XCTAssertNotNil(policy, "Should have a policy back from preapprovedjoin")
+
+            assertTLKShareFor(peerID: bPeerID!, keyUUID: self.manateeKeySet.tlk.uuid, zoneID: CKRecordZone.ID(zoneName: "Manatee"))
+        }
+
+        _ = containerA.dumpSync(test: self)
+        _ = containerB.dumpSync(test: self)
+
+        XCTAssertEqual(containerA.containerMO.honorIDMSListChanges, "UNKNOWN", "honorIDMSListChanges should be unknown")
+        XCTAssertEqual(containerB.containerMO.honorIDMSListChanges, "UNKNOWN", "honorIDMSListChanges should be unknown")
+    }
+
+    func testReloadingContainerIDMSListVariable() throws {
+        let store = tmpStoreDescription(name: "container.db")
+        let contextID = "contextID"
+        var container = try Container(name: ContainerName(container: "test", context: contextID), persistentStoreDescription: store, cuttlefish: cuttlefish)
+
+        let (peerID, permanentInfo, permanentInfoSig, _, _, _, _, error) = container.prepareSync(test: self, epoch: 1, machineID: "aaa", bottleSalt: "123456789", bottleID: UUID().uuidString, modelID: "iPhone1,1")
+        do {
+            let state = container.getStateSync(test: self)
+            XCTAssertFalse( state.bottles.filter { $0.peerID == peerID } .isEmpty, "should have a bottle for peer")
+            let secret = container.loadSecretSync(test: self, label: peerID!)
+            XCTAssertNotNil(secret, "secret should not be nil")
+            XCTAssertNil(error, "error should be nil")
+        }
+        XCTAssertNotNil(peerID)
+        XCTAssertNotNil(permanentInfo)
+        XCTAssertNotNil(permanentInfoSig)
+        XCTAssertNil(error)
+
+        XCTAssertEqual(container.containerMO.honorIDMSListChanges, "UNKNOWN", "honorIDMSListChanges should be unknown")
+
+        _ = container.dumpSync(test: self)
+
+        do {
+            container = try Container(name: ContainerName(container: "test", context: contextID), persistentStoreDescription: store, cuttlefish: cuttlefish)
+            XCTAssertEqual(container.containerMO.honorIDMSListChanges, "UNKNOWN", "honorIDMSListChanges should be unknown")
+
+        } catch {
+            XCTFail("Creating container errored: \(error)")
+        }
+
+        let (peerID2, _, error2) = container.establishSync(test: self, ckksKeys: [self.manateeKeySet], tlkShares: [], preapprovedKeys: [])
+        XCTAssertNil(error2)
+        XCTAssertNotNil(peerID2)
+
+        _ = container.dumpSync(test: self)
+    }
 }
 }
index 934ff6a2210244e84bc44ec9dd7000f7da03b0b4..3b35e8380ee4e81d4d952c2c108c303579b0d70b 100644 (file)
@@ -146,7 +146,8 @@ bool SecCKKSDisable() {
 }
 
 bool SecCKKSResetSyncing(void) {
 }
 
 bool SecCKKSResetSyncing(void) {
-    [CKKSViewManager resetManager: true setTo: nil];
+    // The function name is a bit of a lie, but it does the thing.
+    [OTManager resetManager:true to:nil];
     return SecCKKSIsEnabled();
 }
 
     return SecCKKSIsEnabled();
 }
 
@@ -378,13 +379,15 @@ void CKKSRegisterSyncStatusCallback(CFStringRef cfuuid, SecBoolCFErrorCallback c
 
 void SecCKKSPerformLocalResync() {
 #if OCTAGON
 
 void SecCKKSPerformLocalResync() {
 #if OCTAGON
-    secnotice("ckks", "Local keychain was reset; performing local resync");
-    [[CKKSViewManager manager] rpcResyncLocal:nil reply:^(NSError *result) {
-        if(result) {
-            secnotice("ckks", "Local keychain reset resync finished with an error: %@", result);
-        } else {
-            secnotice("ckks", "Local keychain reset resync finished successfully");
-        }
-    }];
+    if(SecCKKSIsEnabled()) {
+        secnotice("ckks", "Local keychain was reset; performing local resync");
+        [[CKKSViewManager manager] rpcResyncLocal:nil reply:^(NSError *result) {
+            if(result) {
+                secnotice("ckks", "Local keychain reset resync finished with an error: %@", result);
+            } else {
+                secnotice("ckks", "Local keychain reset resync finished successfully");
+            }
+        }];
+    }
 #endif
 }
 #endif
 }
index 6c80b5c5c59c099b8b69e68270fd18c0a5c76f4c..b90f425261d3b8cc58c9985797ac1eb0787be3c4 100644 (file)
@@ -40,6 +40,7 @@ extern NSString* const CKKSAnalyticsLastInCircle;
 
 extern NSString* const OctagonAnalyticsStateMachineState;
 extern NSString* const OctagonAnalyticIcloudAccountState;
 
 extern NSString* const OctagonAnalyticsStateMachineState;
 extern NSString* const OctagonAnalyticIcloudAccountState;
+extern NSString* const OctagonAnalyticCDPBitStatus;
 extern NSString* const OctagonAnalyticsTrustState;
 extern NSString* const OctagonAnalyticsAttemptedJoin;
 extern NSString* const OctagonAnalyticsLastHealthCheck;
 extern NSString* const OctagonAnalyticsTrustState;
 extern NSString* const OctagonAnalyticsAttemptedJoin;
 extern NSString* const OctagonAnalyticsLastHealthCheck;
@@ -66,6 +67,12 @@ extern NSString* const OctagonAnalyticsKeychainSyncEnabled;
 extern NSString* const OctagonAnalyticsCloudKitProvisioned;
 extern NSString* const OctagonAnalyticsCloudKitEnabled;
 
 extern NSString* const OctagonAnalyticsCloudKitProvisioned;
 extern NSString* const OctagonAnalyticsCloudKitEnabled;
 
+extern NSString* const OctagonAnalyticsBottledUniqueTLKsRecovered;
+extern NSString* const OctagonAnalyticsBottledTotalTLKShares;
+extern NSString* const OctagonAnalyticsBottledTotalTLKSharesRecovered;
+extern NSString* const OctagonAnalyticsBottledUniqueTLKsWithSharesCount;
+extern NSString* const OctagonAnalyticsBottledTLKUniqueViewCount;
+
 @class CKKSKeychainView;
 
 @protocol CKKSAnalyticsFailableEvent <NSObject>
 @class CKKSKeychainView;
 
 @protocol CKKSAnalyticsFailableEvent <NSObject>
@@ -129,6 +136,7 @@ extern CKKSAnalyticsFailableEvent* const OctagonEventPreflightVouchWithBottle;
 extern CKKSAnalyticsFailableEvent* const OctagonEventVoucherWithBottle;
 
 /* inner: join with recovery key */
 extern CKKSAnalyticsFailableEvent* const OctagonEventVoucherWithBottle;
 
 /* inner: join with recovery key */
+extern CKKSAnalyticsFailableEvent* const OctagonEventPreflightVouchWithRecoveryKey;
 extern CKKSAnalyticsFailableEvent* const OctagonEventVoucherWithRecoveryKey;
 extern CKKSAnalyticsFailableEvent* const OctagonEventJoinRecoveryKeyValidationFailed;
 extern CKKSAnalyticsFailableEvent* const OctagonEventJoinRecoveryKeyFailed;
 extern CKKSAnalyticsFailableEvent* const OctagonEventVoucherWithRecoveryKey;
 extern CKKSAnalyticsFailableEvent* const OctagonEventJoinRecoveryKeyValidationFailed;
 extern CKKSAnalyticsFailableEvent* const OctagonEventJoinRecoveryKeyFailed;
index dea0502bbf4a6465fafa23426940dbadafd430dc..6547366d6c8f2b48d3a664fce7836ba7240ff09a 100644 (file)
@@ -49,6 +49,8 @@ NSString* const CKKSAnalyticsLastInCircle = @"lastInCircle";
 
 NSString* const OctagonAnalyticsStateMachineState = @"OASMState";
 NSString* const OctagonAnalyticIcloudAccountState = @"OAiC";
 
 NSString* const OctagonAnalyticsStateMachineState = @"OASMState";
 NSString* const OctagonAnalyticIcloudAccountState = @"OAiC";
+NSString* const OctagonAnalyticCDPBitStatus = @"OACDPStatus";
+
 NSString* const OctagonAnalyticsTrustState = @"OATrust";
 NSString* const OctagonAnalyticsAttemptedJoin = @"OAAttemptedJoin";
 NSString* const OctagonAnalyticsLastHealthCheck = @"OAHealthCheck";
 NSString* const OctagonAnalyticsTrustState = @"OATrust";
 NSString* const OctagonAnalyticsAttemptedJoin = @"OAAttemptedJoin";
 NSString* const OctagonAnalyticsLastHealthCheck = @"OAHealthCheck";
@@ -62,6 +64,12 @@ NSString* const OctagonAnalyticsCoreFollowupLastFailureTime = @"OACFULastFailure
 NSString* const OctagonAnalyticsPrerecordPending = @"OAPrerecordPending";
 NSString* const OctagonAnalyticsCDPStateRun = @"OACDPStateRun";
 
 NSString* const OctagonAnalyticsPrerecordPending = @"OAPrerecordPending";
 NSString* const OctagonAnalyticsCDPStateRun = @"OACDPStateRun";
 
+NSString* const OctagonAnalyticsBottledUniqueTLKsRecovered = @"OABottledUniqueTLKsRecoveredCount";
+NSString* const OctagonAnalyticsBottledTotalTLKShares = @"OABottledTotalTLKSharesCount";
+NSString* const OctagonAnalyticsBottledTotalTLKSharesRecovered = @"OABottledTotalTLKSharesRecoveredCount";
+NSString* const OctagonAnalyticsBottledUniqueTLKsWithSharesCount = @"OABottledUniqueTLKsWithSharesCount";
+NSString* const OctagonAnalyticsBottledTLKUniqueViewCount = @"OABottledTLKUniqueViewCount";
+
 NSString* const OctagonAnalyticsHaveMachineID = @"OAMIDPresent";
 NSString* const OctagonAnalyticsMIDOnMemoizedList = @"OAMIDOnList";
 NSString* const OctagonAnalyticsPeersWithMID = @"OAPeersWithMID";
 NSString* const OctagonAnalyticsHaveMachineID = @"OAMIDPresent";
 NSString* const OctagonAnalyticsMIDOnMemoizedList = @"OAMIDOnList";
 NSString* const OctagonAnalyticsPeersWithMID = @"OAPeersWithMID";
@@ -128,6 +136,7 @@ CKKSAnalyticsFailableEvent* const OctagonEventJoinWithVoucher = (CKKSAnalyticsFa
 CKKSAnalyticsFailableEvent* const OctagonEventPreflightVouchWithBottle = (CKKSAnalyticsFailableEvent*)@"OctagonEventPreflightVouchWithBottle";
 CKKSAnalyticsFailableEvent* const OctagonEventVoucherWithBottle = (CKKSAnalyticsFailableEvent*)@"OctagonEventVoucherWithBottle";
 
 CKKSAnalyticsFailableEvent* const OctagonEventPreflightVouchWithBottle = (CKKSAnalyticsFailableEvent*)@"OctagonEventPreflightVouchWithBottle";
 CKKSAnalyticsFailableEvent* const OctagonEventVoucherWithBottle = (CKKSAnalyticsFailableEvent*)@"OctagonEventVoucherWithBottle";
 
+CKKSAnalyticsFailableEvent* const OctagonEventPreflightVouchWithRecoveryKey = (CKKSAnalyticsFailableEvent*)@"OctagonEventPreflightVouchWithRecoveryKey";;
 CKKSAnalyticsFailableEvent* const OctagonEventVoucherWithRecoveryKey = (CKKSAnalyticsFailableEvent*)@"OctagonEventVoucherWithRecoveryKey";
 
 CKKSAnalyticsFailableEvent* const OctagonEventSetRecoveryKey = (CKKSAnalyticsFailableEvent*)@"OctagonEventSetRecoveryKey";
 CKKSAnalyticsFailableEvent* const OctagonEventVoucherWithRecoveryKey = (CKKSAnalyticsFailableEvent*)@"OctagonEventVoucherWithRecoveryKey";
 
 CKKSAnalyticsFailableEvent* const OctagonEventSetRecoveryKey = (CKKSAnalyticsFailableEvent*)@"OctagonEventSetRecoveryKey";
index 3bb5e78d5b0157c3b56e0512bef366afe59b62a8..0aa1311107a5c13ef1cc35ed6f6867002eead0b5 100644 (file)
@@ -88,7 +88,7 @@
                                      error: (NSError * __autoreleasing *) error {
     NSMutableDictionary* whereDict = [@{@"state": CKKSNilToNSNull(state), @"ckzone":CKKSNilToNSNull(zoneID.zoneName)} mutableCopy];
     if(uuid) {
                                      error: (NSError * __autoreleasing *) error {
     NSMutableDictionary* whereDict = [@{@"state": CKKSNilToNSNull(state), @"ckzone":CKKSNilToNSNull(zoneID.zoneName)} mutableCopy];
     if(uuid) {
-        whereDict[@"UUID"] = [CKKSSQLWhereObject op:@">" stringValue:uuid];
+        whereDict[@"UUID"] = [CKKSSQLWhereValue op:CKKSSQLWhereComparatorGreaterThan value:uuid];
     }
     return [self fetch:n
                  where:whereDict
     }
     return [self fetch:n
                  where:whereDict
index f53425a1fe81864886501e8664e6198db654c9f6..e5c96b49d46fb5b9fc86f3911ad0fd54cd0fcd0f 100644 (file)
     NSError* error = NULL;
     NSDictionary* queryAttributes = @{(__bridge NSString*) kSecClass: (__bridge NSString*) classP->name,
                                       (__bridge NSString*) kSecAttrUUID: iqe.uuid,
     NSError* error = NULL;
     NSDictionary* queryAttributes = @{(__bridge NSString*) kSecClass: (__bridge NSString*) classP->name,
                                       (__bridge NSString*) kSecAttrUUID: iqe.uuid,
-                                      (__bridge NSString*) kSecAttrSyncViewHint: ckks.zoneID.zoneName,
                                       (__bridge NSString*) kSecAttrSynchronizable: @(YES)};
     ckksnotice("ckksincoming", ckks, "trying to delete with query: %@", queryAttributes);
     Query *q = query_create_with_limit( (__bridge CFDictionaryRef) queryAttributes, NULL, kSecMatchUnlimited, &cferror);
 
                                       (__bridge NSString*) kSecAttrSynchronizable: @(YES)};
     ckksnotice("ckksincoming", ckks, "trying to delete with query: %@", queryAttributes);
     Query *q = query_create_with_limit( (__bridge CFDictionaryRef) queryAttributes, NULL, kSecMatchUnlimited, &cferror);
 
-
     if(cferror) {
         ckkserror("ckksincoming", ckks, "couldn't create query: %@", cferror);
         SecTranslateError(&error, cferror);
     if(cferror) {
         ckkserror("ckksincoming", ckks, "couldn't create query: %@", cferror);
         SecTranslateError(&error, cferror);
index 10e88f36e4a4ffd03ec79e6400d5cb65f02641ce..4f497ee0a2cd7b977db79458946f501e537bc3ee 100644 (file)
 }
 
 + (NSArray<CKKSKey*>*)selfWrappedKeys:(CKRecordZoneID*)zoneID error: (NSError * __autoreleasing *) error {
 }
 
 + (NSArray<CKKSKey*>*)selfWrappedKeys:(CKRecordZoneID*)zoneID error: (NSError * __autoreleasing *) error {
-    return [self allWhere: @{@"UUID": [CKKSSQLWhereObject op:@"=" string:@"parentKeyUUID"], @"state":  SecCKKSProcessedStateLocal, @"ckzone":zoneID.zoneName} error:error];
+    return [self allWhere: @{@"UUID": [CKKSSQLWhereColumn op:CKKSSQLWhereComparatorEquals
+                                                      column:CKKSSQLWhereColumnNameParentKeyUUID],
+                             @"state":  SecCKKSProcessedStateLocal,
+                             @"ckzone":zoneID.zoneName}
+                    error:error];
 }
 
 + (instancetype _Nullable)currentKeyForClass:(CKKSKeyClass*)keyclass
 }
 
 + (instancetype _Nullable)currentKeyForClass:(CKKSKeyClass*)keyclass
index ac6e0339223d10ae8d7d4d2514102e91dbbad9e4..5e090e1bb228091d967d13dd2dacfcab75f5a676 100644 (file)
@@ -175,10 +175,10 @@ NS_ASSUME_NONNULL_BEGIN
 /* Synchronous operations */
 
 - (void)handleKeychainEventDbConnection:(SecDbConnectionRef)dbconn
 /* Synchronous operations */
 
 - (void)handleKeychainEventDbConnection:(SecDbConnectionRef)dbconn
+                                 source:(SecDbTransactionSource)txionSource
                                   added:(SecDbItemRef _Nullable)added
                                 deleted:(SecDbItemRef _Nullable)deleted
                                   added:(SecDbItemRef _Nullable)added
                                 deleted:(SecDbItemRef _Nullable)deleted
-                            rateLimiter:(CKKSRateLimiter*)rateLimiter
-                           syncCallback:(SecBoolNSErrorCallback)syncCallback;
+                            rateLimiter:(CKKSRateLimiter*)rateLimiter;
 
 - (void)setCurrentItemForAccessGroup:(NSData*)newItemPersistentRef
                                 hash:(NSData*)newItemSHA1
 
 - (void)setCurrentItemForAccessGroup:(NSData*)newItemPersistentRef
                                 hash:(NSData*)newItemSHA1
@@ -208,8 +208,6 @@ NS_ASSUME_NONNULL_BEGIN
 
 - (BOOL)otherDevicesReportHavingTLKs:(CKKSCurrentKeySet*)keyset;
 
 
 - (BOOL)otherDevicesReportHavingTLKs:(CKKSCurrentKeySet*)keyset;
 
-- (NSSet<NSString*>*)_onqueuePriorityOutgoingQueueUUIDs;
-
 /* Asynchronous kickoffs */
 
 - (CKKSOutgoingQueueOperation*)processOutgoingQueue:(CKOperationGroup* _Nullable)ckoperationGroup;
 /* Asynchronous kickoffs */
 
 - (CKKSOutgoingQueueOperation*)processOutgoingQueue:(CKOperationGroup* _Nullable)ckoperationGroup;
@@ -304,6 +302,8 @@ NS_ASSUME_NONNULL_BEGIN
 // Please don't use these unless you're an Operation in this package
 @property NSHashTable<CKKSIncomingQueueOperation*>* incomingQueueOperations;
 @property NSHashTable<CKKSOutgoingQueueOperation*>* outgoingQueueOperations;
 // Please don't use these unless you're an Operation in this package
 @property NSHashTable<CKKSIncomingQueueOperation*>* incomingQueueOperations;
 @property NSHashTable<CKKSOutgoingQueueOperation*>* outgoingQueueOperations;
+
+@property NSHashTable<CKKSScanLocalItemsOperation*>* scanLocalItemsOperations;
 @property CKKSScanLocalItemsOperation* initialScanOperation;
 
 // Returns the current state of this view, fastStatus is the same, but as name promise, no expensive calculations
 @property CKKSScanLocalItemsOperation* initialScanOperation;
 
 // Returns the current state of this view, fastStatus is the same, but as name promise, no expensive calculations
index 3e7a16fed619cda9246d9291d43e7638d55b3407..6a9589a1e874d811af28bbfb5cab45d6d6129fd6 100644 (file)
 @property CKKSResultOperation* processIncomingQueueAfterNextUnlockOperation;
 @property CKKSResultOperation* resultsOfNextIncomingQueueOperationOperation;
 
 @property CKKSResultOperation* processIncomingQueueAfterNextUnlockOperation;
 @property CKKSResultOperation* resultsOfNextIncomingQueueOperationOperation;
 
-@property NSMutableDictionary<NSString*, SecBoolNSErrorCallback>* pendingSyncCallbacks;
-
 // An extra queue for semaphore-waiting-based NSOperations
 @property NSOperationQueue* waitingQueue;
 
 // An extra queue for semaphore-waiting-based NSOperations
 @property NSOperationQueue* waitingQueue;
 
 
         _incomingQueueOperations = [NSHashTable weakObjectsHashTable];
         _outgoingQueueOperations = [NSHashTable weakObjectsHashTable];
 
         _incomingQueueOperations = [NSHashTable weakObjectsHashTable];
         _outgoingQueueOperations = [NSHashTable weakObjectsHashTable];
+        _scanLocalItemsOperations = [NSHashTable weakObjectsHashTable];
         _cloudkitDeleteZoneOperations = [NSHashTable weakObjectsHashTable];
         _localResetOperations = [NSHashTable weakObjectsHashTable];
         _keysetProviderOperations = [NSHashTable weakObjectsHashTable];
         _cloudkitDeleteZoneOperations = [NSHashTable weakObjectsHashTable];
         _localResetOperations = [NSHashTable weakObjectsHashTable];
         _keysetProviderOperations = [NSHashTable weakObjectsHashTable];
                                                                               }];
 
 
                                                                               }];
 
 
-        _pendingSyncCallbacks = [[NSMutableDictionary alloc] init];
-
         _lockStateTracker = lockStateTracker;
         _savedTLKNotifier = savedTLKNotifier;
 
         _lockStateTracker = lockStateTracker;
         _savedTLKNotifier = savedTLKNotifier;
 
 
 #if DEBUG
     // During testing, keep the developer honest: this function should always have the self identities, unless the account has lost trust
 
 #if DEBUG
     // During testing, keep the developer honest: this function should always have the self identities, unless the account has lost trust
-    if(self.trustStatus == CKKSAccountStatusAvailable && ![state isEqualToString:SecCKKSZoneKeyStateLoggedOut]) {
+    // But, beginTrustedOperation is currently racy: if it acquires the queue and sets the trust bit between fetching the trust states and getting on the queue,
+    // this will fire. So, release SecCKKSZoneKeyStateInitialized from this check.
+    if(self.trustStatus == CKKSAccountStatusAvailable
+       && ![state isEqualToString:SecCKKSZoneKeyStateLoggedOut]
+       && ![state isEqualToString:SecCKKSZoneKeyStateInitialized]) {
         bool hasSelfIdentities = false;
         NSAssert(self.currentTrustStates.count > 0, @"Should have at least one trust state");
         for(CKKSPeerProviderState* state in self.currentTrustStates) {
         bool hasSelfIdentities = false;
         NSAssert(self.currentTrustStates.count > 0, @"Should have at least one trust state");
         for(CKKSPeerProviderState* state in self.currentTrustStates) {
 }
 
 - (void) handleKeychainEventDbConnection: (SecDbConnectionRef) dbconn
 }
 
 - (void) handleKeychainEventDbConnection: (SecDbConnectionRef) dbconn
+                                  source:(SecDbTransactionSource)txionSource
                                    added: (SecDbItemRef) added
                                  deleted: (SecDbItemRef) deleted
                              rateLimiter: (CKKSRateLimiter*) rateLimiter
                                    added: (SecDbItemRef) added
                                  deleted: (SecDbItemRef) deleted
                              rateLimiter: (CKKSRateLimiter*) rateLimiter
-                            syncCallback: (SecBoolNSErrorCallback) syncCallback {
+{
     if(!SecCKKSIsEnabled()) {
         ckksnotice("ckks", self, "Skipping handleKeychainEventDbConnection due to disabled CKKS");
         return;
     if(!SecCKKSIsEnabled()) {
         ckksnotice("ckks", self, "Skipping handleKeychainEventDbConnection due to disabled CKKS");
         return;
         return;
     }
 
         return;
     }
 
+    if(txionSource == kSecDbSOSTransaction) {
+        ckksnotice("ckks", self, "Received an incoming %@ from SOS", isAdd ? @"addition" : (isModify ? @"modification" : @"deletion"));
+    }
+
     // Our caller gave us a database connection. We must get on the local queue to ensure atomicity
     // Note that we're at the mercy of the surrounding db transaction, so don't try to rollback here
     [self dispatchSyncWithConnection: dbconn block: ^bool {
     // Our caller gave us a database connection. We must get on the local queue to ensure atomicity
     // Note that we're at the mercy of the surrounding db transaction, so don't try to rollback here
     [self dispatchSyncWithConnection: dbconn block: ^bool {
             self.droppedItems = true;
             ckksnotice("ckks", self, "Dropping sync item modification due to CK account state; will scan to find changes later");
 
             self.droppedItems = true;
             ckksnotice("ckks", self, "Dropping sync item modification due to CK account state; will scan to find changes later");
 
+            // We're positively not logged into CloudKit, and therefore don't expect this item to be synced anytime particularly soon.
+            NSString* uuid = (__bridge NSString*)SecDbItemGetValue(added ? added : deleted, &v10itemuuid, NULL);
+
+            SecBoolNSErrorCallback syncCallback = [[CKKSViewManager manager] claimCallbackForUUID:uuid];
             if(syncCallback) {
             if(syncCallback) {
-                // We're positively not logged into CloudKit, and therefore don't expect this item to be synced anytime particularly soon.
-                [self callSyncCallbackWithErrorNoAccount: syncCallback];
+                [CKKSViewManager callSyncCallbackWithErrorNoAccount: syncCallback];
             }
             }
-            return true;
-        }
 
 
-        // Always record the callback, even if we can't encrypt the item right now. Maybe we'll get to it soon!
-        if(syncCallback) {
-            CFErrorRef cferror = NULL;
-            NSString* uuid = (__bridge_transfer NSString*) CFRetain(SecDbItemGetValue(added, &v10itemuuid, &cferror));
-            if(!cferror && uuid) {
-                self.pendingSyncCallbacks[uuid] = syncCallback;
-            }
-            CFReleaseNull(cferror);
+            return true;
         }
 
         CKKSOutgoingQueueEntry* oqe = nil;
         }
 
         CKKSOutgoingQueueEntry* oqe = nil;
     }
 }
 
     }
 }
 
-- (NSSet<NSString*>*)_onqueuePriorityOutgoingQueueUUIDs
-{
-    return [self.pendingSyncCallbacks.allKeys copy];
-}
-
 - (CKKSOutgoingQueueOperation*)processOutgoingQueue:(CKOperationGroup*)ckoperationGroup {
     return [self processOutgoingQueueAfter:nil ckoperationGroup:ckoperationGroup];
 }
 - (CKKSOutgoingQueueOperation*)processOutgoingQueue:(CKOperationGroup*)ckoperationGroup {
     return [self processOutgoingQueueAfter:nil ckoperationGroup:ckoperationGroup];
 }
 
     [self.outgoingQueueOperationScheduler triggerAt:requiredDelay];
 
 
     [self.outgoingQueueOperationScheduler triggerAt:requiredDelay];
 
+    [op linearDependencies:self.outgoingQueueOperations];
+
     [self scheduleOperation: op];
     ckksnotice("ckksoutgoing", self, "Scheduled %@", op);
     return op;
     [self scheduleOperation: op];
     ckksnotice("ckksoutgoing", self, "Scheduled %@", op);
     return op;
     return [self scanLocalItems:operationName ckoperationGroup:nil after:nil];
 }
 
     return [self scanLocalItems:operationName ckoperationGroup:nil after:nil];
 }
 
-- (CKKSScanLocalItemsOperation*)scanLocalItems:(NSString*)operationName ckoperationGroup:(CKOperationGroup*)operationGroup after:(NSOperation*)after {
-    CKKSScanLocalItemsOperation* scanOperation = [[CKKSScanLocalItemsOperation alloc] initWithCKKSKeychainView:self ckoperationGroup:operationGroup];
+- (CKKSScanLocalItemsOperation*)scanLocalItems:(NSString*)operationName
+                              ckoperationGroup:(CKOperationGroup*)operationGroup
+                                         after:(NSOperation*)after
+{
+    CKKSScanLocalItemsOperation* scanOperation = (CKKSScanLocalItemsOperation*)[self findFirstPendingOperation:self.scanLocalItemsOperations];
+
+    if(scanOperation) {
+        [scanOperation addNullableDependency:after];
+
+        // check (again) for race condition; if the op has started we need to add another (for the dependency)
+        if([scanOperation isPending]) {
+            scanOperation.ckoperationGroup = operationGroup;
+
+            scanOperation.name = [NSString stringWithFormat:@"%@::%@", scanOperation.name, operationName];
+            return scanOperation;
+        }
+    }
+
+    scanOperation = [[CKKSScanLocalItemsOperation alloc] initWithCKKSKeychainView:self ckoperationGroup:operationGroup];
     scanOperation.name = operationName;
 
     [scanOperation addNullableDependency:self.lastFixupOperation];
     scanOperation.name = operationName;
 
     [scanOperation addNullableDependency:self.lastFixupOperation];
     [scanOperation addNullableDependency:self.keyStateReadyDependency];
     [scanOperation addNullableDependency:after];
 
     [scanOperation addNullableDependency:self.keyStateReadyDependency];
     [scanOperation addNullableDependency:after];
 
-    [self scheduleOperation: scanOperation];
+    [scanOperation linearDependencies:self.scanLocalItemsOperations];
+
+    [self scheduleOperation:scanOperation];
     return scanOperation;
 }
 
     return scanOperation;
 }
 
 
     if([state isEqualToString: SecCKKSStateDeleted]) {
         // Hurray, this must be a success
 
     if([state isEqualToString: SecCKKSStateDeleted]) {
         // Hurray, this must be a success
-        SecBoolNSErrorCallback callback = self.pendingSyncCallbacks[oqe.uuid];
-        if(callback) {
-            callback(true, nil);
-            self.pendingSyncCallbacks[oqe.uuid] = nil;
+        SecBoolNSErrorCallback syncCallback = [[CKKSViewManager manager] claimCallbackForUUID:oqe.uuid];
+        if(syncCallback) {
+            syncCallback(true, nil);
         }
 
         [oqe deleteFromDatabase: &localerror];
         }
 
         [oqe deleteFromDatabase: &localerror];
 - (bool)_onqueueErrorOutgoingQueueEntry: (CKKSOutgoingQueueEntry*) oqe itemError: (NSError*) itemError error: (NSError* __autoreleasing*) error {
     dispatch_assert_queue(self.queue);
 
 - (bool)_onqueueErrorOutgoingQueueEntry: (CKKSOutgoingQueueEntry*) oqe itemError: (NSError*) itemError error: (NSError* __autoreleasing*) error {
     dispatch_assert_queue(self.queue);
 
-    SecBoolNSErrorCallback callback = self.pendingSyncCallbacks[oqe.uuid];
+    SecBoolNSErrorCallback callback = [[CKKSViewManager manager] claimCallbackForUUID:oqe.uuid];
     if(callback) {
         callback(false, itemError);
     if(callback) {
         callback(false, itemError);
-        self.pendingSyncCallbacks[oqe.uuid] = nil;
     }
     NSError* localerror = nil;
 
     }
     NSError* localerror = nil;
 
             [self.loggedOut fulfill];
             [self.accountStateKnown fulfill];
 
             [self.loggedOut fulfill];
             [self.accountStateKnown fulfill];
 
-            // Tell all pending sync clients that we don't expect to ever sync
-            for(NSString* callbackUUID in self.pendingSyncCallbacks.allKeys) {
-                [self callSyncCallbackWithErrorNoAccount:self.pendingSyncCallbacks[callbackUUID]];
-                self.pendingSyncCallbacks[callbackUUID] = nil;
-            }
-
             return true;
         }];
     }];
             return true;
         }];
     }];
     [self scheduleAccountStatusOperation: logout];
 }
 
     [self scheduleAccountStatusOperation: logout];
 }
 
-- (void)callSyncCallbackWithErrorNoAccount:(SecBoolNSErrorCallback)syncCallback {
-    CKKSAccountStatus accountStatus = self.accountStatus;
-    dispatch_async(self.queue, ^{
-        syncCallback(false, [NSError errorWithDomain:@"securityd"
-                                                code:errSecNotLoggedIn
-                                            userInfo:@{NSLocalizedDescriptionKey:
-                                                           [NSString stringWithFormat: @"No iCloud account available(%d); item is not expected to sync", (int)accountStatus]}]);
-    });
-}
-
 #pragma mark - Trust operations
 
 - (void)beginTrustedOperation:(NSArray<id<CKKSPeerProvider>>*)peerProviders
 #pragma mark - Trust operations
 
 - (void)beginTrustedOperation:(NSArray<id<CKKSPeerProvider>>*)peerProviders
 {
     [self.launch addEvent:@"changes-fetched"];
 
 {
     [self.launch addEvent:@"changes-fetched"];
 
+    if(changedRecords.count == 0 && deletedRecords.count == 0 && !moreComing && !resync) {
+        // Early-exit, so we don't pick up the account keys or kick off an IncomingQueue operation for no changes
+        [self dispatchSync:^bool {
+            ckkserror("ckksfetch", self, "No record changes in this fetch");
+
+            NSError* error = nil;
+            CKKSZoneStateEntry* state = [CKKSZoneStateEntry state:self.zoneName];
+            state.lastFetchTime = [NSDate date]; // The last fetch happened right now!
+            state.changeToken = newChangeToken;
+            state.moreRecordsInCloudKit = moreComing;
+            [state saveToDatabase:&error];
+            if(error) {
+                ckkserror("ckksfetch", self, "Couldn't save new server change token: %@", error);
+            }
+            return true;
+        }];
+        return;
+    }
+
     [self dispatchSyncWithAccountKeys:^bool{
         for (CKRecord* record in changedRecords) {
             [self _onqueueCKRecordChanged:record resync:resync];
     [self dispatchSyncWithAccountKeys:^bool{
         for (CKRecord* record in changedRecords) {
             [self _onqueueCKRecordChanged:record resync:resync];
         [self.incomingQueueOperations removeAllObjects];
     }
 
         [self.incomingQueueOperations removeAllObjects];
     }
 
+    @synchronized(self.scanLocalItemsOperations) {
+        for(NSOperation* op in self.scanLocalItemsOperations) {
+            [op cancel];
+        }
+        [self.scanLocalItemsOperations removeAllObjects];
+    }
+
     [super cancelAllOperations];
 }
 
     [super cancelAllOperations];
 }
 
     [self.keyStateNonTransientDependency cancel];
     [self.zoneChangeFetcher cancel];
     [self.notifyViewChangedScheduler cancel];
     [self.keyStateNonTransientDependency cancel];
     [self.zoneChangeFetcher cancel];
     [self.notifyViewChangedScheduler cancel];
+    [self.pokeKeyStateMachineScheduler cancel];
 
     [self cancelPendingOperations];
 
 
     [self cancelPendingOperations];
 
index 2cc4684316433221d4d2cce9dae36dd48924e1e5..9cf42a1bd4a59df84ab117453bd54eab9df7c2d5 100644 (file)
@@ -83,6 +83,7 @@
     }
 
     // Synchronous, on some thread. Get back on the CKKS queue for SQL thread-safety.
     }
 
     // Synchronous, on some thread. Get back on the CKKS queue for SQL thread-safety.
+    __block bool enterWaitForTLKUpload = false;
     [ckks dispatchSyncWithAccountKeys: ^bool{
         if(self.cancelled) {
             ckksnotice("ckkstlk", ckks, "CKKSNewTLKOperation cancelled, quitting");
     [ckks dispatchSyncWithAccountKeys: ^bool{
         if(self.cancelled) {
             ckksnotice("ckkstlk", ckks, "CKKSNewTLKOperation cancelled, quitting");
 
         self.keyset = keyset;
 
 
         self.keyset = keyset;
 
-        [ckks _onqueueAdvanceKeyStateMachineToState:SecCKKSZoneKeyStateWaitForTLKUpload withError:nil];
-
+        // Finish this transaction to cause a keychiain db commit
+        // This means that if we provide the new keys to another thread, they'll be able to immediately load them from the keychain
+        enterWaitForTLKUpload = true;
         return true;
     }];
         return true;
     }];
+
+    if(enterWaitForTLKUpload) {
+        // And move the CKKS state machine:
+        [ckks dispatchSyncWithAccountKeys: ^bool{
+            [ckks _onqueueAdvanceKeyStateMachineToState:SecCKKSZoneKeyStateWaitForTLKUpload withError:nil];
+            return true;
+        }];
+    }
 }
 
 - (void)cancel {
 }
 
 - (void)cancel {
index 940cf26a6baa494d9da4f383fb6405b3db3eea22..087ad6a9ff9f4431a59b3e041b826e304c689860 100644 (file)
@@ -26,6 +26,7 @@
 #import <CloudKit/CloudKit.h>
 #import <CloudKit/CloudKit_Private.h>
 
 #import <CloudKit/CloudKit.h>
 #import <CloudKit/CloudKit_Private.h>
 
+#import "keychain/ckks/CKKSViewManager.h"
 #import "CKKSKeychainView.h"
 #import "CKKSCurrentKeyPointer.h"
 #import "CKKSOutgoingQueueOperation.h"
 #import "CKKSKeychainView.h"
 #import "CKKSCurrentKeyPointer.h"
 #import "CKKSOutgoingQueueOperation.h"
@@ -90,7 +91,7 @@
 
         NSError* error = nil;
 
 
         NSError* error = nil;
 
-        NSSet<NSString*>* priorityUUIDs = [ckks _onqueuePriorityOutgoingQueueUUIDs];
+        NSSet<NSString*>* priorityUUIDs = [[CKKSViewManager manager] pendingCallbackUUIDs];
 
         NSMutableArray<CKKSOutgoingQueueEntry*>* priorityEntries = [NSMutableArray array];
         NSMutableSet<NSString*>* priorityEntryUUIDs = [NSMutableSet set];
 
         NSMutableArray<CKKSOutgoingQueueEntry*>* priorityEntries = [NSMutableArray array];
         NSMutableSet<NSString*>* priorityEntryUUIDs = [NSMutableSet set];
index 85f6ad5cf3ba86ece24bff9d232873313a3800ec..a4698b38a08106cc922db6b3792464a8841a8a67 100644 (file)
@@ -135,13 +135,41 @@ NS_ASSUME_NONNULL_BEGIN
 @end
 
 // Helper class to use with where clauses
 @end
 
 // Helper class to use with where clauses
-// If you pass in one of these instead of a concrete value, its substring will be used directly, instead of binding the value as a named parameter
-@interface CKKSSQLWhereObject : NSObject
-@property NSString* sqlOp;
-@property NSString* contents;
-- (instancetype)initWithOperation:(NSString*)op string:(NSString*)str;
-+ (instancetype)op:(NSString*)op string:(NSString*)str;
-+ (instancetype)op:(NSString*)op stringValue:(NSString*)str;  // Will add single quotes around your value.
+// If you pass in one of these in a where dictionary instead of a concrete value, columnName will be
+// used directly, instead of binding as a named parameter. Therefore, it's essential to use
+// compile-time constants for both fields.
+
+typedef NS_ENUM(uint64_t, CKKSSQLWhereComparator) {
+    CKKSSQLWhereComparatorEquals = 1,
+    CKKSSQLWhereComparatorNotEquals = 2,
+    CKKSSQLWhereComparatorGreaterThan = 3,
+    CKKSSQLWhereComparatorLessThan = 4,
+};
+
+NSString* CKKSSQLWhereComparatorAsString(CKKSSQLWhereComparator comparator);
+
+// This typedef is to ensure that CKKSSQLWhereColumn can only ever produce static strings
+typedef NS_ENUM(uint64_t, CKKSSQLWhereColumnName) {
+    CKKSSQLWhereColumnNameUUID = 1,
+    CKKSSQLWhereColumnNameParentKeyUUID = 2,
+};
+NSString* CKKSSQLWhereColumnNameAsString(CKKSSQLWhereColumnName columnName);
+
+@interface CKKSSQLWhereColumn : NSObject
+@property CKKSSQLWhereComparator sqlOp;
+@property CKKSSQLWhereColumnName columnName;
+- (instancetype)initWithOperation:(CKKSSQLWhereComparator)op columnName:(CKKSSQLWhereColumnName)column;
++ (instancetype)op:(CKKSSQLWhereComparator)op column:(CKKSSQLWhereColumnName)columnName;
 @end
 
 @end
 
+// Unlike CKKSSQLWhereColumn, this will insert the value as a parameter in a prepared statement
+// but gives you the flexbility to inject a sqlOp. sqlOp must be a compile-time constant.
+@interface CKKSSQLWhereValue : NSObject
+@property CKKSSQLWhereComparator sqlOp;
+@property NSString* value;
+- (instancetype)initWithOperation:(CKKSSQLWhereComparator)op value:(NSString*)value;
++ (instancetype)op:(CKKSSQLWhereComparator)op value:(NSString*)value;
+@end
+
+
 NS_ASSUME_NONNULL_END
 NS_ASSUME_NONNULL_END
index 356424cc1b21ea45e9188efc3b23c464643b1cbf..f8c70d96a7457b3b86c0c942d33767688e30a955 100644 (file)
             [whereClause appendFormat: @" AND "];
         }
 
             [whereClause appendFormat: @" AND "];
         }
 
-        if([value class] == [CKKSSQLWhereObject class]) {
-            // Use this string verbatim
-            CKKSSQLWhereObject* whereob = (CKKSSQLWhereObject*) value;
-            [whereClause appendFormat: @"%@%@%@", key, whereob.sqlOp, whereob.contents];
+        if([value class] == [CKKSSQLWhereValue class]) {
+            CKKSSQLWhereValue* obj = (CKKSSQLWhereValue*)value;
+            [whereClause appendFormat: @"%@%@(?)", key, CKKSSQLWhereComparatorAsString(obj.sqlOp)];
+
+        } else if([value class] == [CKKSSQLWhereColumn class]) {
+            CKKSSQLWhereColumn* obj = (CKKSSQLWhereColumn*)value;
+            [whereClause appendFormat: @"%@%@%@",
+             key,
+             CKKSSQLWhereComparatorAsString(obj.sqlOp),
+             CKKSSQLWhereColumnNameAsString(obj.columnName)];
+
         } else {
             [whereClause appendFormat: @"%@=(?)", key];
         }
         } else {
             [whereClause appendFormat: @"%@=(?)", key];
         }
     return orderByClause;
 }
 
     return orderByClause;
 }
 
++ (void)bindWhereClause:(sqlite3_stmt*)stmt whereDict:(NSDictionary*)whereDict cferror:(CFErrorRef*)cferror
+{
+    __block int whereObjectsSkipped = 0;
+    [whereDict.allKeys enumerateObjectsUsingBlock:^(id  _Nonnull key, NSUInteger i, BOOL * _Nonnull stop) {
+        if([whereDict[key] class] == [CKKSSQLWhereValue class]) {
+            CKKSSQLWhereValue* obj = (CKKSSQLWhereValue*)whereDict[key];
+            SecDbBindObject(stmt, (int)(i+1-whereObjectsSkipped), (__bridge CFStringRef)obj.value, cferror);
+        } else if([whereDict[key] class] == [CKKSSQLWhereColumn class]) {
+            // skip
+            whereObjectsSkipped += 1;
+        } else {
+            SecDbBindObject(stmt, (int)(i+1-whereObjectsSkipped), (__bridge CFStringRef) whereDict[key], cferror);
+        }
+    }];
+}
+
 + (bool) deleteFromTable: (NSString*) table where: (NSDictionary*) whereDict connection:(SecDbConnectionRef) dbconn error: (NSError * __autoreleasing *) error {
     __block CFErrorRef cferror = NULL;
 
 + (bool) deleteFromTable: (NSString*) table where: (NSDictionary*) whereDict connection:(SecDbConnectionRef) dbconn error: (NSError * __autoreleasing *) error {
     __block CFErrorRef cferror = NULL;
 
 
         NSString * sql = [[NSString alloc] initWithFormat: @"DELETE FROM %@%@;", table, whereClause];
         SecDbPrepare(dbconn, (__bridge CFStringRef) sql, &cferror, ^void (sqlite3_stmt *stmt) {
 
         NSString * sql = [[NSString alloc] initWithFormat: @"DELETE FROM %@%@;", table, whereClause];
         SecDbPrepare(dbconn, (__bridge CFStringRef) sql, &cferror, ^void (sqlite3_stmt *stmt) {
-            __block int whereObjectsSkipped = 0;
-
-            [whereDict.allKeys enumerateObjectsUsingBlock:^(id  _Nonnull key, NSUInteger i, BOOL * _Nonnull stop) {
-                if([whereDict[key] class] != [CKKSSQLWhereObject class]) {
-                    SecDbBindObject(stmt, (int)(i+1-whereObjectsSkipped), (__bridge CFStringRef) whereDict[key], &cferror);
-                } else {
-                    whereObjectsSkipped += 1;
-                }
-            }];
+            [self bindWhereClause:stmt whereDict:whereDict cferror:&cferror];
 
             SecDbStep(dbconn, stmt, &cferror, ^(bool *stop) {
             });
 
             SecDbStep(dbconn, stmt, &cferror, ^(bool *stop) {
             });
 
         NSString * sql = [[NSString alloc] initWithFormat: @"SELECT %@ FROM %@%@%@%@%@;", columns, table, whereClause, groupByClause, orderByClause, limitClause];
         SecDbPrepare(dbconn, (__bridge CFStringRef) sql, &cferror, ^void (sqlite3_stmt *stmt) {
 
         NSString * sql = [[NSString alloc] initWithFormat: @"SELECT %@ FROM %@%@%@%@%@;", columns, table, whereClause, groupByClause, orderByClause, limitClause];
         SecDbPrepare(dbconn, (__bridge CFStringRef) sql, &cferror, ^void (sqlite3_stmt *stmt) {
-            __block int whereObjectsSkipped = 0;
-            [whereDict.allKeys enumerateObjectsUsingBlock:^(id  _Nonnull key, NSUInteger i, BOOL * _Nonnull stop) {
-                if([whereDict[key] class] != [CKKSSQLWhereObject class]) {
-                    SecDbBindObject(stmt, (int)(i+1-whereObjectsSkipped), (__bridge CFStringRef) whereDict[key], &cferror);
-                } else {
-                    whereObjectsSkipped += 1;
-                }
-            }];
+            [self bindWhereClause:stmt whereDict:whereDict cferror:&cferror];
 
             SecDbStep(dbconn, stmt, &cferror, ^(bool *stop) {
                 __block NSMutableDictionary<NSString*, CKKSSQLResult*>* row = [[NSMutableDictionary alloc] init];
 
             SecDbStep(dbconn, stmt, &cferror, ^(bool *stop) {
                 __block NSMutableDictionary<NSString*, CKKSSQLResult*>* row = [[NSMutableDictionary alloc] init];
         
         NSString* sql = [[NSString alloc] initWithFormat:@"SELECT %@ FROM %@%@", columns, quotedTable, whereClause];
         SecDbPrepare(dbconn, (__bridge CFStringRef)sql, &cferror, ^(sqlite3_stmt* stmt) {
         
         NSString* sql = [[NSString alloc] initWithFormat:@"SELECT %@ FROM %@%@", columns, quotedTable, whereClause];
         SecDbPrepare(dbconn, (__bridge CFStringRef)sql, &cferror, ^(sqlite3_stmt* stmt) {
-            [whereDict.allKeys enumerateObjectsUsingBlock:^(id  _Nonnull key, NSUInteger i, BOOL* _Nonnull stop) {
-                if ([whereDict[key] class] != [CKKSSQLWhereObject class]) {
-                    SecDbBindObject(stmt, (int)(i+1), (__bridge CFStringRef) whereDict[key], &cferror);
-                }
-            }];
+            [self bindWhereClause:stmt whereDict:whereDict cferror:&cferror];
             
             SecDbStep(dbconn, stmt, &cferror, ^(bool*stop) {
                 __block NSMutableDictionary<NSString*, CKKSSQLResult*>* row = [[NSMutableDictionary alloc] init];
             
             SecDbStep(dbconn, stmt, &cferror, ^(bool*stop) {
                 __block NSMutableDictionary<NSString*, CKKSSQLResult*>* row = [[NSMutableDictionary alloc] init];
 }
 @end
 
 }
 @end
 
-#pragma mark - CKKSSQLWhereObject
+NSString* CKKSSQLWhereComparatorAsString(CKKSSQLWhereComparator comparator)
+{
+    switch(comparator) {
+        case CKKSSQLWhereComparatorEquals:
+            return @"=";
+        case CKKSSQLWhereComparatorNotEquals:
+            return @"<>";
+        case CKKSSQLWhereComparatorGreaterThan:
+            return @">";
+        case CKKSSQLWhereComparatorLessThan:
+            return @"<";
+    }
+}
 
 
-@implementation CKKSSQLWhereObject
-- (instancetype)initWithOperation:(NSString*)op string: (NSString*) str {
-    if(self = [super init]) {
+NSString* CKKSSQLWhereColumnNameAsString(CKKSSQLWhereColumnName columnName)
+{
+    switch(columnName) {
+        case CKKSSQLWhereColumnNameUUID:
+            return @"uuid";
+        case CKKSSQLWhereColumnNameParentKeyUUID:
+            return @"parentKeyUUID";
+    }
+}
+
+#pragma mark - CKKSSQLWhereColumn
+
+@implementation CKKSSQLWhereColumn
+- (instancetype)initWithOperation:(CKKSSQLWhereComparator)op columnName:(CKKSSQLWhereColumnName)column
+{
+    if((self = [super init])) {
         _sqlOp = op;
         _sqlOp = op;
-        _contents = str;
+        _columnName = column;
     }
     return self;
 }
     }
     return self;
 }
-
-+ (instancetype)op:(NSString*) op string: (NSString*) str {
-    return [[CKKSSQLWhereObject alloc] initWithOperation:op string: str];
++ (instancetype)op:(CKKSSQLWhereComparator)op column:(CKKSSQLWhereColumnName)columnName
+{
+    return [[CKKSSQLWhereColumn alloc] initWithOperation:op columnName:columnName];
 }
 }
+@end
 
 
-+ (instancetype)op:(NSString*) op stringValue: (NSString*) str {
-    return [[CKKSSQLWhereObject alloc] initWithOperation:op string:[NSString stringWithFormat:@"'%@'", str]];
-}
+#pragma mark - CKKSSQLWhereObject
 
 
+@implementation CKKSSQLWhereValue
+- (instancetype)initWithOperation:(CKKSSQLWhereComparator)op value:(NSString*)value
+{
+    if((self = [super init])) {
+        _sqlOp = op;
+        _value = value;
+    }
+    return self;
+}
++ (instancetype)op:(CKKSSQLWhereComparator)op value:(NSString*)value
+{
+    return [[CKKSSQLWhereValue alloc] initWithOperation:op value:value];
 
 
+}
 @end
 @end
index a35a240d37a35fff9eb623055f65446290a0c979..0ad03e5789caf367258f6c5028f4d90fe152518a 100644 (file)
@@ -33,6 +33,8 @@ NS_ASSUME_NONNULL_BEGIN
 @class CKKSEgoManifest;
 
 @interface CKKSScanLocalItemsOperation : CKKSResultOperation
 @class CKKSEgoManifest;
 
 @interface CKKSScanLocalItemsOperation : CKKSResultOperation
+@property CKOperationGroup* ckoperationGroup;
+
 @property (weak) CKKSKeychainView* ckks;
 
 @property size_t recordsFound;
 @property (weak) CKKSKeychainView* ckks;
 
 @property size_t recordsFound;
index 7d285ad6fbe9b2f2a9bb030e2964921e36d14b1c..3c3a6afe4533ca1206a264488113c482aa20f853 100644 (file)
@@ -22,6 +22,9 @@
  */
 
 #if OCTAGON
  */
 
 #if OCTAGON
+#import <TrustedPeers/TPPolicy.h>
+#import <TrustedPeers/TPPBPolicyKeyViewMapping.h>
+#import <TrustedPeers/TPDictionaryMatchingRules.h>
 
 #import "keychain/ckks/CKKSAnalytics.h"
 #import "keychain/ckks/CKKSKeychainView.h"
 
 #import "keychain/ckks/CKKSAnalytics.h"
 #import "keychain/ckks/CKKSKeychainView.h"
@@ -47,7 +50,6 @@
 #import <IMCore/IMCloudKitHooks.h>
 
 @interface CKKSScanLocalItemsOperation ()
 #import <IMCore/IMCloudKitHooks.h>
 
 @interface CKKSScanLocalItemsOperation ()
-@property CKOperationGroup* ckoperationGroup;
 @property (assign) NSUInteger processedItems;
 @end
 
 @property (assign) NSUInteger processedItems;
 @end
 
     return self;
 }
 
     return self;
 }
 
+// returns true if the currently loaded TPPolicy in the view manager says that only items with a VewHint
+// matching the viewName belong to this view.
+- (BOOL)policyRecommendsOnlyViewHintItems:(CKKSKeychainView*)ckks
+{
+    // Early-exit for when feature is not on:
+    if(![[CKKSViewManager manager] useCKKSViewsFromPolicy]) {
+        return YES;
+    }
+
+    TPPBPolicyKeyViewMapping* viewRule = nil;
+
+    // If there's more than one rule matching this view, then exit with NO.
+    for(TPPBPolicyKeyViewMapping* mapping in [CKKSViewManager manager].policy.keyViewMapping) {
+        if([mapping.view isEqualToString:ckks.zoneName]) {
+            if(viewRule == nil) {
+                viewRule = mapping;
+            } else {
+                // Too many rules for this view! Don't perform optimization.
+                ckksnotice("ckksscan", ckks, "Too many policy rules for view %@", ckks.zoneName);
+                return NO;
+            }
+        }
+    }
+
+    if(viewRule.hasMatchingRule &&
+       viewRule.matchingRule.andsCount == 0 &&
+       viewRule.matchingRule.orsCount == 0 &&
+       !viewRule.matchingRule.hasNot &&
+       !viewRule.matchingRule.hasExists &&
+       viewRule.matchingRule.hasMatch) {
+        if([@"vwht" isEqualToString:viewRule.matchingRule.match.fieldName] &&
+           [viewRule.matchingRule.match.regex isEqualToString:[NSString stringWithFormat:@"^%@$", ckks.zoneName]]) {
+            return YES;
+        } else {
+            ckksnotice("ckksscan", ckks, "Policy view rule is not a match against viewhint: %@", viewRule);
+        }
+    } else {
+        ckksnotice("ckksscan", ckks, "Policy view rule is of unknown type: %@", viewRule);
+    }
+
+    return NO;
+}
+
 - (void) main {
     // Take a strong reference.
     CKKSKeychainView* ckks = self.ckks;
 - (void) main {
     // Take a strong reference.
     CKKSKeychainView* ckks = self.ckks;
                 continue;
             }
 
                 continue;
             }
 
-            NSDictionary* queryAttributes = @{(__bridge NSString*) kSecClass: (__bridge NSString*) (*class)->name,
-                                              (__bridge NSString*) kSecReturnRef: @(YES),
-                                              (__bridge NSString*) kSecAttrSynchronizable: @(YES),
-                                              (__bridge NSString*) kSecAttrTombstone: @(NO),
-                                              // This works ~as long as~ item views are chosen by view hint only. It's a significant perf win, though.
-                                              // <rdar://problem/32269541> SpinTracer: CKKSScanLocalItemsOperation expensive on M8 machines
-                                              (__bridge NSString*) kSecAttrSyncViewHint: ckks.zoneName,
-                                              };
-            ckksinfo("ckksscan", ckks, "Scanning all synchronizable items for: %@", queryAttributes);
+            // As a performance optimization, if the current policy says that this view only includes items by viewhint,
+            // add that to the query.
+            BOOL limitToViewHint = [self policyRecommendsOnlyViewHintItems:ckks];
+
+            NSMutableDictionary* queryAttributes = [
+                                                    @{(__bridge NSString*) kSecClass: (__bridge NSString*) (*class)->name,
+                                                      (__bridge NSString*) kSecReturnRef: @(YES),
+                                                      (__bridge NSString*) kSecAttrSynchronizable: @(YES),
+                                                      (__bridge NSString*) kSecAttrTombstone: @(NO),
+                                                    } mutableCopy];
+
+            if(limitToViewHint) {
+                queryAttributes[(__bridge NSString*)kSecAttrSyncViewHint] = ckks.zoneName;
+            }
+
+            ckksnotice("ckksscan", ckks, "Scanning all synchronizable %@ items(%@) for: %@", (__bridge NSString*)(*class)->name, self.name, queryAttributes);
 
             Query *q = query_create_with_limit( (__bridge CFDictionaryRef) queryAttributes, NULL, kSecMatchUnlimited, &cferror);
             bool ok = false;
 
             Query *q = query_create_with_limit( (__bridge CFDictionaryRef) queryAttributes, NULL, kSecMatchUnlimited, &cferror);
             bool ok = false;
index cd6ac8cd6de3a7c04b47dd8e6f05127235ba9bda..f19cccabe43ae3b54517fece49ddb1f563181531 100644 (file)
@@ -65,14 +65,15 @@ NS_ASSUME_NONNULL_BEGIN
 
 @property id<OTSOSAdapter> sosPeerAdapter;
 
 
 @property id<OTSOSAdapter> sosPeerAdapter;
 
-@property (nullable) TPPolicy* policy;
+@property (readonly, nullable) TPPolicy* policy;
 
 
-@property NSMutableDictionary<NSString*, CKKSKeychainView*>* views;
+@property (readonly) NSMutableDictionary<NSString*, CKKSKeychainView*>* views;
 
 
-- (instancetype)initWithContainerName:(NSString*)containerName
-                               usePCS:(bool)usePCS
-                           sosAdapter:(id<OTSOSAdapter> _Nullable)sosAdapter
-            cloudKitClassDependencies:(CKKSCloudKitClassDependencies*)cloudKitClassDependencies;
+- (instancetype)initWithContainer:(CKContainer*)container
+                       sosAdapter:(id<OTSOSAdapter> _Nullable)sosAdapter
+              accountStateTracker:(CKKSAccountStateTracker*)accountTracker
+                 lockStateTracker:(CKKSLockStateTracker*)lockStateTracker
+        cloudKitClassDependencies:(CKKSCloudKitClassDependencies*)cloudKitClassDependencies;
 
 - (CKKSKeychainView*)findView:(NSString*)viewName;
 - (CKKSKeychainView*)findOrCreateView:(NSString*)viewName;
 
 - (CKKSKeychainView*)findView:(NSString*)viewName;
 - (CKKSKeychainView*)findOrCreateView:(NSString*)viewName;
@@ -85,7 +86,7 @@ NS_ASSUME_NONNULL_BEGIN
 
 - (void)setupAnalytics;
 
 
 - (void)setupAnalytics;
 
-- (NSString*)viewNameForItem:(SecDbItemRef)item;
+- (NSString* _Nullable)viewNameForItem:(SecDbItemRef)item;
 
 - (void)handleKeychainEventDbConnection:(SecDbConnectionRef)dbconn
                                  source:(SecDbTransactionSource)txionSource
 
 - (void)handleKeychainEventDbConnection:(SecDbConnectionRef)dbconn
                                  source:(SecDbTransactionSource)txionSource
@@ -112,29 +113,30 @@ NS_ASSUME_NONNULL_BEGIN
 // Cancels pending operations owned by this view manager
 - (void)cancelPendingOperations;
 
 // Cancels pending operations owned by this view manager
 - (void)cancelPendingOperations;
 
-// Use these to acquire (and set) the singleton
 + (instancetype)manager;
 + (instancetype)manager;
-+ (instancetype _Nullable)resetManager:(bool)reset setTo:(CKKSViewManager* _Nullable)obj;
 
 // Called by XPC every 24 hours
 - (void)xpc24HrNotification;
 
 
 // Called by XPC every 24 hours
 - (void)xpc24HrNotification;
 
-/* White-box testing only */
-- (CKKSKeychainView*)restartZone:(NSString*)viewName;
-
-// Returns the viewList for a CKKSViewManager
+// Returns the current set of views
 - (NSSet<NSString*>*)viewList;
 
 - (NSSet<NSString*>*)defaultViewList;
 
 - (NSSet<NSString*>*)viewList;
 
 - (NSSet<NSString*>*)defaultViewList;
 
-- (void)setViewList:(NSSet<NSString*>* _Nullable)newViewList;
+// Call this to set the syncing views+policy that this manager will use.
+// If beginCloudKitOperationOfAllViews has previously been called, then any new views created
+// as a result of this call will begin CK operation.
+- (void)setSyncingViews:(NSSet<NSString*>* _Nullable)viewNames sortingPolicy:(TPPolicy* _Nullable)policy;
 
 - (void)clearAllViews;
 
 // Create all views, but don't begin CK/network operations
 
 - (void)clearAllViews;
 
 // Create all views, but don't begin CK/network operations
+// Remove as part of <rdar://problem/57768740> CKKS: ensure we collect keychain changes made before policy is loaded from disk
 - (void)createViews;
 
 // Call this to begin CK operation of all views
 - (void)createViews;
 
 // Call this to begin CK operation of all views
+// This bit will be 'sticky', in that any new views created with also begin cloudkit operation immediately.
+// (clearAllViews will reset this bit.)
 - (void)beginCloudKitOperationOfAllViews;
 
 // Notify sbd to re-backup.
 - (void)beginCloudKitOperationOfAllViews;
 
 // Notify sbd to re-backup.
@@ -145,11 +147,29 @@ NS_ASSUME_NONNULL_BEGIN
 // first time after launch, only waits the the initial call
 - (BOOL)waitForTrustReady;
 
 // first time after launch, only waits the the initial call
 - (BOOL)waitForTrustReady;
 
-// For testing
-- (void)setOverrideCKKSViewsFromPolicy:(BOOL)value;
+// Helper function to make CK containers
++ (CKContainer*)makeCKContainer:(NSString*)containerName
+                         usePCS:(bool)usePCS;
+
+// Checks featureflags to return whether we should use policy-based views, or use the hardcoded list
 - (BOOL)useCKKSViewsFromPolicy;
 - (BOOL)useCKKSViewsFromPolicy;
+
+// Interfaces to examine sync callbacks
+- (SecBoolNSErrorCallback _Nullable)claimCallbackForUUID:(NSString*)uuid;
+- (NSSet<NSString*>*)pendingCallbackUUIDs;
++ (void)callSyncCallbackWithErrorNoAccount:(SecBoolNSErrorCallback)syncCallback;
+@end
+
+@interface CKKSViewManager (Testing)
+- (void)setOverrideCKKSViewsFromPolicy:(BOOL)value;
+- (void)resetSyncingPolicy;
+
 - (void)haltAll;
 - (void)haltAll;
+- (CKKSKeychainView*)restartZone:(NSString*)viewName;
+- (void)haltZone:(NSString*)viewName;
 
 
+// If set, any set passed to setSyncingViews will be intersected with this set
+- (void)setSyncingViewsAllowList:(NSSet<NSString*>* _Nullable)viewNames;
 @end
 NS_ASSUME_NONNULL_END
 
 @end
 NS_ASSUME_NONNULL_END
 
index 8a226dfc0c0bac8f062d06605f35e437432a4cee..3838cc6b381ea1697dd9e0bde6a917da2337a6ba 100644 (file)
@@ -23,6 +23,7 @@
 
 #import <os/feature_private.h>
 
 
 #import <os/feature_private.h>
 
+#import "keychain/ckks/CKKSAccountStateTracker.h"
 #import "keychain/ckks/CKKSViewManager.h"
 #import "keychain/ckks/CKKSKeychainView.h"
 #import "keychain/ckks/CKKSSynchronizeOperation.h"
 #import "keychain/ckks/CKKSViewManager.h"
 #import "keychain/ckks/CKKSKeychainView.h"
 #import "keychain/ckks/CKKSSynchronizeOperation.h"
 #import "CKKSAnalytics.h"
 #endif
 
 #import "CKKSAnalytics.h"
 #endif
 
+#if !OCTAGON
 @interface CKKSViewManager () <NSXPCListenerDelegate>
 @interface CKKSViewManager () <NSXPCListenerDelegate>
-#if OCTAGON
+#else
+@interface CKKSViewManager () <NSXPCListenerDelegate,
+                               CKKSCloudKitAccountStateListener>
+
 @property NSXPCListener *listener;
 
 @property NSXPCListener *listener;
 
+@property (nullable) NSSet<NSString*>* viewAllowList;
+
 // Once you set these, all CKKSKeychainViews created will use them
 @property CKKSCloudKitClassDependencies* cloudKitClassDependencies;
 
 // Once you set these, all CKKSKeychainViews created will use them
 @property CKKSCloudKitClassDependencies* cloudKitClassDependencies;
 
 
 @property (nonatomic) BOOL overrideCKKSViewsFromPolicy;
 @property (nonatomic) BOOL valueCKKSViewsFromPolicy;
 
 @property (nonatomic) BOOL overrideCKKSViewsFromPolicy;
 @property (nonatomic) BOOL valueCKKSViewsFromPolicy;
+@property (nonatomic) BOOL startCKOperationAtViewCreation;
+
+@property BOOL itemModificationsBeforePolicyLoaded;
+
+// Make writable
+@property (nullable) TPPolicy* policy;
 
 #endif
 @end
 
 #endif
 @end
 @implementation CKKSViewManager
 #if OCTAGON
 
 @implementation CKKSViewManager
 #if OCTAGON
 
-NSSet<NSString*>* _viewList;
-
-- (instancetype)initWithContainerName: (NSString*) containerName
-                               usePCS: (bool)usePCS
-                           sosAdapter:(id<OTSOSAdapter> _Nullable)sosAdapter
-            cloudKitClassDependencies:(CKKSCloudKitClassDependencies*)cloudKitClassDependencies
+- (instancetype)initWithContainer:(CKContainer*)container
+                       sosAdapter:(id<OTSOSAdapter> _Nullable)sosAdapter
+              accountStateTracker:(CKKSAccountStateTracker*)accountTracker
+                 lockStateTracker:(CKKSLockStateTracker*)lockStateTracker
+        cloudKitClassDependencies:(CKKSCloudKitClassDependencies*)cloudKitClassDependencies
 {
     if(self = [super init]) {
         _cloudKitClassDependencies = cloudKitClassDependencies;
         _sosPeerAdapter = sosAdapter;
 {
     if(self = [super init]) {
         _cloudKitClassDependencies = cloudKitClassDependencies;
         _sosPeerAdapter = sosAdapter;
-        _viewList = nil;
 
 
-        _container = [self makeCKContainer: containerName usePCS:usePCS];
-        _accountTracker = [[CKKSAccountStateTracker alloc] init:self.container nsnotificationCenterClass:cloudKitClassDependencies.nsnotificationCenterClass];
-        _lockStateTracker = [[CKKSLockStateTracker alloc] init];
+        _viewAllowList = nil;
+        _container = container;
+        _accountTracker = accountTracker;
+        _lockStateTracker = lockStateTracker;
         [_lockStateTracker addLockStateObserver:self];
         _reachabilityTracker = [[CKKSReachabilityTracker alloc] init];
         [_lockStateTracker addLockStateObserver:self];
         _reachabilityTracker = [[CKKSReachabilityTracker alloc] init];
+        _itemModificationsBeforePolicyLoaded = NO;
 
         _zoneChangeFetcher = [[CKKSZoneChangeFetcher alloc] initWithContainer:_container
                                                                    fetchClass:cloudKitClassDependencies.fetchRecordZoneChangesOperationClass
 
         _zoneChangeFetcher = [[CKKSZoneChangeFetcher alloc] initWithContainer:_container
                                                                    fetchClass:cloudKitClassDependencies.fetchRecordZoneChangesOperationClass
@@ -127,6 +140,8 @@ NSSet<NSString*>* _viewList;
         _views = [[NSMutableDictionary alloc] init];
         _pendingSyncCallbacks = [[NSMutableDictionary alloc] init];
 
         _views = [[NSMutableDictionary alloc] init];
         _pendingSyncCallbacks = [[NSMutableDictionary alloc] init];
 
+        _startCKOperationAtViewCreation = NO;
+
         _completedSecCKKSInitialize = [[CKKSCondition alloc] init];
 
         WEAKIFY(self);
         _completedSecCKKSInitialize = [[CKKSCondition alloc] init];
 
         WEAKIFY(self);
@@ -139,14 +154,21 @@ NSSet<NSString*>* _viewList;
             [self notifyNewTLKsInKeychain];
         }];
 
             [self notifyNewTLKsInKeychain];
         }];
 
+        _policy = nil;
+
         _listener = [NSXPCListener anonymousListener];
         _listener.delegate = self;
         [_listener resume];
         _listener = [NSXPCListener anonymousListener];
         _listener.delegate = self;
         [_listener resume];
+
+        // Start listening for CK account status (for sync callbacks)
+        [_accountTracker registerForNotificationsOfCloudKitAccountStatusChange:self];
     }
     return self;
 }
 
     }
     return self;
 }
 
--(CKContainer*)makeCKContainer:(NSString*)containerName usePCS:(bool)usePCS {
++ (CKContainer*)makeCKContainer:(NSString*)containerName
+                         usePCS:(bool)usePCS
+{
     CKContainer* container = [CKContainer containerWithIdentifier:containerName];
     if(!usePCS) {
         CKContainerOptions* containerOptions = [[CKContainerOptions alloc] init];
     CKContainer* container = [CKContainer containerWithIdentifier:containerName];
     if(!usePCS) {
         CKContainerOptions* containerOptions = [[CKContainerOptions alloc] init];
@@ -312,6 +334,8 @@ dispatch_once_t globalZoneStateQueueOnce;
     }
 }
 
     }
 }
 
+#pragma mark - View List handling
+
 - (NSSet<NSString*>*)defaultViewList {
     NSSet<NSString*>* fullList = [OTSOSActualAdapter sosCKKSViewList];
 
 - (NSSet<NSString*>*)defaultViewList {
     NSSet<NSString*>* fullList = [OTSOSActualAdapter sosCKKSViewList];
 
@@ -338,18 +362,77 @@ dispatch_once_t globalZoneStateQueueOnce;
     return fullList;
 }
 
     return fullList;
 }
 
--(NSSet<NSString*>*)viewList {
-    if (_viewList) {
-        return _viewList;
-    } else {
-        return [self defaultViewList];
+- (NSSet<NSString*>*)viewList {
+    return [self.views.allKeys copy];
+}
+
+- (void)setSyncingViews:(NSSet<NSString*>*)viewNames sortingPolicy:(TPPolicy*)policy
+{
+    secnotice("ckks-policy", "New syncing policy: %@ views: %@", policy, viewNames);
+
+    if(![self useCKKSViewsFromPolicy]) {
+        // Thanks, but no thanks.
+        viewNames = [self defaultViewList];
+        secnotice("ckks-policy", "Reverting to default view list: %@", viewNames);
+    }
+
+    if(self.viewAllowList) {
+        secnotice("ckks-policy", "Intersecting view list with allow list: %@", self.viewAllowList);
+        NSMutableSet<NSString*>* set = [viewNames mutableCopy];
+        [set intersectSet:self.viewAllowList];
+
+        viewNames = set;
+        secnotice("ckks-policy", "Final list: %@", viewNames);
+    }
+
+    self.policy = policy;
+
+    @synchronized(self.views) {
+        NSArray* previousViewNames = [self.views.allKeys copy];
+
+        // First, shut down any views that are no longer in the set
+        for(NSString* viewName in previousViewNames) {
+            if(![viewNames containsObject:viewName]) {
+                secnotice("ckks-policy", "Stopping old view %@", viewName);
+                [self clearView:viewName];
+            }
+        }
+
+        for(NSString* viewName in viewNames) {
+            if([previousViewNames containsObject:viewName]) {
+                CKKSKeychainView* view = [self findView:viewName];
+                secnotice("ckks-policy", "Already have view %@", view);
+            } else {
+                CKKSKeychainView* view = [self findOrCreateView:viewName];
+                secnotice("ckks-policy", "Created new view %@", view);
+            }
+        }
+
+        if(self.itemModificationsBeforePolicyLoaded) {
+            secnotice("ckks-policy", "Issuing scan suggestions to handle missed items");
+            for(CKKSKeychainView* view in [self.views allValues]) {
+                [view scanLocalItems:@"item-added-before-policy"];
+            }
+            self.itemModificationsBeforePolicyLoaded = NO;
+        }
     }
 }
 
     }
 }
 
-- (void)setViewList:(NSSet<NSString*>* _Nullable)newViewList {
-    _viewList = newViewList;
+- (void)setSyncingViewsAllowList:(NSSet<NSString*>*)viewNames
+{
+    self.viewAllowList = viewNames;
+}
+
+- (void)resetSyncingPolicy
+{
+    secnotice("ckks-policy", "Setting policy to nil");
+    self.policy = nil;
+
+    self.startCKOperationAtViewCreation = NO;
 }
 
 }
 
+#pragma mark - View Handling
+
 - (void)setView: (CKKSKeychainView*) obj {
     CKKSKeychainView* kcv = nil;
 
 - (void)setView: (CKKSKeychainView*) obj {
     CKKSKeychainView* kcv = nil;
 
@@ -368,6 +451,8 @@ dispatch_once_t globalZoneStateQueueOnce;
     @synchronized(self.views) {
         tempviews = [self.views.allValues copy];
         [self.views removeAllObjects];
     @synchronized(self.views) {
         tempviews = [self.views.allValues copy];
         [self.views removeAllObjects];
+
+        self.startCKOperationAtViewCreation = NO;
     }
 
     for(CKKSKeychainView* view in tempviews) {
     }
 
     for(CKKSKeychainView* view in tempviews) {
@@ -412,6 +497,10 @@ dispatch_once_t globalZoneStateQueueOnce;
                                                               zoneModifier:self.zoneModifier
                                                           savedTLKNotifier: self.savedTLKNotifier
                                                  cloudKitClassDependencies:self.cloudKitClassDependencies];
                                                               zoneModifier:self.zoneModifier
                                                           savedTLKNotifier: self.savedTLKNotifier
                                                  cloudKitClassDependencies:self.cloudKitClassDependencies];
+
+        if(self.startCKOperationAtViewCreation) {
+            [self.views[viewName] beginCloudKitOperation];
+        }
         return self.views[viewName];
     }
 }
         return self.views[viewName];
     }
 }
@@ -429,18 +518,23 @@ dispatch_once_t globalZoneStateQueueOnce;
 
 - (void)createViews
 {
 
 - (void)createViews
 {
-    // In the future, the CKKSViewManager needs to persist its policy property through daemon restarts
-    // and load it here, before creating whatever views it was told to (in a previous daemon lifetime)
-    for (NSString* viewName in self.viewList) {
-        CKKSKeychainView* view = [self findOrCreateView:viewName];
-        (void)view;
+    if(![self useCKKSViewsFromPolicy]) {
+        // In the future, the CKKSViewManager needs to persist its policy property through daemon restarts
+        // and load it here, before creating whatever views it was told to (in a previous daemon lifetime)
+        for (NSString* viewName in [self defaultViewList]) {
+            CKKSKeychainView* view = [self findOrCreateView:viewName];
+            (void)view;
+        }
+    } else {
+        secnotice("ckks-views", "Not loading default view list due to enabled CKKS4All");
     }
 }
 
 - (void)beginCloudKitOperationOfAllViews
 {
     }
 }
 
 - (void)beginCloudKitOperationOfAllViews
 {
-    for (NSString* viewName in self.viewList) {
-        CKKSKeychainView* view = [self findView:viewName];
+    self.startCKOperationAtViewCreation = YES;
+
+    for (CKKSKeychainView* view in self.views.allValues) {
         [view beginCloudKitOperation];
     }
 }
         [view beginCloudKitOperation];
     }
 }
@@ -460,11 +554,18 @@ dispatch_once_t globalZoneStateQueueOnce;
     return tlks;
 }
 
     return tlks;
 }
 
-- (CKKSKeychainView*)restartZone:(NSString*)viewName {
+- (void)haltZone:(NSString*)viewName
+{
     @synchronized(self.views) {
     @synchronized(self.views) {
-        [self.views[viewName] halt];
+        CKKSKeychainView* view = self.views[viewName];
+        [view halt];
+        [view cancelAllOperations];
         self.views[viewName] = nil;
     }
         self.views[viewName] = nil;
     }
+}
+
+- (CKKSKeychainView*)restartZone:(NSString*)viewName {
+    [self haltZone:viewName];
     return [self findOrCreateView: viewName];
 }
 
     return [self findOrCreateView: viewName];
 }
 
@@ -491,28 +592,42 @@ dispatch_once_t globalZoneStateQueueOnce;
     if (self.overrideCKKSViewsFromPolicy) {
         return self.valueCKKSViewsFromPolicy;
     } else {
     if (self.overrideCKKSViewsFromPolicy) {
         return self.valueCKKSViewsFromPolicy;
     } else {
-        return os_feature_enabled(Security, CKKSViewsFromPolicy);
+        BOOL viewsFromPolicy = os_feature_enabled(Security, CKKSViewsFromPolicy);
+
+        static dispatch_once_t onceToken;
+        dispatch_once(&onceToken, ^{
+            secnotice("ckks", "ViewsFromPolicy feature flag: %@", viewsFromPolicy ? @"on" : @"off");
+        });
+        return viewsFromPolicy;
     }
 }
 
     }
 }
 
-- (NSString*)viewNameForItem: (SecDbItemRef) item {
+- (NSString* _Nullable)viewNameForItem:(SecDbItemRef)item
+{
     if ([self useCKKSViewsFromPolicy]) {
         CFErrorRef cferror = NULL;
     if ([self useCKKSViewsFromPolicy]) {
         CFErrorRef cferror = NULL;
-        NSMutableDictionary *dict = (__bridge_transfer NSMutableDictionary*) SecDbItemCopyPListWithMask(item, ~0, &cferror);
+        NSMutableDictionary *dict = (__bridge_transfer NSMutableDictionary*) SecDbItemCopyPListWithMask(item, kSecDbSyncFlag, &cferror);
 
         if(cferror) {
             secerror("ckks: Couldn't fetch attributes from item: %@", cferror);
             CFReleaseNull(cferror);
 
         if(cferror) {
             secerror("ckks: Couldn't fetch attributes from item: %@", cferror);
             CFReleaseNull(cferror);
-            return [self viewNameForViewHint: nil];
-        }
-        TPPolicy* policy = self.policy;
-        if (policy == nil) {
-            return [self viewNameForViewHint: nil];
+            return nil;
         }
         }
-        NSString* view = [policy mapKeyToView:dict];
+
+        NSString* view = [self.policy mapKeyToView:dict];
         if (view == nil) {
         if (view == nil) {
-            return [self viewNameForViewHint: nil];
+            secerror("ckks: No view returned from policy (%@): %@", self.policy, dict);
+            return nil;
         }
         }
+
+        // Horrible hack until <rdar://problem/57810109> Cuttlefish: remove Safari prefix from view names
+        if([view isEqualToString:@"CreditCards"]) {
+            return @"SafariCreditCards";
+        }
+        if([view isEqualToString:@"Passwords"]) {
+            return @"SafariPasswords";
+        }
+
         return view;
     } else {
         CFErrorRef cferror = NULL;
         return view;
     } else {
         CFErrorRef cferror = NULL;
@@ -531,15 +646,63 @@ dispatch_once_t globalZoneStateQueueOnce;
 - (void)registerSyncStatusCallback: (NSString*) uuid callback: (SecBoolNSErrorCallback) callback {
     // Someone is requesting future notification of this item.
     @synchronized(self.pendingSyncCallbacks) {
 - (void)registerSyncStatusCallback: (NSString*) uuid callback: (SecBoolNSErrorCallback) callback {
     // Someone is requesting future notification of this item.
     @synchronized(self.pendingSyncCallbacks) {
+        secnotice("ckkscallback", "registered callback for UUID: %@", uuid);
         self.pendingSyncCallbacks[uuid] = callback;
     }
 }
 
         self.pendingSyncCallbacks[uuid] = callback;
     }
 }
 
+- (SecBoolNSErrorCallback _Nullable)claimCallbackForUUID:(NSString*)uuid
+{
+    @synchronized(self.pendingSyncCallbacks) {
+        SecBoolNSErrorCallback callback = self.pendingSyncCallbacks[uuid];
+
+        if(callback) {
+            secerror("ckkscallback : fetched UUID: %@", uuid);
+        }
+
+        self.pendingSyncCallbacks[uuid] = nil;
+        return callback;
+    }
+}
+
+- (NSSet<NSString*>*)pendingCallbackUUIDs
+{
+    @synchronized(self.pendingSyncCallbacks) {
+        return [[self.pendingSyncCallbacks allKeys] copy];
+    }
+}
+
+- (void)cloudkitAccountStateChange:(CKAccountInfo* _Nullable)oldAccountInfo to:(CKAccountInfo*)currentAccountInfo
+{
+    if(currentAccountInfo.accountStatus == CKAccountStatusAvailable && currentAccountInfo.hasValidCredentials) {
+        // Account is okay!
+    } else {
+        @synchronized(self.pendingSyncCallbacks) {
+            if(self.pendingSyncCallbacks.count > 0) {
+                secnotice("ckkscallback", "No CK account; failing all pending sync callbacks");
+
+                for(NSString* uuid in [self.pendingSyncCallbacks allKeys]) {
+                    [CKKSViewManager callSyncCallbackWithErrorNoAccount:self.pendingSyncCallbacks[uuid]];
+                }
+
+                [self.pendingSyncCallbacks removeAllObjects];
+            }
+        }
+    }
+}
+
++ (void)callSyncCallbackWithErrorNoAccount:(SecBoolNSErrorCallback)syncCallback
+{
+    // I don't love using this domain, but PCS depends on it
+    syncCallback(false, [NSError errorWithDomain:@"securityd"
+                                            code:errSecNotLoggedIn
+                                     description:@"No iCloud account available; item is not expected to sync"]);
+}
+
 - (void) handleKeychainEventDbConnection: (SecDbConnectionRef) dbconn source:(SecDbTransactionSource)txionSource added: (SecDbItemRef) added deleted: (SecDbItemRef) deleted {
 
     SecDbItemRef modified = added ? added : deleted;
 
 - (void) handleKeychainEventDbConnection: (SecDbConnectionRef) dbconn source:(SecDbTransactionSource)txionSource added: (SecDbItemRef) added deleted: (SecDbItemRef) deleted {
 
     SecDbItemRef modified = added ? added : deleted;
 
-    NSString* viewName = [self viewNameForItem: modified];
     NSString* keyViewName = [CKKSKey isItemKeyForKeychainView: modified];
 
     if(keyViewName) {
     NSString* keyViewName = [CKKSKey isItemKeyForKeychainView: modified];
 
     if(keyViewName) {
@@ -557,44 +720,56 @@ dispatch_once_t globalZoneStateQueueOnce;
         return;
     }
 
         return;
     }
 
-    // When SOS is in charge of a view, CKKS is not.
-    // Since this isn't a CKKS key item, we don't care about it.
-    if(txionSource == kSecDbSOSTransaction) {
-        secinfo("ckks", "Ignoring new non-CKKS item in kSecDbSOSTransaction notification");
+    bool addedSync   = added   && SecDbItemIsSyncable(added);
+    bool deletedSync = deleted && SecDbItemIsSyncable(deleted);
+
+    if(!addedSync && !deletedSync) {
+        // Local-only change. Skip with prejudice.
+        secinfo("ckks", "skipping sync of non-sync item (%d, %d)", addedSync, deletedSync);
+        return;
     }
 
     }
 
-    // Looks like a normal item. Proceed!
-    CKKSKeychainView* view = [self findView:viewName];
+    NSString* viewName = nil;
 
 
-    NSString* uuid = (__bridge NSString*) SecDbItemGetValue(modified, &v10itemuuid, NULL);
-    SecBoolNSErrorCallback syncCallback = nil;
-    if(uuid) {
-        @synchronized(self.pendingSyncCallbacks) {
-            syncCallback = self.pendingSyncCallbacks[uuid];
-            self.pendingSyncCallbacks[uuid] = nil;
+    @synchronized(self.views) {
+        if([self useCKKSViewsFromPolicy] && !self.policy) {
+            secerror("ckks: No policy configured(%@). Skipping item: %@", self.policy, modified);
+            self.itemModificationsBeforePolicyLoaded = YES;
 
 
-            if(syncCallback) {
-                secinfo("ckks", "Have a pending callback for %@; passing along", uuid);
-            }
+            return;
         }
         }
+
+        viewName = [self viewNameForItem:modified];
+    }
+
+    if(!viewName) {
+        secnotice("ckks", "No intended CKKS view for item; skipping: %@", modified);
+        return;
     }
 
     }
 
+    // Looks like a normal item. Proceed!
+    CKKSKeychainView* view = [self findView:viewName];
+
     if(!view) {
     if(!view) {
-        if(viewName) {
-            secnotice("ckks", "No CKKS view for %@, skipping: %@", viewName, modified);
-        } else {
-            secinfo("ckks", "No CKKS view for %@, skipping: %@", viewName, modified);
-        }
+        secnotice("ckks", "No CKKS view for %@, skipping: %@", viewName, modified);
+
+        NSString* uuid = (__bridge NSString*) SecDbItemGetValue(modified, &v10itemuuid, NULL);
+        SecBoolNSErrorCallback syncCallback = [self claimCallbackForUUID:uuid];
+
         if(syncCallback) {
         if(syncCallback) {
-            syncCallback(false, [NSError errorWithDomain:@"securityd"
-                                                    code:kSOSCCNoSuchView
+            syncCallback(false, [NSError errorWithDomain:CKKSErrorDomain
+                                                    code:CKKSNoSuchView
                                                 userInfo:@{NSLocalizedDescriptionKey: [NSString stringWithFormat: @"No syncing view for '%@'", viewName]}]);
         }
         return;
     }
 
     ckksnotice("ckks", view, "Routing item to zone %@: %@", viewName, modified);
                                                 userInfo:@{NSLocalizedDescriptionKey: [NSString stringWithFormat: @"No syncing view for '%@'", viewName]}]);
         }
         return;
     }
 
     ckksnotice("ckks", view, "Routing item to zone %@: %@", viewName, modified);
-    [view handleKeychainEventDbConnection: dbconn added:added deleted:deleted rateLimiter:self.globalRateLimiter syncCallback: syncCallback];
+    [view handleKeychainEventDbConnection:dbconn
+                                   source:txionSource
+                                    added:added
+                                  deleted:deleted
+                              rateLimiter:self.globalRateLimiter];
 }
 
 -(void)setCurrentItemForAccessGroup:(NSData* _Nonnull)newItemPersistentRef
 }
 
 -(void)setCurrentItemForAccessGroup:(NSData* _Nonnull)newItemPersistentRef
@@ -646,40 +821,9 @@ dispatch_once_t globalZoneStateQueueOnce;
                               complete:complete];
 }
 
                               complete:complete];
 }
 
-
-+ (instancetype) manager {
-    return [self resetManager: false setTo: nil];
-}
-
-+ (instancetype) resetManager: (bool) reset setTo: (CKKSViewManager*) obj {
-    static CKKSViewManager* manager = nil;
-
-    if([CKDatabase class] == nil) {
-        secerror("CKKS: CloudKit.framework appears to not be linked. Can't create CKKS objects.");
-        return nil;
-    }
-
-    if(!manager || reset || obj) {
-        @synchronized([self class]) {
-            if(obj != nil) {
-                [manager clearAllViews];
-                manager = obj;
-            } else {
-                if(reset) {
-                    [manager clearAllViews];
-                    manager = nil;
-                } else if (manager == nil && SecCKKSIsEnabled()) {
-                    // The CKKSViewManager doesn't do much with its adapter, so leave as nonessentialq
-                    manager = [[CKKSViewManager alloc] initWithContainerName:SecCKKSContainerName
-                                                                      usePCS:SecCKKSContainerUsePCS
-                                                                  sosAdapter:[[OTSOSActualAdapter alloc] initAsEssential:NO]
-                                                   cloudKitClassDependencies:[CKKSCloudKitClassDependencies forLiveCloudKit]];
-                }
-            }
-        }
-    }
-
-    return manager;
++ (instancetype)manager
+{
+    return [OTManager manager].viewManager;
 }
 
 - (void)cancelPendingOperations {
 }
 
 - (void)cancelPendingOperations {
@@ -907,6 +1051,8 @@ dispatch_once_t globalZoneStateQueueOnce;
                                      @"lockstatetracker":    stringify(self.lockStateTracker),
                                      @"cloudkitRetryAfter":  stringify(self.zoneModifier.cloudkitRetryAfter),
                                      @"lastCKKSPush":        CKKSNilToNSNull(lastCKKSPush),
                                      @"lockstatetracker":    stringify(self.lockStateTracker),
                                      @"cloudkitRetryAfter":  stringify(self.zoneModifier.cloudkitRetryAfter),
                                      @"lastCKKSPush":        CKKSNilToNSNull(lastCKKSPush),
+                                     @"policy":              stringify(self.policy),
+                                     @"viewsFromPolicy":     [self useCKKSViewsFromPolicy] ? @"yes" : @"no",
                                      };
             [a addObject: global];
         }
                                      };
             [a addObject: global];
         }
index b51bf4774f114ff753d65dee4744118feda32cca..c5f878b30011a03d701108904b70b5a59bf7f7cd 100644 (file)
 
     // Bring all operations down, too
     [self cancelAllOperations];
 
     // Bring all operations down, too
     [self cancelAllOperations];
+
+    // And now, wait for all operations that are running
+    for(NSOperation* op in self.operationQueue.operations) {
+        if(op.isExecuting) {
+            [op waitUntilFinished];
+        }
+    }
 }
 
 @end
 }
 
 @end
index 6add06379a4f853969a68674fbead5ae45e8327a..82239815e4ad66348e41888d2ca298c536799319 100644 (file)
@@ -41,6 +41,7 @@
 #import "keychain/ckks/CloudKitCategories.h"
 #import "keychain/categories/NSError+UsefulConstructors.h"
 #import "keychain/ckks/tests/MockCloudKit.h"
 #import "keychain/ckks/CloudKitCategories.h"
 #import "keychain/categories/NSError+UsefulConstructors.h"
 #import "keychain/ckks/tests/MockCloudKit.h"
+#import "keychain/ot/OTManager.h"
 
 @interface CKKSCloudKitTests : XCTestCase
 
 
 @interface CKKSCloudKitTests : XCTestCase
 
                                                                                                               nsdistributednotificationCenterClass:[NSDistributedNotificationCenter class]
                                                                                                                                      notifierClass:[FakeCKKSNotifier class]];
 
                                                                                                               nsdistributednotificationCenterClass:[NSDistributedNotificationCenter class]
                                                                                                                                      notifierClass:[FakeCKKSNotifier class]];
 
-    CKKSViewManager* manager = [[CKKSViewManager alloc] initWithContainerName:containerName
-                                                                       usePCS:SecCKKSContainerUsePCS
-                                                                   sosAdapter:nil
-                                                    cloudKitClassDependencies:cloudKitClassDependencies];
-    [CKKSViewManager resetManager:false setTo:manager];
+    CKContainer* container = [CKKSViewManager makeCKContainer:SecCKKSContainerName usePCS:SecCKKSContainerUsePCS];
+    CKKSAccountStateTracker* accountStateTracker = [[CKKSAccountStateTracker alloc] init:container
+                                                               nsnotificationCenterClass:cloudKitClassDependencies.nsnotificationCenterClass];
+
+    CKKSLockStateTracker* lockStateTracker = [[CKKSLockStateTracker alloc] init];
+
+    CKKSViewManager* manager = [[CKKSViewManager alloc] initWithContainer:container
+                                                               sosAdapter:nil
+                                                      accountStateTracker:accountStateTracker
+                                                         lockStateTracker:lockStateTracker
+                                                cloudKitClassDependencies:cloudKitClassDependencies];
+    // No longer a supported mechanism:
+    //[CKKSViewManager resetManager:false setTo:manager];
+    (void)manager;
 
     // Make a new fake keychain
     NSString* smallName = [self.name componentsSeparatedByString:@" "][1];
 
     // Make a new fake keychain
     NSString* smallName = [self.name componentsSeparatedByString:@" "][1];
diff --git a/keychain/ckks/tests/CKKSDispatchTests.m b/keychain/ckks/tests/CKKSDispatchTests.m
deleted file mode 100644 (file)
index eff091f..0000000
+++ /dev/null
@@ -1,249 +0,0 @@
-/*
- * Copyright (c) 2017 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@
- */
-
-#include <dispatch/dispatch.h>
-#import <XCTest/XCTest.h>
-#import "keychain/ckks/CKKSNearFutureScheduler.h"
-
-@interface CKKSNearFutureSchedulerTests : XCTestCase
-
-@end
-
-@implementation CKKSNearFutureSchedulerTests
-
-- (void)setUp {
-    [super setUp];
-}
-
-- (void)tearDown {
-    [super tearDown];
-}
-
-- (void)testOneShot {
-    XCTestExpectation *expectation = [self expectationWithDescription:@"FutureScheduler fired"];
-
-    CKKSNearFutureScheduler* scheduler = [[CKKSNearFutureScheduler alloc] initWithName: @"test" delay:50*NSEC_PER_MSEC keepProcessAlive:true block:^{
-        [expectation fulfill];
-    }];
-
-    [scheduler trigger];
-
-    [self waitForExpectationsWithTimeout:1 handler:nil];
-}
-
-- (void)testOneShotDelay {
-    XCTestExpectation *toofastexpectation = [self expectationWithDescription:@"FutureScheduler fired (too soon)"];
-    toofastexpectation.inverted = YES;
-
-    XCTestExpectation *expectation = [self expectationWithDescription:@"FutureScheduler fired"];
-
-    CKKSNearFutureScheduler* scheduler = [[CKKSNearFutureScheduler alloc] initWithName: @"test" delay: 200*NSEC_PER_MSEC keepProcessAlive:false block:^{
-        [toofastexpectation fulfill];
-        [expectation fulfill];
-    }];
-
-    [scheduler trigger];
-
-    // Make sure it waits at least 0.1 seconds
-    [self waitForExpectations: @[toofastexpectation] timeout:0.1];
-
-    // But finishes within 1.1s (total)
-    [self waitForExpectations: @[expectation] timeout:1];
-}
-
-- (void)testOneShotManyTrigger {
-    XCTestExpectation *toofastexpectation = [self expectationWithDescription:@"FutureScheduler fired (too soon)"];
-    toofastexpectation.inverted = YES;
-
-    XCTestExpectation *expectation = [self expectationWithDescription:@"FutureScheduler fired"];
-    expectation.assertForOverFulfill = YES;
-
-    CKKSNearFutureScheduler* scheduler = [[CKKSNearFutureScheduler alloc] initWithName: @"test" delay: 200*NSEC_PER_MSEC keepProcessAlive:true block:^{
-        [toofastexpectation fulfill];
-        [expectation fulfill];
-    }];
-
-    [scheduler trigger];
-    [scheduler trigger];
-    [scheduler trigger];
-    [scheduler trigger];
-    [scheduler trigger];
-    [scheduler trigger];
-    [scheduler trigger];
-    [scheduler trigger];
-
-    // Make sure it waits at least 0.1 seconds
-    [self waitForExpectations: @[toofastexpectation] timeout:0.1];
-
-    // But finishes within .6s (total)
-    [self waitForExpectations: @[expectation] timeout:0.5];
-
-    // Ensure we don't get called again in the next 0.3 s
-    XCTestExpectation* waitmore = [self expectationWithDescription:@"waiting"];
-    waitmore.inverted = YES;
-    [self waitForExpectations: @[waitmore] timeout: 0.3];
-}
-
-
-- (void)testMultiShot {
-    XCTestExpectation *first = [self expectationWithDescription:@"FutureScheduler fired (one)"];
-    first.assertForOverFulfill = NO;
-
-    XCTestExpectation *second = [self expectationWithDescription:@"FutureScheduler fired (two)"];
-    second.expectedFulfillmentCount = 2;
-    second.assertForOverFulfill = YES;
-
-    CKKSNearFutureScheduler* scheduler = [[CKKSNearFutureScheduler alloc] initWithName: @"test" delay: 100*NSEC_PER_MSEC keepProcessAlive:false block:^{
-        [first fulfill];
-        [second fulfill];
-    }];
-
-    [scheduler trigger];
-
-    [self waitForExpectations: @[first] timeout:0.2];
-
-    [scheduler trigger];
-    [scheduler trigger];
-    [scheduler trigger];
-
-    [self waitForExpectations: @[second] timeout:0.2];
-
-    XCTestExpectation* waitmore = [self expectationWithDescription:@"waiting"];
-    waitmore.inverted = YES;
-    [self waitForExpectations: @[waitmore] timeout: 0.2];
-}
-
-- (void)testMultiShotDelays {
-    XCTestExpectation *first = [self expectationWithDescription:@"FutureScheduler fired (one)"];
-    first.assertForOverFulfill = NO;
-
-    XCTestExpectation *longdelay = [self expectationWithDescription:@"FutureScheduler fired (long delay expectation)"];
-    longdelay.inverted = YES;
-    longdelay.expectedFulfillmentCount = 2;
-
-    XCTestExpectation *second = [self expectationWithDescription:@"FutureScheduler fired (two)"];
-    second.expectedFulfillmentCount = 2;
-    second.assertForOverFulfill = YES;
-
-    CKKSNearFutureScheduler* scheduler = [[CKKSNearFutureScheduler alloc] initWithName: @"test" initialDelay: 50*NSEC_PER_MSEC continuingDelay:300*NSEC_PER_MSEC keepProcessAlive:false block:^{
-        [first fulfill];
-        [longdelay fulfill];
-        [second fulfill];
-    }];
-
-    [scheduler trigger];
-
-    [self waitForExpectations: @[first] timeout:0.2];
-
-    [scheduler trigger];
-    [scheduler trigger];
-    [scheduler trigger];
-
-    // longdelay should NOT be fulfilled twice in the first 0.3 seconds
-    [self waitForExpectations: @[longdelay] timeout:0.2];
-
-    // But second should be fulfilled in the first 0.8 seconds
-    [self waitForExpectations: @[second] timeout:0.5];
-
-    XCTestExpectation* waitmore = [self expectationWithDescription:@"waiting"];
-    waitmore.inverted = YES;
-    [self waitForExpectations: @[waitmore] timeout: 0.2];
-}
-
-- (void)testCancel {
-    XCTestExpectation *cancelexpectation = [self expectationWithDescription:@"FutureScheduler fired (after cancel)"];
-    cancelexpectation.inverted = YES;
-
-    CKKSNearFutureScheduler* scheduler = [[CKKSNearFutureScheduler alloc] initWithName: @"test" delay: 100*NSEC_PER_MSEC keepProcessAlive:true block:^{
-        [cancelexpectation fulfill];
-    }];
-
-    [scheduler trigger];
-    [scheduler cancel];
-
-    // Make sure it does not fire in 0.5 s
-    [self waitForExpectations: @[cancelexpectation] timeout:0.2];
-}
-
-- (void)testDelayedNoShot {
-    XCTestExpectation *toofastexpectation = [self expectationWithDescription:@"FutureScheduler fired (too soon)"];
-    toofastexpectation.inverted = YES;
-
-    CKKSNearFutureScheduler* scheduler = [[CKKSNearFutureScheduler alloc] initWithName: @"test" delay: 10*NSEC_PER_MSEC keepProcessAlive:false block:^{
-        [toofastexpectation fulfill];
-    }];
-
-    // Tell the scheduler to wait, but don't trigger it. It shouldn't fire.
-    [scheduler waitUntil: 50*NSEC_PER_MSEC];
-
-    [self waitForExpectations: @[toofastexpectation] timeout:0.1];
-}
-
-- (void)testDelayedOneShot {
-    XCTestExpectation *first = [self expectationWithDescription:@"FutureScheduler fired (one)"];
-    first.assertForOverFulfill = NO;
-
-    XCTestExpectation *toofastexpectation = [self expectationWithDescription:@"FutureScheduler fired (too soon)"];
-    toofastexpectation.inverted = YES;
-
-    CKKSNearFutureScheduler* scheduler = [[CKKSNearFutureScheduler alloc] initWithName: @"test" delay: 10*NSEC_PER_MSEC keepProcessAlive:false block:^{
-        [first fulfill];
-        [toofastexpectation fulfill];
-    }];
-
-    [scheduler waitUntil: 150*NSEC_PER_MSEC];
-    [scheduler trigger];
-
-    [self waitForExpectations: @[toofastexpectation] timeout:0.1];
-    [self waitForExpectations: @[first] timeout:0.2];
-}
-
-- (void)testDelayedMultiShot {
-    XCTestExpectation *first = [self expectationWithDescription:@"FutureScheduler fired (one)"];
-    first.assertForOverFulfill = NO;
-
-    XCTestExpectation *toofastexpectation = [self expectationWithDescription:@"FutureScheduler fired (too soon)"];
-    toofastexpectation.expectedFulfillmentCount = 2;
-    toofastexpectation.inverted = YES;
-
-    XCTestExpectation *second = [self expectationWithDescription:@"FutureScheduler fired (two)"];
-    second.expectedFulfillmentCount = 2;
-    second.assertForOverFulfill = YES;
-
-    CKKSNearFutureScheduler* scheduler = [[CKKSNearFutureScheduler alloc] initWithName: @"test" delay: 10*NSEC_PER_MSEC keepProcessAlive:false block:^{
-        [first fulfill];
-        [second fulfill];
-        [toofastexpectation fulfill];
-    }];
-
-    [scheduler trigger];
-    [self waitForExpectations: @[first] timeout:0.2];
-
-    [scheduler waitUntil: 150*NSEC_PER_MSEC];
-    [scheduler trigger];
-
-    [self waitForExpectations: @[toofastexpectation] timeout:0.1];
-    [self waitForExpectations: @[second] timeout:0.3];
-}
-
-@end
index 7fcff165e28aaa3f8adbdd8a40fab64df1eca835..4eb485201408c9dd5bff27714b3392b2025ab844 100644 (file)
@@ -16,6 +16,8 @@ NS_ASSUME_NONNULL_BEGIN
 @property bool excludeSelfPeerFromTrustSet;
 
 @property SOSCCStatus circleStatus;
 @property bool excludeSelfPeerFromTrustSet;
 
 @property SOSCCStatus circleStatus;
+@property (nullable) NSError* circleStatusError;
+
 @property CKKSSOSSelfPeer* selfPeer;
 @property NSMutableSet<id<CKKSSOSPeerProtocol>>* trustedPeers;
 
 @property CKKSSOSSelfPeer* selfPeer;
 @property NSMutableSet<id<CKKSSOSPeerProtocol>>* trustedPeers;
 
index ec18e233052b58bd6d23b91d5928e084da0d7e90..7f8077ec55850dc90c1cbfd7be012c263e24c7e7 100644 (file)
@@ -38,7 +38,8 @@
 {
     if(!self.sosEnabled || self.circleStatus == kSOSCCError) {
         if(error && self.circleStatus == kSOSCCError) {
 {
     if(!self.sosEnabled || self.circleStatus == kSOSCCError) {
         if(error && self.circleStatus == kSOSCCError) {
-            *error = [NSError errorWithDomain:(__bridge NSString*)kSOSErrorDomain code:self.circleStatus userInfo:nil];
+            // I'm not at all sure that the second error here actually is any error in particular
+            *error = self.circleStatusError ?: [NSError errorWithDomain:(__bridge NSString*)kSOSErrorDomain code:self.circleStatus userInfo:nil];
         }
         return kSOSCCError;
     }
         }
         return kSOSCCError;
     }
index 8bd89e46aefcbe5cbe065a679cad8b2d6f926d17..827a5c83a13fd3d2ec78989b696ab5609d8bd6a7 100644 (file)
     XCTAssertEqual       (zse.ckzonesubscribed,   loaded.ckzonesubscribed,    "ckzonesubscribed persisted through db save and load");
     XCTAssertEqualObjects(zse.encodedChangeToken, loaded.encodedChangeToken, "encodedChangeToken persisted through db save and load");
 
     XCTAssertEqual       (zse.ckzonesubscribed,   loaded.ckzonesubscribed,    "ckzonesubscribed persisted through db save and load");
     XCTAssertEqualObjects(zse.encodedChangeToken, loaded.encodedChangeToken, "encodedChangeToken persisted through db save and load");
 
-    XCTAssert([[NSCalendar currentCalendar] isDate:zse.lastFetchTime equalToDate: loaded.lastFetchTime toUnitGranularity:NSCalendarUnitSecond],
+    secnotice("ckkstests", "zse.lastFetchTime: %@", zse.lastFetchTime);
+    secnotice("ckkstests", "loaded.lastFetchTime: %@", loaded.lastFetchTime);
+
+    secnotice("ckkstests", "equal?: %d", [zse.lastFetchTime isEqualToDate:loaded.lastFetchTime]);
+    secnotice("ckkstests", "equal to seconds?: %d", [[NSCalendar currentCalendar] isDate:zse.lastFetchTime equalToDate: loaded.lastFetchTime toUnitGranularity:NSCalendarUnitSecond]);
+
+    // We only compare to the minute level, as that's enough to test the save+load.
+    XCTAssert([[NSCalendar currentCalendar] isDate:zse.lastFetchTime equalToDate: loaded.lastFetchTime toUnitGranularity:NSCalendarUnitMinute],
                                                                    "lastFetchTime persisted through db save and load");
 }
 
                                                                    "lastFetchTime persisted through db save and load");
 }
 
     XCTAssertTrue([wrappedKey saveToDatabase: &error], "key saved to database");
     XCTAssertNil(error, "no error saving key to database");
 
     XCTAssertTrue([wrappedKey saveToDatabase: &error], "key saved to database");
     XCTAssertNil(error, "no error saving key to database");
 
+    NSString* secondUUID = @"8b2aeb7f-0000-0000-0000-70d5c728ebf7";
+    CKKSKey* secondtlk = [[CKKSKey alloc] initSelfWrappedWithAESKey:[[CKKSAESSIVKey alloc] initWithBase64: @"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="]
+                                                               uuid:secondUUID
+                                                           keyclass:SecCKKSKeyClassTLK
+                                                              state:SecCKKSProcessedStateLocal
+                                                             zoneID:self.testZoneID
+                                                    encodedCKRecord:testCKRecord
+                                                         currentkey:true];
+    XCTAssertTrue([secondtlk saveToDatabase: &error], "Second TLK saved to database");
+    XCTAssertNil(error, "no error saving TLK to database");
+
     NSArray<CKKSKey*>* tlks = [CKKSKey allWhere: @{@"UUID": @"8b2aeb7f-4af3-43e9-b6e6-70d5c728ebf7"} error: &error];
     XCTAssertNotNil(tlks, "Returned some array from allWhere");
     XCTAssertNil(error, "no error back from allWhere");
     XCTAssertEqual([tlks count], 1ul, "Received one row (and expected one row)");
 
     NSArray<CKKSKey*>* tlks = [CKKSKey allWhere: @{@"UUID": @"8b2aeb7f-4af3-43e9-b6e6-70d5c728ebf7"} error: &error];
     XCTAssertNotNil(tlks, "Returned some array from allWhere");
     XCTAssertNil(error, "no error back from allWhere");
     XCTAssertEqual([tlks count], 1ul, "Received one row (and expected one row)");
 
-    NSArray<CKKSKey*>* selfWrapped = [CKKSKey allWhere: @{@"parentKeyUUID": [CKKSSQLWhereObject op:@"=" string:@"uuid"]} error: &error];
+    NSArray<CKKSKey*>* selfWrapped = [CKKSKey allWhere: @{@"parentKeyUUID": [CKKSSQLWhereColumn op:CKKSSQLWhereComparatorEquals column:CKKSSQLWhereColumnNameUUID]} error: &error];
     XCTAssertNotNil(selfWrapped, "Returned some array from allWhere");
     XCTAssertNil(error, "no error back from allWhere");
     XCTAssertNotNil(selfWrapped, "Returned some array from allWhere");
     XCTAssertNil(error, "no error back from allWhere");
-    XCTAssertEqual([selfWrapped count], 1ul, "Received one row (and expected one row)");
+    XCTAssertEqual([selfWrapped count], 2ul, "Should have recievied two rows");
 
 
-    // Try using CKKSSQLWhereObject alongside normal binds
-    NSArray<CKKSKey*>* selfWrapped2 = [CKKSKey allWhere: @{@"parentKeyUUID": [CKKSSQLWhereObject op:@"=" string:@"uuid"],
+    // Try using CKKSSQLWhereColumn alongside normal binds
+    NSArray<CKKSKey*>* selfWrapped2 = [CKKSKey allWhere: @{@"parentKeyUUID": [CKKSSQLWhereColumn op:CKKSSQLWhereComparatorEquals column:CKKSSQLWhereColumnNameUUID],
                                                            @"uuid": @"8b2aeb7f-4af3-43e9-b6e6-70d5c728ebf7"}
                                                   error: &error];
     XCTAssertNotNil(selfWrapped2, "Returned some array from allWhere");
     XCTAssertNil(error, "no error back from allWhere");
     XCTAssertEqual([selfWrapped2 count], 1ul, "Received one row (and expected one row)");
                                                            @"uuid": @"8b2aeb7f-4af3-43e9-b6e6-70d5c728ebf7"}
                                                   error: &error];
     XCTAssertNotNil(selfWrapped2, "Returned some array from allWhere");
     XCTAssertNil(error, "no error back from allWhere");
     XCTAssertEqual([selfWrapped2 count], 1ul, "Received one row (and expected one row)");
+    XCTAssertEqualObjects([selfWrapped2[0] uuid], @"8b2aeb7f-4af3-43e9-b6e6-70d5c728ebf7", "Should received first TLK UUID");
+
+    NSArray<CKKSKey*>* selfWrapped3 = [CKKSKey allWhere: @{@"parentKeyUUID": [CKKSSQLWhereColumn op:CKKSSQLWhereComparatorEquals column:CKKSSQLWhereColumnNameUUID],
+                                                           @"uuid": [CKKSSQLWhereValue op:CKKSSQLWhereComparatorNotEquals value:@"8b2aeb7f-4af3-43e9-b6e6-70d5c728ebf7"]}
+                                                  error: &error];
+    XCTAssertNotNil(selfWrapped3, "Returned some array from allWhere");
+    XCTAssertNil(error, "no error back from allWhere");
+    XCTAssertEqual([selfWrapped3 count], 1ul, "Should have received one rows");
+    XCTAssertEqualObjects([selfWrapped3[0] uuid], secondUUID, "Should received second TLK UUID");
 }
 
 - (void)testGroupBy {
 }
 
 - (void)testGroupBy {
     while(count == 0 || uuid != nil) {
         uuid = nil;
         [CKKSSQLDatabaseObject queryDatabaseTable: [CKKSOutgoingQueueEntry sqlTable]
     while(count == 0 || uuid != nil) {
         uuid = nil;
         [CKKSSQLDatabaseObject queryDatabaseTable: [CKKSOutgoingQueueEntry sqlTable]
-                                            where: lastUUID ? @{@"UUID": [CKKSSQLWhereObject op:@">" stringValue:lastUUID]} : nil
+                                            where: lastUUID ? @{@"UUID": [CKKSSQLWhereValue op:CKKSSQLWhereComparatorGreaterThan value:lastUUID]} : nil
                                           columns: @[@"action", @"UUID"]
                                           groupBy:nil
                                           orderBy:@[@"uuid"]
                                           columns: @[@"action", @"UUID"]
                                           groupBy:nil
                                           orderBy:@[@"uuid"]
index 0b8ad45e155da424f01f12c35013722ab536f542..10235c423cdd612e8ec90ce389fc451210dab21d 100644 (file)
@@ -69,6 +69,7 @@
               (id)kSecAttrAccount : account,
               (id)kSecAttrSynchronizable : (id)kCFBooleanTrue,
               (id)kSecValueData : data,
               (id)kSecAttrAccount : account,
               (id)kSecAttrSynchronizable : (id)kCFBooleanTrue,
               (id)kSecValueData : data,
+              (id)kSecAttrSyncViewHint : self.keychainView.zoneName,
               (id)kSecAttrDeriveSyncIDFromItemAttributes : (id)kCFBooleanTrue,
               (id)kSecAttrPCSPlaintextServiceIdentifier : serviceIdentifier,
               (id)kSecAttrPCSPlaintextPublicKey : publicKey,
               (id)kSecAttrDeriveSyncIDFromItemAttributes : (id)kCFBooleanTrue,
               (id)kSecAttrPCSPlaintextServiceIdentifier : serviceIdentifier,
               (id)kSecAttrPCSPlaintextPublicKey : publicKey,
                                     (id)kSecAttrAccessible: (id)kSecAttrAccessibleAfterFirstUnlock,
                                     (id)kSecAttrAccount : @"testaccount",
                                     (id)kSecAttrSynchronizable : (id)kCFBooleanTrue,
                                     (id)kSecAttrAccessible: (id)kSecAttrAccessibleAfterFirstUnlock,
                                     (id)kSecAttrAccount : @"testaccount",
                                     (id)kSecAttrSynchronizable : (id)kCFBooleanTrue,
+                                    (id)kSecAttrSyncViewHint : self.keychainView.zoneName,
                                     (id)kSecValueData : (id) [@"asdf" dataUsingEncoding:NSUTF8StringEncoding],
                                     } mutableCopy];
 
                                     (id)kSecValueData : (id) [@"asdf" dataUsingEncoding:NSUTF8StringEncoding],
                                     } mutableCopy];
 
         [blockExpectation fulfill];
     }), @"_SecItemAddAndNotifyOnSync succeeded");
 
         [blockExpectation fulfill];
     }), @"_SecItemAddAndNotifyOnSync succeeded");
 
+    OCMVerifyAllWithDelay(self.mockDatabase, 10);
+
     [self waitForExpectationsWithTimeout:5.0 handler:nil];
 }
 
     [self waitForExpectationsWithTimeout:5.0 handler:nil];
 }
 
         (id)kSecAttrAccessible: (id)kSecAttrAccessibleAfterFirstUnlock,
         (id)kSecAttrAccount : @"testaccount",
         (id)kSecAttrSynchronizable : (id)kCFBooleanTrue,
         (id)kSecAttrAccessible: (id)kSecAttrAccessibleAfterFirstUnlock,
         (id)kSecAttrAccount : @"testaccount",
         (id)kSecAttrSynchronizable : (id)kCFBooleanTrue,
+        (id)kSecAttrSyncViewHint : self.keychainView.zoneName,
         (id)kSecAttrPCSPlaintextPublicKey : [@"asdf" dataUsingEncoding:NSUTF8StringEncoding],
         (id)kSecValueData : (id) [@"asdf" dataUsingEncoding:NSUTF8StringEncoding],
     } mutableCopy];
         (id)kSecAttrPCSPlaintextPublicKey : [@"asdf" dataUsingEncoding:NSUTF8StringEncoding],
         (id)kSecValueData : (id) [@"asdf" dataUsingEncoding:NSUTF8StringEncoding],
     } mutableCopy];
                                     (id)kSecAttrAccessible: (id)kSecAttrAccessibleAfterFirstUnlock,
                                     (id)kSecAttrAccount : @"testaccount",
                                     (id)kSecAttrSynchronizable : (id)kCFBooleanTrue,
                                     (id)kSecAttrAccessible: (id)kSecAttrAccessibleAfterFirstUnlock,
                                     (id)kSecAttrAccount : @"testaccount",
                                     (id)kSecAttrSynchronizable : (id)kCFBooleanTrue,
+                                    (id)kSecAttrSyncViewHint : self.keychainView.zoneName,
                                     (id)kSecValueData : (id) [@"asdf" dataUsingEncoding:NSUTF8StringEncoding],
                                     (id)kSecAttrSyncViewHint : self.keychainView.zoneName, // @ fake view hint for fake view
                                     } mutableCopy];
                                     (id)kSecValueData : (id) [@"asdf" dataUsingEncoding:NSUTF8StringEncoding],
                                     (id)kSecAttrSyncViewHint : self.keychainView.zoneName, // @ fake view hint for fake view
                                     } mutableCopy];
                                     (id)kSecAttrAccessible: (id)kSecAttrAccessibleAfterFirstUnlock,
                                     (id)kSecAttrAccount : @"testaccount",
                                     (id)kSecAttrSynchronizable : (id)kCFBooleanTrue,
                                     (id)kSecAttrAccessible: (id)kSecAttrAccessibleAfterFirstUnlock,
                                     (id)kSecAttrAccount : @"testaccount",
                                     (id)kSecAttrSynchronizable : (id)kCFBooleanTrue,
+                                    (id)kSecAttrSyncViewHint : self.keychainView.zoneName,
                                     (id)kSecValueData : (id) [@"asdf" dataUsingEncoding:NSUTF8StringEncoding],
                                     } mutableCopy];
 
                                     (id)kSecValueData : (id) [@"asdf" dataUsingEncoding:NSUTF8StringEncoding],
                                     } mutableCopy];
 
                                     (id)kSecAttrAccessible: (id)kSecAttrAccessibleAfterFirstUnlock,
                                     (id)kSecAttrAccount : @"testaccount",
                                     (id)kSecAttrSynchronizable : (id)kCFBooleanTrue,
                                     (id)kSecAttrAccessible: (id)kSecAttrAccessibleAfterFirstUnlock,
                                     (id)kSecAttrAccount : @"testaccount",
                                     (id)kSecAttrSynchronizable : (id)kCFBooleanTrue,
+                                    (id)kSecAttrSyncViewHint : self.keychainView.zoneName,
                                     (id)kSecValueData : (id) [@"asdf" dataUsingEncoding:NSUTF8StringEncoding],
                                     } mutableCopy];
 
                                     (id)kSecValueData : (id) [@"asdf" dataUsingEncoding:NSUTF8StringEncoding],
                                     } mutableCopy];
 
     [self waitForExpectationsWithTimeout:5.0 handler:nil];
 }
 
     [self waitForExpectationsWithTimeout:5.0 handler:nil];
 }
 
+- (void)testAddAndNotifyOnSyncBeforePolicyLoaded {
+    [self putFakeKeyHierarchyInCloudKit:self.keychainZoneID];
+    [self saveTLKMaterialToKeychain:self.keychainZoneID];
+    [self expectCKKSTLKSelfShareUpload:self.keychainZoneID];
+
+    [self startCKKSSubsystem];
+
+    // Allow CKKS to spin up fully
+    XCTAssertEqual(0, [self.keychainView.keyHierarchyConditions[SecCKKSZoneKeyStateReady] wait:20*NSEC_PER_SEC], "CKKS entered ready");
+
+    [self.keychainView waitForOperationsOfClass:[CKKSScanLocalItemsOperation class]];
+    [self.keychainView waitForOperationsOfClass:[CKKSOutgoingQueueOperation class]];
+
+    // Simulate a daemon restart
+    self.automaticallyBeginCKKSViewCloudKitOperation = false;
+    [self.injectedManager resetSyncingPolicy];
+    [self.injectedManager haltZone:self.keychainZoneID.zoneName];
+
+    // Issue the query (to simulate the query starting securityd)
+    NSMutableDictionary* query = [@{
+                                    (id)kSecClass : (id)kSecClassGenericPassword,
+                                    (id)kSecAttrAccessGroup : @"com.apple.security.ckks",
+                                    (id)kSecAttrAccessible: (id)kSecAttrAccessibleAfterFirstUnlock,
+                                    (id)kSecAttrAccount : @"testaccount",
+                                    (id)kSecAttrSynchronizable : (id)kCFBooleanTrue,
+                                    (id)kSecAttrSyncViewHint : self.keychainView.zoneName,
+                                    (id)kSecValueData : (id) [@"asdf" dataUsingEncoding:NSUTF8StringEncoding],
+                                    } mutableCopy];
+
+    XCTestExpectation* blockExpectation = [self expectationWithDescription: @"callback occurs"];
+
+    XCTAssertEqual(errSecSuccess, _SecItemAddAndNotifyOnSync((__bridge CFDictionaryRef) query, NULL, ^(bool didSync, CFErrorRef error) {
+        XCTAssertTrue(didSync, "Item synced");
+        XCTAssertNil((__bridge NSError*)error, "Shouldn't have received an error syncing item");
+
+        [blockExpectation fulfill];
+    }), @"_SecItemAddAndNotifyOnSync succeeded");
+
+    // When the policy is loaded, the item should upload and the callback should fire
+    [self expectCKModifyItemRecords:1 currentKeyPointerRecords:1 zoneID:self.keychainZoneID];
+
+    [self.injectedManager setSyncingViews:self.managedViewList sortingPolicy:self.viewSortingPolicyForManagedViewList];
+    self.keychainView = [self.injectedManager findView:self.keychainZoneID.zoneName];
+
+    [self.injectedManager beginCloudKitOperationOfAllViews];
+    [self beginSOSTrustedViewOperation:self.keychainView];
+
+    XCTAssertEqual(0, [self.keychainView.keyHierarchyConditions[SecCKKSZoneKeyStateReady] wait:20*NSEC_PER_SEC], @"Should have reached key state 'ready'");
+    OCMVerifyAllWithDelay(self.mockDatabase, 20);
+
+    [self waitForExpectations:@[blockExpectation] timeout:5];
+}
+
 - (void)testPCSUnencryptedFieldsAdd {
     [self createAndSaveFakeKeyHierarchy: self.keychainZoneID]; // Make life easy for this test.
 
 - (void)testPCSUnencryptedFieldsAdd {
     [self createAndSaveFakeKeyHierarchy: self.keychainZoneID]; // Make life easy for this test.
 
index c75ef74c027f7e6bd7928a94fdeafa7d4d0154f2..e2b7c7537c09847304e12eed0d7d72ed2e07cc55 100644 (file)
 
     // Check that the record is where we expect it in CloudKit
     [self waitForCKModifications];
 
     // Check that the record is where we expect it in CloudKit
     [self waitForCKModifications];
-    CKRecordID* pcsItemRecordID = [[CKRecordID alloc] initWithRecordName: @"DD7C2F9B-B22D-3B90-C299-E3B48174BFA3" zoneID:self.keychainZoneID];
+    CKRecordID* pcsItemRecordID = [[CKRecordID alloc] initWithRecordName: @"50184A35-4480-E8BA-769B-567CF72F1EC0" zoneID:self.keychainZoneID];
     CKRecord* record = self.keychainZone.currentDatabase[pcsItemRecordID];
     XCTAssertNotNil(record, "Found record in CloudKit at expected UUID");
 
     CKRecord* record = self.keychainZone.currentDatabase[pcsItemRecordID];
     XCTAssertNotNil(record, "Found record in CloudKit at expected UUID");
 
 
     // Check that the record is where we expect it
     [self waitForCKModifications];
 
     // Check that the record is where we expect it
     [self waitForCKModifications];
-    CKRecordID* pcsOtherItemRecordID = [[CKRecordID alloc] initWithRecordName: @"878BEAA6-1EE9-1079-1025-E6832AC8F2F3" zoneID:self.keychainZoneID];
+    CKRecordID* pcsOtherItemRecordID = [[CKRecordID alloc] initWithRecordName: @"2DEA6136-2505-6BFD-E3E8-B44A6E39C3B5" zoneID:self.keychainZoneID];
     CKRecord* recordOther = self.keychainZone.currentDatabase[pcsOtherItemRecordID];
     XCTAssertNotNil(recordOther, "Found other record in CloudKit at expected UUID");
 
     CKRecord* recordOther = self.keychainZone.currentDatabase[pcsOtherItemRecordID];
     XCTAssertNotNil(recordOther, "Found other record in CloudKit at expected UUID");
 
 
     // Check that the record is where we expect it in CloudKit
     [self waitForCKModifications];
 
     // Check that the record is where we expect it in CloudKit
     [self waitForCKModifications];
-    CKRecordID* pcsItemRecordID = [[CKRecordID alloc] initWithRecordName: @"DD7C2F9B-B22D-3B90-C299-E3B48174BFA3" zoneID:self.keychainZoneID];
+    CKRecordID* pcsItemRecordID = [[CKRecordID alloc] initWithRecordName: @"50184A35-4480-E8BA-769B-567CF72F1EC0" zoneID:self.keychainZoneID];
     CKRecord* record = self.keychainZone.currentDatabase[pcsItemRecordID];
     XCTAssertNotNil(record, "Found record in CloudKit at expected UUID");
 
     CKRecord* record = self.keychainZone.currentDatabase[pcsItemRecordID];
     XCTAssertNotNil(record, "Found record in CloudKit at expected UUID");
 
 
     // Check that the records are where we expect them in CloudKit
     [self waitForCKModifications];
 
     // Check that the records are where we expect them in CloudKit
     [self waitForCKModifications];
-    CKRecordID* pcsItemRecordID = [[CKRecordID alloc] initWithRecordName: @"DD7C2F9B-B22D-3B90-C299-E3B48174BFA3" zoneID:self.keychainZoneID];
+    CKRecordID* pcsItemRecordID = [[CKRecordID alloc] initWithRecordName: @"50184A35-4480-E8BA-769B-567CF72F1EC0" zoneID:self.keychainZoneID];
     CKRecord* record = self.keychainZone.currentDatabase[pcsItemRecordID];
     XCTAssertNotNil(record, "Found record in CloudKit at expected UUID");
 
     CKRecord* record = self.keychainZone.currentDatabase[pcsItemRecordID];
     XCTAssertNotNil(record, "Found record in CloudKit at expected UUID");
 
-    CKRecordID* pcsItemRecordID2 = [[CKRecordID alloc] initWithRecordName: @"3AB8E78D-75AF-CFEF-F833-FA3E3E90978A" zoneID:self.keychainZoneID];
+    CKRecordID* pcsItemRecordID2 = [[CKRecordID alloc] initWithRecordName: @"10E76B80-CE1C-A52A-B0CB-462A2EBA05AF" zoneID:self.keychainZoneID];
     CKRecord* record2 = self.keychainZone.currentDatabase[pcsItemRecordID2];
     XCTAssertNotNil(record2, "Found 2nd record in CloudKit at expected UUID");
 
     CKRecord* record2 = self.keychainZone.currentDatabase[pcsItemRecordID2];
     XCTAssertNotNil(record2, "Found 2nd record in CloudKit at expected UUID");
 
 
     // Another machine comes along and updates the pointer!
     CKKSCurrentItemPointer* cip = [[CKKSCurrentItemPointer alloc] initForIdentifier:@"com.apple.security.ckks-pcsservice"
 
     // Another machine comes along and updates the pointer!
     CKKSCurrentItemPointer* cip = [[CKKSCurrentItemPointer alloc] initForIdentifier:@"com.apple.security.ckks-pcsservice"
-                                                                    currentItemUUID:@"DD7C2F9B-B22D-3B90-C299-E3B48174BFA3"
+                                                                    currentItemUUID:@"50184A35-4480-E8BA-769B-567CF72F1EC0"
                                                                               state:SecCKKSProcessedStateRemote
                                                                              zoneID:self.keychainZoneID
                                                                     encodedCKRecord:nil];
                                                                               state:SecCKKSProcessedStateRemote
                                                                              zoneID:self.keychainZoneID
                                                                     encodedCKRecord:nil];
 
     // Check that the record is where we expect it in CloudKit
     [self waitForCKModifications];
 
     // Check that the record is where we expect it in CloudKit
     [self waitForCKModifications];
-    CKRecordID* pcsItemRecordID = [[CKRecordID alloc] initWithRecordName: @"DD7C2F9B-B22D-3B90-C299-E3B48174BFA3" zoneID:self.keychainZoneID];
+    CKRecordID* pcsItemRecordID = [[CKRecordID alloc] initWithRecordName: @"50184A35-4480-E8BA-769B-567CF72F1EC0" zoneID:self.keychainZoneID];
     CKRecord* record = self.keychainZone.currentDatabase[pcsItemRecordID];
     XCTAssertNotNil(record, "Found record in CloudKit at expected UUID");
 
     CKRecord* record = self.keychainZone.currentDatabase[pcsItemRecordID];
     XCTAssertNotNil(record, "Found record in CloudKit at expected UUID");
 
 
     // Another machine comes along and updates the pointer!
     CKKSCurrentItemPointer* cip = [[CKKSCurrentItemPointer alloc] initForIdentifier:@"com.apple.security.ckks-pcsservice"
 
     // Another machine comes along and updates the pointer!
     CKKSCurrentItemPointer* cip = [[CKKSCurrentItemPointer alloc] initForIdentifier:@"com.apple.security.ckks-pcsservice"
-                                                                    currentItemUUID:@"DD7C2F9B-B22D-3B90-C299-E3B48174BFA3"
+                                                                    currentItemUUID:@"50184A35-4480-E8BA-769B-567CF72F1EC0"
                                                                               state:SecCKKSProcessedStateRemote
                                                                              zoneID:self.keychainZoneID
                                                                     encodedCKRecord:nil];
                                                                               state:SecCKKSProcessedStateRemote
                                                                              zoneID:self.keychainZoneID
                                                                     encodedCKRecord:nil];
 
     // Check that the record is where we expect it in CloudKit
     [self waitForCKModifications];
 
     // Check that the record is where we expect it in CloudKit
     [self waitForCKModifications];
-    NSString* recordUUID = @"DD7C2F9B-B22D-3B90-C299-E3B48174BFA3";
+    NSString* recordUUID = @"50184A35-4480-E8BA-769B-567CF72F1EC0";
     CKRecordID* pcsItemRecordID = [[CKRecordID alloc] initWithRecordName:recordUUID zoneID:self.keychainZoneID];
     CKRecord* record = self.keychainZone.currentDatabase[pcsItemRecordID];
     XCTAssertNotNil(record, "Found record in CloudKit at expected UUID");
     CKRecordID* pcsItemRecordID = [[CKRecordID alloc] initWithRecordName:recordUUID zoneID:self.keychainZoneID];
     CKRecord* record = self.keychainZone.currentDatabase[pcsItemRecordID];
     XCTAssertNotNil(record, "Found record in CloudKit at expected UUID");
 
     // Check that the record is where we expect it in CloudKit
     [self waitForCKModifications];
 
     // Check that the record is where we expect it in CloudKit
     [self waitForCKModifications];
-    CKRecordID* pcsItemRecordID = [[CKRecordID alloc] initWithRecordName: @"DD7C2F9B-B22D-3B90-C299-E3B48174BFA3" zoneID:self.keychainZoneID];
+    CKRecordID* pcsItemRecordID = [[CKRecordID alloc] initWithRecordName: @"50184A35-4480-E8BA-769B-567CF72F1EC0" zoneID:self.keychainZoneID];
     CKRecord* record = self.keychainZone.currentDatabase[pcsItemRecordID];
     XCTAssertNotNil(record, "Found record in CloudKit at expected UUID");
 
     CKRecord* record = self.keychainZone.currentDatabase[pcsItemRecordID];
     XCTAssertNotNil(record, "Found record in CloudKit at expected UUID");
 
     // Check that the number is on the CKKSMirrorEntry
     [self.keychainView dispatchSync: ^bool {
         NSError* error = nil;
     // Check that the number is on the CKKSMirrorEntry
     [self.keychainView dispatchSync: ^bool {
         NSError* error = nil;
-        CKKSMirrorEntry* ckme = [CKKSMirrorEntry fromDatabase:@"DD7C2F9B-B22D-3B90-C299-E3B48174BFA3" zoneID:self.keychainZoneID error:&error];
+        CKKSMirrorEntry* ckme = [CKKSMirrorEntry fromDatabase:@"50184A35-4480-E8BA-769B-567CF72F1EC0" zoneID:self.keychainZoneID error:&error];
 
         XCTAssertNil(error, "no error fetching ckme");
         XCTAssertNotNil(ckme, "Received a ckme");
 
         XCTAssertNil(error, "no error fetching ckme");
         XCTAssertNotNil(ckme, "Received a ckme");
 
     // Check that the record is where we expect it in CloudKit
     [self waitForCKModifications];
 
     // Check that the record is where we expect it in CloudKit
     [self waitForCKModifications];
-    CKRecordID* pcsItemRecordID = [[CKRecordID alloc] initWithRecordName: @"DD7C2F9B-B22D-3B90-C299-E3B48174BFA3" zoneID:self.keychainZoneID];
+    CKRecordID* pcsItemRecordID = [[CKRecordID alloc] initWithRecordName: @"50184A35-4480-E8BA-769B-567CF72F1EC0" zoneID:self.keychainZoneID];
     CKRecord* record = self.keychainZone.currentDatabase[pcsItemRecordID];
     XCTAssertNotNil(record, "Found record in CloudKit at expected UUID");
 
     CKRecord* record = self.keychainZone.currentDatabase[pcsItemRecordID];
     XCTAssertNotNil(record, "Found record in CloudKit at expected UUID");
 
 
     // Check that the record is where we expect it in CloudKit
     [self waitForCKModifications];
 
     // Check that the record is where we expect it in CloudKit
     [self waitForCKModifications];
-    CKRecordID* pcsItemRecordID = [[CKRecordID alloc] initWithRecordName: @"DD7C2F9B-B22D-3B90-C299-E3B48174BFA3" zoneID:self.keychainZoneID];
+    CKRecordID* pcsItemRecordID = [[CKRecordID alloc] initWithRecordName: @"50184A35-4480-E8BA-769B-567CF72F1EC0" zoneID:self.keychainZoneID];
     CKRecord* record = self.keychainZone.currentDatabase[pcsItemRecordID];
     XCTAssertNotNil(record, "Found record in CloudKit at expected UUID");
 
     CKRecord* record = self.keychainZone.currentDatabase[pcsItemRecordID];
     XCTAssertNotNil(record, "Found record in CloudKit at expected UUID");
 
     XCTAssertNil(error, "Error should be nil parsing base64 item");
 
     item[@"v_Data"] = [@"conflictingdata" dataUsingEncoding:NSUTF8StringEncoding];
     XCTAssertNil(error, "Error should be nil parsing base64 item");
 
     item[@"v_Data"] = [@"conflictingdata" dataUsingEncoding:NSUTF8StringEncoding];
-    CKRecordID* ckrid = [[CKRecordID alloc] initWithRecordName:@"DD7C2F9B-B22D-3B90-C299-E3B48174BFA3" zoneID:self.keychainZoneID];
+    item[@"vwht"] = @"keychain";
+    CKRecordID* ckrid = [[CKRecordID alloc] initWithRecordName:@"50184A35-4480-E8BA-769B-567CF72F1EC0" zoneID:self.keychainZoneID];
     CKRecord* mismatchedRecord = [self newRecord:ckrid withNewItemData:item];
     [self.keychainZone addToZone: mismatchedRecord];
 
     CKRecord* mismatchedRecord = [self newRecord:ckrid withNewItemData:item];
     [self.keychainZone addToZone: mismatchedRecord];
 
index 1d0b619b42f529511d2bfcc9277399b4d0905e33..57207e33a8486f466321ca3f1da738b71d72d1cb 100644 (file)
 @property FakeCKZone*          limitedZone;
 @property (readonly) ZoneKeys* limitedZoneKeys;
 
 @property FakeCKZone*          limitedZone;
 @property (readonly) ZoneKeys* limitedZoneKeys;
 
+@property CKRecordZoneID*      passwordsZoneID;
+@property CKKSKeychainView*    passwordsView;
+@property FakeCKZone*          passwordsZone;
+@property (readonly) ZoneKeys* passwordsZoneKeys;
+
 - (void)saveFakeKeyHierarchiesToLocalDatabase;
 - (void)putFakeDeviceStatusesInCloudKit;
 - (void)putFakeKeyHierachiesInCloudKit;
 - (void)saveFakeKeyHierarchiesToLocalDatabase;
 - (void)putFakeDeviceStatusesInCloudKit;
 - (void)putFakeKeyHierachiesInCloudKit;
index f3179612fe34181f51859b2c5d400975d2024fe3..0823b71d1cd41ee49c7b4e5e34a1e790652e5f51 100644 (file)
@@ -7,6 +7,9 @@
 #import <notify.h>
 
 #include <Security/SecItemPriv.h>
 #import <notify.h>
 
 #include <Security/SecItemPriv.h>
+#import <TrustedPeers/TrustedPeers.h>
+#import <TrustedPeers/TPPBPolicyKeyViewMapping.h>
+#import <TrustedPeers/TPDictionaryMatchingRules.h>
 
 #import "keychain/ckks/tests/CloudKitMockXCTest.h"
 #import "keychain/ckks/tests/CloudKitKeychainSyncingMockXCTest.h"
 
 #import "keychain/ckks/tests/CloudKitMockXCTest.h"
 #import "keychain/ckks/tests/CloudKitKeychainSyncingMockXCTest.h"
@@ -22,6 +25,7 @@
 #import "keychain/ckks/CKKSZoneStateEntry.h"
 #import "keychain/ckks/CKKSManifest.h"
 
 #import "keychain/ckks/CKKSZoneStateEntry.h"
 #import "keychain/ckks/CKKSManifest.h"
 
+#import "keychain/ckks/tests/CKKSTests+MultiZone.h"
 #import "keychain/ckks/tests/MockCloudKit.h"
 
 #pragma clang diagnostic push
 #import "keychain/ckks/tests/MockCloudKit.h"
 
 #pragma clang diagnostic push
 #include <Security/SecKeyPriv.h>
 #pragma clang diagnostic pop
 
 #include <Security/SecKeyPriv.h>
 #pragma clang diagnostic pop
 
-@interface CloudKitKeychainSyncingMultiZoneTestsBase : CloudKitKeychainSyncingMockXCTest
-
-@property CKRecordZoneID*      engramZoneID;
-@property CKKSKeychainView*    engramView;
-@property FakeCKZone*          engramZone;
-@property (readonly) ZoneKeys* engramZoneKeys;
+@interface CloudKitKeychainSyncingMultiZoneTestsBase ()
+@end
 
 
-@property CKRecordZoneID*      manateeZoneID;
-@property CKKSKeychainView*    manateeView;
-@property FakeCKZone*          manateeZone;
-@property (readonly) ZoneKeys* manateeZoneKeys;
+@implementation CloudKitKeychainSyncingMultiZoneTestsBase
++ (void)setUp {
+    SecCKKSEnable();
+    SecCKKSResetSyncing();
+    [super setUp];
+}
 
 
-@property CKRecordZoneID*      autoUnlockZoneID;
-@property CKKSKeychainView*    autoUnlockView;
-@property FakeCKZone*          autoUnlockZone;
-@property (readonly) ZoneKeys* autoUnlockZoneKeys;
+- (NSSet*)managedViewList {
+    NSMutableSet* parentSet = [[super managedViewList] mutableCopy];
+    [parentSet addObject:@"SafariPasswords"];
+    return parentSet;
+}
 
 
-@property CKRecordZoneID*      healthZoneID;
-@property CKKSKeychainView*    healthView;
-@property FakeCKZone*          healthZone;
-@property (readonly) ZoneKeys* healthZoneKeys;
+// Make a policy as normal for most views, but Passwords is special
+- (TPPolicy*)viewSortingPolicyForManagedViewList
+{
+    NSMutableArray<TPPBPolicyKeyViewMapping*>* rules = [NSMutableArray array];
 
 
-@property CKRecordZoneID*      applepayZoneID;
-@property CKKSKeychainView*    applepayView;
-@property FakeCKZone*          applepayZone;
-@property (readonly) ZoneKeys* applepayZoneKeys;
+    for(NSString* viewName in self.managedViewList) {
+        TPPBPolicyKeyViewMapping* mapping = [[TPPBPolicyKeyViewMapping alloc] init];
+        mapping.view = viewName;
 
 
-@property CKRecordZoneID*      homeZoneID;
-@property CKKSKeychainView*    homeView;
-@property FakeCKZone*          homeZone;
-@property (readonly) ZoneKeys* homeZoneKeys;
+        // The real passwords view is on com.appple.cfnetwork, but for these tests, let's just use the sbd agrp (because of how the entitlements are specified)
+        if([viewName isEqualToString:@"SafariPasswords"]) {
+            mapping.matchingRule = [TPDictionaryMatchingRule fieldMatch:@"agrp"
+                                                             fieldRegex:[NSString stringWithFormat:@"^com\\.apple\\.sbd$"]];
+        } else {
+            mapping.matchingRule = [TPDictionaryMatchingRule fieldMatch:@"vwht"
+                                                             fieldRegex:[NSString stringWithFormat:@"^%@$", viewName]];
+        }
 
 
-@property CKRecordZoneID*      limitedZoneID;
-@property CKKSKeychainView*    limitedView;
-@property FakeCKZone*          limitedZone;
-@property (readonly) ZoneKeys* limitedZoneKeys;
+        [rules addObject:mapping];
+    }
 
 
-@end
+    TPPolicy* policy = [TPPolicy policyWithModelToCategory:@[]
+                                          categoriesByView:@{}
+                                     introducersByCategory:@{}
+                                            keyViewMapping:rules
+                                         unknownRedactions:NO
+                                                   version:[[TPPolicyVersion alloc] initWithVersion:1 hash:@"fake-policy-for-views"]];
 
 
-@implementation CloudKitKeychainSyncingMultiZoneTestsBase
-+ (void)setUp {
-    SecCKKSEnable();
-    SecCKKSResetSyncing();
-    [super setUp];
+    return policy;
 }
 
 - (void)setUp {
 }
 
 - (void)setUp {
     XCTAssertNotNil(self.limitedView, "CKKSViewManager created the LimitedPeersAllowed view");
     [self.ckksViews addObject:self.limitedView];
     [self.ckksZones addObject:self.limitedZoneID];
     XCTAssertNotNil(self.limitedView, "CKKSViewManager created the LimitedPeersAllowed view");
     [self.ckksViews addObject:self.limitedView];
     [self.ckksZones addObject:self.limitedZoneID];
+
+    self.passwordsZoneID = [[CKRecordZoneID alloc] initWithZoneName:@"SafariPasswords" ownerName:CKCurrentUserDefaultName];
+    self.passwordsZone = [[FakeCKZone alloc] initZone: self.passwordsZoneID];
+    self.zones[self.passwordsZoneID] = self.passwordsZone;
+    self.passwordsView = [[CKKSViewManager manager] findOrCreateView:@"SafariPasswords"];
+    XCTAssertNotNil(self.passwordsView, "should have a passwords ckks view");
+    XCTAssertNotNil(self.passwordsView, "CKKSViewManager created the Passwords view");
+    [self.ckksViews addObject:self.passwordsView];
+    [self.ckksZones addObject:self.passwordsZoneID];
+
+    // These tests, at least, will use the policy codepaths!
+    [self.injectedManager setOverrideCKKSViewsFromPolicy:YES];
+    [self.injectedManager setSyncingViews:self.managedViewList sortingPolicy:self.viewSortingPolicyForManagedViewList];
 }
 
 + (void)tearDown {
 }
 
 + (void)tearDown {
     [self.homeView waitUntilAllOperationsAreFinished];
     self.homeView = nil;
 
     [self.homeView waitUntilAllOperationsAreFinished];
     self.homeView = nil;
 
+    [self.limitedView halt];
+    [self.limitedView waitUntilAllOperationsAreFinished];
+    self.limitedView = nil;
+
+    [self.passwordsView halt];
+    [self.passwordsView waitUntilAllOperationsAreFinished];
+    self.passwordsView = nil;
+
     [super tearDown];
 }
 
     [super tearDown];
 }
 
     [self putFakeDeviceStatusInCloudKit: self.applepayZoneID];
     [self putFakeDeviceStatusInCloudKit: self.homeZoneID];
     [self putFakeDeviceStatusInCloudKit: self.limitedZoneID];
     [self putFakeDeviceStatusInCloudKit: self.applepayZoneID];
     [self putFakeDeviceStatusInCloudKit: self.homeZoneID];
     [self putFakeDeviceStatusInCloudKit: self.limitedZoneID];
+    [self putFakeDeviceStatusInCloudKit: self.passwordsZoneID];
 }
 
 - (void)putFakeKeyHierachiesInCloudKit{
 }
 
 - (void)putFakeKeyHierachiesInCloudKit{
     [self putFakeKeyHierarchyInCloudKit: self.applepayZoneID];
     [self putFakeKeyHierarchyInCloudKit: self.homeZoneID];
     [self putFakeKeyHierarchyInCloudKit: self.limitedZoneID];
     [self putFakeKeyHierarchyInCloudKit: self.applepayZoneID];
     [self putFakeKeyHierarchyInCloudKit: self.homeZoneID];
     [self putFakeKeyHierarchyInCloudKit: self.limitedZoneID];
+    [self putFakeKeyHierarchyInCloudKit: self.passwordsZoneID];
 }
 
 - (void)saveTLKsToKeychain{
 }
 
 - (void)saveTLKsToKeychain{
     [self saveTLKMaterialToKeychain:self.applepayZoneID];
     [self saveTLKMaterialToKeychain:self.homeZoneID];
     [self saveTLKMaterialToKeychain:self.limitedZoneID];
     [self saveTLKMaterialToKeychain:self.applepayZoneID];
     [self saveTLKMaterialToKeychain:self.homeZoneID];
     [self saveTLKMaterialToKeychain:self.limitedZoneID];
+    [self saveTLKMaterialToKeychain:self.passwordsZoneID];
 }
 
 - (void)deleteTLKMaterialsFromKeychain{
 }
 
 - (void)deleteTLKMaterialsFromKeychain{
     [self deleteTLKMaterialFromKeychain: self.applepayZoneID];
     [self deleteTLKMaterialFromKeychain: self.homeZoneID];
     [self deleteTLKMaterialFromKeychain:self.limitedZoneID];
     [self deleteTLKMaterialFromKeychain: self.applepayZoneID];
     [self deleteTLKMaterialFromKeychain: self.homeZoneID];
     [self deleteTLKMaterialFromKeychain:self.limitedZoneID];
+    [self deleteTLKMaterialFromKeychain:self.passwordsZoneID];
 }
 
 - (void)waitForKeyHierarchyReadinesses {
 }
 
 - (void)waitForKeyHierarchyReadinesses {
     [self.applepayView waitForKeyHierarchyReadiness];
     [self.homeView waitForKeyHierarchyReadiness];
     [self.limitedView waitForKeyHierarchyReadiness];
     [self.applepayView waitForKeyHierarchyReadiness];
     [self.homeView waitForKeyHierarchyReadiness];
     [self.limitedView waitForKeyHierarchyReadiness];
+    [self.passwordsView waitForKeyHierarchyReadiness];
 }
 
 - (void)expectCKKSTLKSelfShareUploads {
 }
 
 - (void)expectCKKSTLKSelfShareUploads {
     [self waitForExpectations:@[pcsChanged] timeout:0.2];
 }
 
     [self waitForExpectations:@[pcsChanged] timeout:0.2];
 }
 
+- (void)testMultipleZoneAdd {
+    [self saveFakeKeyHierarchiesToLocalDatabase]; // Make life easy for this test.
+
+    // Let the horses loose
+    [self startCKKSSubsystem];
+
+    // We expect a single record to be uploaded to the 'LimitedPeersAllowed' view
+    [self expectCKModifyItemRecords:1 currentKeyPointerRecords:1 zoneID:self.limitedZoneID];
+    [self addGenericPassword: @"data" account: @"account-delete-me-limited-peers" viewHint:(NSString*)kSecAttrViewHintLimitedPeersAllowed];
+    OCMVerifyAllWithDelay(self.mockDatabase, 20);
+
+    // We expect a single record to be uploaded to the 'atv' home
+    [self expectCKModifyItemRecords:1 currentKeyPointerRecords:1 zoneID:self.homeZoneID];
+    [self addGenericPassword: @"data" account: @"account-delete-me-home" viewHint:(NSString*)kSecAttrViewHintHome];
+
+    OCMVerifyAllWithDelay(self.mockDatabase, 20);
+    OCMVerifyAllWithDelay(self.mockCKKSViewManager, 10);
+}
+
+- (void)testMultipleZoneDelete {
+    [self saveFakeKeyHierarchiesToLocalDatabase]; // Make life easy for this test.
+
+    [self startCKKSSubsystem];
+
+    // We expect a single record to be uploaded.
+    [self expectCKModifyItemRecords:1 currentKeyPointerRecords:1 zoneID:self.limitedZoneID];
+    [self addGenericPassword: @"data" account: @"account-delete-me-limited-peers" viewHint:(NSString*)kSecAttrViewHintLimitedPeersAllowed];
+    OCMVerifyAllWithDelay(self.mockDatabase, 20);
+
+    [self expectCKModifyItemRecords:1 currentKeyPointerRecords:1 zoneID:self.homeZoneID];
+    [self addGenericPassword: @"data" account: @"account-delete-me-home" viewHint:(NSString*)kSecAttrViewHintHome];
+    OCMVerifyAllWithDelay(self.mockDatabase, 20);
+    [self waitForCKModifications];
+
+    // We expect a single record to be deleted from the ATV zone
+    [self expectCKDeleteItemRecords:1 zoneID:self.homeZoneID];
+    [self deleteGenericPassword:@"account-delete-me-home"];
+    OCMVerifyAllWithDelay(self.mockDatabase, 20);
+
+    // Now we expect a single record to be deleted from the test zone
+    [self expectCKDeleteItemRecords:1 zoneID:self.limitedZoneID];
+    [self deleteGenericPassword:@"account-delete-me-limited-peers"];
+    OCMVerifyAllWithDelay(self.mockDatabase, 20);
+}
+
+- (void)testAddAndReceiveDeleteForSafariPasswordItem {
+    [self saveFakeKeyHierarchiesToLocalDatabase]; // Make life easy for this test.
+
+    [self startCKKSSubsystem];
+
+    XCTestExpectation* passwordChanged = [self expectChangeForView:self.passwordsView.zoneName];
+
+    // We expect a single record to be uploaded to the Passwords view.
+    [self expectCKModifyItemRecords:1 currentKeyPointerRecords:1 zoneID:self.passwordsZoneID];
+
+    [self addGenericPassword:@"data"
+                     account:@"account-delete-me"
+                      access:(id)kSecAttrAccessibleWhenUnlocked
+                    viewHint:nil
+                 accessGroup:@"com.apple.sbd"
+                   expecting:errSecSuccess
+                     message:@"Item for Password view"];
+
+    OCMVerifyAllWithDelay(self.mockDatabase, 20);
+    [self waitForExpectations:@[passwordChanged] timeout:1];
+    [self waitForCKModifications];
+
+    [self waitForKeyHierarchyReadinesses];
+    [self.passwordsView waitForOperationsOfClass:[CKKSIncomingQueueOperation class]];
+
+    // Now, the item is deleted. Do we properly remove it?
+    CKRecord* itemRecord = nil;
+    for(CKRecord* record in [self.passwordsZone.currentDatabase allValues]) {
+        if([record.recordType isEqualToString:SecCKRecordItemType]) {
+            itemRecord = record;
+            break;
+        }
+    }
+    XCTAssertNotNil(itemRecord, "Should have found the item in the password zone");
+
+    NSDictionary *query = @{(id)kSecClass : (id)kSecClassGenericPassword,
+                            (id)kSecAttrAccessGroup : @"com.apple.sbd",
+                            (id)kSecAttrAccount : @"account-delete-me",
+                            (id)kSecAttrSynchronizable : (id)kCFBooleanTrue,
+                            (id)kSecMatchLimit : (id)kSecMatchLimitOne,
+                            (id)kSecReturnData : (id)kCFBooleanTrue,
+                            };
+
+    CFTypeRef item = NULL;
+    XCTAssertEqual(errSecSuccess, SecItemCopyMatching((__bridge CFDictionaryRef) query, &item), "item should still exist");
+    XCTAssertNotNil((__bridge id)item, "No item should have been found");
+    CFReleaseNull(item);
+
+    // Now, the item is deleted. The passwords view should delete the local item, even though it has the wrong 'vwht' on disk.
+    [self.passwordsZone deleteCKRecordIDFromZone:itemRecord.recordID];
+    [self.passwordsView notifyZoneChange:nil];
+    [self.passwordsView waitForFetchAndIncomingQueueProcessing];
+
+    XCTAssertEqual(errSecItemNotFound, SecItemCopyMatching((__bridge CFDictionaryRef) query, &item), "item should no longer exist");
+    XCTAssertNil((__bridge id)item, "No item should have been found");
+}
+
 - (void)testAddOtherViewHintItem {
     [self saveFakeKeyHierarchiesToLocalDatabase]; // Make life easy for this test.
 
 - (void)testAddOtherViewHintItem {
     [self saveFakeKeyHierarchiesToLocalDatabase]; // Make life easy for this test.
 
 
     // We expect no uploads to CKKS.
     [self addGenericPassword: @"data" account: @"account-delete-me-no-viewhint"];
 
     // We expect no uploads to CKKS.
     [self addGenericPassword: @"data" account: @"account-delete-me-no-viewhint"];
-    [self addGenericPassword: @"data" account: @"account-delete-me-password" viewHint:(NSString*) kSOSViewAutofillPasswords];
+    [self addGenericPassword: @"data" account: @"account-delete-me-password" viewHint:(NSString*) kSOSViewBackupBagV0];
 
     sleep(1);
     OCMVerifyAllWithDelay(self.mockDatabase, 20);
 }
 
 
     sleep(1);
     OCMVerifyAllWithDelay(self.mockDatabase, 20);
 }
 
+- (void)testUploadItemsAddedBeforeStart {
+    [self addGenericPassword:@"data"
+                     account:@"account-delete-me"
+                      access:(id)kSecAttrAccessibleAfterFirstUnlock
+                    viewHint:nil
+                 accessGroup:@"com.apple.sbd"
+                   expecting:errSecSuccess
+                     message:@"Item for Password view"];
+
+    [self addGenericPassword:@"data"
+                     account:@"account-delete-me-2"
+                      access:(id)kSecAttrAccessibleAfterFirstUnlock
+                    viewHint:nil
+                 accessGroup:@"com.apple.sbd"
+                   expecting:errSecSuccess
+                     message:@"Item for Password view"];
+
+    [self addGenericPassword:@"data" account:@"account-delete-me-limited-peers" viewHint:(NSString*)kSecAttrViewHintLimitedPeersAllowed];
+
+    NSError* error = nil;
+    NSDictionary* currentOQEs = [CKKSOutgoingQueueEntry countsByStateInZone:self.passwordsZoneID error:&error];
+    XCTAssertNil(error, "Should be no error counting OQEs");
+    XCTAssertEqual(0, currentOQEs.count, "Should be no OQEs");
+
+    [self saveFakeKeyHierarchiesToLocalDatabase]; // Make life easy for this test.
+
+    // Now CKKS starts up
+    // Upon sign in, these items should be uploaded
+    [self expectCKModifyItemRecords:2 currentKeyPointerRecords:1 zoneID:self.passwordsZoneID
+                          checkItem:[self checkClassCBlock:self.passwordsZoneID message:@"Object was encrypted under class C key in hierarchy"]];
+    [self expectCKModifyItemRecords:1 currentKeyPointerRecords:1 zoneID:self.limitedZoneID];
+    [self startCKKSSubsystem];
+
+    XCTAssertEqual(0, [self.passwordsView.keyHierarchyConditions[SecCKKSZoneKeyStateReady] wait:20*NSEC_PER_SEC], "CKKS entered ready");
+    OCMVerifyAllWithDelay(self.mockDatabase, 20);
+}
+
 - (void)testReceiveItemInView {
     [self saveFakeKeyHierarchiesToLocalDatabase]; // Make life easy for this test.
     [self startCKKSSubsystem];
 - (void)testReceiveItemInView {
     [self saveFakeKeyHierarchiesToLocalDatabase]; // Make life easy for this test.
     [self startCKKSSubsystem];
index 1aaf5cd8204869cc6edf9ece5845faf9a78aa64e..8baf53aa9175d2e00e019c74fb12bf724f572c27 100644 (file)
     [self.keychainView dispatchSync:^bool {
         NSError* error = nil;
 
     [self.keychainView dispatchSync:^bool {
         NSError* error = nil;
 
-        CKRecordID* ckrid = [[CKRecordID alloc] initWithRecordName:@"DD7C2F9B-B22D-3B90-C299-E3B48174BFA3" zoneID:self.keychainZoneID];
+        CKRecordID* ckrid = [[CKRecordID alloc] initWithRecordName:@"50184A35-4480-E8BA-769B-567CF72F1EC0" zoneID:self.keychainZoneID];
 
         CKKSItem* item = [self newItem:ckrid withNewItemData:[self fakeRecordDictionary:account zoneID:self.keychainZoneID] key:self.keychainZoneKeys.classC];
         XCTAssertNotNil(item, "Should be able to create a new fake item");
 
         CKKSItem* item = [self newItem:ckrid withNewItemData:[self fakeRecordDictionary:account zoneID:self.keychainZoneID] key:self.keychainZoneKeys.classC];
         XCTAssertNotNil(item, "Should be able to create a new fake item");
         XCTAssertNil(error, "No error loading IQEs");
         XCTAssertNotNil(iqes, "Could load IQEs");
         XCTAssertEqual(iqes.count, 0u, "Incoming queue is empty");
         XCTAssertNil(error, "No error loading IQEs");
         XCTAssertNotNil(iqes, "Could load IQEs");
         XCTAssertEqual(iqes.count, 0u, "Incoming queue is empty");
+        return false;
     }];
 }
 
     }];
 }
 
                                                                          (id)kSecAttrAccessible: (id)kSecAttrAccessibleWhenUnlocked,
                                                                          (id)kSecAttrAccount : @"account-class-A",
                                                                          (id)kSecAttrSynchronizable : (id)kCFBooleanTrue,
                                                                          (id)kSecAttrAccessible: (id)kSecAttrAccessibleWhenUnlocked,
                                                                          (id)kSecAttrAccount : @"account-class-A",
                                                                          (id)kSecAttrSynchronizable : (id)kCFBooleanTrue,
+                                                                         (id)kSecAttrSyncViewHint : self.keychainView.zoneName,
                                                                          (id)kSecValueData : (id) [@"asdf" dataUsingEncoding:NSUTF8StringEncoding],
                                                                          }, NULL), @"Adding class A item");
     OCMVerifyAllWithDelay(self.mockDatabase, 20);
                                                                          (id)kSecValueData : (id) [@"asdf" dataUsingEncoding:NSUTF8StringEncoding],
                                                                          }, NULL), @"Adding class A item");
     OCMVerifyAllWithDelay(self.mockDatabase, 20);
     [self checkGenericPassword: @"data" account: @"second"];
 }
 
     [self checkGenericPassword: @"data" account: @"second"];
 }
 
-- (void)testMultipleZoneAdd {
-    // Bring up a new zone: we expect a key hierarchy upload.
-    CKKSKeychainView* atvView = [self.injectedManager findOrCreateView:(id)kSecAttrViewHintAppleTV];
-    [self.ckksViews addObject:atvView];
-    CKRecordZoneID* appleTVZoneID = [[CKRecordZoneID alloc] initWithZoneName:(__bridge NSString*) kSecAttrViewHintAppleTV ownerName:CKCurrentUserDefaultName];
-
-    // We also expect the view manager's notifyNewTLKsInKeychain call to fire once (after some delay)
-    OCMExpect([self.mockCKKSViewManager notifyNewTLKsInKeychain]);
-
-    // Let the horses loose
-    [self startCKKSSubsystem];
-    [self performOctagonTLKUpload:self.ckksViews];
-
-    // We expect a single record to be uploaded to the 'keychain' view
-    [self expectCKModifyItemRecords: 1 currentKeyPointerRecords: 1 zoneID:self.keychainZoneID];
-    [self addGenericPassword: @"data" account: @"account-delete-me"];
-    OCMVerifyAllWithDelay(self.mockDatabase, 20);
-
-    // We expect a single record to be uploaded to the 'atv' view
-    [self expectCKModifyItemRecords: 1 currentKeyPointerRecords: 1 zoneID:appleTVZoneID];
-    [self addGenericPassword: @"atv"
-                     account: @"tvaccount"
-                    viewHint:(__bridge NSString*) kSecAttrViewHintAppleTV
-                      access:(id)kSecAttrAccessibleAfterFirstUnlock
-                   expecting:errSecSuccess message:@"AppleTV view-hinted object"];
-
-    OCMVerifyAllWithDelay(self.mockDatabase, 20);
-
-    OCMVerifyAllWithDelay(self.mockCKKSViewManager, 10);
-}
-
-- (void)testMultipleZoneDelete {
-    [self startCKKSSubsystem];
-
-    // Bring up a new zone: we expect a key hierarchy and an item.
-    CKKSKeychainView* atvView = [self.injectedManager findOrCreateView:(id)kSecAttrViewHintAppleTV];
-    XCTAssertNotNil(atvView, "Should have a new ATV view");
-    [self.ckksViews addObject:atvView];
-    [self beginSOSTrustedViewOperation:atvView];
-    CKRecordZoneID* appleTVZoneID = [[CKRecordZoneID alloc] initWithZoneName:(__bridge NSString*) kSecAttrViewHintAppleTV ownerName:CKCurrentUserDefaultName];
-
-    [self performOctagonTLKUpload:self.ckksViews];
-
-    // We expect a single record to be uploaded.
-    [self expectCKModifyItemRecords: 1 currentKeyPointerRecords: 1 zoneID:self.keychainZoneID];
-    [self addGenericPassword: @"data" account: @"account-delete-me"];
-    OCMVerifyAllWithDelay(self.mockDatabase, 20);
-
-    [self expectCKModifyItemRecords: 1 currentKeyPointerRecords: 1 zoneID:appleTVZoneID];
-    [self addGenericPassword: @"atv"
-                     account: @"tvaccount"
-                    viewHint:(__bridge NSString*) kSecAttrViewHintAppleTV
-                      access:(id)kSecAttrAccessibleAfterFirstUnlock
-                   expecting:errSecSuccess
-                     message:@"AppleTV view-hinted object"];
-    OCMVerifyAllWithDelay(self.mockDatabase, 20);
-
-    // We expect a single record to be deleted from the ATV zone
-    [self expectCKDeleteItemRecords: 1 zoneID:appleTVZoneID];
-    [self deleteGenericPassword:@"tvaccount"];
-    OCMVerifyAllWithDelay(self.mockDatabase, 20);
-
-    // Now we expect a single record to be deleted from the test zone
-    [self expectCKDeleteItemRecords: 1 zoneID:self.keychainZoneID];
-    [self deleteGenericPassword:@"account-delete-me"];
-    OCMVerifyAllWithDelay(self.mockDatabase, 20);
-}
-
 - (void)testRestartWithoutRefetch {
     // Restarting the CKKS operation should check that it's been 15 minutes since the last fetch before it fetches again. Simulate this.
     [self startCKKSSubsystem];
 - (void)testRestartWithoutRefetch {
     // Restarting the CKKS operation should check that it's been 15 minutes since the last fetch before it fetches again. Simulate this.
     [self startCKKSSubsystem];
 
     NSError* error = nil;
     NSDictionary* currentOQEs = [CKKSOutgoingQueueEntry countsByStateInZone:self.keychainZoneID error:&error];
 
     NSError* error = nil;
     NSDictionary* currentOQEs = [CKKSOutgoingQueueEntry countsByStateInZone:self.keychainZoneID error:&error];
-    XCTAssertNil(error, "Should be no error coutning OQEs");
+    XCTAssertNil(error, "Should be no error counting OQEs");
     XCTAssertEqual(0, currentOQEs.count, "Should be no OQEs");
 
     // Now, insert a restart to simulate securityd restarting (and throwing away all pending operations), then a real sign in
     XCTAssertEqual(0, currentOQEs.count, "Should be no OQEs");
 
     // Now, insert a restart to simulate securityd restarting (and throwing away all pending operations), then a real sign in
     OCMVerifyAllWithDelay(self.mockDatabase, 20);
 }
 
     OCMVerifyAllWithDelay(self.mockDatabase, 20);
 }
 
+- (void)testSyncableItemAddedOnDaemonRestartBeforePolicyLoaded {
+    [self createAndSaveFakeKeyHierarchy:self.keychainZoneID];
+    [self startCKKSSubsystem];
+
+    XCTAssertEqual(0, [self.keychainView.keyHierarchyConditions[SecCKKSZoneKeyStateReady] wait:20*NSEC_PER_SEC], "CKKS entered ready");
+
+    [self.keychainView waitForOperationsOfClass:[CKKSScanLocalItemsOperation class]];
+    [self.keychainView waitForOperationsOfClass:[CKKSOutgoingQueueOperation class]];
+
+    // Daemon restarts
+    self.automaticallyBeginCKKSViewCloudKitOperation = false;
+    [self.injectedManager resetSyncingPolicy];
+    [self.injectedManager haltZone:self.keychainZoneID.zoneName];
+
+    // This item addition shouldn't be uploaded yet, or in any queues
+    [self addGenericPassword:@"data" account:@"account-delete-me-2"];
+
+    NSError* error = nil;
+    NSDictionary* currentOQEs = [CKKSOutgoingQueueEntry countsByStateInZone:self.keychainZoneID error:&error];
+    XCTAssertNil(error, "Should be no error counting OQEs");
+    XCTAssertEqual(0, currentOQEs.count, "Should be no OQEs");
+
+    [self.injectedManager setSyncingViews:self.managedViewList sortingPolicy:self.viewSortingPolicyForManagedViewList];
+    self.keychainView = [self.injectedManager findView:self.keychainZoneID.zoneName];
+    // end of daemon restart
+
+    [self expectCKModifyItemRecords:1 currentKeyPointerRecords:1 zoneID:self.keychainZoneID
+                          checkItem:[self checkClassCBlock:self.keychainZoneID message:@"Object was encrypted under class C key in hierarchy"]];
+
+    [self.injectedManager beginCloudKitOperationOfAllViews];
+    [self beginSOSTrustedViewOperation:self.keychainView];
+
+    XCTAssertEqual(0, [self.keychainView.keyHierarchyConditions[SecCKKSZoneKeyStateReady] wait:20*NSEC_PER_SEC], "CKKS entered ready");
+    OCMVerifyAllWithDelay(self.mockDatabase, 20);
+}
+
 // Note that this test assumes that the keychainView object was created at daemon restart.
 // I don't really know how to write a test for that...
 - (void)testSyncableItemAddedOnDaemonRestartBeforeCloudKitAccountKnown {
 // Note that this test assumes that the keychainView object was created at daemon restart.
 // I don't really know how to write a test for that...
 - (void)testSyncableItemAddedOnDaemonRestartBeforeCloudKitAccountKnown {
index fddf4ca66f63eeaa49c83b98fb44e0fe558bd4aa..e0701910f012ba6dd04654f9804d66f802bdc900 100644 (file)
@@ -99,7 +99,7 @@
     // Add some current item pointers. They don't necessarily need to point to anything...
 
     CKKSCurrentItemPointer* cip = [[CKKSCurrentItemPointer alloc] initForIdentifier:@"com.apple.security.ckks-pcsservice"
     // Add some current item pointers. They don't necessarily need to point to anything...
 
     CKKSCurrentItemPointer* cip = [[CKKSCurrentItemPointer alloc] initForIdentifier:@"com.apple.security.ckks-pcsservice"
-                                                                    currentItemUUID:@"DD7C2F9B-B22D-3B90-C299-E3B48174BFA3"
+                                                                    currentItemUUID:@"50184A35-4480-E8BA-769B-567CF72F1EC0"
                                                                               state:SecCKKSProcessedStateRemote
                                                                              zoneID:self.keychainZoneID
                                                                     encodedCKRecord:nil];
                                                                               state:SecCKKSProcessedStateRemote
                                                                              zoneID:self.keychainZoneID
                                                                     encodedCKRecord:nil];
     XCTAssertNotNil(currentPointerRecord, "Found record in CloudKit at expected UUID");
 
     CKKSCurrentItemPointer* cip2 = [[CKKSCurrentItemPointer alloc] initForIdentifier:@"com.apple.security.ckks-pcsservice2"
     XCTAssertNotNil(currentPointerRecord, "Found record in CloudKit at expected UUID");
 
     CKKSCurrentItemPointer* cip2 = [[CKKSCurrentItemPointer alloc] initForIdentifier:@"com.apple.security.ckks-pcsservice2"
-                                                                     currentItemUUID:@"3AB8E78D-75AF-CFEF-F833-FA3E3E90978A"
+                                                                     currentItemUUID:@"10E76B80-CE1C-A52A-B0CB-462A2EBA05AF"
                                                                                state:SecCKKSProcessedStateRemote
                                                                               zoneID:self.keychainZoneID
                                                                      encodedCKRecord:nil];
                                                                                state:SecCKKSProcessedStateRemote
                                                                               zoneID:self.keychainZoneID
                                                                      encodedCKRecord:nil];
 
         // And add a garbage CIP
         CKKSCurrentItemPointer* cip3 = [[CKKSCurrentItemPointer alloc] initForIdentifier:@"garbage"
 
         // And add a garbage CIP
         CKKSCurrentItemPointer* cip3 = [[CKKSCurrentItemPointer alloc] initForIdentifier:@"garbage"
-                                                                         currentItemUUID:@"3AB8E78D-75AF-CFEF-F833-FA3E3E90978A"
+                                                                         currentItemUUID:@"10E76B80-CE1C-A52A-B0CB-462A2EBA05AF"
                                                                                    state:SecCKKSProcessedStateLocal
                                                                                   zoneID:self.keychainZoneID
                                                                          encodedCKRecord:nil];
                                                                                    state:SecCKKSProcessedStateLocal
                                                                                   zoneID:self.keychainZoneID
                                                                          encodedCKRecord:nil];
index 933305d3c2d9e4b80feca4f9f9a124c63adf7ad9..f64abed9bf3ab1a68d222ad251f0e55430fc4da0 100644 (file)
@@ -136,6 +136,15 @@ NS_ASSUME_NONNULL_BEGIN
                     access:(NSString*)access
                  expecting:(OSStatus)status
                    message:(NSString*)message;
                     access:(NSString*)access
                  expecting:(OSStatus)status
                    message:(NSString*)message;
+
+- (BOOL)addGenericPassword:(NSString*)password
+                   account:(NSString*)account
+                    access:(NSString*)access
+                  viewHint:(NSString* _Nullable)viewHint
+               accessGroup:(NSString* _Nullable)accessGroup
+                 expecting:(OSStatus)status
+                   message:(NSString*)message;
+
 - (void)addGenericPassword:(NSString*)password account:(NSString*)account expecting:(OSStatus)status message:(NSString*)message;
 
 - (void)updateGenericPassword:(NSString*)newPassword account:(NSString*)account;
 - (void)addGenericPassword:(NSString*)password account:(NSString*)account expecting:(OSStatus)status message:(NSString*)message;
 
 - (void)updateGenericPassword:(NSString*)newPassword account:(NSString*)account;
index 3ea304a42bb9d3e64f9c605c10d55eacc40547b0..f7edec830d5cc3902162ed3718adef9960fe24ab 100644 (file)
     self.suggestTLKUpload = OCMClassMock([CKKSNearFutureScheduler class]);
     OCMStub([self.suggestTLKUpload trigger]);
 
     self.suggestTLKUpload = OCMClassMock([CKKSNearFutureScheduler class]);
     OCMStub([self.suggestTLKUpload trigger]);
 
-    self.ckksZones = [NSMutableSet set];
-    self.ckksViews = [NSMutableSet set];
-    self.keys = [[NSMutableDictionary alloc] init];
+    // If a subclass wants to fill these in before calling setUp, fine.
+    self.ckksZones = self.ckksZones ?: [NSMutableSet set];
+    self.ckksViews = self.ckksViews ?: [NSMutableSet set];
+    self.keys = self.keys ?: [[NSMutableDictionary alloc] init];
 
     [SecMockAKS reset];
 
 
     [SecMockAKS reset];
 
 
 - (void)verifyDatabaseMocks {
     OCMVerifyAllWithDelay(self.mockDatabase, 20);
 
 - (void)verifyDatabaseMocks {
     OCMVerifyAllWithDelay(self.mockDatabase, 20);
+    [self waitForCKModifications];
 }
 
 - (void)createClassCItemAndWaitForUpload:(CKRecordZoneID*)zoneID account:(NSString*)account {
 }
 
 - (void)createClassCItemAndWaitForUpload:(CKRecordZoneID*)zoneID account:(NSString*)account {
@@ -1046,16 +1048,24 @@ static CFDictionaryRef SOSCreatePeerGestaltFromName(CFStringRef name)
     return ret;
 }
 
     return ret;
 }
 
-- (void)addGenericPassword: (NSString*) password account: (NSString*) account viewHint: (NSString*) viewHint access: (NSString*) access expecting: (OSStatus) status message: (NSString*) message {
+- (BOOL)addGenericPassword:(NSString*)password
+                   account:(NSString*)account
+                    access:(NSString*)access
+                  viewHint:(NSString* _Nullable)viewHint
+               accessGroup:(NSString* _Nullable)accessGroup
+                 expecting:(OSStatus)status
+                   message:(NSString*)message
+{
     NSMutableDictionary* query = [@{
                                     (id)kSecClass : (id)kSecClassGenericPassword,
     NSMutableDictionary* query = [@{
                                     (id)kSecClass : (id)kSecClassGenericPassword,
-                                    (id)kSecAttrAccessGroup : @"com.apple.security.ckks",
-                                    (id)kSecAttrAccessible: access,
+                                    (id)kSecAttrAccessible: (id)access,
                                     (id)kSecAttrAccount : account,
                                     (id)kSecAttrSynchronizable : (id)kCFBooleanTrue,
                                     (id)kSecValueData : (id) [password dataUsingEncoding:NSUTF8StringEncoding],
                                     } mutableCopy];
 
                                     (id)kSecAttrAccount : account,
                                     (id)kSecAttrSynchronizable : (id)kCFBooleanTrue,
                                     (id)kSecValueData : (id) [password dataUsingEncoding:NSUTF8StringEncoding],
                                     } mutableCopy];
 
+    query[(id)kSecAttrAccessGroup] = accessGroup ?: @"com.apple.security.ckks";
+
     if(viewHint) {
         query[(id)kSecAttrSyncViewHint] = viewHint;
     } else {
     if(viewHint) {
         query[(id)kSecAttrSyncViewHint] = viewHint;
     } else {
@@ -1066,6 +1076,16 @@ static CFDictionaryRef SOSCreatePeerGestaltFromName(CFStringRef name)
     XCTAssertEqual(status, SecItemAdd((__bridge CFDictionaryRef) query, NULL), @"%@", message);
 }
 
     XCTAssertEqual(status, SecItemAdd((__bridge CFDictionaryRef) query, NULL), @"%@", message);
 }
 
+- (void)addGenericPassword: (NSString*) password account: (NSString*) account viewHint: (NSString*) viewHint access: (NSString*) access expecting: (OSStatus) status message: (NSString*) message {
+    [self addGenericPassword:password
+                     account:account
+                      access:access
+                    viewHint:viewHint
+                 accessGroup:nil
+                   expecting:status
+                     message:message];
+}
+
 - (void)addGenericPassword: (NSString*) password account: (NSString*) account expecting: (OSStatus) status message: (NSString*) message {
     [self addGenericPassword:password account:account viewHint:nil access:(id)kSecAttrAccessibleAfterFirstUnlock expecting:errSecSuccess message:message];
 
 - (void)addGenericPassword: (NSString*) password account: (NSString*) account expecting: (OSStatus) status message: (NSString*) message {
     [self addGenericPassword:password account:account viewHint:nil access:(id)kSecAttrAccessibleAfterFirstUnlock expecting:errSecSuccess message:message];
 
index 2c669584d47f24ef38d28d516b0568bdf6e05082..0cb5406bdba9171e995d676917ee56485f9d9692 100644 (file)
@@ -53,9 +53,6 @@ NS_ASSUME_NONNULL_BEGIN
 
 @property NSCalendar* utcCalendar;
 
 
 @property NSCalendar* utcCalendar;
 
-- (NSSet<NSString*>*)managedViewList;
-
-
 - (ZoneKeys*)keychainZoneKeys;
 
 @end
 - (ZoneKeys*)keychainZoneKeys;
 
 @end
index 35640b3c0f23eab57bee7317f3ceaf7a0188e224..f122a1bdb1140ba40c43d50b7bd14d2c8f2b634c 100644 (file)
@@ -62,7 +62,7 @@
         // Wait for the ViewManager to be brought up
         XCTAssertEqual(0, [self.injectedManager.completedSecCKKSInitialize wait:20*NSEC_PER_SEC], "No timeout waiting for SecCKKSInitialize");
 
         // Wait for the ViewManager to be brought up
         XCTAssertEqual(0, [self.injectedManager.completedSecCKKSInitialize wait:20*NSEC_PER_SEC], "No timeout waiting for SecCKKSInitialize");
 
-        self.keychainView = [[CKKSViewManager manager] findOrCreateView:@"keychain"];
+        self.keychainView = [[CKKSViewManager manager] findView:@"keychain"];
         XCTAssertNotNil(self.keychainView, "CKKSViewManager created the keychain view");
         [self.ckksViews addObject:self.keychainView];
     }
         XCTAssertNotNil(self.keychainView, "CKKSViewManager created the keychain view");
         [self.ckksViews addObject:self.keychainView];
     }
     // Check that your environment is set up correctly
     XCTAssertFalse([CKKSManifest shouldSyncManifests], "Manifests syncing is disabled");
     XCTAssertFalse([CKKSManifest shouldEnforceManifests], "Manifests enforcement is disabled");
     // Check that your environment is set up correctly
     XCTAssertFalse([CKKSManifest shouldSyncManifests], "Manifests syncing is disabled");
     XCTAssertFalse([CKKSManifest shouldEnforceManifests], "Manifests enforcement is disabled");
-
-    self.aksLockState = false; // Lie and say AKS is always unlocked
-    self.mockLockStateTracker = OCMClassMock([CKKSLockStateTracker class]);
-    OCMStub([self.mockLockStateTracker queryAKSLocked]).andCall(self, @selector(aksLockState));
 }
 
 
 }
 
 
index 0e3573d6fd7ecee990daafd647cdc9a9821fd415..4df9a3cba9ccd3b977d926da11d453c6112fd0fc 100644 (file)
@@ -35,6 +35,8 @@
 #import "keychain/ckks/CKKSAccountStateTracker.h"
 #import "keychain/ckks/tests/MockCloudKit.h"
 
 #import "keychain/ckks/CKKSAccountStateTracker.h"
 #import "keychain/ckks/tests/MockCloudKit.h"
 
+#import "keychain/ot/OTManager.h"
+
 NS_ASSUME_NONNULL_BEGIN
 
 @class CKKSKey;
 NS_ASSUME_NONNULL_BEGIN
 
 @class CKKSKey;
@@ -103,10 +105,25 @@ NS_ASSUME_NONNULL_BEGIN
 @property CKKSMockSOSPresentAdapter* mockSOSAdapter;
 @property (nullable) CKKSMockOctagonAdapter *mockOctagonAdapter;
 
 @property CKKSMockSOSPresentAdapter* mockSOSAdapter;
 @property (nullable) CKKSMockOctagonAdapter *mockOctagonAdapter;
 
--(NSSet*)managedViewList;
+- (NSSet<NSString*>*)managedViewList;
+- (TPPolicy*)viewSortingPolicyForManagedViewList;
+
 @property (nullable) id mockCKKSViewManager;
 @property (nullable) CKKSViewManager* injectedManager;
 
 @property (nullable) id mockCKKSViewManager;
 @property (nullable) CKKSViewManager* injectedManager;
 
+// Injected into CKKSViewManager using OCMock
+@property BOOL overrideUseCKKSViewsFromPolicy;
+
+// Set this to true before calling -setup if you're going to configure the ckks view manager yourself
+// !disable format used because this will be initialized to false, and so will happen unless subclasses take positive action
+@property BOOL disableConfigureCKKSViewManagerWithViews;
+
+// Set this to true to override CKKSViewsFromPolicy to NO instead of the default YES
+// !disable format used because this will be initialized to false, and so will happen unless subclasses take positive action
+@property BOOL setCKKSViewsFromPolicyToNo;
+
+@property (nullable) OTManager* injectedOTManager;
+
 // Fill this in to fail the next modifyzones operation
 @property (nullable) NSError* nextModifyRecordZonesError;
 
 // Fill this in to fail the next modifyzones operation
 @property (nullable) NSError* nextModifyRecordZonesError;
 
@@ -160,6 +177,11 @@ NS_ASSUME_NONNULL_BEGIN
 - (NSError* _Nullable)shouldFailModifyRecordZonesOperation;
 - (void)ensureZoneDeletionAllowed:(FakeCKZone*)zone;
 
 - (NSError* _Nullable)shouldFailModifyRecordZonesOperation;
 - (void)ensureZoneDeletionAllowed:(FakeCKZone*)zone;
 
+// Override this to interpose on how an OTManager is created
+// This will be called during setUp, after the CloudKit mocks are in-place
+// This needs to fill in the injectedOTManager variable on this class
+- (OTManager*)setUpOTManager:(CKKSCloudKitClassDependencies*)cloudKitClassDependencies;
+
 // Use this to assert that a fetch occurs (especially if silentFetchesAllowed = false)
 - (void)expectCKFetch;
 
 // Use this to assert that a fetch occurs (especially if silentFetchesAllowed = false)
 - (void)expectCKFetch;
 
index 76916eabf1ee536c5ef3dda6fcec7e6fc3d137d0..f8e42b58f730724f91c1ad068ce4b7577f2efe6c 100644 (file)
 #import <CloudKit/CloudKit_Private.h>
 #import <CloudKit/CKContainer_Private.h>
 #import <OCMock/OCMock.h>
 #import <CloudKit/CloudKit_Private.h>
 #import <CloudKit/CKContainer_Private.h>
 #import <OCMock/OCMock.h>
+#import <TrustedPeers/TrustedPeers.h>
+#import <TrustedPeers/TPPBPolicyKeyViewMapping.h>
+#import <TrustedPeers/TPDictionaryMatchingRules.h>
 
 #include "keychain/securityd/Regressions/SecdTestKeychainUtilities.h"
 #include <utilities/SecFileLocations.h>
 #include "keychain/securityd/SecItemServer.h"
 
 #include "keychain/securityd/Regressions/SecdTestKeychainUtilities.h"
 #include <utilities/SecFileLocations.h>
 #include "keychain/securityd/SecItemServer.h"
+#include "keychain/securityd/SecItemDataSource.h"
 
 #if NO_SERVER
 #include "keychain/securityd/spi.h"
 
 #if NO_SERVER
 #include "keychain/securityd/spi.h"
@@ -54,6 +58,9 @@
 #include "keychain/ckks/CKKSLockStateTracker.h"
 #include "keychain/ckks/CKKSReachabilityTracker.h"
 
 #include "keychain/ckks/CKKSLockStateTracker.h"
 #include "keychain/ckks/CKKSReachabilityTracker.h"
 
+#include "keychain/ot/OT.h"
+#include "keychain/ot/OTManager.h"
+
 #import "tests/secdmockaks/mockaks.h"
 #import "utilities/SecTapToRadar.h"
 
 #import "tests/secdmockaks/mockaks.h"
 #import "utilities/SecTapToRadar.h"
 
     self.operationQueue = [[NSOperationQueue alloc] init];
     self.operationQueue.maxConcurrentOperationCount = 1;
 
     self.operationQueue = [[NSOperationQueue alloc] init];
     self.operationQueue.maxConcurrentOperationCount = 1;
 
-    self.zones = [[NSMutableDictionary alloc] init];
+    self.zones = self.zones ?: [[NSMutableDictionary alloc] init];
 
     self.apsEnvironment = @"fake APS push string";
 
 
     self.apsEnvironment = @"fake APS push string";
 
                                                                 nsdistributednotificationCenterClass:[FakeNSDistributedNotificationCenter class]
                                                                                                                                      notifierClass:[FakeCKKSNotifier class]];
 
                                                                 nsdistributednotificationCenterClass:[FakeNSDistributedNotificationCenter class]
                                                                                                                                      notifierClass:[FakeCKKSNotifier class]];
 
-    self.mockCKKSViewManager = OCMPartialMock(
-        [[CKKSViewManager alloc] initWithContainerName:SecCKKSContainerName
-                                                usePCS:SecCKKSContainerUsePCS
-                                            sosAdapter:self.mockSOSAdapter
-                             cloudKitClassDependencies:cloudKitClassDependencies]);
 
 
+    self.injectedOTManager = [self setUpOTManager:cloudKitClassDependencies];
+    [OTManager resetManager:false to:self.injectedOTManager];
+
+    self.mockCKKSViewManager = OCMPartialMock(self.injectedOTManager.viewManager);
+    self.injectedManager = self.mockCKKSViewManager;
+
+    [self.mockCKKSViewManager setOverrideCKKSViewsFromPolicy:!self.setCKKSViewsFromPolicyToNo];
     OCMStub([self.mockCKKSViewManager defaultViewList]).andCall(self, @selector(managedViewList));
     OCMStub([self.mockCKKSViewManager syncBackupAndNotifyAboutSync]);
     OCMStub([self.mockCKKSViewManager waitForTrustReady]).andReturn(YES);
 
     OCMStub([self.mockCKKSViewManager defaultViewList]).andCall(self, @selector(managedViewList));
     OCMStub([self.mockCKKSViewManager syncBackupAndNotifyAboutSync]);
     OCMStub([self.mockCKKSViewManager waitForTrustReady]).andReturn(YES);
 
-    self.injectedManager = self.mockCKKSViewManager;
-
-    [CKKSViewManager resetManager:false setTo:self.injectedManager];
+    if(!self.disableConfigureCKKSViewManagerWithViews) {
+        // Normally, the Octagon state machine calls this. But, since we won't be running that, help it out.
+        [self.injectedManager setSyncingViews:[self managedViewList]
+                                sortingPolicy:[self viewSortingPolicyForManagedViewList]];
+    }
 
     // Lie and say network is available
     [self.reachabilityTracker setNetworkReachability:true];
 
     // Lie and say network is available
     [self.reachabilityTracker setNetworkReachability:true];
     kc_with_dbt(true, NULL, ^bool (SecDbConnectionRef dbt) { return false; });
 }
 
     kc_with_dbt(true, NULL, ^bool (SecDbConnectionRef dbt) { return false; });
 }
 
+- (OTManager*)setUpOTManager:(CKKSCloudKitClassDependencies*)cloudKitClassDependencies
+{
+    return [[OTManager alloc] initWithSOSAdapter:self.mockSOSAdapter
+                                lockStateTracker:[[CKKSLockStateTracker alloc] init]
+                            cloudKitClassDependencies:cloudKitClassDependencies];
+
+}
+
 - (SOSAccountStatus*)circleStatus {
     NSError* error = nil;
     SOSCCStatus status = [self.mockSOSAdapter circleStatus:&error];
 - (SOSAccountStatus*)circleStatus {
     NSError* error = nil;
     SOSCCStatus status = [self.mockSOSAdapter circleStatus:&error];
     XCTAssertTrue(self.silentZoneDeletesAllowed, "Should be allowing zone deletes");
 }
 
     XCTAssertTrue(self.silentZoneDeletesAllowed, "Should be allowing zone deletes");
 }
 
--(CKKSAccountStateTracker*)accountStateTracker {
-    return self.injectedManager.accountTracker;
+- (CKKSAccountStateTracker*)accountStateTracker {
+    return self.injectedOTManager.accountStateTracker;
 }
 
 -(CKKSLockStateTracker*)lockStateTracker {
 }
 
 -(CKKSLockStateTracker*)lockStateTracker {
-    return self.injectedManager.lockStateTracker;
+    return self.injectedOTManager.lockStateTracker;
 }
 
 -(CKKSReachabilityTracker*)reachabilityTracker {
 }
 
 -(CKKSReachabilityTracker*)reachabilityTracker {
     return (NSSet*) CFBridgingRelease(SOSViewCopyViewSet(kViewSetCKKS));
 }
 
     return (NSSet*) CFBridgingRelease(SOSViewCopyViewSet(kViewSetCKKS));
 }
 
+- (TPPolicy*)viewSortingPolicyForManagedViewList
+{
+    NSMutableArray<TPPBPolicyKeyViewMapping*>* rules = [NSMutableArray array];
+
+    for(NSString* viewName in self.managedViewList) {
+        TPPBPolicyKeyViewMapping* mapping = [[TPPBPolicyKeyViewMapping alloc] init];
+        mapping.view = viewName;
+        mapping.matchingRule = [TPDictionaryMatchingRule fieldMatch:@"vwht"
+                                                         fieldRegex:[NSString stringWithFormat:@"^%@$", viewName]];
+
+        [rules addObject:mapping];
+    }
+
+    TPPolicy* policy = [TPPolicy policyWithModelToCategory:@[]
+                                          categoriesByView:@{}
+                                     introducersByCategory:@{}
+                                            keyViewMapping:rules
+                                         unknownRedactions:NO
+                                                   version:[[TPPolicyVersion alloc] initWithVersion:1 hash:@"fake-policy-for-views"]];
+
+    return policy;
+}
+
 -(void)expectCKFetch {
     [self expectCKFetchAndRunBeforeFinished: nil];
 }
 -(void)expectCKFetch {
     [self expectCKFetchAndRunBeforeFinished: nil];
 }
     [super tearDown];
 
     [self.injectedManager cancelPendingOperations];
     [super tearDown];
 
     [self.injectedManager cancelPendingOperations];
-    [CKKSViewManager resetManager:true setTo:nil];
+    [self.injectedManager clearAllViews];
     self.injectedManager = nil;
     self.injectedManager = nil;
+
     [self.mockCKKSViewManager stopMocking];
     self.mockCKKSViewManager = nil;
 
     [self.mockCKKSViewManager stopMocking];
     self.mockCKKSViewManager = nil;
 
+    self.injectedOTManager.viewManager = nil;
+
+    [self.injectedOTManager clearAllContexts];
+    self.injectedOTManager = nil;
+    [OTManager resetManager:true to:nil];
+
     [self.mockAccountStateTracker stopMocking];
     self.mockAccountStateTracker = nil;
 
     [self.mockAccountStateTracker stopMocking];
     self.mockAccountStateTracker = nil;
 
     _mockSOSAdapter = nil;
     _mockOctagonAdapter = nil;
 
     _mockSOSAdapter = nil;
     _mockOctagonAdapter = nil;
 
+    // Bring the database down and delete it
+
+    NSURL* keychainDir = (NSURL*)CFBridgingRelease(SecCopyHomeURL());
+
+    SecItemDataSourceFactoryReleaseAll();
+    SecKeychainDbForceClose();
+    SecKeychainDbReset(NULL);
+
+    // Only perform the desctructive step if the url matches what we expect!
+    if([keychainDir.path hasPrefix:[NSString stringWithFormat:@"/tmp/%@", testName]]) {
+        secnotice("ckkstest", "Removing test-specific keychain directory at %@", keychainDir);
+
+        NSError* removeError = nil;
+        [[NSFileManager defaultManager] removeItemAtURL:keychainDir error:&removeError];
+
+        XCTAssertNil(removeError, "Should have been able to remove temporary files");
+     } else {
+         XCTFail("Unsure what happened to the keychain directory URL: %@", keychainDir);
+    }
+
     SecCKKSTestResetFlags();
 }
 
     SecCKKSTestResetFlags();
 }
 
diff --git a/keychain/ckks/tests/gen_test_plist.py b/keychain/ckks/tests/gen_test_plist.py
new file mode 100644 (file)
index 0000000..52065f6
--- /dev/null
@@ -0,0 +1,75 @@
+#!/usr/bin/python
+#
+# TODO(kirk or SEAR/QA) after radar 53867279 is fixed, please delete this script
+#
+# WARNING: if you add new tests to the swift octagon tests, it is possible that
+# this script will not find them and then your new tests will not get executed
+# in BATS!
+#
+import sys
+import Foundation
+from glob import glob
+import re
+import os
+import pprint
+
+test_dir = sys.argv[1]
+outfile = sys.argv[2]
+
+test_plist = Foundation.NSMutableDictionary.dictionary()
+test_plist['BATSConfigVersion'] = '0.1.0'
+test_plist['Project'] = 'Security'
+test_list = Foundation.NSMutableArray.array()
+
+test_files = glob(test_dir + '/*.m') + glob(test_dir + '/*.h')
+
+def get_class_names():
+    test_classes = ['CKKSLaunchSequenceTests']
+    for filename in test_files:
+        f = open(filename, 'r')
+        for line in f:
+            match = re.search('@interface ([a-zA-Z0-9_]+) ?: ?CloudKitKeychain[a-zA-Z0-9_]*TestsBase', line)
+            #match = re.search('class (([a-zA-Z0-9_]+Tests)|(OctagonTests[a-zA-Z0-9_]*(?<!Base))): ', line) 
+            if match:
+                test_classes.append(match.group(1))
+
+            # And pick up everything that's a default xctest, too
+            match = re.search('@interface ([a-zA-Z0-9_]+) ?: ?XCTestCase', line)
+            if match:
+                test_classes.append(match.group(1))
+
+            # or based on CloudKitMockXCTest
+            match = re.search('@interface ([a-zA-Z0-9_]+) ?: ?CloudKitMockXCTest', line)
+            if match:
+                test_classes.append(match.group(1))
+        f.close()
+
+    # But we don't want any helper classes
+    base_classes = ['CloudKitMockXCTest', 'CloudKitKeychainSyncingMockXCTest', 'CKKSCloudKitTests']
+    for base_class in base_classes:
+        test_classes = [x for x in test_classes if base_class != x]
+    return test_classes
+
+for x in get_class_names():
+
+    test_dictionary = Foundation.NSMutableDictionary.dictionary()
+    test_dictionary['TestName'] = x
+    test_dictionary['Timeout']= 1200
+    test_dictionary['ShowSubtestResults']= True
+    test_dictionary['WorkingDirectory'] = '/AppleInternal/XCTests/com.apple.security/'
+
+    test_command = Foundation.NSMutableArray.array()
+    test_command.append('BATS_XCTEST_CMD -XCTest {} CKKSTests.xctest'.format(x))
+    test_dictionary['Command'] = test_command
+
+    test_list.append(test_dictionary)
+
+test_plist['Tests'] = test_list
+
+out_dir = os.path.dirname(outfile)
+if not os.path.exists(out_dir):
+    os.makedirs(out_dir)
+success = test_plist.writeToFile_atomically_(outfile, 1)
+if not success:
+    print "test plist failed to write, error!"
+    sys.exit(1)
diff --git a/keychain/ckks/tests/testrunner/KeychainCKKS.plist b/keychain/ckks/tests/testrunner/KeychainCKKS.plist
deleted file mode 100644 (file)
index 7e285bc..0000000
+++ /dev/null
@@ -1,37 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
-<plist version="1.0">
-<dict>
-       <key>BATSConfigVersion</key>
-       <string>0.1.0</string>
-       <key>Project</key>
-       <string>Security</string>
-       <key>Tests</key>
-       <array>
-               <dict>
-                       <key>TestName</key>
-                       <string>CKKSTests</string>
-                       <key>WorkingDirectory</key>
-                       <string>/AppleInternal/XCTests/com.apple.security/</string>
-                       <key>ShowSubtestResults</key>
-                       <true/>
-                       <key>Timeout</key>
-                       <integer>1200</integer>
-                       <key>Command</key>
-                       <array>
-                               <string>BATS_XCTEST_CMD CKKSTests.xctest</string>
-                       </array>
-               </dict>
-               <!--dict>
-                       <key>TestName</key>
-                       <string>CKKSCloudKitTests</string>
-                       <key>WorkingDirectory</key>
-                       <string>/AppleInternal/XCTests/com.apple.security/</string>
-                       <key>Command</key>
-                       <array>
-                               <string>/AppleInternal/CoreOS/tests/Security/KeychainEntitledTestRunner</string>
-                       </array>
-               </dict-->
-       </array>
-</dict>
-</plist>
index bad4d21b63dd4f092a866e512f102d451f4565e3..d27cf2176312a017c413f59ed04d9b8963cee49a 100644 (file)
@@ -284,7 +284,7 @@ static void print_entry(id k, id v, int ind)
     return status;
 }
 
     return status;
 }
 
-- (void)printHumanReadableStatus: (NSString*) view {
+- (void)printHumanReadableStatus:(NSString*)view shortenOutput:(BOOL)shortenOutput {
 #if OCTAGON
     dispatch_semaphore_t sema = dispatch_semaphore_create(0);
 
 #if OCTAGON
     dispatch_semaphore_t sema = dispatch_semaphore_create(0);
 
@@ -306,15 +306,25 @@ static void print_entry(id k, id v, int ind)
             NSString* lockStateTracker = pop(global,@"lockstatetracker", NSString);
             NSString* retry = pop(global,@"cloudkitRetryAfter", NSString);
             NSDate *lastCKKSPush = pop(global, @"lastCKKSPush", NSDate);
             NSString* lockStateTracker = pop(global,@"lockstatetracker", NSString);
             NSString* retry = pop(global,@"cloudkitRetryAfter", NSString);
             NSDate *lastCKKSPush = pop(global, @"lastCKKSPush", NSDate);
+            NSString *syncingPolicy = pop(global, @"policy", NSString);
+            NSString *viewsFromPolicy = pop(global, @"viewsFromPolicy", NSString);
 
 
-            printf("================================================================================\n\n");
-            printf("Global state:\n\n");
-            printf("Reachability:         %s\n", [[reachability description] UTF8String]);
-            printf("Retry:                %s\n", [[retry description] UTF8String]);
-            printf("CK DeviceID:          %s\n", [[ckdeviceID description] UTF8String]);
-            printf("CK DeviceID Error:    %s\n", [[ckdeviceIDError description] UTF8String]);
-            printf("Lock state:           %s\n", [[lockStateTracker description] UTF8String]);
-            printf("Last CKKS push:       %s\n", [[lastCKKSPush description] UTF8String]);
+            if(!shortenOutput) {
+                printf("================================================================================\n\n");
+                printf("Global state:\n\n");
+            }
+
+            printf("Syncing Policy:       %s\n", [[syncingPolicy description] UTF8String]);
+            printf("Views from policy:    %s\n", [[viewsFromPolicy description] UTF8String]);
+
+            if(!shortenOutput) {
+                printf("Reachability:         %s\n", [[reachability description] UTF8String]);
+                printf("Retry:                %s\n", [[retry description] UTF8String]);
+                printf("CK DeviceID:          %s\n", [[ckdeviceID description] UTF8String]);
+                printf("CK DeviceID Error:    %s\n", [[ckdeviceIDError description] UTF8String]);
+                printf("Lock state:           %s\n", [[lockStateTracker description] UTF8String]);
+                printf("Last CKKS push:       %s\n", [[lastCKKSPush description] UTF8String]);
+            }
 
             printf("\n");
         }
 
             printf("\n");
         }
@@ -326,6 +336,16 @@ static void print_entry(id k, id v, int ind)
         }
 
         for(NSDictionary* viewStatus in remainingViews) {
         }
 
         for(NSDictionary* viewStatus in remainingViews) {
+            if(shortenOutput) {
+                NSMutableDictionary* status = [viewStatus mutableCopy];
+
+                NSString* viewName = pop(status, @"view", NSString);
+                NSString* keystate = pop(status, @"keystate", NSString);
+
+                printf("%-25s: %s\n", [viewName UTF8String], [keystate UTF8String]);
+                continue;
+            }
+
             NSMutableDictionary* status = [viewStatus mutableCopy];
 
             NSString* viewName = pop(status,@"view", NSString);
             NSMutableDictionary* status = [viewStatus mutableCopy];
 
             NSString* viewName = pop(status,@"view", NSString);
@@ -525,6 +545,7 @@ static int resetCloudKit = false;
 static int fetch = false;
 static int push = false;
 static int json = false;
 static int fetch = false;
 static int push = false;
 static int json = false;
+static int shortOutput = false;
 static int ckmetric = false;
 
 static char* viewArg = NULL;
 static int ckmetric = false;
 
 static char* viewArg = NULL;
@@ -534,6 +555,7 @@ int main(int argc, char **argv)
     static struct argument options[] = {
         { .shortname='p', .longname="perfcounters", .flag=&perfCounters, .flagval=true, .description="Print CKKS performance counters"},
         { .shortname='j', .longname="json", .flag=&json, .flagval=true, .description="Output in JSON format"},
     static struct argument options[] = {
         { .shortname='p', .longname="perfcounters", .flag=&perfCounters, .flagval=true, .description="Print CKKS performance counters"},
         { .shortname='j', .longname="json", .flag=&json, .flagval=true, .description="Output in JSON format"},
+        { .shortname='s', .longname="short", .flag=&shortOutput, .flagval=true, .description="Output a short format"},
         { .shortname='v', .longname="view", .argument=&viewArg, .description="Operate on a single view"},
 
         { .command="status", .flag=&status, .flagval=true, .description="Report status on CKKS views"},
         { .shortname='v', .longname="view", .argument=&viewArg, .description="Operate on a single view"},
 
         { .command="status", .flag=&status, .flagval=true, .description="Report status on CKKS views"},
@@ -585,7 +607,7 @@ int main(int argc, char **argv)
             }
 
             if(!json) {
             }
 
             if(!json) {
-                [ctl printHumanReadableStatus:view];
+                [ctl printHumanReadableStatus:view shortenOutput:shortOutput];
             }
             return 0;
         } else if(perfCounters) {
             }
             return 0;
         } else if(perfCounters) {
index 689908a9030fd6f9dbb5e85c133e1df4a2cdf297..6dd81f06394a32e5e3aa77ccf363fb57fc4c6ee9 100644 (file)
@@ -30,7 +30,7 @@
 }
 - (BOOL)hasCertCached
 {
 }
 - (BOOL)hasCertCached
 {
-    return _has.certCached;
+    return _has.certCached != 0;
 }
 - (BOOL)hasSerializedPrerecord
 {
 }
 - (BOOL)hasSerializedPrerecord
 {
@@ -49,7 +49,7 @@
 }
 - (BOOL)hasLastCloudServicesTriggerTime
 {
 }
 - (BOOL)hasLastCloudServicesTriggerTime
 {
-    return _has.lastCloudServicesTriggerTime;
+    return _has.lastCloudServicesTriggerTime != 0;
 }
 @synthesize lastEscrowAttemptTime = _lastEscrowAttemptTime;
 - (void)setLastEscrowAttemptTime:(uint64_t)v
 }
 @synthesize lastEscrowAttemptTime = _lastEscrowAttemptTime;
 - (void)setLastEscrowAttemptTime:(uint64_t)v
@@ -63,7 +63,7 @@
 }
 - (BOOL)hasLastEscrowAttemptTime
 {
 }
 - (BOOL)hasLastEscrowAttemptTime
 {
-    return _has.lastEscrowAttemptTime;
+    return _has.lastEscrowAttemptTime != 0;
 }
 @synthesize uploadCompleted = _uploadCompleted;
 - (void)setUploadCompleted:(BOOL)v
 }
 @synthesize uploadCompleted = _uploadCompleted;
 - (void)setUploadCompleted:(BOOL)v
@@ -77,7 +77,7 @@
 }
 - (BOOL)hasUploadCompleted
 {
 }
 - (BOOL)hasUploadCompleted
 {
-    return _has.uploadCompleted;
+    return _has.uploadCompleted != 0;
 }
 @synthesize uploadRetries = _uploadRetries;
 - (void)setUploadRetries:(uint64_t)v
 }
 @synthesize uploadRetries = _uploadRetries;
 - (void)setUploadRetries:(uint64_t)v
@@ -91,7 +91,7 @@
 }
 - (BOOL)hasUploadRetries
 {
 }
 - (BOOL)hasUploadRetries
 {
-    return _has.uploadRetries;
+    return _has.uploadRetries != 0;
 }
 - (BOOL)hasAltDSID
 {
 }
 - (BOOL)hasAltDSID
 {
 }
 - (BOOL)hasTriggerRequestTime
 {
 }
 - (BOOL)hasTriggerRequestTime
 {
-    return _has.triggerRequestTime;
+    return _has.triggerRequestTime != 0;
 }
 
 - (NSString *)description
 }
 
 - (NSString *)description
index f39dabbc1c2cea529d958a7ce281e7c20f959a87..baef2e98404e80d6626311060a6ccfe245bbcf7c 100644 (file)
@@ -770,7 +770,8 @@ extern const CFStringRef kSecAttrKeyClassSymmetric
     @discussion Predefined item attribute constants used to get or set values
         in a dictionary. The kSecAttrKeyType constant is the key
         and its value is one of the constants defined here.
     @discussion Predefined item attribute constants used to get or set values
         in a dictionary. The kSecAttrKeyType constant is the key
         and its value is one of the constants defined here.
-    @constant kSecAttrKeyTypeECSECPrimeRandom.
+    @constant kSecAttrKeyTypeECSECPrimeRandom. The used curve is P-192, P-256, P-384 or P-521.
+        The size is specified by kSecAttrKeySizeInBits attribute. Curves are defined in FIPS PUB 186-4 standard.
     @constant kSecAttrKeyTypeEC This is the legacy name for kSecAttrKeyTypeECSECPrimeRandom, new applications should not use it.
     @constant kSecAttrKeyTypeDSA (OSX only)
     @constant kSecAttrKeyTypeAES (OSX only)
     @constant kSecAttrKeyTypeEC This is the legacy name for kSecAttrKeyTypeECSECPrimeRandom, new applications should not use it.
     @constant kSecAttrKeyTypeDSA (OSX only)
     @constant kSecAttrKeyTypeAES (OSX only)
index 021a672af6b7d8bc0a3e8adf733d6240cd326454..9b0a67b070e03d334b881b04e21edbf27718c7a0 100644 (file)
@@ -871,7 +871,7 @@ __OSX_AVAILABLE(10.12) __IOS_AVAILABLE(10.0) __TVOS_AVAILABLE(10.0) __WATCHOS_AV
     * kSecAttrKeySizeInBits
     * kSecAttrTokenID
     * kSecAttrApplicationLabel
     * kSecAttrKeySizeInBits
     * kSecAttrTokenID
     * kSecAttrApplicationLabel
-    Other values returned in that dictionary are RFU.
+    The set of values is not fixed. Future versions may return more values in this dictionary.
  */
 CFDictionaryRef _Nullable SecKeyCopyAttributes(SecKeyRef key)
 __OSX_AVAILABLE(10.12) __IOS_AVAILABLE(10.0) __TVOS_AVAILABLE(10.0) __WATCHOS_AVAILABLE(3.0);
  */
 CFDictionaryRef _Nullable SecKeyCopyAttributes(SecKeyRef key)
 __OSX_AVAILABLE(10.12) __IOS_AVAILABLE(10.0) __TVOS_AVAILABLE(10.0) __WATCHOS_AVAILABLE(3.0);
index c67395bbd7da7b0e8f580288b13c7d19a7d4c943..868f2bd848362e135583c949c1bb2b7015483ea5 100644 (file)
@@ -189,6 +189,7 @@ enum {NUM_RETRIES = 5};
 - (void)setAllowedMachineIDsWithContainer:(NSString *)container
                                   context:(NSString *)context
                         allowedMachineIDs:(NSSet<NSString*> *)allowedMachineIDs
 - (void)setAllowedMachineIDsWithContainer:(NSString *)container
                                   context:(NSString *)context
                         allowedMachineIDs:(NSSet<NSString*> *)allowedMachineIDs
+                            honorIDMSListChanges:(BOOL)accountIsDemo
                                     reply:(void (^)(BOOL listDifferences, NSError * _Nullable error))reply
 {
     __block int i = 0;
                                     reply:(void (^)(BOOL listDifferences, NSError * _Nullable error))reply
 {
     __block int i = 0;
@@ -204,7 +205,7 @@ enum {NUM_RETRIES = 5};
                         reply(NO, error);
                     }
                     ++i;
                         reply(NO, error);
                     }
                     ++i;
-                }] setAllowedMachineIDsWithContainer:container context:context allowedMachineIDs:allowedMachineIDs reply:reply];
+        }] setAllowedMachineIDsWithContainer:container context:context allowedMachineIDs:allowedMachineIDs honorIDMSListChanges:accountIsDemo reply:reply];
     } while (retry);
 }
 
     } while (retry);
 }
 
@@ -309,7 +310,7 @@ enum {NUM_RETRIES = 5};
                   deviceName:(nullable NSString*)deviceName
                 serialNumber:(NSString *)serialNumber
                    osVersion:(NSString *)osVersion
                   deviceName:(nullable NSString*)deviceName
                 serialNumber:(NSString *)serialNumber
                    osVersion:(NSString *)osVersion
-                         policyVersion:(nullable NSNumber *)policyVersion
+               policyVersion:(nullable TPPolicy *)policyVersion
                policySecrets:(nullable NSDictionary<NSString*,NSData*> *)policySecrets
  signingPrivKeyPersistentRef:(nullable NSData *)spkPr
      encPrivKeyPersistentRef:(nullable NSData*)epkPr
                policySecrets:(nullable NSDictionary<NSString*,NSData*> *)policySecrets
  signingPrivKeyPersistentRef:(nullable NSData *)spkPr
      encPrivKeyPersistentRef:(nullable NSData*)epkPr
@@ -318,6 +319,8 @@ enum {NUM_RETRIES = 5};
                                        NSData * _Nullable permanentInfoSig,
                                        NSData * _Nullable stableInfo,
                                        NSData * _Nullable stableInfoSig,
                                        NSData * _Nullable permanentInfoSig,
                                        NSData * _Nullable stableInfo,
                                        NSData * _Nullable stableInfoSig,
+                                       NSSet<NSString*>* syncingViews,
+                                       TPPolicy* _Nullable syncingPolicy,
                                        NSError * _Nullable error))reply
 {
     __block int i = 0;
                                        NSError * _Nullable error))reply
 {
     __block int i = 0;
@@ -330,10 +333,24 @@ enum {NUM_RETRIES = 5};
                         retry = true;
                     } else {
                         secerror("octagon: Can't talk with TrustedPeersHelper: %@", error);
                         retry = true;
                     } else {
                         secerror("octagon: Can't talk with TrustedPeersHelper: %@", error);
-                        reply(nil, nil, nil, nil, nil, error);
+                        reply(nil, nil, nil, nil, nil, nil, nil, error);
                     }
                     ++i;
                     }
                     ++i;
-                }] prepareWithContainer:container context:context epoch:epoch machineID:machineID bottleSalt:bottleSalt bottleID:bottleID modelID:modelID deviceName:deviceName serialNumber:serialNumber osVersion:osVersion policyVersion:policyVersion policySecrets:policySecrets signingPrivKeyPersistentRef:spkPr encPrivKeyPersistentRef:epkPr reply:reply];
+                }] prepareWithContainer:container
+         context:context
+         epoch:epoch
+         machineID:machineID
+         bottleSalt:bottleSalt
+         bottleID:bottleID
+         modelID:modelID
+         deviceName:deviceName
+         serialNumber:serialNumber
+         osVersion:osVersion
+         policyVersion:policyVersion
+         policySecrets:policySecrets
+         signingPrivKeyPersistentRef:spkPr
+         encPrivKeyPersistentRef:epkPr
+         reply:reply];
     } while (retry);
 }
 
     } while (retry);
 }
 
@@ -396,7 +413,10 @@ enum {NUM_RETRIES = 5};
 - (void)preflightVouchWithBottleWithContainer:(nonnull NSString *)container
                                       context:(nonnull NSString *)context
                                      bottleID:(nonnull NSString *)bottleID
 - (void)preflightVouchWithBottleWithContainer:(nonnull NSString *)container
                                       context:(nonnull NSString *)context
                                      bottleID:(nonnull NSString *)bottleID
-                                        reply:(nonnull void (^)(NSString * _Nullable, NSError * _Nullable))reply {
+                                        reply:(nonnull void (^)(NSString * _Nullable,
+                                                                NSSet<NSString*>* _Nullable peerSyncingViewList,
+                                                                TPPolicy * _Nullable peerSyncingPolicy,
+                                                                NSError * _Nullable))reply {
     __block int i = 0;
     __block bool retry;
     do {
     __block int i = 0;
     __block bool retry;
     do {
@@ -407,7 +427,7 @@ enum {NUM_RETRIES = 5};
                         retry = true;
                     } else {
                         secerror("octagon: Can't talk with TrustedPeersHelper: %@", error);
                         retry = true;
                     } else {
                         secerror("octagon: Can't talk with TrustedPeersHelper: %@", error);
-                        reply(nil, error);
+                        reply(nil, nil, nil, error);
                     }
                     ++i;
                 }] preflightVouchWithBottleWithContainer:container
                     }
                     ++i;
                 }] preflightVouchWithBottleWithContainer:container
@@ -425,6 +445,8 @@ enum {NUM_RETRIES = 5};
                            tlkShares:(NSArray<CKKSTLKShare*> *)tlkShares
                                reply:(void (^)(NSData * _Nullable voucher,
                                                NSData * _Nullable voucherSig,
                            tlkShares:(NSArray<CKKSTLKShare*> *)tlkShares
                                reply:(void (^)(NSData * _Nullable voucher,
                                                NSData * _Nullable voucherSig,
+                                               int64_t uniqueTLKsRecovered,
+                                               int64_t totalTLKSharesRecovered,
                                                NSError * _Nullable error))reply
 {
     __block int i = 0;
                                                NSError * _Nullable error))reply
 {
     __block int i = 0;
@@ -437,13 +459,42 @@ enum {NUM_RETRIES = 5};
                         retry = true;
                     } else {
                         secerror("octagon: Can't talk with TrustedPeersHelper: %@", error);
                         retry = true;
                     } else {
                         secerror("octagon: Can't talk with TrustedPeersHelper: %@", error);
-                        reply(nil, nil, error);
+                        reply(nil, nil, 0, 0, error);
                     }
                     ++i;
                 }] vouchWithBottleWithContainer:container context:context bottleID:bottleID entropy:entropy bottleSalt:bottleSalt tlkShares:tlkShares reply:reply];
     } while (retry);
 }
 
                     }
                     ++i;
                 }] vouchWithBottleWithContainer:container context:context bottleID:bottleID entropy:entropy bottleSalt:bottleSalt tlkShares:tlkShares reply:reply];
     } while (retry);
 }
 
+- (void)preflightVouchWithRecoveryKeyWithContainer:(nonnull NSString *)container
+                                           context:(nonnull NSString *)context
+                                       recoveryKey:(NSString*)recoveryKey
+                                              salt:(NSString*)salt
+                                             reply:(nonnull void (^)(NSString * _Nullable,
+                                                                     NSSet<NSString*>* _Nullable peerSyncingViewList,
+                                                                     TPPolicy * _Nullable peerSyncingPolicy,
+                                                                     NSError * _Nullable))reply {
+    __block int i = 0;
+    __block bool retry;
+    do {
+        retry = false;
+        [[self.cuttlefishXPCConnection synchronousRemoteObjectProxyWithErrorHandler:^(NSError *_Nonnull error) {
+                    if (i < NUM_RETRIES && [self.class retryable:error]) {
+                        secnotice("octagon", "retrying cuttlefish XPC, (%d, %@)", i, error);
+                        retry = true;
+                    } else {
+                        secerror("octagon: Can't talk with TrustedPeersHelper: %@", error);
+                        reply(nil, nil, nil, error);
+                    }
+                    ++i;
+                }] preflightVouchWithRecoveryKeyWithContainer:container
+                                                 context:context
+                                             recoveryKey:recoveryKey
+                                                    salt:salt
+                                                   reply:reply];
+    } while (retry);
+}
+
 - (void)vouchWithRecoveryKeyWithContainer:(NSString *)container
                                   context:(NSString *)context
                               recoveryKey:(NSString*)recoveryKey
 - (void)vouchWithRecoveryKeyWithContainer:(NSString *)container
                                   context:(NSString *)context
                               recoveryKey:(NSString*)recoveryKey
@@ -479,6 +530,8 @@ enum {NUM_RETRIES = 5};
           preapprovedKeys:(NSArray<NSData*> *)preapprovedKeys
                     reply:(void (^)(NSString * _Nullable peerID,
                                     NSArray<CKRecord*>* _Nullable keyHierarchyRecords,
           preapprovedKeys:(NSArray<NSData*> *)preapprovedKeys
                     reply:(void (^)(NSString * _Nullable peerID,
                                     NSArray<CKRecord*>* _Nullable keyHierarchyRecords,
+                                    NSSet<NSString*>* _Nullable viewSet,
+                                    TPPolicy* _Nullable syncingPolicy,
                                     NSError * _Nullable error))reply
 {
     __block int i = 0;
                                     NSError * _Nullable error))reply
 {
     __block int i = 0;
@@ -491,7 +544,7 @@ enum {NUM_RETRIES = 5};
                         retry = true;
                     } else {
                         secerror("octagon: Can't talk with TrustedPeersHelper: %@", error);
                         retry = true;
                     } else {
                         secerror("octagon: Can't talk with TrustedPeersHelper: %@", error);
-                        reply(nil, nil, error);
+                        reply(nil, nil, nil, nil, error);
                     }
                     ++i;
                 }] joinWithContainer:container context:context voucherData:voucherData voucherSig:voucherSig ckksKeys:viewKeySets tlkShares:tlkShares preapprovedKeys:preapprovedKeys reply:reply];
                     }
                     ++i;
                 }] joinWithContainer:container context:context voucherData:voucherData voucherSig:voucherSig ckksKeys:viewKeySets tlkShares:tlkShares preapprovedKeys:preapprovedKeys reply:reply];
@@ -527,6 +580,8 @@ enum {NUM_RETRIES = 5};
                             preapprovedKeys:(NSArray<NSData*> *)preapprovedKeys
                                       reply:(void (^)(NSString * _Nullable peerID,
                                                       NSArray<CKRecord*>* _Nullable keyHierarchyRecords,
                             preapprovedKeys:(NSArray<NSData*> *)preapprovedKeys
                                       reply:(void (^)(NSString * _Nullable peerID,
                                                       NSArray<CKRecord*>* _Nullable keyHierarchyRecords,
+                                                      NSSet<NSString*>* _Nullable syncingViewList,
+                                                      TPPolicy* _Nullable syncingPolicy,
                                                       NSError * _Nullable error))reply
 {
     __block int i = 0;
                                                       NSError * _Nullable error))reply
 {
     __block int i = 0;
@@ -539,7 +594,7 @@ enum {NUM_RETRIES = 5};
                         retry = true;
                     } else {
                         secerror("octagon: Can't talk with TrustedPeersHelper: %@", error);
                         retry = true;
                     } else {
                         secerror("octagon: Can't talk with TrustedPeersHelper: %@", error);
-                        reply(nil, nil, error);
+                        reply(nil, nil, nil, nil, error);
                     }
                     ++i;
                 }] attemptPreapprovedJoinWithContainer:container context:context ckksKeys:ckksKeys tlkShares:tlkShares preapprovedKeys:preapprovedKeys reply:reply];
                     }
                     ++i;
                 }] attemptPreapprovedJoinWithContainer:container context:context ckksKeys:ckksKeys tlkShares:tlkShares preapprovedKeys:preapprovedKeys reply:reply];
@@ -575,7 +630,7 @@ enum {NUM_RETRIES = 5};
 - (void)setPreapprovedKeysWithContainer:(NSString *)container
                                 context:(NSString *)context
                         preapprovedKeys:(NSArray<NSData*> *)preapprovedKeys
 - (void)setPreapprovedKeysWithContainer:(NSString *)container
                                 context:(NSString *)context
                         preapprovedKeys:(NSArray<NSData*> *)preapprovedKeys
-                                  reply:(void (^)(NSError * _Nullable error))reply
+                                  reply:(void (^)(TrustedPeersHelperPeerState* _Nullable peerState, NSError * _Nullable error))reply
 {
     __block int i = 0;
     __block bool retry;
 {
     __block int i = 0;
     __block bool retry;
@@ -587,7 +642,7 @@ enum {NUM_RETRIES = 5};
                         retry = true;
                     } else {
                         secerror("octagon: Can't talk with TrustedPeersHelper: %@", error);
                         retry = true;
                     } else {
                         secerror("octagon: Can't talk with TrustedPeersHelper: %@", error);
-                        reply(error);
+                        reply(nil, error);
                     }
                     ++i;
                 }] setPreapprovedKeysWithContainer:container context:context preapprovedKeys:preapprovedKeys reply:reply];
                     }
                     ++i;
                 }] setPreapprovedKeysWithContainer:container context:context preapprovedKeys:preapprovedKeys reply:reply];
@@ -664,8 +719,8 @@ enum {NUM_RETRIES = 5};
 
 - (void)fetchPolicyDocumentsWithContainer:(NSString*)container
                                   context:(NSString*)context
 
 - (void)fetchPolicyDocumentsWithContainer:(NSString*)container
                                   context:(NSString*)context
-                                     keys:(NSDictionary<NSNumber*,NSString*>*)keys
-                                    reply:(void (^)(NSDictionary<NSNumber*,NSArray<NSString*>*>* _Nullable entries,
+                                 versions:(NSSet<TPPolicyVersion*>*)versions
+                                    reply:(void (^)(NSDictionary<TPPolicyVersion*, NSData*>* _Nullable entries,
                                                     NSError * _Nullable error))reply
 {
     __block int i = 0;
                                                     NSError * _Nullable error))reply
 {
     __block int i = 0;
@@ -681,14 +736,15 @@ enum {NUM_RETRIES = 5};
                         reply(nil, error);
                     }
                     ++i;
                         reply(nil, error);
                     }
                     ++i;
-                }] fetchPolicyDocumentsWithContainer:container context:context keys:keys reply:reply];
+                }] fetchPolicyDocumentsWithContainer:container context:context versions:versions reply:reply];
     } while (retry);
 }
 
     } while (retry);
 }
 
-- (void)fetchPolicyWithContainer:(NSString*)container
-                         context:(NSString*)context
-                           reply:(void (^)(TPPolicy * _Nullable policy,
-                                           NSError * _Nullable error))reply
+- (void)fetchCurrentPolicyWithContainer:(NSString*)container
+                                context:(NSString*)context
+                                  reply:(void (^)(NSSet<NSString*>* _Nullable viewList,
+                                                  TPPolicy * _Nullable policy,
+                                                  NSError * _Nullable error))reply
 {
     __block int i = 0;
     __block bool retry;
 {
     __block int i = 0;
     __block bool retry;
@@ -700,10 +756,10 @@ enum {NUM_RETRIES = 5};
                         retry = true;
                     } else {
                         secerror("octagon: Can't talk with TrustedPeersHelper: %@", error);
                         retry = true;
                     } else {
                         secerror("octagon: Can't talk with TrustedPeersHelper: %@", error);
-                        reply(nil, error);
+                        reply(nil, nil, error);
                     }
                     ++i;
                     }
                     ++i;
-                }] fetchPolicyWithContainer:container context:context reply:reply];
+                }] fetchCurrentPolicyWithContainer:container context:context reply:reply];
     } while (retry);
 }
 
     } while (retry);
 }
 
@@ -820,32 +876,10 @@ enum {NUM_RETRIES = 5};
     } while (retry);
 }
 
     } while (retry);
 }
 
-- (void)getViewsWithContainer:(NSString *)container
-                      context:(NSString *)context
-                     inViews:(NSArray<NSString*>*)inViews
-                        reply:(void (^)(NSArray<NSString*>* _Nullable, NSError* _Nullable))reply
-{
-    __block int i = 0;
-    __block bool retry;
-    do {
-        retry = false;
-        [[self.cuttlefishXPCConnection synchronousRemoteObjectProxyWithErrorHandler:^(NSError *_Nonnull error) {
-                    if (i < NUM_RETRIES && [self.class retryable:error]) {
-                        secnotice("octagon", "retrying cuttlefish XPC, (%d, %@)", i, error);
-                        retry = true;
-                    } else {
-                        secerror("octagon: Can't talk with TrustedPeersHelper: %@", error);
-                        reply(nil, error);
-                    }
-                    ++i;
-                }] getViewsWithContainer:container context:context inViews:inViews reply:reply];
-    } while (retry);
-}
-
 - (void)requestHealthCheckWithContainer:(NSString *)container
                                 context:(NSString *)context
                     requiresEscrowCheck:(BOOL)requiresEscrowCheck
 - (void)requestHealthCheckWithContainer:(NSString *)container
                                 context:(NSString *)context
                     requiresEscrowCheck:(BOOL)requiresEscrowCheck
-                                  reply:(void (^)(BOOL postRepairCFU, BOOL postEscrowCFU, BOOL resetOctagon, NSError* _Nullable))reply
+                                  reply:(void (^)(BOOL postRepairCFU, BOOL postEscrowCFU, BOOL resetOctagon, BOOL leaveTrust, NSError* _Nullable))reply
 {
     __block int i = 0;
     __block bool retry;
 {
     __block int i = 0;
     __block bool retry;
@@ -857,10 +891,10 @@ enum {NUM_RETRIES = 5};
                         retry = true;
                     } else {
                         secerror("octagon: Can't talk with TrustedPeersHelper: %@", error);
                         retry = true;
                     } else {
                         secerror("octagon: Can't talk with TrustedPeersHelper: %@", error);
-                        reply(NO, NO, NO, error);
+                        reply(NO, NO, NO, NO, error);
                     }
                     ++i;
                     }
                     ++i;
-                }] requestHealthCheckWithContainer:container context:context requiresEscrowCheck:requiresEscrowCheck reply:reply];
+        }] requestHealthCheckWithContainer:container context:context requiresEscrowCheck:requiresEscrowCheck reply:reply];
     } while (retry);
 }
 
     } while (retry);
 }
 
index c5246045529e1a29b559e8206524647d948a3cc1..4e515885f9facd588824ec59711e2b2bdde451ab 100644 (file)
@@ -31,6 +31,7 @@ void OctagonInitialize(void)
 {
     OTManager* manager = [OTManager manager];
     [manager initializeOctagon];
 {
     OTManager* manager = [OTManager manager];
     [manager initializeOctagon];
+    [manager setupAnalytics];
 }
 
 // If you want octagon to be initialized in your daemon/tests, you must set this to be true
 }
 
 // If you want octagon to be initialized in your daemon/tests, you must set this to be true
@@ -48,11 +49,7 @@ void OctagonSetShouldPerformInitialization(bool value)
 void SecOctagon24hrNotification(void) {
 #if OCTAGON
     @autoreleasepool {
 void SecOctagon24hrNotification(void) {
 #if OCTAGON
     @autoreleasepool {
-        [[OTManager manager] xpc24HrNotification:OTCKContainerName context:OTDefaultContext skipRateLimitingCheck:NO reply: ^(NSError * error) {
-            if(error){
-                secerror("error attempting to check octagon health: %@", error);
-            }
-        }];
+        [[OTManager manager] xpc24HrNotification];
     }
 #endif
 }
     }
 #endif
 }
index 5539aa1f5d28361ededa927b08326ce9c58d7b4c..e0f4ec032a7deccd9fe86d048e863c2494889c2d 100644 (file)
@@ -20,6 +20,7 @@ NS_ASSUME_NONNULL_BEGIN
 - (NSString* _Nullable)primaryiCloudAccountAltDSID:(NSError **)error;
 
 - (BOOL)accountIsHSA2ByAltDSID:(NSString*)altDSID;
 - (NSString* _Nullable)primaryiCloudAccountAltDSID:(NSError **)error;
 
 - (BOOL)accountIsHSA2ByAltDSID:(NSString*)altDSID;
+- (BOOL)accountIsDemoAccount:(NSError**)error NS_SWIFT_NOTHROW;
 
 - (NSString* _Nullable)machineID:(NSError**)error;
 - (void)fetchCurrentDeviceList:(void (^)(NSSet<NSString*>* _Nullable machineIDs, NSError* _Nullable error))complete;
 
 - (NSString* _Nullable)machineID:(NSError**)error;
 - (void)fetchCurrentDeviceList:(void (^)(NSSet<NSString*>* _Nullable machineIDs, NSError* _Nullable error))complete;
index b9452d2e6e4e17b313c68753fdc29a3a411f40ad..80e1ff92e99e81c25af243d3d8e9753958770715 100644 (file)
 
 - (BOOL)accountIsHSA2ByAltDSID:(NSString*)altDSID
 {
 
 - (BOOL)accountIsHSA2ByAltDSID:(NSString*)altDSID
 {
-    bool hsa2 = false;
+    BOOL hsa2 = NO;
 
     AKAccountManager *manager = [AKAccountManager sharedInstance];
     ACAccount *authKitAccount = [manager authKitAccountWithAltDSID:altDSID];
     AKAppleIDSecurityLevel securityLevel = [manager securityLevelForAccount:authKitAccount];
     if(securityLevel == AKAppleIDSecurityLevelHSA2) {
 
     AKAccountManager *manager = [AKAccountManager sharedInstance];
     ACAccount *authKitAccount = [manager authKitAccountWithAltDSID:altDSID];
     AKAppleIDSecurityLevel securityLevel = [manager securityLevelForAccount:authKitAccount];
     if(securityLevel == AKAppleIDSecurityLevelHSA2) {
-        hsa2 = true;
+        hsa2 = YES;
     }
     secnotice("security-authkit", "Security level for altDSID %@ is %lu", altDSID, (unsigned long)securityLevel);
     return hsa2;
 }
 
     }
     secnotice("security-authkit", "Security level for altDSID %@ is %lu", altDSID, (unsigned long)securityLevel);
     return hsa2;
 }
 
+- (BOOL)accountIsDemoAccount:(NSError**)error
+{
+    NSError* localError = nil;
+    NSString* altDSID = [self primaryiCloudAccountAltDSID:&localError];
+
+    if(altDSID == nil) {
+        secerror("octagon-authkit:could not retrieve altDSID");
+    }
+    if (localError) {
+        secerror("octagon-authkit: hit an error retrieving altDSID: %@", localError);
+        if(error){
+            *error = localError;
+        }
+        return NO;
+    }
+    
+    AKAccountManager *manager = [AKAccountManager sharedInstance];
+    ACAccount *authKitAccount = [manager authKitAccountWithAltDSID:altDSID];
+    BOOL isDemo = [manager demoAccountForAccount:authKitAccount];
+
+    secnotice("security-authkit", "Account with altDSID %@ is a demo account: %@", altDSID, isDemo ? @"true" : @"false");
+
+    return isDemo;
+}
+
 - (NSString* _Nullable)machineID:(NSError**)error
 {
     AKAnisetteProvisioningController* anisetteController = [[AKAnisetteProvisioningController alloc] init];
 - (NSString* _Nullable)machineID:(NSError**)error
 {
     AKAnisetteProvisioningController* anisetteController = [[AKAnisetteProvisioningController alloc] init];
index e3463e6dfa9b6d4c72bd87ef062b38930c5c18c8..430fa43fb34ff640c29a400b1cf133970d7e236d 100644 (file)
@@ -53,6 +53,7 @@ NS_ASSUME_NONNULL_BEGIN
 @property BOOL postRepairCFU;
 @property BOOL postEscrowCFU;
 @property BOOL resetOctagon;
 @property BOOL postRepairCFU;
 @property BOOL postEscrowCFU;
 @property BOOL resetOctagon;
+@property BOOL leaveTrust;
 
 @end
 
 
 @end
 
index b76ce4621d554fb3c6f9c55830d3d997ad4c7c1f..8158222aa12ca97dc03121d85849fad40ab7f1ef 100644 (file)
@@ -61,6 +61,7 @@
         _postRepairCFU = NO;
         _postEscrowCFU = NO;
         _resetOctagon = NO;
         _postRepairCFU = NO;
         _postEscrowCFU = NO;
         _resetOctagon = NO;
+        _leaveTrust = NO;
         _skipRateLimitingCheck = skipRateLimitedCheck;
     }
     return self;
         _skipRateLimitingCheck = skipRateLimitedCheck;
     }
     return self;
         self.error = nil;
 
         lastUpdate = [self.deps.stateHolder lastHealthCheckupDate:&accountLoadError];
         self.error = nil;
 
         lastUpdate = [self.deps.stateHolder lastHealthCheckupDate:&accountLoadError];
-        if([self.deps.viewManager.lockStateTracker isLockedError: accountLoadError]) {
+
+        CKKSViewManager* viewManager = self.deps.viewManager;
+        if([viewManager.lockStateTracker isLockedError: accountLoadError]) {
             secnotice("octagon-health", "device is locked, not performing cuttlefish check");
             [self runBeforeGroupFinished:self.finishOp];
             return;
             secnotice("octagon-health", "device is locked, not performing cuttlefish check");
             [self runBeforeGroupFinished:self.finishOp];
             return;
         NSError* persistedError = nil;
         BOOL persisted = [self.deps.stateHolder persistLastHealthCheck:now error:&persistedError];
 
         NSError* persistedError = nil;
         BOOL persisted = [self.deps.stateHolder persistLastHealthCheck:now error:&persistedError];
 
-        if([self.deps.viewManager.lockStateTracker isLockedError: persistedError]) {
+        if([viewManager.lockStateTracker isLockedError: persistedError]) {
             secnotice("octagon-health", "device is locked, not performing cuttlefish check");
             [self runBeforeGroupFinished:self.finishOp];
             return;
             secnotice("octagon-health", "device is locked, not performing cuttlefish check");
             [self runBeforeGroupFinished:self.finishOp];
             return;
     [self.deps.cuttlefishXPCWrapper requestHealthCheckWithContainer:self.deps.containerName
                                                             context:self.deps.contextID
                                                 requiresEscrowCheck: [self checkIfPasscodeIsSetForDevice]
     [self.deps.cuttlefishXPCWrapper requestHealthCheckWithContainer:self.deps.containerName
                                                             context:self.deps.contextID
                                                 requiresEscrowCheck: [self checkIfPasscodeIsSetForDevice]
-                                                              reply:^(BOOL postRepairCFU, BOOL postEscrowCFU, BOOL resetOctagon, NSError *error) {
+                                                              reply:^(BOOL postRepairCFU, BOOL postEscrowCFU, BOOL resetOctagon, BOOL leaveTrust, NSError *error) {
             STRONGIFY(self);
             if(error) {
                 secerror("octagon-health: error: %@", error);
             STRONGIFY(self);
             if(error) {
                 secerror("octagon-health: error: %@", error);
                 [self runBeforeGroupFinished:self.finishOp];
                 return;
             } else {
                 [self runBeforeGroupFinished:self.finishOp];
                 return;
             } else {
-                secnotice("octagon-health", "cuttlefish came back with these suggestions\n: post repair? %d\n, post escrow? %d\n, reset octagon? %d", postRepairCFU, postEscrowCFU, resetOctagon);
+                secnotice("octagon-health", "cuttlefish came back with these suggestions\n: post repair? %d\n, post escrow? %d\n, reset octagon? %d\n leave trust? %d\n", postRepairCFU, postEscrowCFU, resetOctagon, leaveTrust);
                 [self handleRepairSuggestions:postRepairCFU
                                 postEscrowCFU:postEscrowCFU
                 [self handleRepairSuggestions:postRepairCFU
                                 postEscrowCFU:postEscrowCFU
-                                 resetOctagon:resetOctagon];
+                                 resetOctagon:resetOctagon
+                                   leaveTrust:leaveTrust];
             }
         }];
 }
 
             }
         }];
 }
 
-- (void)handleRepairSuggestions:(BOOL)postRepairCFU postEscrowCFU:(BOOL)postEscrowCFU resetOctagon:(BOOL)resetOctagon
+- (void)handleRepairSuggestions:(BOOL)postRepairCFU postEscrowCFU:(BOOL)postEscrowCFU resetOctagon:(BOOL)resetOctagon leaveTrust:(BOOL)leaveTrust
 {
     self.postEscrowCFU = postEscrowCFU;
     self.postRepairCFU = postRepairCFU;
     self.resetOctagon = resetOctagon;
 {
     self.postEscrowCFU = postEscrowCFU;
     self.postRepairCFU = postRepairCFU;
     self.resetOctagon = resetOctagon;
+    self.leaveTrust = leaveTrust;
 
     if (resetOctagon) {
         secnotice("octagon-health", "Resetting Octagon as per Cuttlefish request");
         self.nextState = OctagonStateHealthCheckReset;
 
     if (resetOctagon) {
         secnotice("octagon-health", "Resetting Octagon as per Cuttlefish request");
         self.nextState = OctagonStateHealthCheckReset;
+    } else if(leaveTrust) {
+        secnotice("octagon-health", "Leaving clique as per Cuttlefish request");
+        self.nextState = OctagonStateHealthCheckLeaveClique;
     } else {
         self.nextState = self.intendedState;
     }
     } else {
         self.nextState = self.intendedState;
     }
index b36a24aacaa260115f75b55fd00168101f3a8129..88f684f1b07bd16dc23b94ec311b6341f7f6f1f5 100644 (file)
@@ -42,7 +42,6 @@
 
 #import "keychain/ot/OTConstants.h"
 #import "keychain/ot/OTClientStateMachine.h"
 
 #import "keychain/ot/OTConstants.h"
 #import "keychain/ot/OTClientStateMachine.h"
-#import "keychain/ot/OTPrepareOperation.h"
 #import "keychain/ot/OTSOSAdapter.h"
 #import "keychain/ot/OTEpochOperation.h"
 #import "keychain/ot/OTClientVoucherOperation.h"
 #import "keychain/ot/OTSOSAdapter.h"
 #import "keychain/ot/OTEpochOperation.h"
 #import "keychain/ot/OTClientVoucherOperation.h"
index 772172214e683f33bdbcf977cce9c49c8ae70aeb..cfe080f159ece349964878f71f355feb2ab91064 100644 (file)
@@ -44,27 +44,41 @@ typedef NS_ENUM(NSInteger, CliqueStatus) {
 #import <Security/SecureObjectSync/SOSTypes.h>
 #import <Security/OTConstants.h>
 
 #import <Security/SecureObjectSync/SOSTypes.h>
 #import <Security/OTConstants.h>
 
+typedef NS_ENUM(NSInteger, OTCDPStatus) {
+    OTCDPStatusUnknown = 0,
+    OTCDPStatusDisabled = 1,
+    OTCDPStatusEnabled = 2,
+};
+
 NS_ASSUME_NONNULL_BEGIN
 
 NSString* OTCliqueStatusToString(CliqueStatus status);
 CliqueStatus OTCliqueStatusFromString(NSString* str);
 NS_ASSUME_NONNULL_BEGIN
 
 NSString* OTCliqueStatusToString(CliqueStatus status);
 CliqueStatus OTCliqueStatusFromString(NSString* str);
+NSString* OTCDPStatusToString(OTCDPStatus status);
 
 @class KCPairingChannelContext;
 @class KCPairingChannel;
 @class OTPairingChannel;
 @class OTPairingChannelContext;
 @class OTControl;
 
 @class KCPairingChannelContext;
 @class KCPairingChannel;
 @class OTPairingChannel;
 @class OTPairingChannelContext;
 @class OTControl;
+@class CKKSControl;
 
 extern NSString* kSecEntitlementPrivateOctagonEscrow;
 
 @interface OTConfigurationContext : NSObject
 
 extern NSString* kSecEntitlementPrivateOctagonEscrow;
 
 @interface OTConfigurationContext : NSObject
-@property (nonatomic, copy, nullable) NSString* context;
-@property (nonatomic, copy) NSString* dsid;
-@property (nonatomic, copy) NSString* altDSID;
+@property (nonatomic, copy) NSString* context;
+@property (nonatomic, copy, nullable) NSString* dsid;
+@property (nonatomic, copy, nullable) NSString* altDSID;
 @property (nonatomic, strong, nullable) SFSignInAnalytics* analytics;
 @property (nonatomic, strong, nullable) SFSignInAnalytics* analytics;
+@property (nonatomic, copy, nullable) NSString* authenticationAppleID;
+@property (nonatomic, copy, nullable) NSString* passwordEquivalentToken;
 
 // Use this to inject your own OTControl object. It must be configured as synchronous.
 @property (nullable, strong) OTControl* otControl;
 
 // Use this to inject your own OTControl object. It must be configured as synchronous.
 @property (nullable, strong) OTControl* otControl;
+
+// Use this to inject your own CKKSControl object. It must be configured as synchronous.
+@property (nullable, strong) CKKSControl* ckksControl;
+
 // Use this to inject your own SecureBackup object. It must conform to the OctagonEscrowRecoverer protocol.
 @property (nullable, strong) id sbd;
 
 // Use this to inject your own SecureBackup object. It must conform to the OctagonEscrowRecoverer protocol.
 @property (nullable, strong) id sbd;
 
@@ -110,10 +124,15 @@ extern OTCliqueCDPContextType OTCliqueCDPContextTypeUpdatePasscode;
 
 /* *
  * @abstract, initializes a clique object given a context.  A clique object enables octagon trust operations for a given context and dsid.
 
 /* *
  * @abstract, initializes a clique object given a context.  A clique object enables octagon trust operations for a given context and dsid.
- * @param ctx, a unique string that is used as a way to retrieve current trust state
+ * @param ctx, a collection of arguments describing the world
  * @return an instance of octagon trust
  */
  * @return an instance of octagon trust
  */
-- (instancetype _Nullable)initWithContextData:(OTConfigurationContext *)ctx error:(NSError * __autoreleasing * _Nonnull)error;
+- (instancetype)initWithContextData:(OTConfigurationContext *)ctx;
+
+/*
+ * Much like initWithContextData, but might fail. There are currently no failures possible.
+ */
+- (instancetype _Nullable)initWithContextData:(OTConfigurationContext *)ctx error:(NSError**)error __deprecated_msg("Use initWithContextData instead");
 
 /* *
  * @abstract   Establish a new clique, reset protected data
 
 /* *
  * @abstract   Establish a new clique, reset protected data
@@ -155,10 +174,8 @@ extern OTCliqueCDPContextType OTCliqueCDPContextTypeUpdatePasscode;
 /* *
  * @abstract   Create pairing channel with
  *
 /* *
  * @abstract   Create pairing channel with
  *
- * @param ctx, context containing parameters to setup OTClique
- * @param pairingChannelContext, context containing parameters to setup the pairing channel as the initiator
- * @return  clique, An instance of an OTClique
- * @return  error, error gets filled if something goes horribly wrong
+ * @param ctx, context containing parameters to setup the pairing channel as the initiator
+ * @return  KCPairingChannel, An instance of a KCPairingCHannel
  */
 - (KCPairingChannel *)setupPairingChannelAsInitiator:(KCPairingChannelContext *)ctx;
 
  */
 - (KCPairingChannel *)setupPairingChannelAsInitiator:(KCPairingChannelContext *)ctx;
 
@@ -167,10 +184,8 @@ extern OTCliqueCDPContextType OTCliqueCDPContextTypeUpdatePasscode;
 /* *
  * @abstract   Configure this peer as the acceptor during piggybacking
  *
 /* *
  * @abstract   Configure this peer as the acceptor during piggybacking
  *
- * @param ctx, context containing parameters to setup OTClique
- * @param pairingChannelContext, context containing parameters to setup the pairing channel as the acceptor
- * @param error, error gets filled if something goes horribly wrong
- * @return  KCPairingChannel, An instance of an OTClique
+ * @param ctx, context containing parameters to setup the pairing channel as the acceptor
+ * @return  KCPairingChannel, An instance of a KCPairingChannel
  */
 - (KCPairingChannel *)setupPairingChannelAsAcceptor:(KCPairingChannelContext *)ctx;
 
  */
 - (KCPairingChannel *)setupPairingChannelAsAcceptor:(KCPairingChannelContext *)ctx;
 
@@ -233,6 +248,15 @@ extern OTCliqueCDPContextType OTCliqueCDPContextTypeUpdatePasscode;
 - (NSDictionary<NSString*,NSString*>* _Nullable)peerDeviceNamesByPeerID:(NSError * __autoreleasing *)error;
 
 
 - (NSDictionary<NSString*,NSString*>* _Nullable)peerDeviceNamesByPeerID:(NSError * __autoreleasing *)error;
 
 
+/*
+ * CDP bit handling
+ */
+
++ (BOOL)setCDPEnabled:(OTConfigurationContext*)arguments
+                error:(NSError* __autoreleasing*)error;
+
++ (OTCDPStatus)getCDPStatus:(OTConfigurationContext*)arguments
+                      error:(NSError* __autoreleasing *)error;
 
 /* SOS glue */
 
 
 /* SOS glue */
 
@@ -321,6 +345,17 @@ extern OTCliqueCDPContextType OTCliqueCDPContextTypeUpdatePasscode;
 // CoreCDP will call this function when they are upgrading an account from SA to HSA2
 - (BOOL)waitForOctagonUpgrade:(NSError** _Nullable)error;
 
 // CoreCDP will call this function when they are upgrading an account from SA to HSA2
 - (BOOL)waitForOctagonUpgrade:(NSError** _Nullable)error;
 
+
+/*
+* @abstract CoreCDP to call this function when they need to reset protected data.
+*   This routine resets all circles, creates a new octagon and sos circle, then puts this device into each circle.
+*   This routine does not create a new escrow record
+*   This routine will need ensure OTConfigurationContext contains appleID and passwordEquivalentToken to delete all CDP records
+* @param data The OTClique configuration data
+* @param error Reports any error along the process
+* @return a new clique
+*/
++ (OTClique* _Nullable)resetProtectedData:(OTConfigurationContext*)data error:(NSError**)error;
 @end
 
 NS_ASSUME_NONNULL_END
 @end
 
 NS_ASSUME_NONNULL_END
index 5bf39ceee3605e50e3d072a024f868072d464a5e..fbf8e5dab09e8b3cfa4c2e7c2626c7a7144711f7 100644 (file)
@@ -47,6 +47,7 @@ const NSString* kSecEntitlementPrivateOctagonEscrow = @"com.apple.private.octago
 #import <CloudServices/SecureBackup.h>
 #import <CloudServices/SecureBackupConstants.h>
 #import "keychain/ot/OTControl.h"
 #import <CloudServices/SecureBackup.h>
 #import <CloudServices/SecureBackupConstants.h>
 #import "keychain/ot/OTControl.h"
+#import "keychain/ckks/CKKSControl.h"
 #import "keychain/ot/categories/OctagonEscrowRecoverer.h"
 
 SOFT_LINK_FRAMEWORK(PrivateFrameworks, KeychainCircle);
 #import "keychain/ot/categories/OctagonEscrowRecoverer.h"
 
 SOFT_LINK_FRAMEWORK(PrivateFrameworks, KeychainCircle);
@@ -58,6 +59,10 @@ SOFT_LINK_CLASS(KeychainCircle, KCPairingChannel);
 SOFT_LINK_CLASS(KeychainCircle, OTPairingChannel);
 SOFT_LINK_CLASS(CloudServices, SecureBackup);
 SOFT_LINK_CONSTANT(CloudServices, kSecureBackupErrorDomain, NSErrorDomain);
 SOFT_LINK_CLASS(KeychainCircle, OTPairingChannel);
 SOFT_LINK_CLASS(CloudServices, SecureBackup);
 SOFT_LINK_CONSTANT(CloudServices, kSecureBackupErrorDomain, NSErrorDomain);
+SOFT_LINK_CONSTANT(CloudServices, kSecureBackupAuthenticationAppleID, NSString*);
+SOFT_LINK_CONSTANT(CloudServices, kSecureBackupAuthenticationPassword, NSString*);
+SOFT_LINK_CONSTANT(CloudServices, kSecureBackupiCloudDataProtectionDeleteAllRecordsKey, NSString*);
+SOFT_LINK_CONSTANT(CloudServices, kSecureBackupContainsiCDPDataKey, NSString*);
 
 #pragma clang diagnostic pop
 #endif
 
 #pragma clang diagnostic pop
 #endif
@@ -106,6 +111,17 @@ CliqueStatus OTCliqueStatusFromString(NSString* str)
     return CliqueStatusError;
 }
 
     return CliqueStatusError;
 }
 
+NSString* OTCDPStatusToString(OTCDPStatus status) {
+    switch(status) {
+        case OTCDPStatusUnknown:
+            return @"unknown";
+        case OTCDPStatusDisabled:
+            return @"disabled";
+        case OTCDPStatusEnabled:
+            return @"enabled";
+    }
+}
+
 
 @implementation OTConfigurationContext
 - (OTControl* _Nullable)makeOTControl:(NSError**)error
 
 @implementation OTConfigurationContext
 - (OTControl* _Nullable)makeOTControl:(NSError**)error
@@ -119,6 +135,26 @@ CliqueStatus OTCliqueStatusFromString(NSString* str)
     return nil;
 #endif
 }
     return nil;
 #endif
 }
+
+- (CKKSControl* _Nullable)makeCKKSControl:(NSError**)error
+{
+#if OCTAGON
+    if(self.ckksControl) {
+        return self.ckksControl;
+    }
+    return [CKKSControl CKKSControlObject:true error:error];
+#else
+    return nil;
+#endif
+}
+
+- (instancetype)init
+{
+    if((self = [super init])) {
+        _context = OTDefaultContext;
+    }
+    return self;
+}
 @end
 
 @implementation OTBottleIDs
 @end
 
 @implementation OTBottleIDs
@@ -190,6 +226,11 @@ CliqueStatus OTCliqueStatusFromString(NSString* str)
 }
 
 - (instancetype)initWithContextData:(OTConfigurationContext *)ctx error:(NSError * __autoreleasing *)error
 }
 
 - (instancetype)initWithContextData:(OTConfigurationContext *)ctx error:(NSError * __autoreleasing *)error
+{
+    return [self initWithContextData:ctx];
+}
+
+- (instancetype)initWithContextData:(OTConfigurationContext *)ctx
 {
 #if OCTAGON
     self = [super init];
 {
 #if OCTAGON
     self = [super init];
@@ -200,13 +241,17 @@ CliqueStatus OTCliqueStatusFromString(NSString* str)
         _ctx.altDSID = [ctx.altDSID copy];
         _ctx.analytics = ctx.analytics;
         _ctx.otControl = ctx.otControl;
         _ctx.altDSID = [ctx.altDSID copy];
         _ctx.analytics = ctx.analytics;
         _ctx.otControl = ctx.otControl;
+        _ctx.ckksControl = ctx.ckksControl;
 
         self.defaults = [NSMutableDictionary dictionary];
     }
     return self;
 #else
     NSAssert(false, @"OTClique is not implemented on this platform");
 
         self.defaults = [NSMutableDictionary dictionary];
     }
     return self;
 #else
     NSAssert(false, @"OTClique is not implemented on this platform");
-    return nil;
+
+    // make the build analyzer happy
+    self = [super init];
+    return self;
 #endif // OCTAGON
 }
 
 #endif // OCTAGON
 }
 
@@ -242,6 +287,7 @@ CliqueStatus OTCliqueStatusFromString(NSString* str)
         SOSPeerInfoRef me = SOSCCCopyMyPeerInfo(&error);
         retPeerID =  (NSString*)CFBridgingRelease(CFRetainSafe(SOSPeerInfoGetPeerID(me)));
         CFReleaseNull(me);
         SOSPeerInfoRef me = SOSCCCopyMyPeerInfo(&error);
         retPeerID =  (NSString*)CFBridgingRelease(CFRetainSafe(SOSPeerInfoGetPeerID(me)));
         CFReleaseNull(me);
+        CFBridgingRelease(error);
     }
 
     secnotice("clique", "cliqueMemberIdentifier complete: %@", retPeerID);
     }
 
     secnotice("clique", "cliqueMemberIdentifier complete: %@", retPeerID);
@@ -278,7 +324,7 @@ CliqueStatus OTCliqueStatusFromString(NSString* str)
         if(operationError) {
             secnotice("clique-establish", "establish returned an error: %@", operationError);
         }
         if(operationError) {
             secnotice("clique-establish", "establish returned an error: %@", operationError);
         }
-        success = !!operationError;
+        success = operationError == nil;
         localError = operationError;
     }];
     
         localError = operationError;
     }];
     
@@ -312,7 +358,7 @@ CliqueStatus OTCliqueStatusFromString(NSString* str)
         if(operationError) {
             secnotice("clique-resetandestablish", "resetAndEstablish returned an error: %@", operationError);
         }
         if(operationError) {
             secnotice("clique-resetandestablish", "resetAndEstablish returned an error: %@", operationError);
         }
-        success = !!operationError;
+        success = operationError == nil;
         localError = operationError;
     }];
 
         localError = operationError;
     }];
 
@@ -341,7 +387,7 @@ CliqueStatus OTCliqueStatusFromString(NSString* str)
     bool subTaskSuccess = false;
     OctagonSignpost performEscrowRecoverySignpost = OctagonSignpostBegin(OctagonSignpostNameMakeNewFriends);
 
     bool subTaskSuccess = false;
     OctagonSignpost performEscrowRecoverySignpost = OctagonSignpostBegin(OctagonSignpostNameMakeNewFriends);
 
-    OTClique* clique = [[OTClique alloc] initWithContextData:data error:error];
+    OTClique* clique = [[OTClique alloc] initWithContextData:data];
 
     if(OctagonIsEnabled()) {
         NSError* localError = nil;
 
     if(OctagonIsEnabled()) {
         NSError* localError = nil;
@@ -361,22 +407,14 @@ CliqueStatus OTCliqueStatusFromString(NSString* str)
 
     if([OTClique platformSupportsSOS]) {
         CFErrorRef resetError = NULL;
 
     if([OTClique platformSupportsSOS]) {
         CFErrorRef resetError = NULL;
-        NSData* analyticsData = nil;
-        if(data.analytics) {
-            NSError* encodingError = nil;
-            analyticsData = [NSKeyedArchiver archivedDataWithRootObject:data.analytics requiringSecureCoding:YES error:&encodingError];
-
-            if(encodingError) {
-                secnotice("clique-newfriends", "newFriendsWithContextData: unable to serialize analytics: %@", encodingError);
-            }
-        }
-
         result = SOSCCResetToOffering(&resetError);
 
         if(!result || resetError){
             secnotice("clique-newfriends", "newFriendsWithContextData: resetToOffering failed: %@", resetError);
             if(error) {
                 *error = CFBridgingRelease(resetError);
         result = SOSCCResetToOffering(&resetError);
 
         if(!result || resetError){
             secnotice("clique-newfriends", "newFriendsWithContextData: resetToOffering failed: %@", resetError);
             if(error) {
                 *error = CFBridgingRelease(resetError);
+            } else {
+                CFBridgingRelease(resetError);
             }
             OctagonSignpostEnd(performEscrowRecoverySignpost, OctagonSignpostNameMakeNewFriends, OctagonSignpostNumber1(OctagonSignpostNameMakeNewFriends), (int)subTaskSuccess);
             return nil;
             }
             OctagonSignpostEnd(performEscrowRecoverySignpost, OctagonSignpostNameMakeNewFriends, OctagonSignpostNumber1(OctagonSignpostNameMakeNewFriends), (int)subTaskSuccess);
             return nil;
@@ -407,17 +445,8 @@ CliqueStatus OTCliqueStatusFromString(NSString* str)
     OctagonSignpost performEscrowRecoverySignpost = OctagonSignpostBegin(OctagonSignpostNamePerformEscrowRecovery);
     bool subTaskSuccess = false;
     NSError* localError = nil;
     OctagonSignpost performEscrowRecoverySignpost = OctagonSignpostBegin(OctagonSignpostNamePerformEscrowRecovery);
     bool subTaskSuccess = false;
     NSError* localError = nil;
-    OTClique* clique = [[OTClique alloc] initWithContextData:data
-                                                       error:&localError];
 
 
-    if(!clique || localError) {
-        secnotice("clique-recovery", "unable to create otclique: %@", localError);
-        if(error) {
-            *error = localError;
-        }
-        OctagonSignpostEnd(performEscrowRecoverySignpost, OctagonSignpostNamePerformEscrowRecovery, OctagonSignpostNumber1(OctagonSignpostNamePerformEscrowRecovery), (int)subTaskSuccess);
-        return nil;
-    }
+    OTClique* clique = [[OTClique alloc] initWithContextData:data];
 
     // Attempt the recovery from sbd
     secnotice("clique-recovery", "attempting an escrow recovery for context:%@, altdsid:%@", data.context, data.altDSID);
 
     // Attempt the recovery from sbd
     secnotice("clique-recovery", "attempting an escrow recovery for context:%@, altdsid:%@", data.context, data.altDSID);
@@ -441,6 +470,7 @@ CliqueStatus OTCliqueStatusFromString(NSString* str)
                 } else {
                     secnotice("clique-recovery", "resetting SOS circle successful");
                 }
                 } else {
                     secnotice("clique-recovery", "resetting SOS circle successful");
                 }
+                CFBridgingRelease(blowItAwayError);
             } else {
                 secnotice("clique-recovery", "Legacy restore failed on a non-SOS platform");
             }
             } else {
                 secnotice("clique-recovery", "Legacy restore failed on a non-SOS platform");
             }
@@ -1166,12 +1196,12 @@ CliqueStatus OTCliqueStatusFromString(NSString* str)
         }
 
         BOOL setCredentialsResult = result ? YES : NO;
         }
 
         BOOL setCredentialsResult = result ? YES : NO;
+        secnotice("clique-legacy", "setUserCredentialsAndDSID results: %d %@", setCredentialsResult, setCredentialsErrorRef);
         if (error) {
             *error = (NSError*)CFBridgingRelease(setCredentialsErrorRef);
         } else {
             CFBridgingRelease(setCredentialsErrorRef);
         }
         if (error) {
             *error = (NSError*)CFBridgingRelease(setCredentialsErrorRef);
         } else {
             CFBridgingRelease(setCredentialsErrorRef);
         }
-        secnotice("clique-legacy", "setUserCredentialsAndDSID results: %d %@", setCredentialsResult, setCredentialsErrorRef);
         subTaskSuccess = result;
         OctagonSignpostEnd(signPost, OctagonSignpostNameSetUserCredentialsAndDSID, OctagonSignpostNumber1(OctagonSignpostNameSetUserCredentialsAndDSID), (int)subTaskSuccess);
 
         subTaskSuccess = result;
         OctagonSignpostEnd(signPost, OctagonSignpostNameSetUserCredentialsAndDSID, OctagonSignpostNumber1(OctagonSignpostNameSetUserCredentialsAndDSID), (int)subTaskSuccess);
 
@@ -1204,12 +1234,12 @@ CliqueStatus OTCliqueStatusFromString(NSString* str)
                                                      &tryCredentialsErrorRef);
 
         BOOL tryCredentialsResult = result ? YES : NO;
                                                      &tryCredentialsErrorRef);
 
         BOOL tryCredentialsResult = result ? YES : NO;
+        secnotice("clique-legacy", "tryUserCredentialsAndDSID results: %d %@", tryCredentialsResult, tryCredentialsErrorRef);
         if (error) {
             *error = (NSError*)CFBridgingRelease(tryCredentialsErrorRef);
         } else {
             CFBridgingRelease(tryCredentialsErrorRef);
         }
         if (error) {
             *error = (NSError*)CFBridgingRelease(tryCredentialsErrorRef);
         } else {
             CFBridgingRelease(tryCredentialsErrorRef);
         }
-        secnotice("clique-legacy", "tryUserCredentialsAndDSID results: %d %@", tryCredentialsResult, tryCredentialsErrorRef);
         subTaskSuccess = result;
         OctagonSignpostEnd(signPost, OctagonSignpostNameTryUserCredentialsAndDSID, OctagonSignpostNumber1(OctagonSignpostNameTryUserCredentialsAndDSID), (int)subTaskSuccess);
         return tryCredentialsResult;
         subTaskSuccess = result;
         OctagonSignpostEnd(signPost, OctagonSignpostNameTryUserCredentialsAndDSID, OctagonSignpostNumber1(OctagonSignpostNameTryUserCredentialsAndDSID), (int)subTaskSuccess);
         return tryCredentialsResult;
@@ -1238,12 +1268,12 @@ CliqueStatus OTCliqueStatusFromString(NSString* str)
 
         NSArray* peerList = (result ? (NSArray*)(CFBridgingRelease(result)) : nil);
 
 
         NSArray* peerList = (result ? (NSArray*)(CFBridgingRelease(result)) : nil);
 
+        secnotice("clique-legacy", "copyPeerPeerInfo results: %@ (%@)", peerList, copyPeerErrorRef);
         if (error) {
             *error = (NSError*)CFBridgingRelease(copyPeerErrorRef);
         } else {
             CFBridgingRelease(copyPeerErrorRef);
         }
         if (error) {
             *error = (NSError*)CFBridgingRelease(copyPeerErrorRef);
         } else {
             CFBridgingRelease(copyPeerErrorRef);
         }
-        secnotice("clique-legacy", "copyPeerPeerInfo results: %@", peerList);
         subTaskSuccess = (peerList != nil) ? true : false;
         OctagonSignpostEnd(signPost, OctagonSignpostNameCopyPeerPeerInfo, OctagonSignpostNumber1(OctagonSignpostNameCopyPeerPeerInfo), (int)subTaskSuccess);
         return peerList;
         subTaskSuccess = (peerList != nil) ? true : false;
         OctagonSignpostEnd(signPost, OctagonSignpostNameCopyPeerPeerInfo, OctagonSignpostNumber1(OctagonSignpostNameCopyPeerPeerInfo), (int)subTaskSuccess);
         return peerList;
@@ -1273,12 +1303,13 @@ CliqueStatus OTCliqueStatusFromString(NSString* str)
         if(result){
             viewsEnabledResult = CFBooleanGetValue(result) ? YES : NO;
         }
         if(result){
             viewsEnabledResult = CFBooleanGetValue(result) ? YES : NO;
         }
+        secnotice("clique-legacy", "peersHaveViewsEnabled results: %@ (%@)", viewsEnabledResult ? @"YES" : @"NO",
+                  viewsEnabledErrorRef);
         if (error) {
             *error = (NSError*)CFBridgingRelease(viewsEnabledErrorRef);
         } else {
             CFBridgingRelease(viewsEnabledErrorRef);
         }
         if (error) {
             *error = (NSError*)CFBridgingRelease(viewsEnabledErrorRef);
         } else {
             CFBridgingRelease(viewsEnabledErrorRef);
         }
-        secnotice("clique-legacy", "peersHaveViewsEnabled results: %@", viewsEnabledResult ? @"YES" : @"NO");
         subTaskSuccess = viewsEnabledResult ? true : false;
         OctagonSignpostEnd(signPost, OctagonSignpostNamePeersHaveViewsEnabled, OctagonSignpostNumber1(OctagonSignpostNamePeersHaveViewsEnabled), (int)subTaskSuccess);
         return viewsEnabledResult;
         subTaskSuccess = viewsEnabledResult ? true : false;
         OctagonSignpostEnd(signPost, OctagonSignpostNamePeersHaveViewsEnabled, OctagonSignpostNumber1(OctagonSignpostNamePeersHaveViewsEnabled), (int)subTaskSuccess);
         return viewsEnabledResult;
@@ -1297,7 +1328,6 @@ CliqueStatus OTCliqueStatusFromString(NSString* str)
 - (BOOL)requestToJoinCircle:(NSError *__autoreleasing*)error
 {
     bool result = false;
 - (BOOL)requestToJoinCircle:(NSError *__autoreleasing*)error
 {
     bool result = false;
-    CFErrorRef joinErrorRef = NULL;
     bool subTaskSuccess = false;
     OctagonSignpost signPost = OctagonSignpostBegin(OctagonSignpostNameRequestToJoinCircle);
 
     bool subTaskSuccess = false;
     OctagonSignpost signPost = OctagonSignpostBegin(OctagonSignpostNameRequestToJoinCircle);
 
@@ -1340,7 +1370,7 @@ CliqueStatus OTCliqueStatusFromString(NSString* str)
 
         // If we didn't early-exit, and we aren't going to invoke SOS below, we succeeded.
         if(!OctagonPlatformSupportsSOS()) {
 
         // If we didn't early-exit, and we aren't going to invoke SOS below, we succeeded.
         if(!OctagonPlatformSupportsSOS()) {
-            secnotice("clique-legacy", "requestToJoinCircle results: %d %@", result, joinErrorRef);
+            secnotice("clique-legacy", "requestToJoinCircle platform does not support SOS");
             subTaskSuccess = true;
             OctagonSignpostEnd(signPost, OctagonSignpostNameRequestToJoinCircle, OctagonSignpostNumber1(OctagonSignpostNameRequestToJoinCircle), (int)subTaskSuccess);
             return YES;
             subTaskSuccess = true;
             OctagonSignpostEnd(signPost, OctagonSignpostNameRequestToJoinCircle, OctagonSignpostNumber1(OctagonSignpostNameRequestToJoinCircle), (int)subTaskSuccess);
             return YES;
@@ -1350,6 +1380,7 @@ CliqueStatus OTCliqueStatusFromString(NSString* str)
 
     if([OTClique platformSupportsSOS]) {
         NSData* analyticsData = nil;
 
     if([OTClique platformSupportsSOS]) {
         NSData* analyticsData = nil;
+        CFErrorRef joinErrorRef = NULL;
         if(self.ctx.analytics){
             NSError* encodingError = nil;
             analyticsData = [NSKeyedArchiver archivedDataWithRootObject:self.ctx.analytics requiringSecureCoding:YES error:&encodingError];
         if(self.ctx.analytics){
             NSError* encodingError = nil;
             analyticsData = [NSKeyedArchiver archivedDataWithRootObject:self.ctx.analytics requiringSecureCoding:YES error:&encodingError];
@@ -1362,13 +1393,13 @@ CliqueStatus OTCliqueStatusFromString(NSString* str)
         }
 
         secnotice("clique-legacy", "sos requestToJoinCircle complete: %d %@", result, joinErrorRef);
         }
 
         secnotice("clique-legacy", "sos requestToJoinCircle complete: %d %@", result, joinErrorRef);
+        if (error) {
+            *error = (NSError*)CFBridgingRelease(joinErrorRef);
+        } else {
+            CFBridgingRelease(joinErrorRef);
+        }
     }
 
     }
 
-    if (error) {
-        *error = (NSError*)CFBridgingRelease(joinErrorRef);
-    } else {
-        CFBridgingRelease(joinErrorRef);
-    }
     subTaskSuccess = result;
     OctagonSignpostEnd(signPost, OctagonSignpostNameRequestToJoinCircle, OctagonSignpostNumber1(OctagonSignpostNameRequestToJoinCircle), (int)subTaskSuccess);
 
     subTaskSuccess = result;
     OctagonSignpostEnd(signPost, OctagonSignpostNameRequestToJoinCircle, OctagonSignpostNumber1(OctagonSignpostNameRequestToJoinCircle), (int)subTaskSuccess);
 
@@ -1741,6 +1772,184 @@ CliqueStatus OTCliqueStatusFromString(NSString* str)
     [self performedCDPStateMachineRun:type success:YES error:nil reply:reply];
 }
 
     [self performedCDPStateMachineRun:type success:YES error:nil reply:reply];
 }
 
++ (BOOL)setCDPEnabled:(OTConfigurationContext*)arguments
+                error:(NSError* __autoreleasing*)error
+{
+#if OCTAGON
+    NSError *controlError = nil;
+    OTControl* control = [arguments makeOTControl:&controlError];
+    if (!control) {
+        secerror("octagon-setcdpenabled: failed to fetch OTControl object: %@", controlError);
+        if (error) {
+            *error = controlError;
+        }
+        return NO;
+    }
+
+    __block NSError* reterror = nil;
+
+    [control setCDPEnabled:nil
+                 contextID:arguments.context
+                     reply:^(NSError * _Nullable resultError) {
+        if(resultError) {
+            secnotice("octagon-setcdpenabled", "failed to set CDP bit: %@", resultError);
+            reterror = resultError;
+        } else {
+            secnotice("octagon-setcdpenabled", "successfully set CDP bit");
+        }
+    }];
+
+    if(reterror && error) {
+        *error = reterror;
+    }
+
+    return (reterror == nil);
+#else // !OCTAGON
+    if(error) {
+        *error = [NSError errorWithDomain:NSOSStatusErrorDomain code:errSecUnimplemented userInfo:nil];
+    }
+    return NO;
+#endif
+}
+
++ (OTCDPStatus)getCDPStatus:(OTConfigurationContext*)arguments
+                      error:(NSError* __autoreleasing *)error
+{
+#if OCTAGON
+    NSError *controlError = nil;
+    OTControl* control = [arguments makeOTControl:&controlError];
+    if (!control) {
+        secerror("octagon-cdp-status: failed to fetch OTControl object: %@", controlError);
+        if (error) {
+            *error = controlError;
+        }
+        return OTCDPStatusUnknown;
+    }
+
+    __block NSError* reterror = nil;
+    __block OTCDPStatus retcdpstatus = OTCDPStatusUnknown;
+
+    [control getCDPStatus:nil
+                contextID:arguments.context
+                    reply:^(OTCDPStatus status, NSError * _Nullable resultError) {
+        if(resultError) {
+            secnotice("octagon-cdp-status", "failed to fetch CDP status: %@", resultError);
+            reterror = resultError;
+
+        } else {
+            secnotice("octagon-cdp-status", "successfully fetched CDP status as %@", OTCDPStatusToString(status));
+            retcdpstatus = status;
+        }
+    }];
+
+    if(reterror && error) {
+       *error = reterror;
+    }
+
+    return retcdpstatus;
+#else // !OCTAGON
+    if(error) {
+        *error = [NSError errorWithDomain:NSOSStatusErrorDomain code:errSecUnimplemented userInfo:nil];
+    }
+    return OTCDPStatusDisabled;
+#endif
+}
+
++ (OTClique* _Nullable)resetProtectedData:(OTConfigurationContext*)data error:(NSError**)error
+{
+#if OCTAGON
+    NSError *controlError = nil;
+    OTControl *control = [data makeOTControl:&controlError];
+    if (!control) {
+        secerror("clique-reset-protected-data: unable to create otcontrol: %@", controlError);
+        if (error) {
+            *error = controlError;
+        }
+        return nil;
+    }
+
+    __block NSError* localError = nil;
+
+    //delete all records
+    id<OctagonEscrowRecovererPrococol> sb = data.sbd ?: [[getSecureBackupClass() alloc] init];
+
+    NSDictionary* deletionInformation = @{ getkSecureBackupAuthenticationAppleID() : data.authenticationAppleID,
+                                           getkSecureBackupAuthenticationPassword() : data.passwordEquivalentToken,
+                                           getkSecureBackupiCloudDataProtectionDeleteAllRecordsKey() : @YES,
+                                           getkSecureBackupContainsiCDPDataKey() : @YES};
+
+    NSError* sbError = [sb disableWithInfo:deletionInformation];
+    if(sbError) {
+        secerror("clique-reset-protected-data: secure backup escrow record deletion failed: %@", sbError);
+        if(error) {
+            *error = sbError;
+        }
+        return nil;
+    } else {
+        secnotice("clique-reset-protected-data", "sbd disableWithInfo succeeded");
+    }
+
+    //reset sos
+    if(OctagonPlatformSupportsSOS()) {
+        //reset SOS
+        CFErrorRef sosError = NULL;
+        bool resetSuccess = SOSCCResetToOffering(&sosError);
+
+        if(sosError || !resetSuccess) {
+            secerror("clique-reset-protected-data: sos reset failed: %@", sosError);
+            if(error) {
+                *error = (NSError*)CFBridgingRelease(sosError);
+            }
+            return nil;
+        } else {
+            secnotice("clique-reset-protected-data", "sos reset succeeded");
+        }
+    } else {
+        secnotice("clique-reset-protected-data", "platform does not support sos");
+    }
+
+    //reset octagon
+    OTClique* clique = [[OTClique alloc] initWithContextData:data];
+    if(OctagonIsEnabled()) {
+        [clique resetAndEstablish:CuttlefishResetReasonUserInitiatedReset error:&localError];
+
+        if(localError) {
+            secerror("clique-reset-protected-data: account reset failed: %@", localError);
+            if(error) {
+                *error = localError;
+            }
+            return nil;
+        } else {
+            secnotice("clique-reset-protected-data", "Octagon account reset succeeded");
+        }
+    }
+
+    //reset ckks
+    CKKSControl* ckksControl = [data makeCKKSControl:&localError];
+    [ckksControl rpcResetCloudKit:nil reason:@"clique-reset-protected-data" reply:^(NSError* resetError){
+        localError = resetError;
+    }];
+
+    if(localError) {
+        secerror("clique-reset-protected-data: ckks cloudkit reset failed: %@", localError);
+        if(error) {
+            *error = localError;
+        }
+        return nil;
+    } else {
+        secnotice("clique-reset-protected-data", "ckks cloudkit reset succeeded");
+    }
+
+    return clique;
+#else // !OCTAGON
+    if(error) {
+        *error = [NSError errorWithDomain:NSOSStatusErrorDomain code:errSecUnimplemented userInfo:nil];
+    }
+    return nil;
+#endif
+
+}
+
 @end
 
 #endif /* OBJC2 */
 @end
 
 #endif /* OBJC2 */
index 6e8b48def52127092f3fbb0681e9645f1a2618ac..c279f62c8070a64ccd3ff790bc795bc1763ad6c2 100644 (file)
@@ -85,12 +85,12 @@ NS_ASSUME_NONNULL_BEGIN
                                             NSError * _Nullable error))reply;
 
 - (void)rpcPrepareIdentityAsApplicantWithConfiguration:(OTJoiningConfiguration*)config
                                             NSError * _Nullable error))reply;
 
 - (void)rpcPrepareIdentityAsApplicantWithConfiguration:(OTJoiningConfiguration*)config
-                                              reply:(void (^)(NSString * _Nullable peerID,
-                                                              NSData * _Nullable permanentInfo,
-                                                              NSData * _Nullable permanentInfoSig,
-                                                              NSData * _Nullable stableInfo,
-                                                              NSData * _Nullable stableInfoSig,
-                                                              NSError * _Nullable error))reply;
+                                                 reply:(void (^)(NSString * _Nullable peerID,
+                                                                 NSData * _Nullable permanentInfo,
+                                                                 NSData * _Nullable permanentInfoSig,
+                                                                 NSData * _Nullable stableInfo,
+                                                                 NSData * _Nullable stableInfoSig,
+                                                                 NSError * _Nullable error))reply;
 - (void)rpcVoucherWithConfiguration:(OTJoiningConfiguration*)config
                              peerID:(NSString*)peerID
                       permanentInfo:(NSData *)permanentInfo
 - (void)rpcVoucherWithConfiguration:(OTJoiningConfiguration*)config
                              peerID:(NSString*)peerID
                       permanentInfo:(NSData *)permanentInfo
@@ -102,7 +102,6 @@ NS_ASSUME_NONNULL_BEGIN
 - (void)rpcJoinWithConfiguration:(OTJoiningConfiguration*)config
                        vouchData:(NSData*)vouchData
                         vouchSig:(NSData*)vouchSig
 - (void)rpcJoinWithConfiguration:(OTJoiningConfiguration*)config
                        vouchData:(NSData*)vouchData
                         vouchSig:(NSData*)vouchSig
-                 preapprovedKeys:(NSArray<NSData*>* _Nullable)preapprovedKeys
                            reply:(void (^)(NSError * _Nullable error))reply;
 
 
                            reply:(void (^)(NSError * _Nullable error))reply;
 
 
@@ -235,6 +234,18 @@ skipRateLimitingCheck:(BOOL)skipRateLimitingCheck
              radar:(NSString *)radar
              reply:(void (^)(NSError* _Nullable error))reply;
 
              radar:(NSString *)radar
              reply:(void (^)(NSError* _Nullable error))reply;
 
+- (void)setCDPEnabled:(NSString* _Nullable)containerName
+            contextID:(NSString*)contextID
+                reply:(void (^)(NSError* _Nullable error))reply;
+
+- (void)getCDPStatus:(NSString* _Nullable)containerName
+           contextID:(NSString*)contextID
+               reply:(void (^)(OTCDPStatus status, NSError* _Nullable error))reply;
+
+- (void)refetchCKKSPolicy:(NSString* _Nullable)containerName
+                contextID:(NSString*)contextID
+                    reply:(void (^)(NSError* _Nullable error))reply;
+
 @end
 
 NS_ASSUME_NONNULL_END
 @end
 
 NS_ASSUME_NONNULL_END
index ce1d0965a8514c325c7f44763dc450fec15bad3d..e5669901fb6269076970a86df86bf5ce4fd51fa7 100644 (file)
 - (void)rpcJoinWithConfiguration:(OTJoiningConfiguration*)config
                        vouchData:(NSData*)vouchData
                         vouchSig:(NSData*)vouchSig
 - (void)rpcJoinWithConfiguration:(OTJoiningConfiguration*)config
                        vouchData:(NSData*)vouchData
                         vouchSig:(NSData*)vouchSig
-                 preapprovedKeys:(NSArray<NSData*>* _Nullable)preapprovedKeys
                            reply:(void (^)(NSError * _Nullable error))reply
 {
 #if OCTAGON
     [[self getConnection: ^(NSError* error) {
         reply(error);
                            reply:(void (^)(NSError * _Nullable error))reply
 {
 #if OCTAGON
     [[self getConnection: ^(NSError* error) {
         reply(error);
-    }] rpcJoinWithConfiguration:config vouchData:vouchData vouchSig:vouchSig preapprovedKeys:preapprovedKeys reply:^(NSError* e) {
+    }] rpcJoinWithConfiguration:config vouchData:vouchData vouchSig:vouchSig reply:^(NSError* e) {
         reply(e);
     }];
 #else
         reply(e);
     }];
 #else
@@ -482,6 +481,33 @@ skipRateLimitingCheck:(BOOL)skipRateLimitingCheck
     }] tapToRadar:action description:description radar:radar reply:reply];
 }
 
     }] tapToRadar:action description:description radar:radar reply:reply];
 }
 
+- (void)refetchCKKSPolicy:(NSString* _Nullable)container
+                contextID:(NSString*)contextID
+                    reply:(void (^)(NSError* _Nullable error))reply
+{
+    [[self getConnection: ^(NSError* error) {
+        reply(error);
+    }] refetchCKKSPolicy:container contextID:contextID reply:reply];
+}
+
+- (void)setCDPEnabled:(NSString* _Nullable)containerName
+            contextID:(NSString*)contextID
+                reply:(void (^)(NSError* _Nullable error))reply
+{
+    [[self getConnection: ^(NSError* connectionError) {
+        reply(connectionError);
+    }] setCDPEnabled:containerName contextID:contextID reply:reply];
+}
+
+- (void)getCDPStatus:(NSString* _Nullable)containerName
+           contextID:(NSString*)contextID
+               reply:(void (^)(OTCDPStatus status, NSError* _Nullable error))reply
+{
+    [[self getConnection: ^(NSError* connectionError) {
+        reply(OTCDPStatusUnknown, connectionError);
+    }] getCDPStatus:containerName contextID:contextID reply:reply];
+}
+
 + (OTControl*)controlObject:(NSError* __autoreleasing *)error {
     return [OTControl controlObject:false error:error];
 }
 + (OTControl*)controlObject:(NSError* __autoreleasing *)error {
     return [OTControl controlObject:false error:error];
 }
index 6af4bbefbb17298b180588773727aa3f0c099c43..48f99a779fe9f0cc941ddbe06044d88ea5761da1 100644 (file)
@@ -86,7 +86,6 @@ typedef void (^OTNextJoinCompleteBlock)(BOOL finished, NSData* _Nullable message
 - (void)rpcJoinWithConfiguration:(OTJoiningConfiguration*)config
                        vouchData:(NSData*)vouchData
                         vouchSig:(NSData*)vouchSig
 - (void)rpcJoinWithConfiguration:(OTJoiningConfiguration*)config
                        vouchData:(NSData*)vouchData
                         vouchSig:(NSData*)vouchSig
-                 preapprovedKeys:(NSArray<NSData*>* _Nullable)preapprovedKeys
                            reply:(void (^)(NSError * _Nullable error))reply;
 
 - (void)preflightBottledPeer:(NSString*)contextID
                            reply:(void (^)(NSError * _Nullable error))reply;
 
 - (void)preflightBottledPeer:(NSString*)contextID
@@ -206,6 +205,18 @@ skipRateLimitingCheck:(BOOL)skipRateLimitingCheck
              radar:(NSString *)radar
              reply:(void (^)(NSError* _Nullable error))reply;
 
              radar:(NSString *)radar
              reply:(void (^)(NSError* _Nullable error))reply;
 
+- (void)refetchCKKSPolicy:(NSString* _Nullable)container
+                contextID:(NSString*)contextID
+                    reply:(void (^)(NSError* _Nullable error))reply;
+
+- (void)setCDPEnabled:(NSString* _Nullable)containerName
+            contextID:(NSString*)contextID
+                reply:(void (^)(NSError* _Nullable error))reply;
+
+- (void)getCDPStatus:(NSString* _Nullable)containerName
+           contextID:(NSString*)contextID
+               reply:(void (^)(OTCDPStatus status, NSError* _Nullable error))reply;
+
 @end
 
 NSXPCInterface* OTSetupControlProtocol(NSXPCInterface* interface);
 @end
 
 NSXPCInterface* OTSetupControlProtocol(NSXPCInterface* interface);
index f98ae5026232346f78f001d35a8b92029e42a59d..9c0df1987c201f7d01d891b60c807e468acd4dfb 100644 (file)
@@ -83,7 +83,7 @@ NSXPCInterface* OTSetupControlProtocol(NSXPCInterface* interface) {
                 argumentIndex:2
                       ofReply:YES];
         [interface setClasses:errorClasses
                 argumentIndex:2
                       ofReply:YES];
         [interface setClasses:errorClasses
-                  forSelector:@selector(rpcJoinWithConfiguration:vouchData:vouchSig:preapprovedKeys:reply:)
+                  forSelector:@selector(rpcJoinWithConfiguration:vouchData:vouchSig:reply:)
                 argumentIndex:0
                       ofReply:YES];
 #endif /* __OBJC2__ */
                 argumentIndex:0
                       ofReply:YES];
 #endif /* __OBJC2__ */
index 4630b7f9c6e896a785f989d38899e5a5016b99d8..abdb5e32f6a3cbbcc737e2c045de8434b9476b7a 100644 (file)
@@ -3,6 +3,7 @@
 #import <dispatch/dispatch.h>
 
 #import "keychain/ot/proto/generated_source/OTAccountMetadataClassC.h"
 #import <dispatch/dispatch.h>
 
 #import "keychain/ot/proto/generated_source/OTAccountMetadataClassC.h"
+#import "keychain/ot/categories/OTAccountMetadataClassC+KeychainSupport.h"
 
 extern NSString* _Nonnull OTCuttlefishContextErrorDomain;
 typedef NS_ENUM(uint32_t, OTCuttlefishContextErrors) {
 
 extern NSString* _Nonnull OTCuttlefishContextErrorDomain;
 typedef NS_ENUM(uint32_t, OTCuttlefishContextErrors) {
@@ -35,10 +36,10 @@ NS_ASSUME_NONNULL_BEGIN
 
 - (BOOL)persistNewEpoch:(uint64_t)epoch error:(NSError**)error;
 
 
 - (BOOL)persistNewEpoch:(uint64_t)epoch error:(NSError**)error;
 
-- (BOOL)persistAccountChanges:(OTAccountMetadataClassC* (^)(OTAccountMetadataClassC* metadata))makeChanges
+- (BOOL)persistAccountChanges:(OTAccountMetadataClassC* _Nullable (^)(OTAccountMetadataClassC* metadata))makeChanges
                         error:(NSError**)error;
 
                         error:(NSError**)error;
 
-- (BOOL)_onqueuePersistAccountChanges:(OTAccountMetadataClassC* (^)(OTAccountMetadataClassC* metadata))makeChanges
+- (BOOL)_onqueuePersistAccountChanges:(OTAccountMetadataClassC* _Nullable (^)(OTAccountMetadataClassC* metadata))makeChanges
                                 error:(NSError**)error;
 
 - (NSDate *)lastHealthCheckupDate:(NSError * _Nullable *)error;
                                 error:(NSError**)error;
 
 - (NSDate *)lastHealthCheckupDate:(NSError * _Nullable *)error;
index 2455c54360cd88141899f1d1bb16e20ecd0702b0..b6d596d12e3923cc63593e5a57ac2a0a19409173 100644 (file)
     } error:error];
 }
 
     } error:error];
 }
 
-- (BOOL)persistAccountChanges:(OTAccountMetadataClassC* (^)(OTAccountMetadataClassC*))makeChanges
+- (BOOL)persistAccountChanges:(OTAccountMetadataClassC* _Nullable (^)(OTAccountMetadataClassC*))makeChanges
                         error:(NSError**)error
 {
     __block NSError* localError = nil;
                         error:(NSError**)error
 {
     __block NSError* localError = nil;
         }
 
         newState = makeChanges([oldState copy]);
         }
 
         newState = makeChanges([oldState copy]);
-        if(![newState saveToKeychainForContainer:self.containerName contextID:self.contextID error:&localError]) {
+        if(newState && ![newState saveToKeychainForContainer:self.containerName contextID:self.contextID error:&localError]) {
             newState = nil;
         }
     });
             newState = nil;
         }
     });
     } error:error];
 }
 
     } error:error];
 }
 
-- (BOOL)_onqueuePersistAccountChanges:(OTAccountMetadataClassC* (^)(OTAccountMetadataClassC* metadata))makeChanges
+- (BOOL)_onqueuePersistAccountChanges:(OTAccountMetadataClassC* _Nullable (^)(OTAccountMetadataClassC* metadata))makeChanges
                                 error:(NSError**)error
 {
     __block NSError* localError = nil;
                                 error:(NSError**)error
 {
     __block NSError* localError = nil;
index 426c5dc8669b6f600d05adfb3bbb505845c9405e..714b7013b4eeaf7010684d673e77f676d4510739 100644 (file)
@@ -35,6 +35,7 @@
 #import "keychain/ckks/CKKSCondition.h"
 #import "keychain/TrustedPeersHelper/TrustedPeersHelperProtocol.h"
 #import "OTDeviceInformation.h"
 #import "keychain/ckks/CKKSCondition.h"
 #import "keychain/TrustedPeersHelper/TrustedPeersHelperProtocol.h"
 #import "OTDeviceInformation.h"
+#import "keychain/ot/OTConstants.h"
 #import "keychain/ot/OTDefines.h"
 #import "keychain/ot/OTClique.h"
 #import "keychain/ot/OTFollowup.h"
 #import "keychain/ot/OTDefines.h"
 #import "keychain/ot/OTClique.h"
 #import "keychain/ot/OTFollowup.h"
@@ -76,15 +77,13 @@ NS_ASSUME_NONNULL_BEGIN
 @property (nonatomic, readonly) CKKSLockStateTracker        *lockStateTracker;
 @property (nonatomic, readonly) OTCuttlefishAccountStateHolder* accountMetadataStore;
 @property (readonly) OctagonStateMachine* stateMachine;
 @property (nonatomic, readonly) CKKSLockStateTracker        *lockStateTracker;
 @property (nonatomic, readonly) OTCuttlefishAccountStateHolder* accountMetadataStore;
 @property (readonly) OctagonStateMachine* stateMachine;
-@property (readonly) BOOL postedRepairCFU;
-@property (readonly) BOOL postedEscrowRepairCFU;
-@property (readonly) BOOL postedRecoveryKeyCFU;
 @property (nullable, nonatomic) CKKSNearFutureScheduler* apsRateLimiter;
 @property (nullable, nonatomic) CKKSNearFutureScheduler* sosConsistencyRateLimiter;
 
 @property (readonly, nullable) CKKSViewManager*             viewManager;
 
 // Dependencies (for injection)
 @property (nullable, nonatomic) CKKSNearFutureScheduler* apsRateLimiter;
 @property (nullable, nonatomic) CKKSNearFutureScheduler* sosConsistencyRateLimiter;
 
 @property (readonly, nullable) CKKSViewManager*             viewManager;
 
 // Dependencies (for injection)
+@property (readonly) id<OTDeviceInformationAdapter> deviceAdapter;
 @property id<OTAuthKitAdapter> authKitAdapter;
 
 @property dispatch_queue_t queue;
 @property id<OTAuthKitAdapter> authKitAdapter;
 
 @property dispatch_queue_t queue;
@@ -107,6 +106,11 @@ NS_ASSUME_NONNULL_BEGIN
 - (BOOL)accountNoLongerAvailable:(NSError**)error;
 - (BOOL)idmsTrustLevelChanged:(NSError**)error;
 
 - (BOOL)accountNoLongerAvailable:(NSError**)error;
 - (BOOL)idmsTrustLevelChanged:(NSError**)error;
 
+// Call these to manipulate the "CDP-ness" of the account
+// Note that there is no way to turn CDP back off again
+- (OTCDPStatus)getCDPStatus:(NSError* __autoreleasing *)error;
+- (BOOL)setCDPEnabled:(NSError* __autoreleasing *)error;
+
 - (void)deviceNameUpdated;
 
 - (void)startOctagonStateMachine;
 - (void)deviceNameUpdated;
 
 - (void)startOctagonStateMachine;
@@ -122,7 +126,6 @@ NS_ASSUME_NONNULL_BEGIN
                                                               NSError * _Nullable error))reply;
 - (void)rpcJoin:(NSData*)vouchData
        vouchSig:(NSData*)vouchSig
                                                               NSError * _Nullable error))reply;
 - (void)rpcJoin:(NSData*)vouchData
        vouchSig:(NSData*)vouchSig
-preapprovedKeys:(NSArray<NSData*>* _Nullable)preapprovedKeys
           reply:(void (^)(NSError * _Nullable error))reply;
 
 - (void)rpcResetAndEstablish:(CuttlefishResetReason)resetReason reply:(nonnull void (^)(NSError * _Nullable))reply;
           reply:(void (^)(NSError * _Nullable error))reply;
 
 - (void)rpcResetAndEstablish:(CuttlefishResetReason)resetReason reply:(nonnull void (^)(NSError * _Nullable))reply;
@@ -165,6 +168,8 @@ preapprovedKeys:(NSArray<NSData*>* _Nullable)preapprovedKeys
                                       NSError* _Nullable error))reply;
 - (void)rpcSetRecoveryKey:(NSString*)recoveryKey reply:(void (^)(NSError * _Nullable error))reply;
 
                                       NSError* _Nullable error))reply;
 - (void)rpcSetRecoveryKey:(NSString*)recoveryKey reply:(void (^)(NSError * _Nullable error))reply;
 
+- (void)rpcRefetchCKKSPolicy:(void (^)(NSError * _Nullable error))reply;
+
 - (void)requestTrustedDeviceListRefresh;
 
 - (OTDeviceInformation*)prepareInformation;
 - (void)requestTrustedDeviceListRefresh;
 
 - (OTDeviceInformation*)prepareInformation;
@@ -178,24 +183,24 @@ preapprovedKeys:(NSArray<NSData*>* _Nullable)preapprovedKeys
 
 - (void)waitForOctagonUpgrade:(void (^)(NSError* error))reply NS_SWIFT_NAME(waitForOctagonUpgrade(reply:));
 
 
 - (void)waitForOctagonUpgrade:(void (^)(NSError* error))reply NS_SWIFT_NAME(waitForOctagonUpgrade(reply:));
 
-- (void)clearPendingCFUFlags;
-
 - (BOOL)waitForReady:(int64_t)timeOffset;
 
 
 // For testing.
 - (BOOL)waitForReady:(int64_t)timeOffset;
 
 
 // For testing.
-- (void)setPostedBool:(BOOL)posted;
 - (OTAccountMetadataClassC_AccountState)currentMemoizedAccountState;
 - (OTAccountMetadataClassC_TrustState)currentMemoizedTrustState;
 - (NSDate* _Nullable) currentMemoizedLastHealthCheck;
 - (void) checkTrustStatusAndPostRepairCFUIfNecessary:(void (^ _Nullable)(CliqueStatus status, BOOL posted, BOOL hasIdentity, NSError * _Nullable error))reply;
 - (void) setAccountStateHolder:(OTCuttlefishAccountStateHolder*)accountMetadataStore;
 
 - (OTAccountMetadataClassC_AccountState)currentMemoizedAccountState;
 - (OTAccountMetadataClassC_TrustState)currentMemoizedTrustState;
 - (NSDate* _Nullable) currentMemoizedLastHealthCheck;
 - (void) checkTrustStatusAndPostRepairCFUIfNecessary:(void (^ _Nullable)(CliqueStatus status, BOOL posted, BOOL hasIdentity, NSError * _Nullable error))reply;
 - (void) setAccountStateHolder:(OTCuttlefishAccountStateHolder*)accountMetadataStore;
 
+- (void)clearCKKSViewManager;
+
+@property (nullable) TPPolicyVersion* policyOverride;
+
 // Octagon Health Check Helpers
 - (void)checkOctagonHealth:(BOOL)skipRateLimitingCheck reply:(void (^)(NSError * _Nullable error))reply;
 - (BOOL)postRepairCFU:(NSError**)error;
 - (void)postConfirmPasscodeCFU:(NSError**)error;
 // Octagon Health Check Helpers
 - (void)checkOctagonHealth:(BOOL)skipRateLimitingCheck reply:(void (^)(NSError * _Nullable error))reply;
 - (BOOL)postRepairCFU:(NSError**)error;
 - (void)postConfirmPasscodeCFU:(NSError**)error;
-- (void)postRecoveryKeyCFU:(NSError**)error;
 
 // For reporting
 - (BOOL)machineIDOnMemoizedList:(NSString*)machineID error:(NSError**)error NS_SWIFT_NOTHROW;
 
 // For reporting
 - (BOOL)machineIDOnMemoizedList:(NSString*)machineID error:(NSError**)error NS_SWIFT_NOTHROW;
index 0eac866a536730399539168e9bbce49796b5cade..be6ecf968e25bdb8331bb8dfe4187657f8a5364b 100644 (file)
  */
 #if OCTAGON
 
  */
 #if OCTAGON
 
-#include <sys/sysctl.h>
-
+#import <CoreCDP/CDPAccount.h>
+#import <notify.h>
 #import <os/feature_private.h>
 #import <os/feature_private.h>
-
 #import <Security/Security.h>
 #import <Security/Security.h>
-
-#include <utilities/SecFileLocations.h>
 #include <Security/SecRandomP.h>
 #import <SecurityFoundation/SFKey_Private.h>
 #include <Security/SecRandomP.h>
 #import <SecurityFoundation/SFKey_Private.h>
+#include <sys/sysctl.h>
+#import <TrustedPeers/TrustedPeers.h>
+
 
 #import "keychain/TrustedPeersHelper/TrustedPeersHelperProtocol.h"
 
 #import "keychain/TrustedPeersHelper/TrustedPeersHelperProtocol.h"
-#import <TrustedPeers/TrustedPeers.h>
+
+#import "keychain/analytics/CKKSLaunchSequence.h"
+#import "keychain/categories/NSError+UsefulConstructors.h"
 #import "keychain/ckks/CKKS.h"
 #import "keychain/ckks/CKKS.h"
+#import "keychain/ckks/CKKSAccountStateTracker.h"
 #import "keychain/ckks/CKKSAnalytics.h"
 #import "keychain/ckks/CKKSAnalytics.h"
+#import "keychain/ckks/CKKSKeychainView.h"
 #import "keychain/ckks/CKKSResultOperation.h"
 #import "keychain/ckks/CKKSResultOperation.h"
-
+#import "keychain/ckks/CKKSViewManager.h"
+#import "keychain/ckks/CloudKitCategories.h"
 #import "keychain/ckks/OctagonAPSReceiver.h"
 #import "keychain/ckks/OctagonAPSReceiver.h"
-#import "keychain/analytics/CKKSLaunchSequence.h"
-#import "keychain/ot/OTDeviceInformationAdapter.h"
-
-
-#import "keychain/ot/OctagonStateMachine.h"
-#import "keychain/ot/OctagonStateMachineHelpers.h"
-#import "keychain/ot/OctagonCKKSPeerAdapter.h"
-#import "keychain/ot/OctagonCheckTrustStateOperation.h"
-#import "keychain/ot/OTStates.h"
-#import "keychain/ot/OTFollowup.h"
+#import "keychain/escrowrequest/EscrowRequestServer.h"
 #import "keychain/ot/OTAuthKitAdapter.h"
 #import "keychain/ot/OTAuthKitAdapter.h"
-#import "keychain/ot/OTConstants.h"
-#import "keychain/ot/OTOperationDependencies.h"
+#import "keychain/ot/OTCheckHealthOperation.h"
+#import "keychain/ot/OTClientVoucherOperation.h"
 #import "keychain/ot/OTClique.h"
 #import "keychain/ot/OTClique.h"
+#import "keychain/ot/OTConstants.h"
+#import "keychain/ot/OTCuttlefishAccountStateHolder.h"
 #import "keychain/ot/OTCuttlefishContext.h"
 #import "keychain/ot/OTCuttlefishContext.h"
-#import "keychain/ot/OTPrepareOperation.h"
-#import "keychain/ot/OTSOSAdapter.h"
-#import "keychain/ot/OTSOSUpgradeOperation.h"
-#import "keychain/ot/OTUpdateTPHOperation.h"
+#import "keychain/ot/OTDetermineCDPBitStatusOperation.h"
+#import "keychain/ot/OTDetermineHSA2AccountStatusOperation.h"
+#import "keychain/ot/OTDeviceInformationAdapter.h"
+#import "keychain/ot/OTEnsureOctagonKeyConsistency.h"
 #import "keychain/ot/OTEpochOperation.h"
 #import "keychain/ot/OTEpochOperation.h"
-#import "keychain/ot/OTClientVoucherOperation.h"
-#import "keychain/ot/OTLeaveCliqueOperation.h"
-#import "keychain/ot/OTRemovePeersOperation.h"
-#import "keychain/ot/OTJoinWithVoucherOperation.h"
-#import "keychain/ot/OTVouchWithBottleOperation.h"
-#import "keychain/ot/OTVouchWithRecoveryKeyOperation.h"
 #import "keychain/ot/OTEstablishOperation.h"
 #import "keychain/ot/OTEstablishOperation.h"
+#import "keychain/ot/OTFetchViewsOperation.h"
+#import "keychain/ot/OTFollowup.h"
+#import "keychain/ot/OTJoinWithVoucherOperation.h"
+#import "keychain/ot/OTLeaveCliqueOperation.h"
 #import "keychain/ot/OTLocalCKKSResetOperation.h"
 #import "keychain/ot/OTLocalCKKSResetOperation.h"
-#import "keychain/ot/OTUpdateTrustedDeviceListOperation.h"
-#import "keychain/ot/OTSOSUpdatePreapprovalsOperation.h"
-#import "keychain/ot/OTResetOperation.h"
 #import "keychain/ot/OTLocalCuttlefishReset.h"
 #import "keychain/ot/OTLocalCuttlefishReset.h"
-#import "keychain/ot/OTSetRecoveryKeyOperation.h"
+#import "keychain/ot/OTOperationDependencies.h"
+#import "keychain/ot/OTPrepareOperation.h"
+#import "keychain/ot/OTRemovePeersOperation.h"
 #import "keychain/ot/OTResetCKKSZonesLackingTLKsOperation.h"
 #import "keychain/ot/OTResetCKKSZonesLackingTLKsOperation.h"
+#import "keychain/ot/OTResetOperation.h"
+#import "keychain/ot/OTSOSAdapter.h"
+#import "keychain/ot/OTSOSUpdatePreapprovalsOperation.h"
+#import "keychain/ot/OTSOSUpgradeOperation.h"
+#import "keychain/ot/OTSetCDPBitOperation.h"
+#import "keychain/ot/OTSetRecoveryKeyOperation.h"
+#import "keychain/ot/OTStates.h"
+#import "keychain/ot/OTTriggerEscrowUpdateOperation.h"
+#import "keychain/ot/OTUpdateTPHOperation.h"
+#import "keychain/ot/OTUpdateTrustedDeviceListOperation.h"
 #import "keychain/ot/OTUploadNewCKKSTLKsOperation.h"
 #import "keychain/ot/OTUploadNewCKKSTLKsOperation.h"
-#import "keychain/ot/OTCuttlefishAccountStateHolder.h"
+#import "keychain/ot/OTVouchWithBottleOperation.h"
+#import "keychain/ot/OTVouchWithRecoveryKeyOperation.h"
 #import "keychain/ot/ObjCImprovements.h"
 #import "keychain/ot/ObjCImprovements.h"
+#import "keychain/ot/OctagonCKKSPeerAdapter.h"
+#import "keychain/ot/OctagonCheckTrustStateOperation.h"
+#import "keychain/ot/OctagonStateMachine.h"
+#import "keychain/ot/OctagonStateMachineHelpers.h"
+#import "keychain/ot/categories/OTAccountMetadataClassC+KeychainSupport.h"
 #import "keychain/ot/proto/generated_source/OTAccountMetadataClassC.h"
 #import "keychain/ot/proto/generated_source/OTAccountMetadataClassC.h"
-#import "keychain/ot/OTTriggerEscrowUpdateOperation.h"
-#import "keychain/ot/OTCheckHealthOperation.h"
-#import "keychain/ot/OTEnsureOctagonKeyConsistency.h"
-#import "keychain/ot/OTDetermineHSA2AccountStatusOperation.h"
-#import "keychain/ckks/CKKSAccountStateTracker.h"
-#import "keychain/ckks/CloudKitCategories.h"
-#import "keychain/escrowrequest/EscrowRequestServer.h"
+#import "keychain/securityd/SOSCloudCircleServer.h"
+
+#import "utilities/SecFileLocations.h"
+#import "utilities/SecTapToRadar.h"
 
 #if TARGET_OS_WATCH
 #import "keychain/otpaird/OTPairingClient.h"
 #endif /* TARGET_OS_WATCH */
 
 
 #if TARGET_OS_WATCH
 #import "keychain/otpaird/OTPairingClient.h"
 #endif /* TARGET_OS_WATCH */
 
-#import "keychain/ckks/CKKSViewManager.h"
-#import "keychain/ckks/CKKSKeychainView.h"
 
 
-#import "utilities/SecTapToRadar.h"
 
 
-#import "keychain/categories/NSError+UsefulConstructors.h"
-#import <CoreCDP/CDPAccount.h>
-#import <notify.h>
 
 NSString* OTCuttlefishContextErrorDomain = @"otcuttlefish";
 static dispatch_time_t OctagonStateTransitionDefaultTimeout = 10*NSEC_PER_SEC;
 
 NSString* OTCuttlefishContextErrorDomain = @"otcuttlefish";
 static dispatch_time_t OctagonStateTransitionDefaultTimeout = 10*NSEC_PER_SEC;
@@ -110,7 +112,6 @@ static dispatch_time_t OctagonStateTransitionDefaultTimeout = 10*NSEC_PER_SEC;
     NSString* _bottleID;
     NSString* _bottleSalt;
     NSData* _entropy;
     NSString* _bottleID;
     NSString* _bottleSalt;
     NSData* _entropy;
-    NSArray<NSData*>* _preapprovedKeys;
     NSString* _recoveryKey;
     CuttlefishResetReason _resetReason;
     BOOL _skipRateLimitingCheck;
     NSString* _recoveryKey;
     CuttlefishResetReason _resetReason;
     BOOL _skipRateLimitingCheck;
@@ -125,21 +126,17 @@ static dispatch_time_t OctagonStateTransitionDefaultTimeout = 10*NSEC_PER_SEC;
 @property CKAccountInfo* cloudKitAccountInfo;
 @property CKKSCondition *cloudKitAccountStateKnown;
 
 @property CKAccountInfo* cloudKitAccountInfo;
 @property CKKSCondition *cloudKitAccountStateKnown;
 
-@property BOOL getViewsSuccess;
-
 @property CKKSNearFutureScheduler* suggestTLKUploadNotifier;
 
 @property CKKSNearFutureScheduler* suggestTLKUploadNotifier;
 
+// Make writable
+@property (nullable) CKKSViewManager* viewManager;
+
 // Dependencies (for injection)
 @property id<OTSOSAdapter> sosAdapter;
 @property id<CKKSPeerProvider> octagonAdapter;
 // Dependencies (for injection)
 @property id<OTSOSAdapter> sosAdapter;
 @property id<CKKSPeerProvider> octagonAdapter;
-@property id<OTDeviceInformationAdapter> deviceAdapter;
 @property (readonly) Class<OctagonAPSConnection> apsConnectionClass;
 @property (readonly) Class<SecEscrowRequestable> escrowRequestClass;
 
 @property (readonly) Class<OctagonAPSConnection> apsConnectionClass;
 @property (readonly) Class<SecEscrowRequestable> escrowRequestClass;
 
-@property (nonatomic) BOOL postedRepairCFU;
-@property (nonatomic) BOOL postedEscrowRepairCFU;
-@property (nonatomic) BOOL postedRecoveryKeyCFU;
-
 @property (nonatomic) BOOL initialBecomeUntrustedPosted;
 
 @end
 @property (nonatomic) BOOL initialBecomeUntrustedPosted;
 
 @end
@@ -166,9 +163,6 @@ static dispatch_time_t OctagonStateTransitionDefaultTimeout = 10*NSEC_PER_SEC;
         _contextID = contextID;
 
         _viewManager = viewManager;
         _contextID = contextID;
 
         _viewManager = viewManager;
-        _postedRepairCFU = NO;
-        _postedRecoveryKeyCFU = NO;
-        _postedEscrowRepairCFU = NO;
 
         _initialBecomeUntrustedPosted = NO;
 
 
         _initialBecomeUntrustedPosted = NO;
 
@@ -222,6 +216,11 @@ static dispatch_time_t OctagonStateTransitionDefaultTimeout = 10*NSEC_PER_SEC;
     return self;
 }
 
     return self;
 }
 
+- (void)clearCKKSViewManager
+{
+    self.viewManager = nil;
+}
+
 - (void)dealloc
 {
     // TODO: how to invalidate this?
 - (void)dealloc
 {
     // TODO: how to invalidate this?
@@ -444,6 +443,7 @@ static dispatch_time_t OctagonStateTransitionDefaultTimeout = 10*NSEC_PER_SEC;
                                                                             metadata.icloudAccountState = OTAccountMetadataClassC_AccountState_NO_ACCOUNT;
                                                                             metadata.altDSID = nil;
                                                                             metadata.trustState = OTAccountMetadataClassC_TrustState_UNKNOWN;
                                                                             metadata.icloudAccountState = OTAccountMetadataClassC_AccountState_NO_ACCOUNT;
                                                                             metadata.altDSID = nil;
                                                                             metadata.trustState = OTAccountMetadataClassC_TrustState_UNKNOWN;
+                                                                            metadata.cdpState = OTAccountMetadataClassC_CDPState_UNKNOWN;
 
                                                                             return metadata;
                                                                         } error:&localError];
 
                                                                             return metadata;
                                                                         } error:&localError];
@@ -476,6 +476,59 @@ static dispatch_time_t OctagonStateTransitionDefaultTimeout = 10*NSEC_PER_SEC;
     return YES;
 }
 
     return YES;
 }
 
+- (OTCDPStatus)getCDPStatus:(NSError*__autoreleasing*)error
+{
+    NSError* localError = nil;
+    OTAccountMetadataClassC* accountState = [self.accountMetadataStore loadOrCreateAccountMetadata:&localError];
+
+    if(localError) {
+        secnotice("octagon-cdp-status", "error fetching account metadata: %@", localError);
+        if(error) {
+            *error = localError;
+        }
+
+        return OTCDPStatusUnknown;
+    }
+
+    OTCDPStatus status = OTCDPStatusUnknown;
+    switch(accountState.cdpState) {
+        case OTAccountMetadataClassC_CDPState_UNKNOWN:
+            status = OTCDPStatusUnknown;
+            break;
+        case OTAccountMetadataClassC_CDPState_DISABLED:
+            status = OTCDPStatusDisabled;
+            break;
+        case OTAccountMetadataClassC_CDPState_ENABLED:
+            status = OTCDPStatusEnabled;
+            break;
+    }
+
+    secnotice("octagon-cdp-status", "current cdp status is: %@", OTCDPStatusToString(status));
+    return status;
+}
+
+- (BOOL)setCDPEnabled:(NSError* __autoreleasing *)error
+{
+    NSError* localError = nil;
+    [self.accountMetadataStore persistAccountChanges:^OTAccountMetadataClassC * _Nonnull(OTAccountMetadataClassC * _Nonnull metadata) {
+        metadata.cdpState = OTAccountMetadataClassC_CDPState_ENABLED;
+        return metadata;
+    } error:&localError];
+
+    [self.stateMachine handleFlag:OctagonFlagCDPEnabled];
+
+    if(localError) {
+        secerror("octagon-cdp-status: unable to persist CDP enablement: %@", localError);
+        if(error) {
+            *error = localError;
+        }
+        return NO;
+    }
+
+    secnotice("octagon-cdp-status", "Successfully set CDP status bit to 'enabled''");
+    return YES;
+}
+
 - (void)resetOctagonStateMachine
 {
     OctagonStateTransitionOperation* op = [OctagonStateTransitionOperation named:@"resetting-state-machine"
 - (void)resetOctagonStateMachine
 {
     OctagonStateTransitionOperation* op = [OctagonStateTransitionOperation named:@"resetting-state-machine"
@@ -507,21 +560,23 @@ static dispatch_time_t OctagonStateTransitionDefaultTimeout = 10*NSEC_PER_SEC;
 - (NSDictionary*)establishStatePathDictionary
 {
     return @{
 - (NSDictionary*)establishStatePathDictionary
 {
     return @{
-        OctagonStateReEnactDeviceList: @{
-            OctagonStateReEnactPrepare: @{
-                OctagonStateReEnactReadyToEstablish: @{
-                    OctagonStateEscrowTriggerUpdate: @{
-                        OctagonStateBecomeReady: @{
-                            OctagonStateReady: [OctagonStateTransitionPathStep success],
+        OctagonStateEstablishEnableCDPBit: @{
+            OctagonStateReEnactDeviceList: @{
+                OctagonStateReEnactPrepare: @{
+                    OctagonStateReEnactReadyToEstablish: @{
+                        OctagonStateEscrowTriggerUpdate: @{
+                            OctagonStateBecomeReady: @{
+                                OctagonStateReady: [OctagonStateTransitionPathStep success],
+                            },
                         },
                         },
-                    },
 
 
-                    // Error handling extra states:
-                    OctagonStateEstablishCKKSReset: @{
-                        OctagonStateEstablishAfterCKKSReset: @{
-                            OctagonStateEscrowTriggerUpdate: @{
-                                OctagonStateBecomeReady: @{
-                                    OctagonStateReady: [OctagonStateTransitionPathStep success],
+                        // Error handling extra states:
+                        OctagonStateEstablishCKKSReset: @{
+                            OctagonStateEstablishAfterCKKSReset: @{
+                                OctagonStateEscrowTriggerUpdate: @{
+                                    OctagonStateBecomeReady: @{
+                                        OctagonStateReady: [OctagonStateTransitionPathStep success],
+                                    },
                                 },
                             },
                         },
                                 },
                             },
                         },
@@ -697,6 +752,38 @@ static dispatch_time_t OctagonStateTransitionDefaultTimeout = 10*NSEC_PER_SEC;
         return [self cloudKitAccountNewlyAvailableOperation];
     }
 
         return [self cloudKitAccountNewlyAvailableOperation];
     }
 
+    if([currentState isEqualToString:OctagonStateDetermineCDPState]) {
+        return [[OTDetermineCDPBitStatusOperation alloc] initWithDependencies:self.operationDependencies
+                                                       intendedState:OctagonStateCheckTrustState
+                                                          errorState:OctagonStateWaitForCDP];
+    }
+
+    if([currentState isEqualToString:OctagonStateWaitForCDP]) {
+        if([flags _onqueueContains:OctagonFlagCDPEnabled]) {
+            [flags _onqueueRemoveFlag:OctagonFlagCDPEnabled];
+            secnotice("octagon", "CDP is newly available!");
+
+            return [OctagonStateTransitionOperation named:@"cdp_enabled"
+                                                 entering:OctagonStateDetermineiCloudAccountState];
+
+        } else if([flags _onqueueContains:OctagonFlagCuttlefishNotification]) {
+            [flags _onqueueRemoveFlag:OctagonFlagCuttlefishNotification];
+            return [OctagonStateTransitionOperation named:@"cdp_enabled_push_received"
+                                                 entering:OctagonStateWaitForCDPUpdated];
+
+        } else {
+            return nil;
+        }
+    }
+
+    if([currentState isEqualToString:OctagonStateWaitForCDPUpdated]) {
+        return [[OTUpdateTPHOperation alloc] initWithDependencies:self.operationDependencies
+                                                    intendedState:OctagonStateDetermineCDPState
+                                                 peerUnknownState:OctagonStateDetermineCDPState
+                                                       errorState:OctagonStateError
+                                                        retryFlag:OctagonFlagCuttlefishNotification];
+    }
+
     if([currentState isEqualToString:OctagonStateCheckTrustState]) {
         return [[OctagonCheckTrustStateOperation alloc] initWithDependencies:self.operationDependencies
                                                                intendedState:OctagonStateBecomeUntrusted
     if([currentState isEqualToString:OctagonStateCheckTrustState]) {
         return [[OctagonCheckTrustStateOperation alloc] initWithDependencies:self.operationDependencies
                                                                intendedState:OctagonStateBecomeUntrusted
@@ -705,12 +792,18 @@ static dispatch_time_t OctagonStateTransitionDefaultTimeout = 10*NSEC_PER_SEC;
 #pragma mark --- Octagon Health Check States
     if([currentState isEqualToString:OctagonStateHSA2HealthCheck]) {
         return [[OTDetermineHSA2AccountStatusOperation alloc] initWithDependencies:self.operationDependencies
 #pragma mark --- Octagon Health Check States
     if([currentState isEqualToString:OctagonStateHSA2HealthCheck]) {
         return [[OTDetermineHSA2AccountStatusOperation alloc] initWithDependencies:self.operationDependencies
-                                                                       stateIfHSA2:OctagonStateSecurityTrustCheck
+                                                                       stateIfHSA2:OctagonStateCDPHealthCheck
                                                                     stateIfNotHSA2:OctagonStateWaitForHSA2
                                                                   stateIfNoAccount:OctagonStateNoAccount
                                                                         errorState:OctagonStateError];
     }
 
                                                                     stateIfNotHSA2:OctagonStateWaitForHSA2
                                                                   stateIfNoAccount:OctagonStateNoAccount
                                                                         errorState:OctagonStateError];
     }
 
+    if([currentState isEqualToString:OctagonStateCDPHealthCheck]) {
+        return [[OTDetermineCDPBitStatusOperation alloc] initWithDependencies:self.operationDependencies
+                                                       intendedState:OctagonStateSecurityTrustCheck
+                                                          errorState:OctagonStateWaitForCDP];
+    }
+
     if([currentState isEqualToString:OctagonStateSecurityTrustCheck]) {
         return [self evaluateSecdOctagonTrust];
     }
     if([currentState isEqualToString:OctagonStateSecurityTrustCheck]) {
         return [self evaluateSecdOctagonTrust];
     }
@@ -749,6 +842,12 @@ static dispatch_time_t OctagonStateTransitionDefaultTimeout = 10*NSEC_PER_SEC;
     if([currentState isEqualToString:OctagonStateBecomeReady]) {
         return [self becomeReadyOperation];
     }
     if([currentState isEqualToString:OctagonStateBecomeReady]) {
         return [self becomeReadyOperation];
     }
+
+    if([currentState isEqualToString:OctagonStateRefetchCKKSPolicy]) {
+        return [[OTFetchViewsOperation alloc] initWithDependencies:self.operationDependencies
+                                                     intendedState:OctagonStateBecomeReady
+                                                        errorState:OctagonStateError];
+    }
     
     if([currentState isEqualToString:OctagonStateNoAccount]) {
         // We only want to move out of untrusted if something useful has happened!
     
     if([currentState isEqualToString:OctagonStateNoAccount]) {
         // We only want to move out of untrusted if something useful has happened!
@@ -797,11 +896,18 @@ static dispatch_time_t OctagonStateTransitionDefaultTimeout = 10*NSEC_PER_SEC;
         if([flags _onqueueContains:OctagonFlagIDMSLevelChanged]) {
             [flags _onqueueRemoveFlag:OctagonFlagIDMSLevelChanged];
         }
         if([flags _onqueueContains:OctagonFlagIDMSLevelChanged]) {
             [flags _onqueueRemoveFlag:OctagonFlagIDMSLevelChanged];
         }
+
+        // We're untrusted; no need for the CDP level flag anymore
+        if([flags _onqueueContains:OctagonFlagCDPEnabled]) {
+            secnotice("octagon", "Removing 'CDP enabled' flag");
+            [flags _onqueueRemoveFlag:OctagonFlagCDPEnabled];
+        }
     }
 
     if([currentState isEqualToString:OctagonStateUntrustedUpdated]) {
             return [[OTUpdateTPHOperation alloc] initWithDependencies:self.operationDependencies
                                                         intendedState:OctagonStateUntrusted
     }
 
     if([currentState isEqualToString:OctagonStateUntrustedUpdated]) {
             return [[OTUpdateTPHOperation alloc] initWithDependencies:self.operationDependencies
                                                         intendedState:OctagonStateUntrusted
+                                                     peerUnknownState:OctagonStateBecomeUntrusted
                                                            errorState:OctagonStateError
                                                             retryFlag:OctagonFlagCuttlefishNotification];
     }
                                                            errorState:OctagonStateError
                                                             retryFlag:OctagonFlagCuttlefishNotification];
     }
@@ -901,7 +1007,8 @@ static dispatch_time_t OctagonStateTransitionDefaultTimeout = 10*NSEC_PER_SEC;
                                                      intendedState:OctagonStateBecomeReady
                                                  ckksConflictState:OctagonStateSOSUpgradeCKKSReset
                                                         errorState:OctagonStateBecomeUntrusted
                                                      intendedState:OctagonStateBecomeReady
                                                  ckksConflictState:OctagonStateSOSUpgradeCKKSReset
                                                         errorState:OctagonStateBecomeUntrusted
-                                                        deviceInfo:self.prepareInformation];
+                                                        deviceInfo:self.prepareInformation
+                                                    policyOverride:self.policyOverride];
 
     } else if([currentState isEqualToString:OctagonStateSOSUpgradeCKKSReset]) {
         return [[OTLocalCKKSResetOperation alloc] initWithDependencies:self.operationDependencies
 
     } else if([currentState isEqualToString:OctagonStateSOSUpgradeCKKSReset]) {
         return [[OTLocalCKKSResetOperation alloc] initWithDependencies:self.operationDependencies
@@ -913,28 +1020,32 @@ static dispatch_time_t OctagonStateTransitionDefaultTimeout = 10*NSEC_PER_SEC;
                                                      intendedState:OctagonStateBecomeReady
                                                  ckksConflictState:OctagonStateBecomeUntrusted
                                                         errorState:OctagonStateBecomeUntrusted
                                                      intendedState:OctagonStateBecomeReady
                                                  ckksConflictState:OctagonStateBecomeUntrusted
                                                         errorState:OctagonStateBecomeUntrusted
-                                                        deviceInfo:self.prepareInformation];
+                                                        deviceInfo:self.prepareInformation
+                                                    policyOverride:self.policyOverride];
 
 
     } else if([currentState isEqualToString:OctagonStateCreateIdentityForRecoveryKey]) {
 
 
     } else if([currentState isEqualToString:OctagonStateCreateIdentityForRecoveryKey]) {
-        OTPrepareOperation* op = [[OTPrepareOperation alloc] initWithDependencies:self.operationDependencies
-                                                                    intendedState:OctagonStateVouchWithRecoveryKey
-                                                                       errorState:OctagonStateBecomeUntrusted
-                                                                       deviceInfo:[self prepareInformation]
-                                                                            epoch:1];
-        return op;
+        return [[OTPrepareOperation alloc] initWithDependencies:self.operationDependencies
+                                                  intendedState:OctagonStateVouchWithRecoveryKey
+                                                     errorState:OctagonStateBecomeUntrusted
+                                                     deviceInfo:[self prepareInformation]
+                                                 policyOverride:self.policyOverride
+                                                          epoch:1];
 
 
-    } else if([currentState isEqualToString:OctagonStateInitiatorCreateIdentity]) {
-        OTPrepareOperation* op = [[OTPrepareOperation alloc] initWithDependencies:self.operationDependencies
-                                                                           intendedState:OctagonStateInitiatorVouchWithBottle
-                                                                              errorState:OctagonStateBecomeUntrusted
-                                                                              deviceInfo:[self prepareInformation]
-                                                                                   epoch:1];
-        return op;
+    } else if([currentState isEqualToString:OctagonStateBottleJoinCreateIdentity]) {
+        return [[OTPrepareOperation alloc] initWithDependencies:self.operationDependencies
+                                                  intendedState:OctagonStateBottleJoinVouchWithBottle
+                                                     errorState:OctagonStateBecomeUntrusted
+                                                     deviceInfo:[self prepareInformation]
+                                                 policyOverride:self.policyOverride
+                                                          epoch:1];
+
+    } else if([currentState isEqualToString:OctagonStateBottleJoinVouchWithBottle]) {
+        // <rdar://problem/57768490> Octagon: ensure we use appropriate CKKS policy when joining octagon with future policy
+        // When we join with a bottle, we need to be sure that we've found all the TLKShares that we can reasonably unpack via the bottle
 
 
-    } else if([currentState isEqualToString:OctagonStateInitiatorVouchWithBottle]) {
         OTVouchWithBottleOperation* pendingOp  = [[OTVouchWithBottleOperation alloc] initWithDependencies:self.operationDependencies
         OTVouchWithBottleOperation* pendingOp  = [[OTVouchWithBottleOperation alloc] initWithDependencies:self.operationDependencies
-                                                                                            intendedState:OctagonStateInitiatorUpdateDeviceList
+                                                                                            intendedState:OctagonStateInitiatorSetCDPBit
                                                                                                errorState:OctagonStateBecomeUntrusted
                                                                                                  bottleID:_bottleID
                                                                                                   entropy:_entropy
                                                                                                errorState:OctagonStateBecomeUntrusted
                                                                                                  bottleID:_bottleID
                                                                                                   entropy:_entropy
@@ -953,7 +1064,7 @@ static dispatch_time_t OctagonStateTransitionDefaultTimeout = 10*NSEC_PER_SEC;
 
     } else if([currentState isEqualToString:OctagonStateVouchWithRecoveryKey]) {
         OTVouchWithRecoveryKeyOperation* pendingOp  = [[OTVouchWithRecoveryKeyOperation alloc] initWithDependencies:self.operationDependencies
 
     } else if([currentState isEqualToString:OctagonStateVouchWithRecoveryKey]) {
         OTVouchWithRecoveryKeyOperation* pendingOp  = [[OTVouchWithRecoveryKeyOperation alloc] initWithDependencies:self.operationDependencies
-                                                                                            intendedState:OctagonStateInitiatorUpdateDeviceList
+                                                                                            intendedState:OctagonStateInitiatorSetCDPBit
                                                                                                errorState:OctagonStateBecomeUntrusted
                                                                                                  recoveryKey:_recoveryKey];
 
                                                                                                errorState:OctagonStateBecomeUntrusted
                                                                                                  recoveryKey:_recoveryKey];
 
@@ -968,6 +1079,11 @@ static dispatch_time_t OctagonStateTransitionDefaultTimeout = 10*NSEC_PER_SEC;
 
         return pendingOp;
 
 
         return pendingOp;
 
+    } else if([currentState isEqualToString:OctagonStateInitiatorSetCDPBit]) {
+        return [[OTSetCDPBitOperation alloc] initWithDependencies:self.operationDependencies
+                                                    intendedState:OctagonStateInitiatorUpdateDeviceList
+                                                       errorState:OctagonStateDetermineCDPState];
+
     } else if([currentState isEqualToString:OctagonStateInitiatorUpdateDeviceList]) {
         // As part of the 'initiate' flow, we need to update the trusted device list-you're probably on it already
         OTUpdateTrustedDeviceListOperation* op = [[OTUpdateTrustedDeviceListOperation alloc] initWithDependencies:self.operationDependencies
     } else if([currentState isEqualToString:OctagonStateInitiatorUpdateDeviceList]) {
         // As part of the 'initiate' flow, we need to update the trusted device list-you're probably on it already
         OTUpdateTrustedDeviceListOperation* op = [[OTUpdateTrustedDeviceListOperation alloc] initWithDependencies:self.operationDependencies
@@ -983,8 +1099,7 @@ static dispatch_time_t OctagonStateTransitionDefaultTimeout = 10*NSEC_PER_SEC;
                                                                                  ckksConflictState:OctagonStateInitiatorJoinCKKSReset
                                                                                         errorState:OctagonStateBecomeUntrusted
                                                                                        voucherData:_vouchData
                                                                                  ckksConflictState:OctagonStateInitiatorJoinCKKSReset
                                                                                         errorState:OctagonStateBecomeUntrusted
                                                                                        voucherData:_vouchData
-                                                                                        voucherSig:_vouchSig
-                                                                                   preapprovedKeys:_preapprovedKeys];
+                                                                                        voucherSig:_vouchSig];
         return op;
 
     } else if([currentState isEqualToString:OctagonStateInitiatorJoinCKKSReset]) {
         return op;
 
     } else if([currentState isEqualToString:OctagonStateInitiatorJoinCKKSReset]) {
@@ -998,8 +1113,7 @@ static dispatch_time_t OctagonStateTransitionDefaultTimeout = 10*NSEC_PER_SEC;
                                                       ckksConflictState:OctagonStateBecomeUntrusted
                                                              errorState:OctagonStateBecomeUntrusted
                                                             voucherData:_vouchData
                                                       ckksConflictState:OctagonStateBecomeUntrusted
                                                              errorState:OctagonStateBecomeUntrusted
                                                             voucherData:_vouchData
-                                                             voucherSig:_vouchSig
-                                                        preapprovedKeys:_preapprovedKeys];
+                                                             voucherSig:_vouchSig];
 
     } else if([currentState isEqualToString:OctagonStateResetBecomeUntrusted]) {
         return [self becomeUntrustedOperation:OctagonStateResetAndEstablish];
 
     } else if([currentState isEqualToString:OctagonStateResetBecomeUntrusted]) {
         return [self becomeUntrustedOperation:OctagonStateResetAndEstablish];
@@ -1014,22 +1128,29 @@ static dispatch_time_t OctagonStateTransitionDefaultTimeout = 10*NSEC_PER_SEC;
 
     } else if([currentState isEqualToString:OctagonStateResetAnyMissingTLKCKKSViews]) {
         return [[OTResetCKKSZonesLackingTLKsOperation alloc] initWithDependencies:self.operationDependencies
 
     } else if([currentState isEqualToString:OctagonStateResetAnyMissingTLKCKKSViews]) {
         return [[OTResetCKKSZonesLackingTLKsOperation alloc] initWithDependencies:self.operationDependencies
-                                                           intendedState:OctagonStateReEnactDeviceList
+                                                           intendedState:OctagonStateEstablishEnableCDPBit
                                                               errorState:OctagonStateError];
 
                                                               errorState:OctagonStateError];
 
+    } else if([currentState isEqualToString:OctagonStateEstablishEnableCDPBit]) {
+        return [[OTSetCDPBitOperation alloc] initWithDependencies:self.operationDependencies
+                                                    intendedState:OctagonStateReEnactDeviceList
+                                                       errorState:OctagonStateError];
+
     } else if([currentState isEqualToString:OctagonStateReEnactDeviceList]) {
         return [[OTUpdateTrustedDeviceListOperation alloc] initWithDependencies:self.operationDependencies
                                                                   intendedState:OctagonStateReEnactPrepare
                                                                listUpdatesState:OctagonStateReEnactPrepare
     } else if([currentState isEqualToString:OctagonStateReEnactDeviceList]) {
         return [[OTUpdateTrustedDeviceListOperation alloc] initWithDependencies:self.operationDependencies
                                                                   intendedState:OctagonStateReEnactPrepare
                                                                listUpdatesState:OctagonStateReEnactPrepare
-                                                                     errorState:OctagonStateError
+                                                                     errorState:OctagonStateBecomeUntrusted
                                                                       retryFlag:nil];
 
     } else if([currentState isEqualToString:OctagonStateReEnactPrepare]) {
                                                                       retryFlag:nil];
 
     } else if([currentState isEqualToString:OctagonStateReEnactPrepare]) {
+        // <rdar://problem/56270219> Octagon: use epoch transmitted across pairing channel
         // Note: Resetting the account returns epoch to 0.
         return [[OTPrepareOperation alloc] initWithDependencies:self.operationDependencies
                                                   intendedState:OctagonStateReEnactReadyToEstablish
                                                      errorState:OctagonStateError
                                                      deviceInfo:[self prepareInformation]
         // Note: Resetting the account returns epoch to 0.
         return [[OTPrepareOperation alloc] initWithDependencies:self.operationDependencies
                                                   intendedState:OctagonStateReEnactReadyToEstablish
                                                      errorState:OctagonStateError
                                                      deviceInfo:[self prepareInformation]
+                                                 policyOverride:self.policyOverride
                                                           epoch:0];
 
     } else if([currentState isEqualToString:OctagonStateReEnactReadyToEstablish]) {
                                                           epoch:0];
 
     } else if([currentState isEqualToString:OctagonStateReEnactReadyToEstablish]) {
@@ -1051,11 +1172,15 @@ static dispatch_time_t OctagonStateTransitionDefaultTimeout = 10*NSEC_PER_SEC;
                                                        errorState:OctagonStateBecomeUntrusted];
 
     } else if ([currentState isEqualToString:OctagonStateEscrowTriggerUpdate]){
                                                        errorState:OctagonStateBecomeUntrusted];
 
     } else if ([currentState isEqualToString:OctagonStateEscrowTriggerUpdate]){
-
         return [[OTTriggerEscrowUpdateOperation alloc] initWithDependencies:self.operationDependencies
                                                               intendedState:OctagonStateBecomeReady
                                                                  errorState:OctagonStateError];
 
         return [[OTTriggerEscrowUpdateOperation alloc] initWithDependencies:self.operationDependencies
                                                               intendedState:OctagonStateBecomeReady
                                                                  errorState:OctagonStateError];
 
+    } else if ([currentState isEqualToString:OctagonStateHealthCheckLeaveClique]) {
+        return [[OTLeaveCliqueOperation alloc] initWithDependencies: self.operationDependencies
+                                                      intendedState: OctagonStateBecomeUntrusted
+                                                         errorState: OctagonStateBecomeUntrusted];
+
     } else if([currentState isEqualToString: OctagonStateWaitForUnlock]) {
         if([flags _onqueueContains:OctagonFlagUnlocked]) {
             [flags _onqueueRemoveFlag:OctagonFlagUnlocked];
     } else if([currentState isEqualToString: OctagonStateWaitForUnlock]) {
         if([flags _onqueueContains:OctagonFlagUnlocked]) {
             [flags _onqueueRemoveFlag:OctagonFlagUnlocked];
@@ -1156,6 +1281,12 @@ static dispatch_time_t OctagonStateTransitionDefaultTimeout = 10*NSEC_PER_SEC;
             [flags _onqueueRemoveFlag:OctagonFlagIDMSLevelChanged];
         }
 
             [flags _onqueueRemoveFlag:OctagonFlagIDMSLevelChanged];
         }
 
+        // We're ready; no need for the CDP level flag anymore
+        if([flags _onqueueContains:OctagonFlagCDPEnabled]) {
+            secnotice("octagon", "Removing 'CDP enabled' flag");
+            [flags _onqueueRemoveFlag:OctagonFlagCDPEnabled];
+        }
+
         secnotice("octagon", "Entering state ready");
         [[CKKSAnalytics logger] setDateProperty:[NSDate date] forKey:OctagonAnalyticsLastKeystateReady];
         [self.launchSequence launch];
         secnotice("octagon", "Entering state ready");
         [[CKKSAnalytics logger] setDateProperty:[NSDate date] forKey:OctagonAnalyticsLastKeystateReady];
         [self.launchSequence launch];
@@ -1163,10 +1294,14 @@ static dispatch_time_t OctagonStateTransitionDefaultTimeout = 10*NSEC_PER_SEC;
     } else if([currentState isEqualToString:OctagonStateReadyUpdated]) {
         return [[OTUpdateTPHOperation alloc] initWithDependencies:self.operationDependencies
                                                     intendedState:OctagonStateReady
     } else if([currentState isEqualToString:OctagonStateReadyUpdated]) {
         return [[OTUpdateTPHOperation alloc] initWithDependencies:self.operationDependencies
                                                     intendedState:OctagonStateReady
+                                                 peerUnknownState:OctagonStateBecomeUntrusted
                                                        errorState:OctagonStateError
                                                         retryFlag:OctagonFlagCuttlefishNotification];
 
                                                        errorState:OctagonStateError
                                                         retryFlag:OctagonFlagCuttlefishNotification];
 
-    } else if ([currentState isEqualToString:OctagonStateError]) {
+    }
+
+    if ([currentState isEqualToString:OctagonStateError]) {
+        return nil;
     }
 
     return nil;
     }
 
     return nil;
@@ -1237,7 +1372,7 @@ static dispatch_time_t OctagonStateTransitionDefaultTimeout = 10*NSEC_PER_SEC;
 
                            } else {
                                secnotice("octagon-health", "trust state (%@). checking in with TPH", [account trustStateAsString:account.trustState]);
 
                            } else {
                                secnotice("octagon-health", "trust state (%@). checking in with TPH", [account trustStateAsString:account.trustState]);
-                               op.nextState = [self repairAccountIfTrustedByTPHWithIntededState:OctagonStateTPHTrustCheck errorState:OctagonStatePostRepairCFU];
+                               op.nextState = [self repairAccountIfTrustedByTPHWithIntendedState:OctagonStateTPHTrustCheck];
                            }
                        }];
 }
                            }
                        }];
 }
@@ -1284,8 +1419,8 @@ static dispatch_time_t OctagonStateTransitionDefaultTimeout = 10*NSEC_PER_SEC;
 
     CKKSResultOperation* callback = [CKKSResultOperation named:@"rpcHealthCheck"
                                                      withBlock:^{
 
     CKKSResultOperation* callback = [CKKSResultOperation named:@"rpcHealthCheck"
                                                      withBlock:^{
-                                                         secnotice("octagon-health", "Returning from cuttlefish trust check call: postRepairCFU(%d), postEscrowCFU(%d), resetOctagon(%d)",
-                                                                   op.postRepairCFU, op.postEscrowCFU, op.resetOctagon);
+                                                         secnotice("octagon-health", "Returning from cuttlefish trust check call: postRepairCFU(%d), postEscrowCFU(%d), resetOctagon(%d), leaveTrust(%d)",
+                                                                   op.postRepairCFU, op.postEscrowCFU, op.resetOctagon, op.leaveTrust);
                                                          if(op.postRepairCFU) {
                                                              secnotice("octagon-health", "Posting Repair CFU");
                                                              NSError* postRepairCFUError = nil;
                                                          if(op.postRepairCFU) {
                                                              secnotice("octagon-health", "Posting Repair CFU");
                                                              NSError* postRepairCFUError = nil;
@@ -1312,7 +1447,15 @@ static dispatch_time_t OctagonStateTransitionDefaultTimeout = 10*NSEC_PER_SEC;
                                                              } else {
                                                                  secnotice("octagon-health", "Not posting confirm passcode CFU, already pending a prerecord upload");
                                                              }
                                                              } else {
                                                                  secnotice("octagon-health", "Not posting confirm passcode CFU, already pending a prerecord upload");
                                                              }
+
                                                          }
                                                          }
+                                                        if(op.leaveTrust){
+                                                            secnotice("octagon-health", "Leaving Octagon and SOS trust");
+                                                            NSError* leaveError = nil;
+                                                            if(![self leaveTrust:&leaveError]) {
+                                                                op.error = leaveError;
+                                                            }
+                                                        }
                                                      }];
     [callback addDependency:op];
     [self.operationQueue addOperation: callback];
                                                      }];
     [callback addDependency:op];
     [self.operationQueue addOperation: callback];
@@ -1343,7 +1486,7 @@ static dispatch_time_t OctagonStateTransitionDefaultTimeout = 10*NSEC_PER_SEC;
 {
     WEAKIFY(self);
     return [OctagonStateTransitionOperation named:@"octagon-icloud-account-available"
 {
     WEAKIFY(self);
     return [OctagonStateTransitionOperation named:@"octagon-icloud-account-available"
-                                        intending:OctagonStateCheckTrustState
+                                        intending:OctagonStateDetermineCDPState
                                        errorState:OctagonStateError
                               withBlockTakingSelf:^(OctagonStateTransitionOperation * _Nonnull op) {
                                   STRONGIFY(self);
                                        errorState:OctagonStateError
                               withBlockTakingSelf:^(OctagonStateTransitionOperation * _Nonnull op) {
                                   STRONGIFY(self);
@@ -1375,12 +1518,12 @@ static dispatch_time_t OctagonStateTransitionDefaultTimeout = 10*NSEC_PER_SEC;
                               }];
 }
 
                               }];
 }
 
-- (OctagonState*) repairAccountIfTrustedByTPHWithIntededState:(OctagonState*)intendedState errorState:(OctagonState*)errorState
+- (OctagonState*) repairAccountIfTrustedByTPHWithIntendedState:(OctagonState*)intendedState
 {
     __block OctagonState* nextState = intendedState;
 
     //let's check in with TPH real quick to make sure it agrees with our local assessment
 {
     __block OctagonState* nextState = intendedState;
 
     //let's check in with TPH real quick to make sure it agrees with our local assessment
-    secnotice("octagon-health", "repairAccountIfTrustedByTPHWithIntededState: calling into TPH for trust status");
+    secnotice("octagon-health", "repairAccountIfTrustedByTPHWithIntendedState: calling into TPH for trust status");
    
     OTOperationConfiguration *config = [[OTOperationConfiguration alloc]init];
    
    
     OTOperationConfiguration *config = [[OTOperationConfiguration alloc]init];
    
@@ -1390,7 +1533,7 @@ static dispatch_time_t OctagonStateTransitionDefaultTimeout = 10*NSEC_PER_SEC;
                                         BOOL isExcluded,
                                         NSError * _Nullable error) {
         BOOL hasIdentity = egoPeerID != nil;
                                         BOOL isExcluded,
                                         NSError * _Nullable error) {
         BOOL hasIdentity = egoPeerID != nil;
-        secnotice("octagon-health", "repairAccountIfTrustedByTPHWithIntededState status: %ld, peerID: %@, isExcluded: %d error: %@", (long)status, egoPeerID, isExcluded, error);
+        secnotice("octagon-health", "repairAccountIfTrustedByTPHWithIntendedState status: %ld, peerID: %@, isExcluded: %d error: %@", (long)status, egoPeerID, isExcluded, error);
 
         if (error) {
             secnotice("octagon-health", "got an error from tph, returning to become_ready state: %@", error);
 
         if (error) {
             secnotice("octagon-health", "got an error from tph, returning to become_ready state: %@", error);
@@ -1399,66 +1542,31 @@ static dispatch_time_t OctagonStateTransitionDefaultTimeout = 10*NSEC_PER_SEC;
         }
 
         if(OctagonAuthoritativeTrustIsEnabled() && hasIdentity && status == CliqueStatusIn) {
         }
 
         if(OctagonAuthoritativeTrustIsEnabled() && hasIdentity && status == CliqueStatusIn) {
-            dispatch_semaphore_t sema = dispatch_semaphore_create(0);
-            [self rpcStatus:^(NSDictionary *dump, NSError *dumpError) {
-                if(dumpError) {
-                    secerror("octagon-health: error fetching ego peer id!: %@", dumpError);
-                    nextState = errorState;
-                } else {
-                    NSDictionary* egoInformation = dump[@"self"];
-                    NSString* peerID = egoInformation[@"peerID"];
-                    NSError* persistError = nil;
-                    BOOL persisted = [self.accountMetadataStore persistAccountChanges:^OTAccountMetadataClassC * _Nonnull(OTAccountMetadataClassC * _Nonnull metadata) {
-                        metadata.trustState = OTAccountMetadataClassC_TrustState_TRUSTED;
-                        metadata.icloudAccountState = OTAccountMetadataClassC_AccountState_ACCOUNT_AVAILABLE;
-                        metadata.peerID = peerID;
-                        return metadata;
-                    } error:&persistError];
-                    if(!persisted || persistError) {
-                        secerror("octagon-health: couldn't persist results: %@", persistError);
-                        nextState = errorState;
-                    } else {
-                        secnotice("octagon-health", "added trusted identity to account metadata");
-                        nextState = intendedState;
-                    }
-                }
-                dispatch_semaphore_signal(sema);
-            }];
-            if (0 != dispatch_semaphore_wait(sema, dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC * 10))) {
-                secerror("octagon: Timed out checking trust status");
+            secnotice("octagon-health", "TPH believes we're trusted, accepting ego peerID as %@", egoPeerID);
+
+            NSError* persistError = nil;
+            BOOL persisted = [self.accountMetadataStore persistAccountChanges:^OTAccountMetadataClassC * _Nonnull(OTAccountMetadataClassC * _Nonnull metadata) {
+                metadata.trustState = OTAccountMetadataClassC_TrustState_TRUSTED;
+                metadata.peerID = egoPeerID;
+                return metadata;
+            } error:&persistError];
+            if(!persisted || persistError) {
+                secerror("octagon-health: couldn't persist results: %@", persistError);
+                nextState = OctagonStateError;
+            } else {
+                secnotice("octagon-health", "added trusted identity to account metadata");
+                nextState = intendedState;
             }
             }
-        } else if (OctagonAuthoritativeTrustIsEnabled() && (self.postedRepairCFU == NO) && hasIdentity && status != CliqueStatusIn){
-            nextState = errorState;
+
+        } else if (OctagonAuthoritativeTrustIsEnabled() && hasIdentity && status != CliqueStatusIn){
+            secnotice("octagon-health", "TPH believes we're not trusted, requesting CFU post");
+            nextState = OctagonStatePostRepairCFU;
         }
     }];
 
     return nextState;
 }
 
         }
     }];
 
     return nextState;
 }
 
-- (BOOL) didDeviceAttemptToJoinOctagon:(NSError**)error
-{
-    NSError* fetchAttemptError = nil;
-    OTAccountMetadataClassC_AttemptedAJoinState attemptedAJoin = [self.accountMetadataStore fetchPersistedJoinAttempt:&fetchAttemptError];
-    if(fetchAttemptError) {
-        secerror("octagon: failed to fetch data indicating device attempted to join octagon, assuming it did: %@", fetchAttemptError);
-        if(error){
-            *error = fetchAttemptError;
-        }
-        return YES;
-    }
-    BOOL attempted = YES;
-    switch (attemptedAJoin) {
-        case OTAccountMetadataClassC_AttemptedAJoinState_NOTATTEMPTED:
-            attempted = NO;
-            break;
-        case OTAccountMetadataClassC_AttemptedAJoinState_ATTEMPTED:
-        case OTAccountMetadataClassC_AttemptedAJoinState_UNKNOWN:
-        default:
-            break;
-    }
-    return attempted;
-}
-
 - (void)checkTrustStatusAndPostRepairCFUIfNecessary:(void (^ _Nullable)(CliqueStatus status, BOOL posted, BOOL hasIdentity, NSError * _Nullable error))reply
 {
     WEAKIFY(self);
 - (void)checkTrustStatusAndPostRepairCFUIfNecessary:(void (^ _Nullable)(CliqueStatus status, BOOL posted, BOOL hasIdentity, NSError * _Nullable error))reply
 {
     WEAKIFY(self);
@@ -1505,17 +1613,38 @@ static dispatch_time_t OctagonStateTransitionDefaultTimeout = 10*NSEC_PER_SEC;
         // On platforms with SOS, we only want to post a CFU if we've attempted to join at least once.
         // This prevents us from posting a CFU, then performing an SOS upgrade and succeeding.
         if(self.sosAdapter.sosEnabled) {
         // On platforms with SOS, we only want to post a CFU if we've attempted to join at least once.
         // This prevents us from posting a CFU, then performing an SOS upgrade and succeeding.
         if(self.sosAdapter.sosEnabled) {
-            NSError* fetchAttemptError = nil;
-            BOOL attemptedToJoin = [self didDeviceAttemptToJoinOctagon:&fetchAttemptError];
-            if(fetchAttemptError){
-                secerror("octagon: failed to retrieve joining attempt information: %@", fetchAttemptError);
-                attemptedToJoin = YES;
-            }
+            NSError* fetchError = nil;
+            OTAccountMetadataClassC* accountState = [self.accountMetadataStore loadOrCreateAccountMetadata:&fetchError];
 
 
-            if(!attemptedToJoin) {
-                secnotice("octagon", "SOS is enabled and we haven't attempted to join; not posting CFU");
-                reply(status, NO, hasIdentity, nil);
-                return;
+            if(!accountState || fetchError){
+                secerror("octagon: failed to retrieve joining attempt information: %@", fetchError);
+                // fall through to below: posting the CFU is better than a false negative
+
+            } else if(accountState.attemptedJoin == OTAccountMetadataClassC_AttemptedAJoinState_ATTEMPTED) {
+                // Normal flow, fall through to below
+            } else {
+                // Triple-check with SOS: if it's in a bad state, post the CFU anyway
+                secnotice("octagon", "SOS is enabled and we haven't attempted to join; checking with SOS");
+
+                NSError* circleError = nil;
+                SOSCCStatus sosStatus = [self.sosAdapter circleStatus:&circleError];
+
+                if(circleError && [circleError.domain isEqualToString:(__bridge NSString*)kSOSErrorDomain] && circleError.code == kSOSErrorNotReady) {
+                    secnotice("octagon", "SOS is not ready, not posting CFU until it becomes so");
+                    reply(status, NO, hasIdentity, nil);
+                    return;
+
+                } else if(circleError) {
+                    // Any other error probably indicates that there is some circle, but we're not in it
+                    secnotice("octagon", "SOS is in an unknown error state, posting CFU: %@", circleError);
+
+                } else if(sosStatus == kSOSCCInCircle) {
+                    secnotice("octagon", "SOS is InCircle, not posting CFU");
+                    reply(status, NO, hasIdentity, nil);
+                    return;
+                } else {
+                    secnotice("octagon", "SOS is %@, posting CFU", (__bridge NSString*)SOSCCGetStatusDescription(sosStatus));
+                }
             }
         }
 
             }
         }
 
@@ -1565,7 +1694,10 @@ static dispatch_time_t OctagonStateTransitionDefaultTimeout = 10*NSEC_PER_SEC;
                                   STRONGIFY(self);
                                   NSError* localError = nil;
 
                                   STRONGIFY(self);
                                   NSError* localError = nil;
 
-                                  [self.accountStateTracker triggerOctagonStatusFetch];
+                                  // During testing, don't kick this off until it's needed
+                                  if([self.contextID isEqualToString:OTDefaultContext]) {
+                                      [self.accountStateTracker triggerOctagonStatusFetch];
+                                  }
 
                                   [self checkTrustStatusAndPostRepairCFUIfNecessary:^(CliqueStatus status, BOOL posted, BOOL hasIdentity, NSError * _Nullable postError) {
 
 
                                   [self checkTrustStatusAndPostRepairCFUIfNecessary:^(CliqueStatus status, BOOL posted, BOOL hasIdentity, NSError * _Nullable postError) {
 
@@ -1616,59 +1748,89 @@ static dispatch_time_t OctagonStateTransitionDefaultTimeout = 10*NSEC_PER_SEC;
                               withBlockTakingSelf:^(OctagonStateTransitionOperation * _Nonnull op) {
                                   STRONGIFY(self);
 
                               withBlockTakingSelf:^(OctagonStateTransitionOperation * _Nonnull op) {
                                   STRONGIFY(self);
 
-                                  // Note: we don't modify the account metadata store here; that will have been done
-                                  // by a join or upgrade operation, possibly long ago
+        if([self.contextID isEqualToString:OTDefaultContext]) {
+            [self.accountStateTracker triggerOctagonStatusFetch];
+        }
 
 
-                                  [self.accountStateTracker triggerOctagonStatusFetch];
+        // Note: we don't modify the account metadata trust state; that will have been done
+        // by a join or upgrade operation, possibly long ago
 
 
-                                  NSError* localError = nil;
-                                  NSString* peerID = [self.accountMetadataStore getEgoPeerID:&localError];
-                                  if(!peerID || localError) {
-                                      secerror("octagon-ckks: No peer ID to pass to CKKS. Syncing will be disabled.");
-                                  } else {
-                                      OctagonCKKSPeerAdapter* octagonAdapter = [[OctagonCKKSPeerAdapter alloc] initWithPeerID:peerID operationDependencies:[self operationDependencies]];
+        // but, we do set the 'attempted join' bit, just in case the device joined before we started setting this bit
 
 
-                                      // This octagon adapter must be able to load the self peer keys, or we're in trouble.
-                                      NSError* egoPeerKeysError = nil;
-                                      CKKSSelves* selves = [octagonAdapter fetchSelfPeers:&egoPeerKeysError];
-                                      if(!selves || egoPeerKeysError) {
-                                          secerror("octagon-ckks: Unable to fetch self peers for %@: %@", octagonAdapter, egoPeerKeysError);
+        // Also, ensure that the CKKS policy is correctly present and set in the view manager
+        __block NSString* peerID = nil;
+        NSError* localError = nil;
 
 
-                                          if([self.lockStateTracker isLockedError:egoPeerKeysError]) {
-                                              secnotice("octagon-ckks", "Waiting for device unlock to proceed");
-                                              op.nextState = OctagonStateWaitForUnlock;
-                                          } else {
-                                              secnotice("octagon-ckks", "Error is scary; becoming untrusted");
-                                              op.nextState = OctagonStateBecomeUntrusted;
-                                          }
-                                          return;
-                                      }
+        __block NSSet<NSString*>* viewList = nil;
+        __block TPPolicy* policy = nil;
 
 
-                                      // stash a reference to the adapter so we can provided updates later
-                                      self.octagonAdapter = octagonAdapter;
+        [self.accountMetadataStore persistAccountChanges:^OTAccountMetadataClassC * _Nullable(OTAccountMetadataClassC * _Nonnull metadata) {
+            peerID = metadata.peerID;
+            viewList = metadata.syncingViews.count > 0 ? [NSSet setWithArray:metadata.syncingViews] : nil;
+            policy = metadata.hasSyncingPolicy ? [metadata getTPPolicy] : nil;
 
 
-                                      // Start all our CKKS views!
-                                      for (id key in self.viewManager.views) {
-                                          CKKSKeychainView* view = self.viewManager.views[key];
-                                          secnotice("octagon-ckks", "Informing CKKS view '%@' of trusted operation with self peer %@", view.zoneName, peerID);
+            if(metadata.attemptedJoin == OTAccountMetadataClassC_AttemptedAJoinState_ATTEMPTED) {
+                return nil;
+            }
+            metadata.attemptedJoin = OTAccountMetadataClassC_AttemptedAJoinState_ATTEMPTED;
+            return metadata;
+        } error:&localError];
+
+        if(!peerID || localError) {
+            secerror("octagon-ckks: No peer ID to pass to CKKS. Syncing will be disabled.");
+        } else if(!viewList || !policy) {
+            secerror("octagon-ckks: No memoized CKKS policy, re-fetching");
+            op.nextState = OctagonStateRefetchCKKSPolicy;
+            return;
 
 
-                                          NSArray<id<CKKSPeerProvider>>* peerProviders = nil;
+        } else {
+            secnotice("octagon-ckks", "Initializing CKKS views with view list %@ and policy %@", viewList, policy);
+            [self.viewManager setSyncingViews:viewList
+                                sortingPolicy:policy];
 
 
-                                          if(self.sosAdapter.sosEnabled) {
-                                              peerProviders = @[self.octagonAdapter, self.sosAdapter];
+            OctagonCKKSPeerAdapter* octagonAdapter = [[OctagonCKKSPeerAdapter alloc] initWithPeerID:peerID operationDependencies:[self operationDependencies]];
 
 
-                                          } else {
-                                              peerProviders = @[self.octagonAdapter];
-                                          }
+            // This octagon adapter must be able to load the self peer keys, or we're in trouble.
+            NSError* egoPeerKeysError = nil;
+            CKKSSelves* selves = [octagonAdapter fetchSelfPeers:&egoPeerKeysError];
+            if(!selves || egoPeerKeysError) {
+                secerror("octagon-ckks: Unable to fetch self peers for %@: %@", octagonAdapter, egoPeerKeysError);
 
 
-                                          [view beginTrustedOperation:peerProviders
-                                                     suggestTLKUpload:self.suggestTLKUploadNotifier];
-                                      }
-                                  }
-                                  [self notifyTrustChanged:OTAccountMetadataClassC_TrustState_TRUSTED];
+                if([self.lockStateTracker isLockedError:egoPeerKeysError]) {
+                    secnotice("octagon-ckks", "Waiting for device unlock to proceed");
+                    op.nextState = OctagonStateWaitForUnlock;
+                } else {
+                    secnotice("octagon-ckks", "Error is scary; becoming untrusted");
+                    op.nextState = OctagonStateBecomeUntrusted;
+                }
+                return;
+            }
 
 
-                                  op.nextState = op.intendedState;
-                              }];
+            // stash a reference to the adapter so we can provide updates later
+            self.octagonAdapter = octagonAdapter;
+
+            // Start all our CKKS views!
+            for (id key in self.viewManager.views) {
+                CKKSKeychainView* view = self.viewManager.views[key];
+                secnotice("octagon-ckks", "Informing CKKS view '%@' of trusted operation with self peer %@", view.zoneName, peerID);
+
+                NSArray<id<CKKSPeerProvider>>* peerProviders = nil;
+
+                if(self.sosAdapter.sosEnabled) {
+                    peerProviders = @[self.octagonAdapter, self.sosAdapter];
+
+                } else {
+                    peerProviders = @[self.octagonAdapter];
+                }
+
+                [view beginTrustedOperation:peerProviders
+                           suggestTLKUpload:self.suggestTLKUploadNotifier];
+            }
+        }
+        [self notifyTrustChanged:OTAccountMetadataClassC_TrustState_TRUSTED];
+
+        op.nextState = op.intendedState;
+    }];
 }
 
 #pragma mark --- Utilities to run at times
 }
 
 #pragma mark --- Utilities to run at times
@@ -1744,7 +1906,7 @@ static dispatch_time_t OctagonStateTransitionDefaultTimeout = 10*NSEC_PER_SEC;
 
 - (void)notifyContainerChangeWithUserInfo:(NSDictionary*)userInfo
 {
 
 - (void)notifyContainerChangeWithUserInfo:(NSDictionary*)userInfo
 {
-    secerror("OTCuttlefishContext: received a cuttlefish push notification (%@): %@",
+    secnotice("octagonpush", "received a cuttlefish push notification (%@): %@",
              self.containerName, userInfo);
 
     NSDictionary *cfDictionary = userInfo[@"cf"];
              self.containerName, userInfo);
 
     NSDictionary *cfDictionary = userInfo[@"cf"];
@@ -1952,13 +2114,14 @@ static dispatch_time_t OctagonStateTransitionDefaultTimeout = 10*NSEC_PER_SEC;
     }
 
     secnotice("otrpc", "Preparing identity as applicant");
     }
 
     secnotice("otrpc", "Preparing identity as applicant");
+
     OTPrepareOperation* pendingOp = [[OTPrepareOperation alloc] initWithDependencies:self.operationDependencies
                                                                        intendedState:OctagonStateInitiatorAwaitingVoucher
                                                                           errorState:OctagonStateBecomeUntrusted
                                                                           deviceInfo:[self prepareInformation]
     OTPrepareOperation* pendingOp = [[OTPrepareOperation alloc] initWithDependencies:self.operationDependencies
                                                                        intendedState:OctagonStateInitiatorAwaitingVoucher
                                                                           errorState:OctagonStateBecomeUntrusted
                                                                           deviceInfo:[self prepareInformation]
+                                                                      policyOverride:self.policyOverride
                                                                                epoch:epoch];
 
                                                                                epoch:epoch];
 
-
     dispatch_time_t timeOut = 0;
     if(config.timeout != 0) {
         timeOut = config.timeout;
     dispatch_time_t timeOut = 0;
     if(config.timeout != 0) {
         timeOut = config.timeout;
@@ -2010,8 +2173,8 @@ static dispatch_time_t OctagonStateTransitionDefaultTimeout = 10*NSEC_PER_SEC;
     }
 
     OctagonStateTransitionPath* path = [OctagonStateTransitionPath pathFromDictionary:@{
     }
 
     OctagonStateTransitionPath* path = [OctagonStateTransitionPath pathFromDictionary:@{
-        OctagonStateInitiatorCreateIdentity: @{
-            OctagonStateInitiatorVouchWithBottle: [self joinStatePathDictionary],
+        OctagonStateBottleJoinCreateIdentity: @{
+            OctagonStateBottleJoinVouchWithBottle: [self joinStatePathDictionary],
         },
     }];
 
         },
     }];
 
@@ -2048,16 +2211,18 @@ static dispatch_time_t OctagonStateTransitionDefaultTimeout = 10*NSEC_PER_SEC;
 - (NSDictionary*)joinStatePathDictionary
 {
     return @{
 - (NSDictionary*)joinStatePathDictionary
 {
     return @{
-        OctagonStateInitiatorUpdateDeviceList: @{
-            OctagonStateInitiatorJoin: @{
-                OctagonStateBecomeReady: @{
-                    OctagonStateReady: [OctagonStateTransitionPathStep success],
-                },
+        OctagonStateInitiatorSetCDPBit: @{
+            OctagonStateInitiatorUpdateDeviceList: @{
+                OctagonStateInitiatorJoin: @{
+                    OctagonStateBecomeReady: @{
+                        OctagonStateReady: [OctagonStateTransitionPathStep success],
+                    },
 
 
-                OctagonStateInitiatorJoinCKKSReset: @{
-                    OctagonStateInitiatorJoinAfterCKKSReset: @{
-                        OctagonStateBecomeReady: @{
-                            OctagonStateReady: [OctagonStateTransitionPathStep success]
+                    OctagonStateInitiatorJoinCKKSReset: @{
+                        OctagonStateInitiatorJoinAfterCKKSReset: @{
+                            OctagonStateBecomeReady: @{
+                                OctagonStateReady: [OctagonStateTransitionPathStep success]
+                            },
                         },
                     },
                 },
                         },
                     },
                 },
@@ -2068,13 +2233,11 @@ static dispatch_time_t OctagonStateTransitionDefaultTimeout = 10*NSEC_PER_SEC;
 
 - (void)rpcJoin:(NSData*)vouchData
        vouchSig:(NSData*)vouchSig
 
 - (void)rpcJoin:(NSData*)vouchData
        vouchSig:(NSData*)vouchSig
-preapprovedKeys:(NSArray<NSData*>* _Nullable)preapprovedKeys
           reply:(void (^)(NSError * _Nullable error))reply
 {
 
     _vouchData = vouchData;
     _vouchSig = vouchSig;
           reply:(void (^)(NSError * _Nullable error))reply
 {
 
     _vouchData = vouchData;
     _vouchSig = vouchSig;
-    _preapprovedKeys = preapprovedKeys;
 
     if ([self checkForCKAccount:nil] != CKKSAccountStatusAvailable) {
         secnotice("octagon", "No cloudkit account present");
 
     if ([self checkForCKAccount:nil] != CKKSAccountStatusAvailable) {
         secnotice("octagon", "No cloudkit account present");
@@ -2181,10 +2344,19 @@ preapprovedKeys:(NSArray<NSData*>* _Nullable)preapprovedKeys
     result[@"statePendingFlags"] = [self.stateMachine dumpPendingFlags];
     result[@"stateFlags"] = [self.stateMachine.flags dumpFlags];
 
     result[@"statePendingFlags"] = [self.stateMachine dumpPendingFlags];
     result[@"stateFlags"] = [self.stateMachine.flags dumpFlags];
 
-    result[@"memoizedTrustState"] = @(self.currentMemoizedTrustState);
-    result[@"memoizedAccountState"] = @(self.currentMemoizedAccountState);
+    NSError* metadataError = nil;
+    OTAccountMetadataClassC* currentAccountMetadata = [self.accountMetadataStore loadOrCreateAccountMetadata:&metadataError];
+    if(metadataError) {
+        secnotice("octagon", "Failed to load account metaada for container (%@) and context (%@): %@", self.containerName, self.contextID, metadataError);
+    }
+
+    result[@"memoizedTrustState"] = @(currentAccountMetadata.trustState);
+    result[@"memoizedAccountState"] = @(currentAccountMetadata.icloudAccountState);
+    result[@"memoizedCDPStatus"] = @(currentAccountMetadata.cdpState);
     result[@"octagonLaunchSeqence"] = [self.launchSequence eventsByTime];
     result[@"octagonLaunchSeqence"] = [self.launchSequence eventsByTime];
-    result[@"memoizedlastHealthCheck"] = self.currentMemoizedLastHealthCheck ? self.currentMemoizedLastHealthCheck : @"Never checked";
+
+    NSDate* lastHealthCheck = self.currentMemoizedLastHealthCheck;
+    result[@"memoizedlastHealthCheck"] = lastHealthCheck ?: @"Never checked";
     if (self.sosAdapter.sosEnabled) {
         result[@"sosTrustedPeersStatus"] = [self sosTrustedPeersStatus];
         result[@"sosSelvesStatus"] = [self sosSelvesStatus];
     if (self.sosAdapter.sosEnabled) {
         result[@"sosTrustedPeersStatus"] = [self sosTrustedPeersStatus];
         result[@"sosSelvesStatus"] = [self sosSelvesStatus];
@@ -2398,12 +2570,6 @@ preapprovedKeys:(NSArray<NSData*>* _Nullable)preapprovedKeys
         }
     }];
 
         }
     }];
 
-    if(trustStatus == CliqueStatusIn && self.postedRepairCFU == YES){
-        NSError* clearError = nil;
-        [self.followupHandler clearFollowUp:OTFollowupContextTypeStateRepair error:&clearError];
-        // TODO(caw): should we clear this flag if `clearFollowUpForContext` fails?
-        self.postedRepairCFU = NO;
-    }
     reply(trustStatus, peerID, peerModelCounts, excluded, localError);
 }
 
     reply(trustStatus, peerID, peerModelCounts, excluded, localError);
 }
 
@@ -2476,6 +2642,19 @@ preapprovedKeys:(NSArray<NSData*>* _Nullable)preapprovedKeys
         }];
 }
 
         }];
 }
 
+- (void)rpcRefetchCKKSPolicy:(void (^)(NSError * _Nullable error))reply
+{
+    [self.stateMachine doWatchedStateMachineRPC:@"octagon-refetch-ckks-policy"
+                                   sourceStates:[NSMutableSet setWithArray: @[OctagonStateReady]]
+                                           path:[OctagonStateTransitionPath pathFromDictionary:@{
+                                               OctagonStateRefetchCKKSPolicy: @{
+                                                       OctagonStateBecomeReady: @{
+                                                               OctagonStateReady: [OctagonStateTransitionPathStep success],
+                                                       },
+                                               },
+                                           }]
+                                          reply:reply];
+}
 
 #pragma mark --- Testing
 - (void) setAccountStateHolder:(OTCuttlefishAccountStateHolder*)accountMetadataStore
 
 #pragma mark --- Testing
 - (void) setAccountStateHolder:(OTCuttlefishAccountStateHolder*)accountMetadataStore
@@ -2483,32 +2662,22 @@ preapprovedKeys:(NSArray<NSData*>* _Nullable)preapprovedKeys
     self.accountMetadataStore = accountMetadataStore;
 }
 
     self.accountMetadataStore = accountMetadataStore;
 }
 
-- (void)setPostedBool:(BOOL)posted
-{
-    self.postedRepairCFU = posted;
-}
-
 #pragma mark --- Health Checker
 
 - (BOOL)postRepairCFU:(NSError**)error
 {
     NSError* localError = nil;
     BOOL postSuccess = NO;
 #pragma mark --- Health Checker
 
 - (BOOL)postRepairCFU:(NSError**)error
 {
     NSError* localError = nil;
     BOOL postSuccess = NO;
-    if (self.postedRepairCFU == NO) {
-        [self.followupHandler postFollowUp:OTFollowupContextTypeStateRepair error:&localError];
-        if(localError){
-            secerror("octagon-health: CoreCDP repair failed: %@", localError);
-            if(error){
-                *error = localError;
-            }
-        }
-        else{
-            secnotice("octagon-health", "CoreCDP post repair success");
-            self.postedRepairCFU = YES;
-            postSuccess = YES;
+    [self.followupHandler postFollowUp:OTFollowupContextTypeStateRepair error:&localError];
+    if(localError){
+        secerror("octagon-health: CoreCDP repair failed: %@", localError);
+        if(error){
+            *error = localError;
         }
         }
-    } else {
-        secnotice("octagon-health", "already posted a repair CFU!");
+    }
+    else{
+        secnotice("octagon-health", "CoreCDP post repair success");
+        postSuccess = YES;
     }
     return postSuccess;
 }
     }
     return postSuccess;
 }
@@ -2543,38 +2712,36 @@ preapprovedKeys:(NSArray<NSData*>* _Nullable)preapprovedKeys
     }
 }
 
     }
 }
 
-- (void)postConfirmPasscodeCFU:(NSError**)error
+- (BOOL)leaveTrust:(NSError**)error
 {
 {
-    NSError* localError = nil;
-    if (self.postedEscrowRepairCFU == NO) {
-        [self.followupHandler postFollowUp:OTFollowupContextTypeOfflinePasscodeChange error:&localError];
-        if(localError){
-            secerror("octagon-health: CoreCDP offline passcode change failed: %@", localError);
-            *error = localError;
-        }
-        else{
-            secnotice("octagon-health", "CoreCDP offline passcode change success");
-            self.postedEscrowRepairCFU = YES;
+    if (OctagonPlatformSupportsSOS()) {
+        CFErrorRef cfError = NULL;
+        bool left = SOSCCRemoveThisDeviceFromCircle_Server(&cfError);
+
+        if(!left || cfError) {
+            secerror("failed to leave SOS circle: %@", cfError);
+            if(error) {
+                *error = (NSError*)CFBridgingRelease(cfError);
+            } else {
+                CFReleaseNull(cfError);
+            }
+            return NO;
         }
         }
-    } else {
-        secnotice("octagon-health", "already posted escrow CFU");
     }
     }
+    secnotice("octagon-health", "Successfully left SOS");
+    return YES;
 }
 
 }
 
-- (void)postRecoveryKeyCFU:(NSError**)error
+- (void)postConfirmPasscodeCFU:(NSError**)error
 {
     NSError* localError = nil;
 {
     NSError* localError = nil;
-    if (self.postedRecoveryKeyCFU == NO) {
-        [self.followupHandler postFollowUp:OTFollowupContextTypeRecoveryKeyRepair error:&localError];
-        if(localError){
-            secerror("octagon-health: CoreCDP recovery key cfu failed: %@", localError);
-        }
-        else{
-            secnotice("octagon-health", "CoreCDP recovery key cfu success");
-            self.postedRecoveryKeyCFU = YES;
-        }
-    } else {
-        secnotice("octagon-health", "already posted recovery key CFU");
+    [self.followupHandler postFollowUp:OTFollowupContextTypeOfflinePasscodeChange error:&localError];
+    if(localError){
+        secerror("octagon-health: CoreCDP offline passcode change failed: %@", localError);
+        *error = localError;
+    }
+    else{
+        secnotice("octagon-health", "CoreCDP offline passcode change success");
     }
 }
 
     }
 }
 
@@ -2589,18 +2756,22 @@ preapprovedKeys:(NSArray<NSData*>* _Nullable)preapprovedKeys
                                    sourceStates:OctagonHealthSourceStates()
                                            path:[OctagonStateTransitionPath pathFromDictionary:@{
                                                OctagonStateHSA2HealthCheck: @{
                                    sourceStates:OctagonHealthSourceStates()
                                            path:[OctagonStateTransitionPath pathFromDictionary:@{
                                                OctagonStateHSA2HealthCheck: @{
-                                                   OctagonStateSecurityTrustCheck: @{
-                                                       OctagonStateTPHTrustCheck: @{
-                                                           OctagonStateCuttlefishTrustCheck: @{
-                                                               OctagonStateBecomeReady: @{
-                                                                   OctagonStateReady: [OctagonStateTransitionPathStep success],
-                                                                   OctagonStateWaitForUnlock: [OctagonStateTransitionPathStep success],
+                                                   OctagonStateCDPHealthCheck: @{
+                                                       OctagonStateSecurityTrustCheck: @{
+                                                           OctagonStateTPHTrustCheck: @{
+                                                               OctagonStateCuttlefishTrustCheck: @{
+                                                                   OctagonStateBecomeReady: @{
+                                                                       OctagonStateReady: [OctagonStateTransitionPathStep success],
+                                                                       OctagonStateWaitForUnlock: [OctagonStateTransitionPathStep success],
+                                                                   },
+                                                                   // Cuttlefish can suggest we reset the world. Consider reaching here a success,
+                                                                   // instead of tracking the whole reset.
+                                                                   OctagonStateHealthCheckReset: [OctagonStateTransitionPathStep success],
+                                                                   OctagonStateHealthCheckLeaveClique: [OctagonStateTransitionPathStep success],
                                                                },
                                                                },
-                                                               // Cuttlefish can suggest we reset the world. Consider reaching here a success,
-                                                               // instead of tracking the whole reset.
-                                                               OctagonStateHealthCheckReset: [OctagonStateTransitionPathStep success],
                                                            },
                                                        },
                                                            },
                                                        },
+                                                       OctagonStateWaitForCDP: [OctagonStateTransitionPathStep success],
                                                    },
                                                    OctagonStateWaitForHSA2: [OctagonStateTransitionPathStep success],
                                                }
                                                    },
                                                    OctagonStateWaitForHSA2: [OctagonStateTransitionPathStep success],
                                                }
@@ -2624,7 +2795,8 @@ preapprovedKeys:(NSArray<NSData*>* _Nullable)preapprovedKeys
                                                                              intendedState:OctagonStateBecomeReady
                                                                          ckksConflictState:OctagonStateBecomeUntrusted
                                                                                 errorState:OctagonStateBecomeUntrusted
                                                                              intendedState:OctagonStateBecomeReady
                                                                          ckksConflictState:OctagonStateBecomeUntrusted
                                                                                 errorState:OctagonStateBecomeUntrusted
-                                                                                deviceInfo:self.prepareInformation];
+                                                                                deviceInfo:self.prepareInformation
+                                                                            policyOverride:self.policyOverride];
 
     OctagonStateTransitionRequest<OTSOSUpgradeOperation*>* request = [[OctagonStateTransitionRequest alloc] init:@"attempt-sos-upgrade"
                                                                                                     sourceStates:sourceStates
 
     OctagonStateTransitionRequest<OTSOSUpgradeOperation*>* request = [[OctagonStateTransitionRequest alloc] init:@"attempt-sos-upgrade"
                                                                                                     sourceStates:sourceStates
@@ -2687,13 +2859,6 @@ preapprovedKeys:(NSArray<NSData*>* _Nullable)preapprovedKeys
                                           reply:reply];
 }
 
                                           reply:reply];
 }
 
-- (void)clearPendingCFUFlags
-{
-    self.postedRecoveryKeyCFU = NO;
-    self.postedEscrowRepairCFU = NO;
-    self.postedRepairCFU = NO;
-}
-
 // Metrics passthroughs
 
 - (BOOL)machineIDOnMemoizedList:(NSString*)machineID error:(NSError**)error
 // Metrics passthroughs
 
 - (BOOL)machineIDOnMemoizedList:(NSString*)machineID error:(NSError**)error
index 8d5f6e71fdb7f46637bb51ff1b272cc1ac0e877d..d391c48fd71feaa2224885b9671c64c7c0ea1b24 100644 (file)
@@ -90,6 +90,7 @@ typedef NS_ERROR_ENUM(OctagonErrorDomain, OctagonError) {
     OTAuthKitNoAuthenticationController     = 45,
     OTAuthKitMachineIDMissing               = 46,
     OTAuthKitPrimaryAccountHaveNoDSID       = 47,
     OTAuthKitNoAuthenticationController     = 45,
     OTAuthKitMachineIDMissing               = 46,
     OTAuthKitPrimaryAccountHaveNoDSID       = 47,
+    OTErrorFailedToLeaveClique              = 48,
 };
 
 #define OTMasterSecretLength 72
 };
 
 #define OTMasterSecretLength 72
@@ -97,6 +98,7 @@ typedef NS_ERROR_ENUM(OctagonErrorDomain, OctagonError) {
 typedef NS_ENUM(NSInteger, TrustedPeersHelperErrorCode) {
     TrustedPeersHelperErrorNoPreparedIdentity = 1,
     TrustedPeersHelperErrorNoPeersPreapprovePreparedIdentity = 14,
 typedef NS_ENUM(NSInteger, TrustedPeersHelperErrorCode) {
     TrustedPeersHelperErrorNoPreparedIdentity = 1,
     TrustedPeersHelperErrorNoPeersPreapprovePreparedIdentity = 14,
+    TrustedPeersHelperErrorCodeUntrustedRecoveryKeys    = 32,
     TrustedPeersHelperErrorCodeNotEnrolled   = 34,
 };
 
     TrustedPeersHelperErrorCodeNotEnrolled   = 34,
 };
 
diff --git a/keychain/ot/OTDetermineCDPBitStatusOperation.h b/keychain/ot/OTDetermineCDPBitStatusOperation.h
new file mode 100644 (file)
index 0000000..f4b377c
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2019 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@
+ */
+
+#if OCTAGON
+
+#import <Foundation/Foundation.h>
+#import "keychain/ckks/CKKSGroupOperation.h"
+#import "keychain/ot/OctagonStateMachineHelpers.h"
+#import "keychain/ot/OTOperationDependencies.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface OTDetermineCDPBitStatusOperation : CKKSGroupOperation <OctagonStateTransitionOperationProtocol>
+
+- (instancetype)initWithDependencies:(OTOperationDependencies*)dependencies
+                       intendedState:(OctagonState*)intendedState
+                          errorState:(OctagonState*)errorState;
+
+@end
+
+NS_ASSUME_NONNULL_END
+
+#endif // OCTAGON
diff --git a/keychain/ot/OTDetermineCDPBitStatusOperation.m b/keychain/ot/OTDetermineCDPBitStatusOperation.m
new file mode 100644 (file)
index 0000000..8ad69c1
--- /dev/null
@@ -0,0 +1,149 @@
+/*
+ * Copyright (c) 2019 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@
+ */
+
+#if OCTAGON
+
+#import "utilities/debugging.h"
+
+#import "keychain/ot/ObjCImprovements.h"
+#import "keychain/ot/OTDetermineCDPBitStatusOperation.h"
+#import "keychain/ot/OTStates.h"
+
+@interface OTDetermineCDPBitStatusOperation ()
+@property OTOperationDependencies* deps;
+@end
+
+@implementation OTDetermineCDPBitStatusOperation
+@synthesize intendedState = _intendedState;
+@synthesize nextState = _nextState;
+
+- (instancetype)initWithDependencies:(OTOperationDependencies*)dependencies
+                       intendedState:(OctagonState*)intendedState
+                          errorState:(OctagonState*)errorState
+{
+    if((self = [super init])) {
+        _deps = dependencies;
+        _intendedState = intendedState;
+        _nextState = errorState;
+    }
+    return self;
+}
+
+- (void)groupStart
+{
+    secnotice("octagon-cdp-status", "Checking CDP status");
+
+    NSError* localError = nil;
+    OTAccountMetadataClassC* account = [self.deps.stateHolder loadOrCreateAccountMetadata:&localError];
+    if(localError && [self.deps.lockStateTracker isLockedError:localError]) {
+        secnotice("octagon-cdp-status", "Device is locked! restarting on unlock");
+        self.nextState = OctagonStateWaitForUnlock;
+        return;
+    }
+
+    if(localError) {
+        secnotice("octagon-cdp-status", "Failed to load account metadata: %@", localError);
+        self.error = localError;
+        return;
+    }
+
+    secnotice("octagon-cdp-status", "CDP is %@", OTAccountMetadataClassC_CDPStateAsString(account.cdpState));
+
+    if(account.cdpState == OTAccountMetadataClassC_CDPState_ENABLED) {
+        self.nextState = self.intendedState;
+    } else {
+        // If the CDP status is unknown or disabled, double-check with TPH.
+        // If there are any peers, the CDP status really should be ENABLED.
+        __block OTAccountMetadataClassC_CDPState newState = OTAccountMetadataClassC_CDPState_UNKNOWN;
+
+        WEAKIFY(self);
+        [self.deps.cuttlefishXPCWrapper trustStatusWithContainer:self.deps.containerName
+                                                         context:self.deps.contextID
+                                                           reply:^(TrustedPeersHelperEgoPeerStatus *egoStatus,
+                                                                   NSError *xpcError) {
+            STRONGIFY(self);
+            if(xpcError) {
+                secnotice("octagon-cdp-status", "Unable to talk with TPH; leaving CDP status as 'unknown': %@", xpcError);
+                return;
+            }
+
+            secnotice("octagon-cdp-status", "Octagon reports %d peers", (int)egoStatus.numberOfPeersInOctagon);
+            if(egoStatus.numberOfPeersInOctagon > 0) {
+                newState = OTAccountMetadataClassC_CDPState_ENABLED;
+            } else {
+                // As a last gasp, check in with SOS (if enabled). If there's a circle (in or out), CDP is on
+                if(self.deps.sosAdapter.sosEnabled) {
+                    secnotice("octagon-cdp-status", "Requesting SOS status...");
+
+                    NSError* circleError = nil;
+                    SOSCCStatus circleStatus = [self.deps.sosAdapter circleStatus:&circleError];
+
+                    if(circleError || circleStatus == kSOSCCError) {
+                        secnotice("octagon-cdp-status", "Error fetching circle status. Leaving CDP status as 'unknown': %@", circleError);
+                    } else if(circleStatus == kSOSCCCircleAbsent) {
+                        secnotice("octagon-cdp-status", "SOS reports circle absent. Setting CDP to 'disabled'");
+                        newState = OTAccountMetadataClassC_CDPState_DISABLED;
+                    } else {
+                        secnotice("octagon-cdp-status", "SOS reports some existing circle (%d). Setting CDP to 'enabled'", (int)circleStatus);
+                        newState = OTAccountMetadataClassC_CDPState_ENABLED;
+                    }
+                } else {
+                    // No SOS? no CDP.
+                    secnotice("octagon-cdp-status", "No SOS. CDP bit is off.");
+                    newState = OTAccountMetadataClassC_CDPState_DISABLED;
+                }
+            }
+        }];
+
+        if(account.cdpState != newState) {
+            NSError* stateError = nil;
+            [self.deps.stateHolder persistAccountChanges:^OTAccountMetadataClassC * _Nonnull(OTAccountMetadataClassC * _Nonnull metadata) {
+                if(metadata.cdpState == OTAccountMetadataClassC_CDPState_ENABLED) {
+                    secnotice("octagon-cdp-status", "CDP bit is enabled on-disk, not modifying (would have been %@)", OTAccountMetadataClassC_CDPStateAsString(newState));
+
+                    // Set this here to perform the right state choice later
+                    newState = OTAccountMetadataClassC_CDPState_ENABLED;
+                    return nil;
+                } else {
+                    secnotice("octagon-cdp-status", "Writing CDP bit as %@", OTAccountMetadataClassC_CDPStateAsString(newState));
+                    metadata.cdpState = newState;
+                    return metadata;
+                }
+            } error:&stateError];
+
+            if(stateError) {
+                secnotice("octagon-cdp-status", "Failed to load account metadata: %@", stateError);
+            }
+        }
+
+        if(newState == OTAccountMetadataClassC_CDPState_ENABLED) {
+            self.nextState = self.intendedState;
+        } else {
+            self.nextState = OctagonStateWaitForCDP;
+        }
+    }
+}
+
+@end
+
+#endif // OCTAGON
index cfb4a621b0df4a4b3d89eecdd9bce3068b1de945..0acbd9ffe885ed5f0cedc167c69dba5e01228ba6 100644 (file)
@@ -31,6 +31,7 @@
 #import "keychain/ckks/CloudKitCategories.h"
 #import "keychain/ckks/CKKSCurrentKeyPointer.h"
 #import "keychain/ckks/CKKSKeychainView.h"
 #import "keychain/ckks/CloudKitCategories.h"
 #import "keychain/ckks/CKKSCurrentKeyPointer.h"
 #import "keychain/ckks/CKKSKeychainView.h"
+#import "keychain/SecureObjectSync/SOSAccount.h"
 
 #import "keychain/TrustedPeersHelper/TrustedPeersHelperProtocol.h"
 #import "keychain/ot/ObjCImprovements.h"
 
 #import "keychain/TrustedPeersHelper/TrustedPeersHelperProtocol.h"
 #import "keychain/ot/ObjCImprovements.h"
     WEAKIFY(self);
 
     NSArray<NSData*>* publicSigningSPKIs = nil;
     WEAKIFY(self);
 
     NSArray<NSData*>* publicSigningSPKIs = nil;
-
     if(self.operationDependencies.sosAdapter.sosEnabled) {
     if(self.operationDependencies.sosAdapter.sosEnabled) {
-        NSError* peerError = nil;
-
-        secnotice("octagon-sos", "SOS not enabled; no preapproved keys");
-        NSSet<id<CKKSRemotePeerProtocol>>* peerSet = [self.operationDependencies.sosAdapter fetchTrustedPeers:&peerError];
+        NSError* sosPreapprovalError = nil;
+        publicSigningSPKIs = [OTSOSAdapterHelpers peerPublicSigningKeySPKIsForCircle:self.operationDependencies.sosAdapter error:&sosPreapprovalError];
 
 
-        if(!peerSet || peerError) {
-            secerror("octagon-sos: Can't fetch trusted peers during establish: %@", peerError);
+        if(publicSigningSPKIs) {
+            secnotice("octagon-sos", "SOS preapproved keys are %@", publicSigningSPKIs);
+        } else {
+            secnotice("octagon-sos", "Unable to fetch SOS preapproved keys: %@", sosPreapprovalError);
         }
 
         }
 
-        publicSigningSPKIs = [OTSOSActualAdapter peerPublicSigningKeySPKIs:peerSet];
-        secnotice("octagon-sos", "SOS preapproved keys are %@", publicSigningSPKIs);
     } else {
         secnotice("octagon-sos", "SOS not enabled; no preapproved keys");
     }
     } else {
         secnotice("octagon-sos", "SOS not enabled; no preapproved keys");
     }
index fc7fd5ed170978c96fb73d4a4aeec70a5d2d4566..13613b2e5d21ce86a0103bb4285d2d620df2f35e 100644 (file)
                                                                 self.tlkShares = tlkShares;
                                                                 self.pendingTLKShares = pendingTLKShares;
 
                                                                 self.tlkShares = tlkShares;
                                                                 self.pendingTLKShares = pendingTLKShares;
 
-                                                                secnotice("octagon-ckks", "Fetched %d key sets, %d broken key set,s %d tlk shares, and %d pendingTLKShares",
+                                                                secnotice("octagon-ckks", "Fetched %d key sets, %d broken key sets, %d tlk shares, and %d pendingTLKShares",
                                                                           (int)self.viewKeySets.count,
                                                                           (int)self.incompleteKeySets.count,
                                                                           (int)self.tlkShares.count,
                                                                           (int)self.viewKeySets.count,
                                                                           (int)self.incompleteKeySets.count,
                                                                           (int)self.tlkShares.count,
index b7fbb418dd46987c08d7e70b9d86cf1c10f74bdc..f866bd21a3e787c36f1e7179f1287b1d4f0f4c36 100644 (file)
 
 NS_ASSUME_NONNULL_BEGIN
 
 
 NS_ASSUME_NONNULL_BEGIN
 
-@interface OTFetchViewsOperation : CKKSGroupOperation
+@interface OTFetchViewsOperation : CKKSGroupOperation <OctagonStateTransitionOperationProtocol>
 
 
-- (instancetype)initWithDependencies:(OTOperationDependencies*)dependencies;
-
-@property (nonatomic) NSSet<NSString*>* viewList;
-@property (nonatomic) TPPolicy* _Nullable policy;
+- (instancetype)initWithDependencies:(OTOperationDependencies*)dependencies
+                       intendedState:(OctagonState*)intendedState
+                          errorState:(OctagonState*)errorState;
 
 @end
 
 
 @end
 
index cbe4e56aee154a1f3b7c4c5639c2dea2cf3d6002..3b63b205451684b52ebd62021a1afc788236163f 100644 (file)
 #import "keychain/ot/OTFetchViewsOperation.h"
 #import "keychain/ot/ObjCImprovements.h"
 #import "keychain/TrustedPeersHelper/TrustedPeersHelperProtocol.h"
 #import "keychain/ot/OTFetchViewsOperation.h"
 #import "keychain/ot/ObjCImprovements.h"
 #import "keychain/TrustedPeersHelper/TrustedPeersHelperProtocol.h"
+#import "keychain/ot/categories/OTAccountMetadataClassC+KeychainSupport.h"
 #import "keychain/ckks/CKKSAnalytics.h"
 
 @interface OTFetchViewsOperation ()
 @property OTOperationDependencies* deps;
 #import "keychain/ckks/CKKSAnalytics.h"
 
 @interface OTFetchViewsOperation ()
 @property OTOperationDependencies* deps;
-@property NSOperation* finishedOp;
-@property CKKSViewManager* ckm;
 @end
 
 @implementation OTFetchViewsOperation
 @end
 
 @implementation OTFetchViewsOperation
+@synthesize intendedState = _intendedState;
+@synthesize nextState = _nextState;
 
 - (instancetype)initWithDependencies:(OTOperationDependencies*)dependencies
 
 - (instancetype)initWithDependencies:(OTOperationDependencies*)dependencies
+                       intendedState:(OctagonState*)intendedState
+                          errorState:(OctagonState*)errorState
 {
     if ((self = [super init])) {
         _deps = dependencies;
 {
     if ((self = [super init])) {
         _deps = dependencies;
-        _ckm = dependencies.viewManager;
+
+        _intendedState = intendedState;
+        _nextState = errorState;
     }
     return self;
 }
     }
     return self;
 }
 {
     secnotice("octagon", "fetching views");
 
 {
     secnotice("octagon", "fetching views");
 
-    self.finishedOp = [[NSOperation alloc] init];
-    [self dependOnBeforeGroupFinished:self.finishedOp];
-
-    NSSet<NSString*>* sosViewList = [self.ckm viewList];
-    self.policy = nil;
-    self.viewList = sosViewList;
-
-    if ([self.ckm useCKKSViewsFromPolicy]) {
-        WEAKIFY(self);
-        
-        [self.deps.cuttlefishXPCWrapper fetchPolicyWithContainer:self.deps.containerName context:self.deps.contextID reply:^(TPPolicy* _Nullable policy, NSError* _Nullable error) {
-                STRONGIFY(self);
-                if (error) {
-                    secerror("octagon: failed to retrieve policy: %@", error);
-                    [[CKKSAnalytics logger] logResultForEvent:OctagonEventFetchViews hardFailure:true result:error];
-                    self.error = error;
-                    [self runBeforeGroupFinished:self.finishedOp];
-                } else {
-                    if (policy == nil) {
-                        secerror("octagon: no policy returned");
-                    }
-                    self.policy = policy;
-                    NSArray<NSString*>* sosViews = [sosViewList allObjects];
-                    [self.deps.cuttlefishXPCWrapper getViewsWithContainer:self.deps.containerName context:self.deps.contextID inViews:sosViews reply:^(NSArray<NSString*>* _Nullable outViews, NSError* _Nullable error) {
-                            STRONGIFY(self);
-                            if (error) {
-                                secerror("octagon: failed to retrieve list of views: %@", error);
-                                [[CKKSAnalytics logger] logResultForEvent:OctagonEventFetchViews hardFailure:true result:error];
-                                self.error = error;
-                                [self runBeforeGroupFinished:self.finishedOp];
-                            } else {
-                                if (outViews == nil) {
-                                    secerror("octagon: bad results from getviews");
-                                } else {
-                                    self.viewList = [NSSet setWithArray:outViews];
-                                }
-                                [self complete];
-                            }
-                        }];
-                }
-            }];
-    } else {
-        [self complete];
-    }
-}
+    WEAKIFY(self);
+    [self.deps.cuttlefishXPCWrapper fetchCurrentPolicyWithContainer:self.deps.containerName
+                                                            context:self.deps.contextID
+                                                              reply:^(NSSet<NSString*>* _Nullable viewList,
+                                                                      TPPolicy* _Nullable policy,
+                                                                      NSError* _Nullable error) {
+        STRONGIFY(self);
+        [[CKKSAnalytics logger] logResultForEvent:OctagonEventFetchViews hardFailure:true result:error];
+
+        if (error) {
+            secerror("octagon: failed to retrieve policy+views: %@", error);
+            self.error = error;
+            return;
+        }
+
+        secnotice("octagon-ckks", "Received policy %@ with view list: %@", policy, viewList);
+        // Write them down before continuing
+
+        NSError* stateError = nil;
+        [self.deps.stateHolder persistAccountChanges:^OTAccountMetadataClassC * _Nullable(OTAccountMetadataClassC * _Nonnull metadata) {
+            metadata.syncingViews = [viewList mutableCopy];
+            [metadata setTPPolicy:policy];
+            return metadata;
+        } error:&stateError];
+
+        if(stateError) {
+            secerror("octagon: failed to save policy+views: %@", stateError);
+            self.error = stateError;
+            return;
+        }
 
 
-- (void)complete {
-    secnotice("octagon", "viewList: %@", self.viewList);
-    self.ckm.policy = self.policy;
-    self.ckm.viewList = self.viewList;
+        [self.deps.viewManager setSyncingViews:viewList sortingPolicy:policy];
 
 
-    [self.ckm createViews];
-    [self.ckm beginCloudKitOperationOfAllViews];
-    [self runBeforeGroupFinished:self.finishedOp];
+        self.nextState = self.intendedState;
+    }];
 }
 
 @end
 }
 
 @end
index e6f738ca058c90088d099f35c3b2a43a7edcd5cb..a70959290dc6543b64214e10eef38ec0d9c855bf 100644 (file)
@@ -57,7 +57,12 @@ NSString* OTFollowupContextTypeToString(OTFollowupContextType contextType);
 
 - (NSDictionary *)sysdiagnoseStatus;
 - (NSDictionary<NSString*,NSNumber*> *)sfaStatus;
 
 - (NSDictionary *)sysdiagnoseStatus;
 - (NSDictionary<NSString*,NSNumber*> *)sfaStatus;
+@end
 
 
+@interface OTFollowup (Testing)
+// Reports on whether this individual OTFollowUp object has posted a CFU of this type.
+- (BOOL)hasPosted:(OTFollowupContextType)contextType;
+- (void)clearAllPostedFlags;
 @end
 
 NS_ASSUME_NONNULL_END
 @end
 
 NS_ASSUME_NONNULL_END
index 419f399c126b6bad3c91f7d450ecf40cb319e8d2..649dc59d0437b7aa9e32b23295f77d46b0035f0e 100644 (file)
@@ -56,6 +56,8 @@ NSString* OTFollowupContextTypeToString(OTFollowupContextType contextType)
 @property NSTimeInterval previousFollowupEnd;
 @property NSTimeInterval followupStart;
 @property NSTimeInterval followupEnd;
 @property NSTimeInterval previousFollowupEnd;
 @property NSTimeInterval followupStart;
 @property NSTimeInterval followupEnd;
+
+@property NSMutableSet<NSString*>* postedCFUTypes;
 @end
 
 @implementation OTFollowup : NSObject
 @end
 
 @implementation OTFollowup : NSObject
@@ -64,6 +66,8 @@ NSString* OTFollowupContextTypeToString(OTFollowupContextType contextType)
 {
     if (self = [super init]) {
         self.cdpd = cdpFollowupController;
 {
     if (self = [super init]) {
         self.cdpd = cdpFollowupController;
+
+        _postedCFUTypes = [NSMutableSet set];
     }
     return self;
 }
     }
     return self;
 }
@@ -95,9 +99,16 @@ NSString* OTFollowupContextTypeToString(OTFollowupContextType contextType)
     }
 
     NSError *followupError = nil;
     }
 
     NSError *followupError = nil;
+
+    secnotice("followup", "Posting a follow up (for Octagon) of type %@", OTFollowupContextTypeToString(contextType));
     BOOL result = [self.cdpd postFollowUpWithContext:context error:&followupError];
     BOOL result = [self.cdpd postFollowUpWithContext:context error:&followupError];
-    if (error) {
-        *error = followupError;
+
+    if(result) {
+        [self.postedCFUTypes addObject:OTFollowupContextTypeToString(contextType)];
+    } else {
+        if (error) {
+            *error = followupError;
+        }
     }
 
     return result;
     }
 
     return result;
@@ -112,7 +123,13 @@ NSString* OTFollowupContextTypeToString(OTFollowupContextType contextType)
         return NO;
     }
 
         return NO;
     }
 
-    return [self.cdpd clearFollowUpWithContext:context error:error];
+    secnotice("followup", "Clearing follow ups (for Octagon) of type %@", OTFollowupContextTypeToString(contextType));
+    BOOL result = [self.cdpd clearFollowUpWithContext:context error:error];
+    if(result) {
+        [self.postedCFUTypes removeObject:OTFollowupContextTypeToString(contextType)];
+    }
+
+    return result;
 }
 
 
 }
 
 
@@ -168,7 +185,18 @@ NSString* OTFollowupContextTypeToString(OTFollowupContextType contextType)
     return values;
 }
 
     return values;
 }
 
+@end
 
 
+@implementation OTFollowup (Testing)
+- (BOOL)hasPosted:(OTFollowupContextType)contextType
+{
+    return [self.postedCFUTypes containsObject:OTFollowupContextTypeToString(contextType)];
+}
+
+- (void)clearAllPostedFlags
+{
+    [self.postedCFUTypes removeAllObjects];
+}
 @end
 
 #endif // OCTAGON
 @end
 
 #endif // OCTAGON
index 3668f4d1e02b0e9dc310111f2dfcdff117f6577f..c5a9addba3b2b28c740c74c077a170df6e2f4538 100644 (file)
@@ -40,12 +40,10 @@ NS_ASSUME_NONNULL_BEGIN
                    ckksConflictState:(OctagonState*)ckksConflictState
                           errorState:(OctagonState*)errorState
                          voucherData:(NSData*)voucherData
                    ckksConflictState:(OctagonState*)ckksConflictState
                           errorState:(OctagonState*)errorState
                          voucherData:(NSData*)voucherData
-                          voucherSig:(NSData*)voucherSig
-                     preapprovedKeys:(NSArray<NSData *>*)preapprovedKeys;
+                          voucherSig:(NSData*)voucherSig;
 
 @property (nonatomic) NSData* voucherData;
 @property (nonatomic) NSData* voucherSig;
 
 @property (nonatomic) NSData* voucherData;
 @property (nonatomic) NSData* voucherSig;
-@property (nonatomic) NSArray<NSData *>* preapprovedKeys;
 
 @property (nonatomic) NSString* peerID;
 
 
 @property (nonatomic) NSString* peerID;
 
index f68765d5c70949c9846d9619682cff5427da153a..6a7c5b97970d8febebcda9e4c2be17c0294138e8 100644 (file)
@@ -55,7 +55,6 @@
                           errorState:(OctagonState*)errorState
                          voucherData:(NSData*)voucherData
                           voucherSig:(NSData*)voucherSig
                           errorState:(OctagonState*)errorState
                          voucherData:(NSData*)voucherData
                           voucherSig:(NSData*)voucherSig
-                     preapprovedKeys:(NSArray<NSData *>*)preapprovedKeys
 {
     if((self = [super init])) {
         _deps = dependencies;
 {
     if((self = [super init])) {
         _deps = dependencies;
@@ -66,7 +65,6 @@
 
         _voucherData = voucherData;
         _voucherSig = voucherSig;
 
         _voucherData = voucherData;
         _voucherSig = voucherSig;
-        _preapprovedKeys = preapprovedKeys;
     }
     return self;
 }
     }
     return self;
 }
 {
     WEAKIFY(self);
 
 {
     WEAKIFY(self);
 
+    NSArray<NSData*>* publicSigningSPKIs = nil;
+    if(self.deps.sosAdapter.sosEnabled) {
+        NSError* sosPreapprovalError = nil;
+        publicSigningSPKIs = [OTSOSAdapterHelpers peerPublicSigningKeySPKIsForCircle:self.deps.sosAdapter error:&sosPreapprovalError];
+
+        if(publicSigningSPKIs) {
+            secnotice("octagon-sos", "SOS preapproved keys are %@", publicSigningSPKIs);
+        } else {
+            secnotice("octagon-sos", "Unable to fetch SOS preapproved keys: %@", sosPreapprovalError);
+        }
+
+    } else {
+        secnotice("octagon-sos", "SOS not enabled; no preapproved keys");
+    }
+
     [self.deps.cuttlefishXPCWrapper joinWithContainer:self.deps.containerName
                                               context:self.deps.contextID
                                           voucherData:self.voucherData
                                            voucherSig:self.voucherSig
                                              ckksKeys:viewKeySets
                                             tlkShares:pendingTLKShares
     [self.deps.cuttlefishXPCWrapper joinWithContainer:self.deps.containerName
                                               context:self.deps.contextID
                                           voucherData:self.voucherData
                                            voucherSig:self.voucherSig
                                              ckksKeys:viewKeySets
                                             tlkShares:pendingTLKShares
-                                      preapprovedKeys:self.preapprovedKeys
-                                                reply:^(NSString * _Nullable peerID, NSArray<CKRecord*>* keyHierarchyRecords, NSError * _Nullable error) {
+                                      preapprovedKeys:publicSigningSPKIs
+                                                reply:^(NSString * _Nullable peerID,
+                                                        NSArray<CKRecord*>* keyHierarchyRecords,
+                                                        NSSet<NSString*>* _Nullable syncingViews,
+                                                        TPPolicy* _Nullable syncingPolicy,
+                                                        NSError * _Nullable error) {
             STRONGIFY(self);
             if(error){
                 secerror("octagon: Error joining with voucher: %@", error);
             STRONGIFY(self);
             if(error){
                 secerror("octagon: Error joining with voucher: %@", error);
 
                 [[CKKSAnalytics logger] logSuccessForEventNamed:OctagonEventJoinWithVoucher];
 
 
                 [[CKKSAnalytics logger] logSuccessForEventNamed:OctagonEventJoinWithVoucher];
 
+                [self.deps.viewManager setSyncingViews:syncingViews sortingPolicy:syncingPolicy];
+
                 NSError* localError = nil;
                 BOOL persisted = [self.deps.stateHolder persistAccountChanges:^OTAccountMetadataClassC * _Nonnull(OTAccountMetadataClassC * _Nonnull metadata) {
                 NSError* localError = nil;
                 BOOL persisted = [self.deps.stateHolder persistAccountChanges:^OTAccountMetadataClassC * _Nonnull(OTAccountMetadataClassC * _Nonnull metadata) {
-                        metadata.trustState = OTAccountMetadataClassC_TrustState_TRUSTED;
-                        metadata.peerID = peerID;
-                        return metadata;
-                    } error:&localError];
+                    metadata.trustState = OTAccountMetadataClassC_TrustState_TRUSTED;
+                    metadata.peerID = peerID;
+                    metadata.syncingViews = [syncingViews mutableCopy];
+                    [metadata setTPPolicy:syncingPolicy];
+                    return metadata;
+                } error:&localError];
                 if(!persisted || localError) {
                     secnotice("octagon", "Couldn't persist results: %@", localError);
                     self.error = localError;
                 if(!persisted || localError) {
                     secnotice("octagon", "Couldn't persist results: %@", localError);
                     self.error = localError;
index af3b327ac89ab751311424a81849c9d00177d281..9207a19fc5be6da106b6bcb3ca7d66cb5ae6976b 100644 (file)
@@ -44,14 +44,6 @@ NS_ASSUME_NONNULL_BEGIN
 // Set this to non-zero if you want to configure your timeouts
 @property int64_t timeout;
 
 // Set this to non-zero if you want to configure your timeouts
 @property int64_t timeout;
 
-- (instancetype)initWithProtocolType:(NSString*)protocolType
-                      uniqueDeviceID:(NSString*)uniqueDeviceID
-                      uniqueClientID:(NSString*)uniqueClientID
-                       containerName:(NSString* _Nullable)containerName
-                           contextID:(NSString*)contextID
-                               epoch:(uint64_t)epoch
-                         isInitiator:(BOOL)isInitiator;
-
 - (instancetype)initWithProtocolType:(NSString*)protocolType
                       uniqueDeviceID:(NSString*)uniqueDeviceID
                       uniqueClientID:(NSString*)uniqueClientID
 - (instancetype)initWithProtocolType:(NSString*)protocolType
                       uniqueDeviceID:(NSString*)uniqueDeviceID
                       uniqueClientID:(NSString*)uniqueClientID
index 339311f531b598017e56e734e1f3838936cbe022..f744254011168cc32a951a6ed7f0c45485309cd2 100644 (file)
@@ -33,24 +33,6 @@ NS_ASSUME_NONNULL_BEGIN
     return YES;
 }
 
     return YES;
 }
 
-- (instancetype)initWithProtocolType:(NSString*)protocolType
-                      uniqueDeviceID:(NSString*)uniqueDeviceID
-                      uniqueClientID:(NSString*)uniqueClientID
-                       containerName:(NSString* _Nullable)containerName
-                           contextID:(NSString*)contextID
-                               epoch:(uint64_t)epoch
-                         isInitiator:(BOOL)isInitiator
-{
-    return [self initWithProtocolType:protocolType
-                       uniqueDeviceID:uniqueDeviceID
-                       uniqueClientID:uniqueClientID
-                          pairingUUID:[NSUUID UUID].UUIDString
-                        containerName:containerName
-                            contextID:contextID
-                                epoch:(uint64_t)epoch
-                          isInitiator:isInitiator];
-}
-
 - (instancetype)initWithProtocolType:(NSString*)protocolType
                       uniqueDeviceID:(NSString*)uniqueDeviceID
                       uniqueClientID:(NSString*)uniqueClientID
 - (instancetype)initWithProtocolType:(NSString*)protocolType
                       uniqueDeviceID:(NSString*)uniqueDeviceID
                       uniqueClientID:(NSString*)uniqueClientID
index 88a1820f84c94ea0c9cb0185e5e8935b000c1513..d8fcd385e5a205d74de85e5e4c407b5c6dd0e9c4 100644 (file)
@@ -36,6 +36,7 @@
 #import "keychain/ot/OTCuttlefishAccountStateHolder.h"
 #import "keychain/escrowrequest/Framework/SecEscrowRequest.h"
 #import "keychain/ckks/CKKSAccountStateTracker.h"
 #import "keychain/ot/OTCuttlefishAccountStateHolder.h"
 #import "keychain/escrowrequest/Framework/SecEscrowRequest.h"
 #import "keychain/ckks/CKKSAccountStateTracker.h"
+#import "keychain/ckks/CKKSViewManager.h"
 #include "keychain/securityd/SecDbItem.h"
 #import <CoreCDP/CDPAccount.h>
 NS_ASSUME_NONNULL_BEGIN
 #include "keychain/securityd/SecDbItem.h"
 #import <CoreCDP/CDPAccount.h>
 NS_ASSUME_NONNULL_BEGIN
@@ -45,22 +46,27 @@ NS_ASSUME_NONNULL_BEGIN
 @class OTClientStateMachine;
 @class CKKSLockStateTracker;
 @class CKKSAccountStateTracker;
 @class OTClientStateMachine;
 @class CKKSLockStateTracker;
 @class CKKSAccountStateTracker;
+@class CloudKitClassDependencies;
 
 @interface OTManager : NSObject <OTControlProtocol>
 
 @property (nonatomic, readonly) CKKSLockStateTracker* lockStateTracker;
 
 @interface OTManager : NSObject <OTControlProtocol>
 
 @property (nonatomic, readonly) CKKSLockStateTracker* lockStateTracker;
-@property id<CKKSCloudKitAccountStateTrackingProvider> accountStateTracker;
+@property CKKSAccountStateTracker* accountStateTracker;
 
 
-- (instancetype)init NS_UNAVAILABLE;
+@property (readonly) CKContainer* cloudKitContainer;
+@property (nullable) CKKSViewManager* viewManager;
+
+// Creates an OTManager ready for use with live external systems.
+- (instancetype)init;
 
 - (instancetype)initWithSOSAdapter:(id<OTSOSAdapter>)sosAdapter
 
 - (instancetype)initWithSOSAdapter:(id<OTSOSAdapter>)sosAdapter
-                   authKitAdapter:(id<OTAuthKitAdapter>)authKitAdapter
+                    authKitAdapter:(id<OTAuthKitAdapter>)authKitAdapter
           deviceInformationAdapter:(id<OTDeviceInformationAdapter>)deviceInformationAdapter
                 apsConnectionClass:(Class<OctagonAPSConnection>)apsConnectionClass
                 escrowRequestClass:(Class<SecEscrowRequestable>)escrowRequestClass
           deviceInformationAdapter:(id<OTDeviceInformationAdapter>)deviceInformationAdapter
                 apsConnectionClass:(Class<OctagonAPSConnection>)apsConnectionClass
                 escrowRequestClass:(Class<SecEscrowRequestable>)escrowRequestClass
-                       loggerClass:(Class<SFAnalyticsProtocol> _Nullable)loggerClass
-                  lockStateTracker:(CKKSLockStateTracker* _Nullable)lockStateTracker
-               accountStateTracker:(id<CKKSCloudKitAccountStateTrackingProvider>)accountStateTracker
+                       loggerClass:(Class<SFAnalyticsProtocol>)loggerClass
+                  lockStateTracker:(CKKSLockStateTracker*)lockStateTracker
+         cloudKitClassDependencies:(CKKSCloudKitClassDependencies*)cloudKitClassDependencies
            cuttlefishXPCConnection:(id<NSXPCProxyCreating> _Nullable)cuttlefishXPCConnection
                               cdpd:(id<OctagonFollowUpControllerProtocol>)cdpd;
 
            cuttlefishXPCConnection:(id<NSXPCProxyCreating> _Nullable)cuttlefishXPCConnection
                               cdpd:(id<OctagonFollowUpControllerProtocol>)cdpd;
 
@@ -69,9 +75,12 @@ NS_ASSUME_NONNULL_BEGIN
 - (BOOL)waitForReady:(NSString* _Nullable)containerName context:(NSString*)context wait:(int64_t)wait;
 - (void)moveToCheckTrustedStateForContainer:(NSString* _Nullable)containerName context:(NSString*)context;
 
 - (BOOL)waitForReady:(NSString* _Nullable)containerName context:(NSString*)context wait:(int64_t)wait;
 - (void)moveToCheckTrustedStateForContainer:(NSString* _Nullable)containerName context:(NSString*)context;
 
+// Call this to ensure SFA is ready
+- (void)setupAnalytics;
+
 + (instancetype _Nullable)manager;
 + (instancetype _Nullable)resetManager:(bool)reset to:(OTManager* _Nullable)obj;
 + (instancetype _Nullable)manager;
 + (instancetype _Nullable)resetManager:(bool)reset to:(OTManager* _Nullable)obj;
-- (void)xpc24HrNotification:(NSString* _Nullable)containerName context:(NSString*)context skipRateLimitingCheck:(BOOL)skipRateLimitingCheck reply:(void (^)(NSError *error))reply;
+- (void)xpc24HrNotification;
 
 - (OTCuttlefishContext*)contextForContainerName:(NSString* _Nullable)containerName
                                       contextID:(NSString*)contextID
 
 - (OTCuttlefishContext*)contextForContainerName:(NSString* _Nullable)containerName
                                       contextID:(NSString*)contextID
@@ -127,10 +136,20 @@ NS_ASSUME_NONNULL_BEGIN
                 containerName:(NSString* _Nullable)containerName
                   contextName:(NSString *)contextName
                         reply:(void (^)(NSError *error))reply;
                 containerName:(NSString* _Nullable)containerName
                   contextName:(NSString *)contextName
                         reply:(void (^)(NSError *error))reply;
+@end
 
 
-//test only
+@interface OTManager (Testing)
 - (void)setSOSEnabledForPlatformFlag:(bool) value;
 - (void)setSOSEnabledForPlatformFlag:(bool) value;
+
+- (void)clearAllContexts;
+
+// Note that the OTManager returned by this will not work particularly well, if you want to do Octagon things
+// This should only be used for the CKKS tests
+- (instancetype)initWithSOSAdapter:(id<OTSOSAdapter>)sosAdapter
+                  lockStateTracker:(CKKSLockStateTracker*)lockStateTracker
+         cloudKitClassDependencies:(CKKSCloudKitClassDependencies*)cloudKitClassDependencies;
 @end
 @end
+
 NS_ASSUME_NONNULL_END
 
 #endif  // OCTAGON
 NS_ASSUME_NONNULL_END
 
 #endif  // OCTAGON
index 92c0caae60551be701dcfc56e07a19eb44adf0bc..79d43f93ed5f809909507c2de1a98175a90f1650 100644 (file)
@@ -49,6 +49,7 @@
 #import "keychain/ckks/CKKS.h"
 #import "keychain/ckks/CKKSViewManager.h"
 #import "keychain/ckks/CKKSLockStateTracker.h"
 #import "keychain/ckks/CKKS.h"
 #import "keychain/ckks/CKKSViewManager.h"
 #import "keychain/ckks/CKKSLockStateTracker.h"
+#import "keychain/ckks/CKKSCloudKitClassDependencies.h"
 
 #import <CloudKit/CloudKit.h>
 #import <CloudKit/CloudKit_Private.h>
 
 #import <CloudKit/CloudKit.h>
 #import <CloudKit/CloudKit_Private.h>
@@ -135,11 +136,11 @@ static NSString* const kOTRampZoneName = @"metadata_zone";
 
 - (instancetype)init
 {
 
 - (instancetype)init
 {
-    // Under Octagon, the sos adatper is not considered essential.
+    // Under Octagon, the sos adapter is not considered essential.
     id<OTSOSAdapter> sosAdapter = (OctagonPlatformSupportsSOS() ?
                                    [[OTSOSActualAdapter alloc] initAsEssential:NO] :
                                    [[OTSOSMissingAdapter alloc] init]);
     id<OTSOSAdapter> sosAdapter = (OctagonPlatformSupportsSOS() ?
                                    [[OTSOSActualAdapter alloc] initAsEssential:NO] :
                                    [[OTSOSMissingAdapter alloc] init]);
-    
+
     return [self initWithSOSAdapter:sosAdapter
                      authKitAdapter:[[OTAuthKitActualAdapter alloc] init]
            deviceInformationAdapter:[[OTDeviceInformationActualAdapter alloc] init]
     return [self initWithSOSAdapter:sosAdapter
                      authKitAdapter:[[OTAuthKitActualAdapter alloc] init]
            deviceInformationAdapter:[[OTDeviceInformationActualAdapter alloc] init]
@@ -147,23 +148,21 @@ static NSString* const kOTRampZoneName = @"metadata_zone";
                  escrowRequestClass:[EscrowRequestServer class] // Use the server class here to skip the XPC layer
                         loggerClass:[CKKSAnalytics class]
                    lockStateTracker:[CKKSLockStateTracker globalTracker]
                  escrowRequestClass:[EscrowRequestServer class] // Use the server class here to skip the XPC layer
                         loggerClass:[CKKSAnalytics class]
                    lockStateTracker:[CKKSLockStateTracker globalTracker]
-                                    // The use of CKKS's account tracker here is an inversion, and will be fixed with CKKS-for-all when we
-                                    // have Octagon own the CKKS objects
-                accountStateTracker:[CKKSViewManager manager].accountTracker
+          cloudKitClassDependencies:[CKKSCloudKitClassDependencies forLiveCloudKit]
             cuttlefishXPCConnection:nil
                                cdpd:[[CDPFollowUpController alloc] init]];
 }
 
             cuttlefishXPCConnection:nil
                                cdpd:[[CDPFollowUpController alloc] init]];
 }
 
--(instancetype)initWithSOSAdapter:(id<OTSOSAdapter>)sosAdapter
-                   authKitAdapter:(id<OTAuthKitAdapter>)authKitAdapter
-         deviceInformationAdapter:(id<OTDeviceInformationAdapter>)deviceInformationAdapter
-               apsConnectionClass:(Class<OctagonAPSConnection>)apsConnectionClass
-               escrowRequestClass:(Class<SecEscrowRequestable>)escrowRequestClass
-                      loggerClass:(Class<SFAnalyticsProtocol>)loggerClass
-                 lockStateTracker:(CKKSLockStateTracker*)lockStateTracker
-              accountStateTracker:(id<CKKSCloudKitAccountStateTrackingProvider>)accountStateTracker
-          cuttlefishXPCConnection:(id<NSXPCProxyCreating>)cuttlefishXPCConnection
-                             cdpd:(id<OctagonFollowUpControllerProtocol>)cdpd
+- (instancetype)initWithSOSAdapter:(id<OTSOSAdapter>)sosAdapter
+                    authKitAdapter:(id<OTAuthKitAdapter>)authKitAdapter
+          deviceInformationAdapter:(id<OTDeviceInformationAdapter>)deviceInformationAdapter
+                apsConnectionClass:(Class<OctagonAPSConnection>)apsConnectionClass
+                escrowRequestClass:(Class<SecEscrowRequestable>)escrowRequestClass
+                       loggerClass:(Class<SFAnalyticsProtocol>)loggerClass
+                  lockStateTracker:(CKKSLockStateTracker*)lockStateTracker
+         cloudKitClassDependencies:(CKKSCloudKitClassDependencies*)cloudKitClassDependencies
+           cuttlefishXPCConnection:(id<NSXPCProxyCreating>)cuttlefishXPCConnection
+                              cdpd:(id<OctagonFollowUpControllerProtocol>)cdpd
 {
     if((self = [super init])) {
         _sosAdapter = sosAdapter;
 {
     if((self = [super init])) {
         _sosAdapter = sosAdapter;
@@ -171,10 +170,13 @@ static NSString* const kOTRampZoneName = @"metadata_zone";
         _deviceInformationAdapter = deviceInformationAdapter;
         _loggerClass = loggerClass;
         _lockStateTracker = lockStateTracker;
         _deviceInformationAdapter = deviceInformationAdapter;
         _loggerClass = loggerClass;
         _lockStateTracker = lockStateTracker;
-        _accountStateTracker = accountStateTracker;
         _sosEnabledForPlatform = OctagonPlatformSupportsSOS();
         _cuttlefishXPCConnection = cuttlefishXPCConnection;
 
         _sosEnabledForPlatform = OctagonPlatformSupportsSOS();
         _cuttlefishXPCConnection = cuttlefishXPCConnection;
 
+        _cloudKitContainer = [CKKSViewManager makeCKContainer:SecCKKSContainerName usePCS:SecCKKSContainerUsePCS];
+        _accountStateTracker = [[CKKSAccountStateTracker alloc] init:_cloudKitContainer
+                                           nsnotificationCenterClass:cloudKitClassDependencies.nsnotificationCenterClass];
+
         self.contexts = [NSMutableDictionary dictionary];
         self.clients = [NSMutableDictionary dictionary];
 
         self.contexts = [NSMutableDictionary dictionary];
         self.clients = [NSMutableDictionary dictionary];
 
@@ -185,19 +187,41 @@ static NSString* const kOTRampZoneName = @"metadata_zone";
 
         _cdpd = cdpd;
 
 
         _cdpd = cdpd;
 
+        _viewManager = [[CKKSViewManager alloc] initWithContainer:_cloudKitContainer
+                                                       sosAdapter:sosAdapter
+                                              accountStateTracker:_accountStateTracker
+                                                 lockStateTracker:lockStateTracker
+                                        cloudKitClassDependencies:cloudKitClassDependencies];
+
         // The default CuttlefishContext always exists:
         (void) [self contextForContainerName:OTCKContainerName contextID:OTDefaultContext];
         // The default CuttlefishContext always exists:
         (void) [self contextForContainerName:OTCKContainerName contextID:OTDefaultContext];
-
-        // Tell SFA to expect us
-        if(OctagonIsEnabled()){
-            [self setupAnalytics];
-        }
         
         secnotice("octagon", "otmanager init");
     }
     return self;
 }
 
         
         secnotice("octagon", "otmanager init");
     }
     return self;
 }
 
+- (instancetype)initWithSOSAdapter:(id<OTSOSAdapter>)sosAdapter
+                  lockStateTracker:(CKKSLockStateTracker*)lockStateTracker
+         cloudKitClassDependencies:(CKKSCloudKitClassDependencies*)cloudKitClassDependencies
+{
+    if((self = [super init])) {
+        _sosAdapter = sosAdapter;
+        _lockStateTracker = lockStateTracker;
+
+        _cloudKitContainer = [CKKSViewManager makeCKContainer:SecCKKSContainerName usePCS:SecCKKSContainerUsePCS];
+        _accountStateTracker = [[CKKSAccountStateTracker alloc] init:_cloudKitContainer
+                                           nsnotificationCenterClass:cloudKitClassDependencies.nsnotificationCenterClass];
+
+        _viewManager = [[CKKSViewManager alloc] initWithContainer:_cloudKitContainer
+                                                       sosAdapter:sosAdapter
+                                              accountStateTracker:_accountStateTracker
+                                                 lockStateTracker:lockStateTracker
+                                        cloudKitClassDependencies:cloudKitClassDependencies];
+    }
+    return self;
+}
+
 - (void)initializeOctagon
 {
     secnotice("octagon", "Initializing Octagon...");
 - (void)initializeOctagon
 {
     secnotice("octagon", "Initializing Octagon...");
@@ -584,7 +608,7 @@ static NSString* const kOTRampZoneName = @"metadata_zone";
         CKKSViewManager* viewManager = nil;
         if([containerName isEqualToString:SecCKKSContainerName] &&
            [contextID isEqualToString:OTDefaultContext]) {
         CKKSViewManager* viewManager = nil;
         if([containerName isEqualToString:SecCKKSContainerName] &&
            [contextID isEqualToString:OTDefaultContext]) {
-            viewManager = [CKKSViewManager manager];
+            viewManager = self.viewManager;
         }
 
         if(!context) {
         }
 
         if(!context) {
@@ -607,6 +631,15 @@ static NSString* const kOTRampZoneName = @"metadata_zone";
     return context;
 }
 
     return context;
 }
 
+- (void)clearAllContexts
+{
+    if(self.contexts) {
+        dispatch_sync(self.queue, ^{
+            [self.contexts removeAllObjects];
+        });
+    }
+}
+
 - (void)fetchEgoPeerID:(NSString* _Nullable)container
                context:(NSString*)context
                  reply:(void (^)(NSString* _Nullable peerID, NSError* _Nullable error))reply
 - (void)fetchEgoPeerID:(NSString* _Nullable)container
                context:(NSString*)context
                  reply:(void (^)(NSString* _Nullable peerID, NSError* _Nullable error))reply
@@ -842,7 +875,14 @@ static NSString* const kOTRampZoneName = @"metadata_zone";
     OTCuttlefishContext* cfshContext = [self contextForContainerName:config.containerName contextID:config.contextID];
     [cfshContext handlePairingRestart:config];
     [cfshContext startOctagonStateMachine];
     OTCuttlefishContext* cfshContext = [self contextForContainerName:config.containerName contextID:config.contextID];
     [cfshContext handlePairingRestart:config];
     [cfshContext startOctagonStateMachine];
-    [cfshContext rpcPrepareIdentityAsApplicantWithConfiguration:config epoch:config.epoch reply:^(NSString * _Nullable peerID, NSData * _Nullable permanentInfo, NSData * _Nullable permanentInfoSig, NSData * _Nullable stableInfo, NSData * _Nullable stableInfoSig, NSError * _Nullable error) {
+    [cfshContext rpcPrepareIdentityAsApplicantWithConfiguration:config
+                                                          epoch:config.epoch
+                                                          reply:^(NSString * _Nullable peerID,
+                                                                  NSData * _Nullable permanentInfo,
+                                                                  NSData * _Nullable permanentInfoSig,
+                                                                  NSData * _Nullable stableInfo,
+                                                                  NSData * _Nullable stableInfoSig,
+                                                                  NSError * _Nullable error) {
         reply(peerID, permanentInfo, permanentInfoSig, stableInfo, stableInfoSig, error);
     }];
 }
         reply(peerID, permanentInfo, permanentInfoSig, stableInfo, stableInfoSig, error);
     }];
 }
@@ -850,13 +890,12 @@ static NSString* const kOTRampZoneName = @"metadata_zone";
 - (void)rpcJoinWithConfiguration:(OTJoiningConfiguration*)config
                        vouchData:(NSData*)vouchData
                         vouchSig:(NSData*)vouchSig
 - (void)rpcJoinWithConfiguration:(OTJoiningConfiguration*)config
                        vouchData:(NSData*)vouchData
                         vouchSig:(NSData*)vouchSig
-                 preapprovedKeys:(NSArray<NSData*>* _Nullable)preapprovedKeys
                            reply:(void (^)(NSError * _Nullable error))reply
 {
     OTCuttlefishContext* cfshContext = [self contextForContainerName:config.containerName contextID:config.contextID];
     [cfshContext handlePairingRestart:config];
     [cfshContext startOctagonStateMachine];
                            reply:(void (^)(NSError * _Nullable error))reply
 {
     OTCuttlefishContext* cfshContext = [self contextForContainerName:config.containerName contextID:config.contextID];
     [cfshContext handlePairingRestart:config];
     [cfshContext startOctagonStateMachine];
-    [cfshContext rpcJoin:vouchData vouchSig:vouchSig preapprovedKeys:preapprovedKeys reply:^(NSError * _Nullable error) {
+    [cfshContext rpcJoin:vouchData vouchSig:vouchSig reply:^(NSError * _Nullable error) {
         reply(error);
     }];
 }
         reply(error);
     }];
 }
@@ -980,6 +1019,7 @@ static NSString* const kOTRampZoneName = @"metadata_zone";
             secnotice("octagon-analytics", "Error fetching Octagon metadata: %@", metadataError);
         }
         values[OctagonAnalyticIcloudAccountState] = metadata ? @(metadata.icloudAccountState) : nil;
             secnotice("octagon-analytics", "Error fetching Octagon metadata: %@", metadataError);
         }
         values[OctagonAnalyticIcloudAccountState] = metadata ? @(metadata.icloudAccountState) : nil;
+        values[OctagonAnalyticCDPBitStatus] = metadata? @(metadata.cdpState) : nil;
         values[OctagonAnalyticsTrustState] = metadata ? @(metadata.trustState) : nil;
 
         NSDate* healthCheck = [cuttlefishContext currentMemoizedLastHealthCheck];
         values[OctagonAnalyticsTrustState] = metadata ? @(metadata.trustState) : nil;
 
         NSDate* healthCheck = [cuttlefishContext currentMemoizedLastHealthCheck];
@@ -1176,8 +1216,9 @@ static NSString* const kOTRampZoneName = @"metadata_zone";
     [cfshContext startOctagonStateMachine];
 
     [cfshContext joinWithRecoveryKey:recoveryKey reply:^(NSError *error) {
     [cfshContext startOctagonStateMachine];
 
     [cfshContext joinWithRecoveryKey:recoveryKey reply:^(NSError *error) {
-        if (error.code == TrustedPeersHelperErrorCodeNotEnrolled && [error.domain isEqualToString:TrustedPeersHelperErrorDomain]) {
-            // If we hit this error, let's reset and establish octagon then enroll this recovery key in the new circle
+        if ((error.code == TrustedPeersHelperErrorCodeNotEnrolled || error.code == TrustedPeersHelperErrorCodeUntrustedRecoveryKeys)
+            && [error.domain isEqualToString:TrustedPeersHelperErrorDomain]){
+            // If we hit either of these errors, let's reset and establish octagon then enroll this recovery key in the new circle
             secerror("octagon, recovery key is not enrolled in octagon, resetting octagon circle");
             [[self.loggerClass logger] logResultForEvent:OctagonEventJoinRecoveryKeyCircleReset hardFailure:NO result:error];
 
             secerror("octagon, recovery key is not enrolled in octagon, resetting octagon circle");
             [[self.loggerClass logger] logResultForEvent:OctagonEventJoinRecoveryKeyCircleReset hardFailure:NO result:error];
 
@@ -1219,17 +1260,22 @@ static NSString* const kOTRampZoneName = @"metadata_zone";
     }];
 }
 
     }];
 }
 
-- (void)healthCheck:(NSString *)container context:(NSString *)context skipRateLimitingCheck:(BOOL)skipRateLimitingCheck reply:(void (^)(NSError *_Nullable error))reply
+- (void)xpc24HrNotification
 {
 {
-    [self xpc24HrNotification:container context:context skipRateLimitingCheck:skipRateLimitingCheck reply:reply];
-}
+    secnotice("octagon-health", "Received 24hr xpc notification");
 
 
+    [self healthCheck:OTCKContainerName context:OTDefaultContext skipRateLimitingCheck:NO reply:^(NSError * _Nullable error) {
+        if(error) {
+            secerror("octagon: error attempting to check octagon health: %@", error);
+        } else {
+            secnotice("octagon", "health check success");
+        }
+    }];
+}
 
 
-- (void)xpc24HrNotification:(NSString* _Nullable)containerName context:(NSString*)context skipRateLimitingCheck:(BOOL)skipRateLimitingCheck reply:(void (^)(NSError *error))reply
+- (void)healthCheck:(NSString *)container context:(NSString *)context skipRateLimitingCheck:(BOOL)skipRateLimitingCheck reply:(void (^)(NSError *_Nullable error))reply
 {
 {
-    secnotice("octagon-health", "Received 24 xpc notification");
-
-    OTCuttlefishContext* cfshContext = [self contextForContainerName:containerName contextID:context];
+    OTCuttlefishContext* cfshContext = [self contextForContainerName:container contextID:context];
 
     secnotice("octagon", "notifying container of change");
 
 
     secnotice("octagon", "notifying container of change");
 
@@ -1253,6 +1299,9 @@ static NSString* const kOTRampZoneName = @"metadata_zone";
 {
     for(OTCuttlefishContext* context in self.contexts.allValues) {
         [context.stateMachine haltOperation];
 {
     for(OTCuttlefishContext* context in self.contexts.allValues) {
         [context.stateMachine haltOperation];
+
+        // Also, clear the viewManager strong pointer
+        [context clearCKKSViewManager];
     }
 }
 
     }
 }
 
@@ -1349,9 +1398,6 @@ static NSString* const kOTRampZoneName = @"metadata_zone";
         [[CKKSAnalytics logger] setNumberProperty:NULL forKey:countName];
     }
 
         [[CKKSAnalytics logger] setNumberProperty:NULL forKey:countName];
     }
 
-    // Clear all CFU state variables, too
-    [cuttlefishContext clearPendingCFUFlags];
-
     // Always return without error
     reply(nil);
 }
     // Always return without error
     reply(nil);
 }
@@ -1366,6 +1412,79 @@ static NSString* const kOTRampZoneName = @"metadata_zone";
     reply(NULL);
 }
 
     reply(NULL);
 }
 
+- (void)refetchCKKSPolicy:(NSString * _Nullable)containerName
+                contextID:(nonnull NSString *)contextID
+                    reply:(nonnull void (^)(NSError * _Nullable))reply {
+    secnotice("octagon-ckks", "refetch-ckks-policy");
+
+    if(!containerName) {
+        containerName = OTCKContainerName;
+    }
+
+    OTCuttlefishContext* cfshContext = [self contextForContainerName:containerName contextID:contextID];
+
+    if(!cfshContext) {
+        reply([NSError errorWithDomain:OctagonErrorDomain
+                                  code:OTErrorNoSuchContext
+                           description:[NSString stringWithFormat:@"No context for (%@,%@)", containerName, contextID]]);
+        return;
+    }
+
+    [cfshContext rpcRefetchCKKSPolicy:^(NSError* error) {
+        secnotice("octagon-ckks", "refetch-ckks-policy result: %@", error ?: @"no error");
+        reply(error);
+    }];
+}
+
+
+- (void)getCDPStatus:(NSString * _Nullable)containerName
+           contextID:(nonnull NSString *)contextID
+               reply:(nonnull void (^)(OTCDPStatus, NSError * _Nullable))reply {
+    secnotice("octagon-cdp", "get-cdp-status");
+
+    if(!containerName) {
+        containerName = OTCKContainerName;
+    }
+
+    OTCuttlefishContext* cfshContext = [self contextForContainerName:containerName contextID:contextID];
+
+    if(!cfshContext) {
+        reply(OTCDPStatusUnknown, [NSError errorWithDomain:OctagonErrorDomain
+                                                      code:OTErrorNoSuchContext
+                                               description:[NSString stringWithFormat:@"No context for (%@,%@)", containerName, contextID]]);
+        return;
+    }
+
+    NSError* error = nil;
+    OTCDPStatus status = [cfshContext getCDPStatus:&error];
+
+    reply(status, error);
+}
+
+
+- (void)setCDPEnabled:(NSString * _Nullable)containerName
+            contextID:(nonnull NSString *)contextID
+                reply:(nonnull void (^)(NSError * _Nullable))reply {
+    secnotice("octagon-cdp", "set-cdp-enabled");
+    if(!containerName) {
+        containerName = OTCKContainerName;
+    }
+
+    OTCuttlefishContext* cfshContext = [self contextForContainerName:containerName contextID:contextID];
+
+    if(!cfshContext) {
+        reply([NSError errorWithDomain:OctagonErrorDomain
+                                  code:OTErrorNoSuchContext
+                           description:[NSString stringWithFormat:@"No context for (%@,%@)", containerName, contextID]]);
+        return;
+    }
+
+    NSError* localError = nil;
+    [cfshContext setCDPEnabled:&localError];
+
+    reply(localError);
+}
+
 @end
 
 #endif
 @end
 
 #endif
index e9e5aeb60868469070ffe225c79ecbfbae86753f..373177f6536eafafc0e601f18490b0ed77aff390 100644 (file)
@@ -27,7 +27,7 @@ NS_ASSUME_NONNULL_BEGIN
 @property id<OTAuthKitAdapter> authKitAdapter;
 @property id<OTDeviceInformationAdapter> deviceInformationAdapter;
 @property (readonly) CuttlefishXPCWrapper* cuttlefishXPCWrapper;
 @property id<OTAuthKitAdapter> authKitAdapter;
 @property id<OTDeviceInformationAdapter> deviceInformationAdapter;
 @property (readonly) CuttlefishXPCWrapper* cuttlefishXPCWrapper;
-@property CKKSViewManager* viewManager;
+@property (readonly, weak) CKKSViewManager* viewManager;
 @property CKKSLockStateTracker* lockStateTracker;
 @property Class<SecEscrowRequestable> escrowRequestClass;
 
 @property CKKSLockStateTracker* lockStateTracker;
 @property Class<SecEscrowRequestable> escrowRequestClass;
 
index 9f03512ae871550c1dbb612b87eed0f49f727b0a..a18e23a0760212c09910c019d056dcd80cd5d209 100644 (file)
@@ -24,6 +24,7 @@
 #if OCTAGON
 
 #import <Foundation/Foundation.h>
 #if OCTAGON
 
 #import <Foundation/Foundation.h>
+#import <TrustedPeers/TrustedPeers.h>
 
 #import "keychain/ckks/CKKSGroupOperation.h"
 #import "keychain/ot/OctagonStateMachineHelpers.h"
 
 #import "keychain/ckks/CKKSGroupOperation.h"
 #import "keychain/ot/OctagonStateMachineHelpers.h"
@@ -41,6 +42,7 @@ NS_ASSUME_NONNULL_BEGIN
                        intendedState:(OctagonState*)intendedState
                           errorState:(OctagonState*)errorState
                           deviceInfo:(OTDeviceInformation*)deviceInfo
                        intendedState:(OctagonState*)intendedState
                           errorState:(OctagonState*)errorState
                           deviceInfo:(OTDeviceInformation*)deviceInfo
+                      policyOverride:(TPPolicyVersion* _Nullable)policyOverride
                                epoch:(uint64_t)epoch;
 
 @property (nonatomic) uint64_t epoch;
                                epoch:(uint64_t)epoch;
 
 @property (nonatomic) uint64_t epoch;
@@ -52,6 +54,8 @@ NS_ASSUME_NONNULL_BEGIN
 @property (nullable) NSData* stableInfo;
 @property (nullable) NSData* stableInfoSig;
 
 @property (nullable) NSData* stableInfo;
 @property (nullable) NSData* stableInfoSig;
 
+@property (nullable) TPPolicyVersion* policyOverride;
+
 @end
 
 NS_ASSUME_NONNULL_END
 @end
 
 NS_ASSUME_NONNULL_END
index 2b34ec232179015ca8a0af644a8baa64560ed7db..d0e0ca45c8434360efa9f5778193b07b3d8e5e12 100644 (file)
@@ -28,7 +28,6 @@
 #import <Security/SecKeyPriv.h>
 
 #import "keychain/ot/OTCuttlefishContext.h"
 #import <Security/SecKeyPriv.h>
 
 #import "keychain/ot/OTCuttlefishContext.h"
-#import "keychain/ot/OTFetchViewsOperation.h"
 #import "keychain/ot/OTOperationDependencies.h"
 #import "keychain/ot/OTPrepareOperation.h"
 
 #import "keychain/ot/OTOperationDependencies.h"
 #import "keychain/ot/OTPrepareOperation.h"
 
@@ -48,6 +47,7 @@
                        intendedState:(OctagonState*)intendedState
                           errorState:(OctagonState*)errorState
                           deviceInfo:(OTDeviceInformation*)deviceInfo
                        intendedState:(OctagonState*)intendedState
                           errorState:(OctagonState*)errorState
                           deviceInfo:(OTDeviceInformation*)deviceInfo
+                      policyOverride:(TPPolicyVersion* _Nullable)policyOverride
                                epoch:(uint64_t)epoch
 {
     if((self = [super init])) {
                                epoch:(uint64_t)epoch
 {
     if((self = [super init])) {
@@ -58,6 +58,8 @@
 
         _intendedState = intendedState;
         _nextState = errorState;
 
         _intendedState = intendedState;
         _nextState = errorState;
+
+        _policyOverride = policyOverride;
     }
     return self;
 }
     }
     return self;
 }
                                               deviceName:self.deviceInfo.deviceName
                                             serialNumber:self.deviceInfo.serialNumber
                                                osVersion:self.deviceInfo.osVersion
                                               deviceName:self.deviceInfo.deviceName
                                             serialNumber:self.deviceInfo.serialNumber
                                                osVersion:self.deviceInfo.osVersion
-                                           policyVersion:nil
+                                           policyVersion:self.policyOverride
                                            policySecrets:nil
                              signingPrivKeyPersistentRef:signingKeyPersistRef
                                  encPrivKeyPersistentRef:encryptionKeyPersistRef
                                            policySecrets:nil
                              signingPrivKeyPersistentRef:signingKeyPersistRef
                                  encPrivKeyPersistentRef:encryptionKeyPersistRef
-                                                   reply:^(NSString * _Nullable peerID, NSData * _Nullable permanentInfo, NSData * _Nullable permanentInfoSig, NSData * _Nullable stableInfo, NSData * _Nullable stableInfoSig, NSError * _Nullable error) {
+                                                   reply:^(NSString * _Nullable peerID,
+                                                           NSData * _Nullable permanentInfo,
+                                                           NSData * _Nullable permanentInfoSig,
+                                                           NSData * _Nullable stableInfo,
+                                                           NSData * _Nullable stableInfoSig,
+                                                           NSSet<NSString*>* _Nullable syncingViews,
+                                                           TPPolicy* _Nullable syncingPolicy,
+                                                           NSError * _Nullable error) {
             STRONGIFY(self);
             [[CKKSAnalytics logger] logResultForEvent:OctagonEventPrepareIdentity hardFailure:true result:error];
             if(error) {
             STRONGIFY(self);
             [[CKKSAnalytics logger] logResultForEvent:OctagonEventPrepareIdentity hardFailure:true result:error];
             if(error) {
                 self.stableInfoSig = stableInfoSig;
 
                 NSError* localError = nil;
                 self.stableInfoSig = stableInfoSig;
 
                 NSError* localError = nil;
-                BOOL persisted = [self.deps.stateHolder persistNewEgoPeerID:peerID error:&localError];
+
+                secnotice("octagon-ckks", "New syncing policy: %@ views: %@", syncingPolicy, syncingViews);
+
+                BOOL persisted = [self.deps.stateHolder persistAccountChanges:^OTAccountMetadataClassC * _Nullable(OTAccountMetadataClassC * _Nonnull metadata) {
+                    metadata.peerID = peerID;
+                    metadata.syncingViews = [syncingViews mutableCopy];
+                    [metadata setTPPolicy:syncingPolicy];
+
+                    return metadata;
+                } error:&localError];
+
                 if(!persisted || localError) {
                 if(!persisted || localError) {
-                    secnotice("octagon", "Couldn't persist peer ID: %@", localError);
+                    secnotice("octagon", "Couldn't persist metadata: %@", localError);
                     self.error = localError;
                     [self runBeforeGroupFinished:self.finishedOp];
                     self.error = localError;
                     [self runBeforeGroupFinished:self.finishedOp];
-                } else {
-                    WEAKIFY(self);
-
-                    CKKSResultOperation *doneOp = [CKKSResultOperation named:@"ot-prepare"
-                                                                   withBlock:^{
-                            STRONGIFY(self);
-                            self.nextState = self.intendedState;
-                        }];
-
-                    OTFetchViewsOperation *fetchViewsOp = [[OTFetchViewsOperation alloc] initWithDependencies:self.deps];
-                    [self runBeforeGroupFinished:fetchViewsOp];
-                    [doneOp addDependency:fetchViewsOp];
-                    [self runBeforeGroupFinished:doneOp];
-                    [self.finishedOp addDependency:doneOp];
-                    [self runBeforeGroupFinished:self.finishedOp];
+                    return;
                 }
                 }
+
+                // Let CKKS know of the new policy, so it can spin up
+                [self.deps.viewManager setSyncingViews:syncingViews sortingPolicy:syncingPolicy];
+
+                self.nextState = self.intendedState;
+                [self runBeforeGroupFinished:self.finishedOp];
             }
         }];
 }
             }
         }];
 }
index 0a09dd2fe573c3b91d37b5fa59a857d9a8364288..0f2dbc3879e9d7c59255dac8459f5fca68cc6d64 100644 (file)
@@ -73,7 +73,8 @@
 
     for(CKKSCurrentKeySet* incompleteKeySet in incompleteKeySets) {
         if(incompleteKeySet.error == nil) {
 
     for(CKKSCurrentKeySet* incompleteKeySet in incompleteKeySets) {
         if(incompleteKeySet.error == nil) {
-            CKKSKeychainView* viewMatchingSet = [self.deps.viewManager findView:incompleteKeySet.viewName];
+            CKKSViewManager* viewManager = self.deps.viewManager;
+            CKKSKeychainView* viewMatchingSet = [viewManager findView:incompleteKeySet.viewName];
 
             if(!viewMatchingSet) {
                 secnotice("octagon-ckks", "No view matching viewset %@?", incompleteKeySet);
 
             if(!viewMatchingSet) {
                 secnotice("octagon-ckks", "No view matching viewset %@?", incompleteKeySet);
index dddda03b65bf7811611f0a47a5b5b2135062e584..976476e1bdd3c3e745fa41b29315fea8279df4a1 100644 (file)
@@ -19,16 +19,20 @@ NS_ASSUME_NONNULL_BEGIN
 - (instancetype)init NS_UNAVAILABLE;
 - (instancetype)initAsEssential:(BOOL)essential;
 
 - (instancetype)init NS_UNAVAILABLE;
 - (instancetype)initAsEssential:(BOOL)essential;
 
-// Helper methods
-+ (NSArray<NSData*>*)peerPublicSigningKeySPKIs:(NSSet<id<CKKSPeer>>* _Nullable)peers;
-
+// Helper methods.
 + (NSSet<NSString*>*)sosCKKSViewList;
 @end
 
 + (NSSet<NSString*>*)sosCKKSViewList;
 @end
 
+
 // This adapter is for a platform which does not have SOS (e.g., aTV, Watch, HomePod)
 @interface OTSOSMissingAdapter : NSObject <OTSOSAdapter>
 @end
 
 // This adapter is for a platform which does not have SOS (e.g., aTV, Watch, HomePod)
 @interface OTSOSMissingAdapter : NSObject <OTSOSAdapter>
 @end
 
+// Helper code
+@interface OTSOSAdapterHelpers : NSObject
++ (NSArray<NSData*>* _Nullable)peerPublicSigningKeySPKIsForCircle:(id<OTSOSAdapter>)sosAdapter error:(NSError**)error;
+@end
+
 NS_ASSUME_NONNULL_END
 
 #endif // OCTAGON
 NS_ASSUME_NONNULL_END
 
 #endif // OCTAGON
index 69ed211f95b93f20e8c0b578a995f1c365bef87a..4513d980a57c6d650f62975f064fc138551a162d 100644 (file)
@@ -98,7 +98,7 @@
                                              code:CKKSNoPeersAvailable
                                       description:@"Not in SOS circle, but no error returned"];
         }
                                              code:CKKSNoPeersAvailable
                                       description:@"Not in SOS circle, but no error returned"];
         }
-        secerror("octagon-sos: Not in circle : %d %@", circleStatus, localerror);
+        secerror("octagon-sos: Not in circle : %@ %@", SOSAccountGetSOSCCStatusString(circleStatus), localerror);
         if(error) {
             *error = localerror;
         }
         if(error) {
             *error = localerror;
         }
 
     return result;
 }
 
     return result;
 }
-
-+ (NSArray<NSData*>*)peerPublicSigningKeySPKIs:(NSSet<id<CKKSPeer>>* _Nullable)peerSet
-{
-    NSMutableArray<NSData*>* publicSigningSPKIs = [NSMutableArray array];
-
-    for(id<CKKSPeer> peer in peerSet) {
-        NSData* spki = [peer.publicSigningKey encodeSubjectPublicKeyInfo];
-        if(!spki) {
-            secerror("octagon-sos: Can't create SPKI for peer: %@", peer);
-        } else {
-            secerror("octagon-sos: Created SPKI for peer: %@", peer);
-            [publicSigningSPKIs addObject:spki];
-        }
-    }
-    return publicSigningSPKIs;
-}
 @end
 
 @implementation OTSOSMissingAdapter
 @end
 
 @implementation OTSOSMissingAdapter
                                                          trustedPeersError:unimplementedError];
 }
 
                                                          trustedPeersError:unimplementedError];
 }
 
+@end
+
+@implementation OTSOSAdapterHelpers
+
++ (NSArray<NSData*>*)peerPublicSigningKeySPKIs:(NSSet<id<CKKSPeer>>* _Nullable)peerSet
+{
+    NSMutableArray<NSData*>* publicSigningSPKIs = [NSMutableArray array];
+
+    for(id<CKKSPeer> peer in peerSet) {
+        NSData* spki = [peer.publicSigningKey encodeSubjectPublicKeyInfo];
+        if(!spki) {
+            secerror("octagon-sos: Can't create SPKI for peer: %@", peer);
+        } else {
+            secerror("octagon-sos: Created SPKI for peer: %@", peer);
+            [publicSigningSPKIs addObject:spki];
+        }
+    }
+    return publicSigningSPKIs;
+}
+
++ (NSArray<NSData*>* _Nullable)peerPublicSigningKeySPKIsForCircle:(id<OTSOSAdapter>)sosAdapter error:(NSError**)error
+{
+    NSError* peerError = nil;
+    SOSCCStatus sosStatus = [sosAdapter circleStatus:&peerError];
+
+    if(sosStatus != kSOSCCInCircle || peerError) {
+        secerror("octagon-sos: Not in circle; not preapproving keys: %@ (%@)", SOSAccountGetSOSCCStatusString(sosStatus), peerError);
+        if(error) {
+            *error = peerError;
+        }
+        return nil;
+    } else {
+        // We're in-circle: preapprove these peers
+        NSError* peerError = nil;
 
 
+        NSSet<id<CKKSRemotePeerProtocol>>* peerSet = [sosAdapter fetchTrustedPeers:&peerError];
 
 
+        if(!peerSet || peerError) {
+            secerror("octagon-sos: Can't fetch trusted peer SPKIs: %@", peerError);
+            if(error) {
+                *error = peerError;
+            }
+            return nil;
+        }
+
+        return [self peerPublicSigningKeySPKIs:peerSet];
+    }
+}
 @end
 #endif // OCTAGON
 @end
 #endif // OCTAGON
index 7abf6522e49dafa8e87ae566d1c29951f9176a65..096f1cdd9c6ee7cf7e6d13a16008d5b72ae263c7 100644 (file)
     }];
     [self dependOnBeforeGroupFinished:self.finishedOp];
 
     }];
     [self dependOnBeforeGroupFinished:self.finishedOp];
 
-    NSError* error = nil;
-    NSSet<id<CKKSRemotePeerProtocol>>* peerSet = [self.deps.sosAdapter fetchTrustedPeers:&error];
+    NSError* sosPreapprovalError = nil;
+    NSArray<NSData*>* publicSigningSPKIs = [OTSOSAdapterHelpers peerPublicSigningKeySPKIsForCircle:self.deps.sosAdapter error:&sosPreapprovalError];
 
 
-    if(!peerSet || error) {
-        secerror("octagon-sos: Can't fetch trusted peers; stopping preapproved key update: %@", error);
-        self.error = error;
+    if(!publicSigningSPKIs || sosPreapprovalError) {
+        secerror("octagon-sos: Can't fetch trusted peers; stopping preapproved key update: %@", sosPreapprovalError);
+        self.error = sosPreapprovalError;
         self.nextState = self.sosNotPresentState;
         [self runBeforeGroupFinished:self.finishedOp];
         return;
     }
 
         self.nextState = self.sosNotPresentState;
         [self runBeforeGroupFinished:self.finishedOp];
         return;
     }
 
-    NSArray<NSData*>* publicSigningSPKIs = [OTSOSActualAdapter peerPublicSigningKeySPKIs:peerSet];
     secnotice("octagon-sos", "Updating SOS preapproved keys to %@", publicSigningSPKIs);
 
     [self.deps.cuttlefishXPCWrapper setPreapprovedKeysWithContainer:self.deps.containerName
                                                             context:self.deps.contextID
                                                     preapprovedKeys:publicSigningSPKIs
     secnotice("octagon-sos", "Updating SOS preapproved keys to %@", publicSigningSPKIs);
 
     [self.deps.cuttlefishXPCWrapper setPreapprovedKeysWithContainer:self.deps.containerName
                                                             context:self.deps.contextID
                                                     preapprovedKeys:publicSigningSPKIs
-                                                              reply:^(NSError* error) {
+                                                              reply:^(TrustedPeersHelperPeerState* _Nullable peerState, NSError* error) {
             STRONGIFY(self);
             if(error) {
                 secerror("octagon-sos: unable to update preapproved keys: %@", error);
                 self.error = error;
             } else {
                 secnotice("octagon-sos", "Updated SOS preapproved keys");
             STRONGIFY(self);
             if(error) {
                 secerror("octagon-sos: unable to update preapproved keys: %@", error);
                 self.error = error;
             } else {
                 secnotice("octagon-sos", "Updated SOS preapproved keys");
+
+                if (peerState.memberChanges) {
+                    secnotice("octagon", "Member list changed");
+                    [self.deps.octagonAdapter sendTrustedPeerSetChangedUpdate];
+                }
+
                 self.nextState = self.intendedState;
             }
             [self runBeforeGroupFinished:self.finishedOp];
                 self.nextState = self.intendedState;
             }
             [self runBeforeGroupFinished:self.finishedOp];
index 04bbfec95f6bdd7428f2fb4767871bd9f2ffcd37..8c87b128e53d7c9a015ef2b9975a79c50c5d4801 100644 (file)
@@ -2,6 +2,7 @@
 #if OCTAGON
 
 #import <Foundation/Foundation.h>
 #if OCTAGON
 
 #import <Foundation/Foundation.h>
+#import <TrustedPeers/TrustedPeers.h>
 #import "keychain/ckks/CKKSGroupOperation.h"
 #import "keychain/ot/OctagonStateMachineHelpers.h"
 #import "keychain/ot/OTStates.h"
 #import "keychain/ckks/CKKSGroupOperation.h"
 #import "keychain/ot/OctagonStateMachineHelpers.h"
 #import "keychain/ot/OTStates.h"
@@ -16,11 +17,14 @@ NS_ASSUME_NONNULL_BEGIN
 
 @interface OTSOSUpgradeOperation : CKKSGroupOperation <OctagonStateTransitionOperationProtocol>
 
 
 @interface OTSOSUpgradeOperation : CKKSGroupOperation <OctagonStateTransitionOperationProtocol>
 
+@property (readonly, nullable) TPPolicyVersion* policyOverride;
+
 - (instancetype)initWithDependencies:(OTOperationDependencies*)dependencies
                        intendedState:(OctagonState*)intendedState
                    ckksConflictState:(OctagonState*)ckksConflictState
                           errorState:(OctagonState*)errorState
 - (instancetype)initWithDependencies:(OTOperationDependencies*)dependencies
                        intendedState:(OctagonState*)intendedState
                    ckksConflictState:(OctagonState*)ckksConflictState
                           errorState:(OctagonState*)errorState
-                          deviceInfo:(OTDeviceInformation*)deviceInfo;
+                          deviceInfo:(OTDeviceInformation*)deviceInfo
+                      policyOverride:(TPPolicyVersion* _Nullable)policyOverride;
 
 @end
 
 
 @end
 
index 8f22398473b08abf9e5e6c49aa4975ee19d81f21..4972beed2305332ca777cabbd32e316de5567f7e 100644 (file)
@@ -45,6 +45,7 @@
                    ckksConflictState:(OctagonState*)ckksConflictState
                           errorState:(OctagonState*)errorState
                           deviceInfo:(OTDeviceInformation*)deviceInfo
                    ckksConflictState:(OctagonState*)ckksConflictState
                           errorState:(OctagonState*)errorState
                           deviceInfo:(OTDeviceInformation*)deviceInfo
+                      policyOverride:(TPPolicyVersion* _Nullable)policyOverride
 {
     if((self = [super init])) {
         _deps = dependencies;
 {
     if((self = [super init])) {
         _deps = dependencies;
@@ -54,6 +55,7 @@
         _ckksConflictState = ckksConflictState;
 
         _deviceInfo = deviceInfo;
         _ckksConflictState = ckksConflictState;
 
         _deviceInfo = deviceInfo;
+        _policyOverride = policyOverride;
     }
     return self;
 }
     }
     return self;
 }
         return;
     }
 
         return;
     }
 
-    // Now that we have some non-error SOS status, write down that we attempted an SOS Upgrade.
+    // Now that we have some non-error SOS status, write down that we attempted an SOS Upgrade (and make sure the CDP bit is on)
     NSError* persistError = nil;
     NSError* persistError = nil;
-    BOOL persisted = [self.deps.stateHolder persistOctagonJoinAttempt:OTAccountMetadataClassC_AttemptedAJoinState_ATTEMPTED error:&persistError];
+    BOOL persisted = [self.deps.stateHolder persistAccountChanges:^OTAccountMetadataClassC * _Nonnull(OTAccountMetadataClassC * _Nonnull metadata) {
+        metadata.attemptedJoin = OTAccountMetadataClassC_AttemptedAJoinState_ATTEMPTED;
+        metadata.cdpState = OTAccountMetadataClassC_CDPState_ENABLED;
+        return metadata;
+    } error:&persistError];
     if(!persisted || persistError) {
         secerror("octagon: failed to save 'attempted join' state: %@", persistError);
     }
     if(!persisted || persistError) {
         secerror("octagon: failed to save 'attempted join' state: %@", persistError);
     }
                                               deviceName:self.deviceInfo.deviceName
                                             serialNumber:self.self.deviceInfo.serialNumber
                                                osVersion:self.deviceInfo.osVersion
                                               deviceName:self.deviceInfo.deviceName
                                             serialNumber:self.self.deviceInfo.serialNumber
                                                osVersion:self.deviceInfo.osVersion
-                                           policyVersion:nil
+                                           policyVersion:self.policyOverride
                                            policySecrets:nil
                              signingPrivKeyPersistentRef:signingKeyPersistRef
                                  encPrivKeyPersistentRef:encryptionKeyPersistRef
                                            policySecrets:nil
                              signingPrivKeyPersistentRef:signingKeyPersistRef
                                  encPrivKeyPersistentRef:encryptionKeyPersistRef
                                                            NSData * _Nullable permanentInfoSig,
                                                            NSData * _Nullable stableInfo,
                                                            NSData * _Nullable stableInfoSig,
                                                            NSData * _Nullable permanentInfoSig,
                                                            NSData * _Nullable stableInfo,
                                                            NSData * _Nullable stableInfoSig,
+                                                           NSSet<NSString*>* syncingViews,
+                                                           TPPolicy* _Nullable syncingPolicy,
                                                            NSError * _Nullable error) {
             STRONGIFY(self);
 
                                                            NSError * _Nullable error) {
             STRONGIFY(self);
 
                 [self handlePrepareErrors:error nextExpectedState:OctagonStateBecomeUntrusted];
 
                 [self runBeforeGroupFinished:self.finishedOp];
                 [self handlePrepareErrors:error nextExpectedState:OctagonStateBecomeUntrusted];
 
                 [self runBeforeGroupFinished:self.finishedOp];
-            } else {
-                secnotice("octagon-sos", "Prepared: %@ %@ %@", peerID, permanentInfo, permanentInfoSig);
+                return;
+            }
+
+            secnotice("octagon-sos", "Prepared: %@ %@ %@", peerID, permanentInfo, permanentInfoSig);
+
+            NSError* localError = nil;
+            BOOL persisted = [self.deps.stateHolder persistAccountChanges:^OTAccountMetadataClassC * _Nullable(OTAccountMetadataClassC * _Nonnull metadata) {
+                metadata.syncingViews = [syncingViews mutableCopy];
+                [metadata setTPPolicy:syncingPolicy];
+                return metadata;
+            } error:&localError];
 
 
-                [self afterPrepare];
+            if(!persisted || localError) {
+                secerror("octagon-ckks: Error persisting new views and policy: %@", localError);
+                self.error = localError;
+                [self handlePrepareErrors:error nextExpectedState:OctagonStateBecomeUntrusted];
+                [self runBeforeGroupFinished:self.finishedOp];
+                return;
             }
 
             }
 
+            [self.deps.viewManager setSyncingViews:syncingViews sortingPolicy:syncingPolicy];
+
+            [self afterPrepare];
         }];
 }
 
         }];
 }
 
                                                                     reply:^(BOOL launchOkay, NSError * _Nullable error) {
             STRONGIFY(self);
 
                                                                     reply:^(BOOL launchOkay, NSError * _Nullable error) {
             STRONGIFY(self);
 
+            // Preflight preapprovedjoin should return the policy used by the peers we eventually will trust
+            // <rdar://problem/57768490> Octagon: ensure we use appropriate CKKS policy when joining octagon with future policy
+
             [[CKKSAnalytics logger] logResultForEvent:OctagonEventUpgradePreflightPreapprovedJoin hardFailure:true result:error];
             if(error) {
                 secerror("octagon-sos: preflightPreapprovedJoin failed: %@", error);
             [[CKKSAnalytics logger] logResultForEvent:OctagonEventUpgradePreflightPreapprovedJoin hardFailure:true result:error];
             if(error) {
                 secerror("octagon-sos: preflightPreapprovedJoin failed: %@", error);
 {
     WEAKIFY(self);
 
 {
     WEAKIFY(self);
 
-    OTFetchViewsOperation *fetchViews = [[OTFetchViewsOperation alloc] initWithDependencies:self.deps];
-    [self runBeforeGroupFinished:fetchViews];
-
     OTFetchCKKSKeysOperation* fetchKeysOp = [[OTFetchCKKSKeysOperation alloc] initWithDependencies:self.deps];
     OTFetchCKKSKeysOperation* fetchKeysOp = [[OTFetchCKKSKeysOperation alloc] initWithDependencies:self.deps];
-    [fetchKeysOp addDependency:fetchViews];
     [self runBeforeGroupFinished:fetchKeysOp];
     
     secnotice("octagon-sos", "Fetching keys from CKKS");
     [self runBeforeGroupFinished:fetchKeysOp];
     
     secnotice("octagon-sos", "Fetching keys from CKKS");
 
     secnotice("octagon-sos", "Fetching trusted peers from SOS");
 
 
     secnotice("octagon-sos", "Fetching trusted peers from SOS");
 
-    NSError* error = nil;
-    NSSet<id<CKKSRemotePeerProtocol>>* peerSet = [self.deps.sosAdapter fetchTrustedPeers:&error];
+    NSArray<NSData*>* publicSigningSPKIs = nil;
+    if(self.deps.sosAdapter.sosEnabled) {
+        NSError* sosPreapprovalError = nil;
+        publicSigningSPKIs = [OTSOSAdapterHelpers peerPublicSigningKeySPKIsForCircle:self.deps.sosAdapter error:&sosPreapprovalError];
 
 
-    if(!peerSet || error) {
-        secerror("octagon-sos: Can't fetch trusted peers; stopping upgrade: %@", error);
-        self.error = error;
-        self.nextState = OctagonStateBecomeUntrusted;
-        [self runBeforeGroupFinished:self.finishedOp];
-        return;
-    }
+        if(publicSigningSPKIs) {
+            secnotice("octagon-sos", "SOS preapproved keys are %@", publicSigningSPKIs);
+        } else {
+            secnotice("octagon-sos", "Unable to fetch SOS preapproved keys: %@", sosPreapprovalError);
+        }
 
 
-    NSArray<NSData*>* publicSigningSPKIs = [OTSOSActualAdapter peerPublicSigningKeySPKIs:peerSet];
-    secnotice("octagon-sos", "Creating SOS preapproved keys as %@", publicSigningSPKIs);
+    } else {
+        secnotice("octagon-sos", "SOS not enabled; no preapproved keys");
+    }
 
 
-    secnotice("octagon-sos", "Beginning SOS upgrade with %d key sets and %d SOS peers", (int)viewKeySets.count, (int)peerSet.count);
+    secnotice("octagon-sos", "Beginning SOS upgrade with %d key sets and %d SOS peers", (int)viewKeySets.count, (int)publicSigningSPKIs.count);
 
     [self.deps.cuttlefishXPCWrapper attemptPreapprovedJoinWithContainer:self.deps.containerName
                                                                 context:self.deps.contextID
                                                                ckksKeys:viewKeySets
                                                               tlkShares:pendingTLKShares
                                                         preapprovedKeys:publicSigningSPKIs
 
     [self.deps.cuttlefishXPCWrapper attemptPreapprovedJoinWithContainer:self.deps.containerName
                                                                 context:self.deps.contextID
                                                                ckksKeys:viewKeySets
                                                               tlkShares:pendingTLKShares
                                                         preapprovedKeys:publicSigningSPKIs
-                                                                  reply:^(NSString * _Nullable peerID, NSArray<CKRecord*>* keyHierarchyRecords, NSError * _Nullable error) {
-            STRONGIFY(self);
+                                                                  reply:^(NSString * _Nullable peerID,
+                                                                          NSArray<CKRecord*>* keyHierarchyRecords,
+                                                                          NSSet<NSString*>* _Nullable syncingViewList,
+                                                                          TPPolicy* _Nullable syncingPolicy,
+                                                                          NSError * _Nullable error) {
+        STRONGIFY(self);
 
 
-            [[CKKSAnalytics logger] logResultForEvent:OctagonEventUpgradePreapprovedJoin hardFailure:true result:error];
-            if(error) {
-                secerror("octagon-sos: attemptPreapprovedJoin failed: %@", error);
-
-                if ([error isCuttlefishError:CuttlefishErrorKeyHierarchyAlreadyExists]) {
-                    secnotice("octagon-ckks", "A CKKS key hierarchy is out of date; requesting reset");
-                    self.nextState = self.ckksConflictState;
-                } else {
-                    self.error = error;
-                    self.nextState = OctagonStateBecomeUntrusted;
-                }
-                [self runBeforeGroupFinished:self.finishedOp];
-                return;
+        [[CKKSAnalytics logger] logResultForEvent:OctagonEventUpgradePreapprovedJoin hardFailure:true result:error];
+        if(error) {
+            secerror("octagon-sos: attemptPreapprovedJoin failed: %@", error);
+
+            if ([error isCuttlefishError:CuttlefishErrorKeyHierarchyAlreadyExists]) {
+                secnotice("octagon-ckks", "A CKKS key hierarchy is out of date; requesting reset");
+                self.nextState = self.ckksConflictState;
+            } else {
+                self.error = error;
+                self.nextState = OctagonStateBecomeUntrusted;
             }
             }
+            [self runBeforeGroupFinished:self.finishedOp];
+            return;
+        }
 
 
-            [self requestSilentEscrowUpdate];
+        [self requestSilentEscrowUpdate];
 
 
-            secerror("octagon-sos: attemptPreapprovedJoin succeded");
+        secerror("octagon-sos: attemptPreapprovedJoin succeded");
+        [self.deps.viewManager setSyncingViews:syncingViewList sortingPolicy:syncingPolicy];
 
 
-            NSError* localError = nil;
-            BOOL persisted = [self.deps.stateHolder persistAccountChanges:^OTAccountMetadataClassC *  _Nonnull(OTAccountMetadataClassC * _Nonnull metadata) {
-                    metadata.trustState = OTAccountMetadataClassC_TrustState_TRUSTED;
-                    metadata.peerID = peerID;
-                    return metadata;
-                } error:&localError];
+        NSError* localError = nil;
+        BOOL persisted = [self.deps.stateHolder persistAccountChanges:^OTAccountMetadataClassC *  _Nonnull(OTAccountMetadataClassC * _Nonnull metadata) {
+            metadata.trustState = OTAccountMetadataClassC_TrustState_TRUSTED;
+            metadata.peerID = peerID;
+            metadata.syncingViews = [syncingViewList mutableCopy];
+            [metadata setTPPolicy:syncingPolicy];
 
 
-            if(!persisted || localError) {
-                secnotice("octagon-sos", "Couldn't persist results: %@", localError);
-                self.error = localError;
-                self.nextState = OctagonStateError;
-                [self runBeforeGroupFinished:self.finishedOp];
-                return;
-            }
+            return metadata;
+        } error:&localError];
 
 
-            self.nextState = self.intendedState;
+        if(!persisted || localError) {
+            secnotice("octagon-sos", "Couldn't persist results: %@", localError);
+            self.error = localError;
+            self.nextState = OctagonStateError;
+            [self runBeforeGroupFinished:self.finishedOp];
+            return;
+        }
 
 
-            // Tell CKKS about our shiny new records!
-            for (id key in self.deps.viewManager.views) {
-                CKKSKeychainView* view = self.deps.viewManager.views[key];
-                secnotice("octagon-ckks", "Providing ck records (from sos upgrade) to %@", view);
-                [view receiveTLKUploadRecords: keyHierarchyRecords];
-            }
+        self.nextState = self.intendedState;
 
 
-            [self runBeforeGroupFinished:self.finishedOp];
-        }];
+        // Tell CKKS about our shiny new records!
+        for (id key in self.deps.viewManager.views) {
+            CKKSKeychainView* view = self.deps.viewManager.views[key];
+            secnotice("octagon-ckks", "Providing ck records (from sos upgrade) to %@", view);
+            [view receiveTLKUploadRecords: keyHierarchyRecords];
+        }
+
+        [self runBeforeGroupFinished:self.finishedOp];
+    }];
 }
 
 @end
 }
 
 @end
diff --git a/keychain/ot/OTSetCDPBitOperation.h b/keychain/ot/OTSetCDPBitOperation.h
new file mode 100644 (file)
index 0000000..03a5396
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2019 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@
+ */
+
+#if OCTAGON
+
+#import <Foundation/Foundation.h>
+#import "keychain/ckks/CKKSGroupOperation.h"
+#import "keychain/ot/OctagonStateMachineHelpers.h"
+#import "keychain/ot/OTOperationDependencies.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface OTSetCDPBitOperation : CKKSGroupOperation <OctagonStateTransitionOperationProtocol>
+
+- (instancetype)initWithDependencies:(OTOperationDependencies*)dependencies
+                       intendedState:(OctagonState*)intendedState
+                          errorState:(OctagonState*)errorState;
+
+@end
+
+NS_ASSUME_NONNULL_END
+
+#endif // OCTAGON
diff --git a/keychain/ot/OTSetCDPBitOperation.m b/keychain/ot/OTSetCDPBitOperation.m
new file mode 100644 (file)
index 0000000..9b1b359
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2019 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@
+ */
+
+#if OCTAGON
+
+#import "utilities/debugging.h"
+
+#import "keychain/ot/OTSetCDPBitOperation.h"
+#import "keychain/ot/ObjCImprovements.h"
+
+@interface OTSetCDPBitOperation ()
+@property OTOperationDependencies* deps;
+
+@property NSOperation* finishedOp;
+@end
+
+@implementation OTSetCDPBitOperation
+@synthesize intendedState = _intendedState;
+@synthesize nextState = _nextState;
+
+- (instancetype)initWithDependencies:(OTOperationDependencies*)dependencies
+                       intendedState:(OctagonState*)intendedState
+                          errorState:(OctagonState*)errorState
+{
+    if((self = [super init])) {
+        _deps = dependencies;
+        _intendedState = intendedState;
+        _nextState = errorState;
+    }
+    return self;
+}
+
+- (void)groupStart
+{
+    NSError* localError = nil;
+    [self.deps.stateHolder persistAccountChanges:^OTAccountMetadataClassC * _Nonnull(OTAccountMetadataClassC * _Nonnull metadata) {
+        metadata.cdpState = OTAccountMetadataClassC_CDPState_ENABLED;
+        return metadata;
+    } error:&localError];
+
+    if(localError) {
+        secnotice("octagon-cdp-status", "Unable to set CDP bit: %@", localError);
+    } else {
+        secnotice("octagon-cdp-status", "Successfully set CDP bit");
+        self.nextState = self.intendedState;
+    }
+}
+
+@end
+
+#endif // OCTAGON
index ede2cb05312f7a19d94e21081341804dcdffcec9..52cee05b3794758fec6348b13a43ed860e5918b9 100644 (file)
@@ -33,9 +33,11 @@ NS_ASSUME_NONNULL_BEGIN
 //   No iCloud Account (the state machine won't help at all)
 //   Untrusted (user interaction is required to resolve)
 //   WaitForHSA2 (there's some primary icloud account, but it's not HSA2 (yet))
 //   No iCloud Account (the state machine won't help at all)
 //   Untrusted (user interaction is required to resolve)
 //   WaitForHSA2 (there's some primary icloud account, but it's not HSA2 (yet))
+//   WaitForCDP (there's some HSA2 primary icloud account, but it's not CDP-enabled (yet)
 extern OctagonState* const OctagonStateNoAccount;
 extern OctagonState* const OctagonStateUntrusted;
 extern OctagonState* const OctagonStateWaitForHSA2;
 extern OctagonState* const OctagonStateNoAccount;
 extern OctagonState* const OctagonStateUntrusted;
 extern OctagonState* const OctagonStateWaitForHSA2;
+extern OctagonState* const OctagonStateWaitForCDP;
 
 // Entering this state will mark down that the device is untrusted, then go to OctagonStateUntrusted
 extern OctagonState* const OctagonStateBecomeUntrusted;
 
 // Entering this state will mark down that the device is untrusted, then go to OctagonStateUntrusted
 extern OctagonState* const OctagonStateBecomeUntrusted;
@@ -50,6 +52,9 @@ extern OctagonState* const OctagonStateReady;
 // This state runs any final preparation to enter the Ready state
 extern OctagonState* const OctagonStateBecomeReady;
 
 // This state runs any final preparation to enter the Ready state
 extern OctagonState* const OctagonStateBecomeReady;
 
+// BecomeReady might go here, if it's not actually ready
+extern OctagonState* const OctagonStateRefetchCKKSPolicy;
+
 // Enter this state if you'd like the state machine to double-check everything
 extern OctagonState* const OctagonStateEnsureConsistency;
 extern OctagonState* const OctagonStateEnsureOctagonKeysAreConsistent;
 // Enter this state if you'd like the state machine to double-check everything
 extern OctagonState* const OctagonStateEnsureConsistency;
 extern OctagonState* const OctagonStateEnsureOctagonKeysAreConsistent;
@@ -59,23 +64,25 @@ extern OctagonState* const OctagonStateEnsureUpdatePreapprovals;
 extern OctagonState* const OctagonStateInitializing;
 extern OctagonState* const OctagonStateWaitingForCloudKitAccount;
 extern OctagonState* const OctagonStateCloudKitNewlyAvailable;
 extern OctagonState* const OctagonStateInitializing;
 extern OctagonState* const OctagonStateWaitingForCloudKitAccount;
 extern OctagonState* const OctagonStateCloudKitNewlyAvailable;
+extern OctagonState* const OctagonStateDetermineCDPState;
 extern OctagonState* const OctagonStateCheckTrustState;
 
 /*Piggybacking and ProximitySetup as Initiator, Octagon only*/
 extern OctagonState* const OctagonStateInitiatorAwaitingVoucher;
 
 extern OctagonState* const OctagonStateCheckTrustState;
 
 /*Piggybacking and ProximitySetup as Initiator, Octagon only*/
 extern OctagonState* const OctagonStateInitiatorAwaitingVoucher;
 
+extern OctagonState* const OctagonStateInitiatorSetCDPBit;
 extern OctagonState* const OctagonStateInitiatorUpdateDeviceList;
 extern OctagonState* const OctagonStateInitiatorJoin;
 extern OctagonState* const OctagonStateInitiatorJoinCKKSReset;
 extern OctagonState* const OctagonStateInitiatorJoinAfterCKKSReset;
 
 extern OctagonState* const OctagonStateInitiatorUpdateDeviceList;
 extern OctagonState* const OctagonStateInitiatorJoin;
 extern OctagonState* const OctagonStateInitiatorJoinCKKSReset;
 extern OctagonState* const OctagonStateInitiatorJoinAfterCKKSReset;
 
-extern OctagonState* const OctagonStateInitiatorVouchWithBottle;
+extern OctagonState* const OctagonStateBottleJoinVouchWithBottle;
 extern OctagonState* const OctagonStateIdentityPrepared;
 // OctagonStateIdentityPrepared leads directly to
 extern OctagonState* const OctagonStateDeviceListUpdated;
 
 /* used for join with bottle */
 extern OctagonState* const OctagonStateIdentityPrepared;
 // OctagonStateIdentityPrepared leads directly to
 extern OctagonState* const OctagonStateDeviceListUpdated;
 
 /* used for join with bottle */
-extern OctagonState* const OctagonStateInitiatorCreateIdentity;
+extern OctagonState* const OctagonStateBottleJoinCreateIdentity;
 
 /* used for join with recovery key */
 extern OctagonState* const OctagonStateCreateIdentityForRecoveryKey;
 
 /* used for join with recovery key */
 extern OctagonState* const OctagonStateCreateIdentityForRecoveryKey;
@@ -87,6 +94,7 @@ extern OctagonState* const OctagonStateVouchWithRecoveryKey;
 extern OctagonState* const OctagonStateResetBecomeUntrusted;
 extern OctagonState* const OctagonStateResetAndEstablish;
 extern OctagonState* const OctagonStateResetAnyMissingTLKCKKSViews;
 extern OctagonState* const OctagonStateResetBecomeUntrusted;
 extern OctagonState* const OctagonStateResetAndEstablish;
 extern OctagonState* const OctagonStateResetAnyMissingTLKCKKSViews;
+extern OctagonState* const OctagonStateEstablishEnableCDPBit;
 extern OctagonState* const OctagonStateReEnactDeviceList;
 extern OctagonState* const OctagonStateReEnactPrepare;
 extern OctagonState* const OctagonStateReEnactReadyToEstablish;
 extern OctagonState* const OctagonStateReEnactDeviceList;
 extern OctagonState* const OctagonStateReEnactPrepare;
 extern OctagonState* const OctagonStateReEnactReadyToEstablish;
@@ -96,6 +104,7 @@ extern OctagonState* const OctagonStateEstablishAfterCKKSReset;
 
 /* used for trust health checks */
 extern OctagonState* const OctagonStateHSA2HealthCheck;
 
 /* used for trust health checks */
 extern OctagonState* const OctagonStateHSA2HealthCheck;
+extern OctagonState* const OctagonStateCDPHealthCheck;
 extern OctagonState* const OctagonStateSecurityTrustCheck;
 extern OctagonState* const OctagonStateTPHTrustCheck;
 extern OctagonState* const OctagonStateCuttlefishTrustCheck;
 extern OctagonState* const OctagonStateSecurityTrustCheck;
 extern OctagonState* const OctagonStateTPHTrustCheck;
 extern OctagonState* const OctagonStateCuttlefishTrustCheck;
@@ -104,6 +113,9 @@ extern OctagonState* const OctagonStateHealthCheckReset;
 
 // End of account reset state flow
 
 
 // End of account reset state flow
 
+//Leave Clique
+extern OctagonState* const OctagonStateHealthCheckLeaveClique;
+
 // Part of the signout flow
 extern OctagonState* const OctagonStateNoAccountDoReset;
 //
 // Part of the signout flow
 extern OctagonState* const OctagonStateNoAccountDoReset;
 //
@@ -131,6 +143,9 @@ extern OctagonState* const OctagonStateAssistCKKSTLKUploadAfterCKKSReset;
 // Call out to otpaird (KCPairing via IDS), then proceed to BecomeUntrusted
 extern OctagonState* const OctagonStateStartCompanionPairing;
 
 // Call out to otpaird (KCPairing via IDS), then proceed to BecomeUntrusted
 extern OctagonState* const OctagonStateStartCompanionPairing;
 
+// Cuttlefish notification while waiting for CDP
+extern OctagonState* const OctagonStateWaitForCDPUpdated;
+
 // Untrusted cuttlefish notification.
 extern OctagonState* const OctagonStateUntrustedUpdated;
 
 // Untrusted cuttlefish notification.
 extern OctagonState* const OctagonStateUntrustedUpdated;
 
@@ -163,6 +178,7 @@ extern OctagonFlag* const OctagonFlagCuttlefishNotification NS_SWIFT_NAME(Octago
 extern OctagonFlag* const OctagonFlagFetchAuthKitMachineIDList;
 
 extern OctagonFlag* const OctagonFlagAccountIsAvailable;
 extern OctagonFlag* const OctagonFlagFetchAuthKitMachineIDList;
 
 extern OctagonFlag* const OctagonFlagAccountIsAvailable;
+extern OctagonFlag* const OctagonFlagCDPEnabled;
 
 extern OctagonFlag* const OctagonFlagAttemptSOSUpgrade;
 extern OctagonFlag* const OctagonFlagUnlocked;
 
 extern OctagonFlag* const OctagonFlagAttemptSOSUpgrade;
 extern OctagonFlag* const OctagonFlagUnlocked;
index 5af0ffb4ab082d9bee795558908de4178e5f3766..5db76711fa2a93c57ac4b4402e2c4fe9e5b2170c 100644 (file)
@@ -33,6 +33,7 @@
 OctagonState* const OctagonStateNoAccount = (OctagonState*) @"no_account";
 
 OctagonState* const OctagonStateWaitForHSA2 = (OctagonState*) @"wait_for_hsa2";
 OctagonState* const OctagonStateNoAccount = (OctagonState*) @"no_account";
 
 OctagonState* const OctagonStateWaitForHSA2 = (OctagonState*) @"wait_for_hsa2";
+OctagonState* const OctagonStateWaitForCDP = (OctagonState*) @"wait_for_cdp_enable";
 
 OctagonState* const OctagonStateUntrusted = (OctagonState*) @"untrusted";
 OctagonState* const OctagonStateBecomeUntrusted = (OctagonState*) @"become_untrusted";
 
 OctagonState* const OctagonStateUntrusted = (OctagonState*) @"untrusted";
 OctagonState* const OctagonStateBecomeUntrusted = (OctagonState*) @"become_untrusted";
@@ -47,11 +48,14 @@ OctagonState* const OctagonStateEnsureUpdatePreapprovals = (OctagonState*)@"ensu
 OctagonState* const OctagonStateInitializing = (OctagonState*) @"initializing";
 OctagonState* const OctagonStateWaitingForCloudKitAccount = (OctagonState*) @"waiting_for_cloudkit_account";
 OctagonState* const OctagonStateCloudKitNewlyAvailable = (OctagonState*) @"account_newly_available";
 OctagonState* const OctagonStateInitializing = (OctagonState*) @"initializing";
 OctagonState* const OctagonStateWaitingForCloudKitAccount = (OctagonState*) @"waiting_for_cloudkit_account";
 OctagonState* const OctagonStateCloudKitNewlyAvailable = (OctagonState*) @"account_newly_available";
+OctagonState* const OctagonStateRefetchCKKSPolicy = (OctagonState*) @"ckks_fetch_policy";
+OctagonState* const OctagonStateDetermineCDPState = (OctagonState*) @"check_cdp_state";
 OctagonState* const OctagonStateCheckTrustState = (OctagonState*) @"check_trust_state";
 
 OctagonState* const OctagonStateUpdateSOSPreapprovals = (OctagonState*) @"update_sos_preapprovals";
 
 /*Piggybacking and ProximitySetup as Initiator Octagon only*/
 OctagonState* const OctagonStateCheckTrustState = (OctagonState*) @"check_trust_state";
 
 OctagonState* const OctagonStateUpdateSOSPreapprovals = (OctagonState*) @"update_sos_preapprovals";
 
 /*Piggybacking and ProximitySetup as Initiator Octagon only*/
+OctagonState* const OctagonStateInitiatorSetCDPBit = (OctagonState*) @"initiator_set_cdp";
 OctagonState* const OctagonStateInitiatorUpdateDeviceList = (OctagonState*) @"initiator_device_list_update";
 OctagonState* const OctagonStateInitiatorAwaitingVoucher = (OctagonState*)@"await_voucher";
 OctagonState* const OctagonStateInitiatorJoin = (OctagonState*)@"join";
 OctagonState* const OctagonStateInitiatorUpdateDeviceList = (OctagonState*) @"initiator_device_list_update";
 OctagonState* const OctagonStateInitiatorAwaitingVoucher = (OctagonState*)@"await_voucher";
 OctagonState* const OctagonStateInitiatorJoin = (OctagonState*)@"join";
@@ -59,8 +63,8 @@ OctagonState* const OctagonStateInitiatorJoinCKKSReset = (OctagonState*)@"join_c
 OctagonState* const OctagonStateInitiatorJoinAfterCKKSReset = (OctagonState*)@"join_after_ckks_reset";
 
 /* used in restore (join with bottle)*/
 OctagonState* const OctagonStateInitiatorJoinAfterCKKSReset = (OctagonState*)@"join_after_ckks_reset";
 
 /* used in restore (join with bottle)*/
-OctagonState* const OctagonStateInitiatorCreateIdentity = (OctagonState*)@"create_identity";
-OctagonState* const OctagonStateInitiatorVouchWithBottle = (OctagonState*)@"vouchWithBottle";
+OctagonState* const OctagonStateBottleJoinCreateIdentity = (OctagonState*)@"bottle_join_create_identity";
+OctagonState* const OctagonStateBottleJoinVouchWithBottle = (OctagonState*)@"bottle_join_vouch_with_bottle";
 OctagonState* const OctagonStateCreateIdentityForRecoveryKey = (OctagonState*)@"vouchWithRecovery";
 
 /* used in resotre (join with recovery key)*/
 OctagonState* const OctagonStateCreateIdentityForRecoveryKey = (OctagonState*)@"vouchWithRecovery";
 
 /* used in resotre (join with recovery key)*/
@@ -68,6 +72,8 @@ OctagonState* const OctagonStateVouchWithRecoveryKey = (OctagonState*)@"vouchWit
 
 OctagonState* const OctagonStateStartCompanionPairing = (OctagonState*)@"start_companion_pairing";
 
 
 OctagonState* const OctagonStateStartCompanionPairing = (OctagonState*)@"start_companion_pairing";
 
+OctagonState* const OctagonStateWaitForCDPUpdated = (OctagonState*)@"wait_for_cdp_update";
+
 // Untrusted cuttlefish notification.
 OctagonState* const OctagonStateUntrustedUpdated = (OctagonState*)@"untrusted_update";
 
 // Untrusted cuttlefish notification.
 OctagonState* const OctagonStateUntrustedUpdated = (OctagonState*)@"untrusted_update";
 
@@ -87,6 +93,7 @@ OctagonState* const OctagonStateUnimplemented = (OctagonState*) @"unimplemented"
 OctagonState* const OctagonStateResetBecomeUntrusted = (OctagonState*) @"reset_become_untrusted";
 OctagonState* const OctagonStateResetAndEstablish = (OctagonState*) @"reset_and_establish";
 OctagonState* const OctagonStateResetAnyMissingTLKCKKSViews = (OctagonState*) @"reset_ckks_missing_views";
 OctagonState* const OctagonStateResetBecomeUntrusted = (OctagonState*) @"reset_become_untrusted";
 OctagonState* const OctagonStateResetAndEstablish = (OctagonState*) @"reset_and_establish";
 OctagonState* const OctagonStateResetAnyMissingTLKCKKSViews = (OctagonState*) @"reset_ckks_missing_views";
+OctagonState* const OctagonStateEstablishEnableCDPBit = (OctagonState*) @"reenact_cdp_bit";
 OctagonState* const OctagonStateReEnactDeviceList = (OctagonState*) @"reenact_device_list";
 OctagonState* const OctagonStateReEnactPrepare = (OctagonState*) @"reenact_prepare";
 OctagonState* const OctagonStateReEnactReadyToEstablish = (OctagonState*) @"reenact_ready_to_establish";
 OctagonState* const OctagonStateReEnactDeviceList = (OctagonState*) @"reenact_device_list";
 OctagonState* const OctagonStateReEnactPrepare = (OctagonState*) @"reenact_prepare";
 OctagonState* const OctagonStateReEnactReadyToEstablish = (OctagonState*) @"reenact_ready_to_establish";
@@ -95,6 +102,7 @@ OctagonState* const OctagonStateEstablishAfterCKKSReset = (OctagonState*) @"reen
 
 /* used for trust health checks */
 OctagonState* const OctagonStateHSA2HealthCheck = (OctagonState*) @"health_hsa2_check";
 
 /* used for trust health checks */
 OctagonState* const OctagonStateHSA2HealthCheck = (OctagonState*) @"health_hsa2_check";
+OctagonState* const OctagonStateCDPHealthCheck = (OctagonState*) @"health_cdp_check";
 OctagonState* const OctagonStateTPHTrustCheck = (OctagonState*) @"tph_trust_check";
 OctagonState* const OctagonStateCuttlefishTrustCheck = (OctagonState*) @"cuttlefish_trust_check";
 OctagonState* const OctagonStatePostRepairCFU = (OctagonState*) @"post_repair_cfu";
 OctagonState* const OctagonStateTPHTrustCheck = (OctagonState*) @"tph_trust_check";
 OctagonState* const OctagonStateCuttlefishTrustCheck = (OctagonState*) @"cuttlefish_trust_check";
 OctagonState* const OctagonStatePostRepairCFU = (OctagonState*) @"post_repair_cfu";
@@ -109,6 +117,8 @@ OctagonState* const OctagonStateAssistCKKSTLKUpload = (OctagonState*) @"assist_c
 OctagonState* const OctagonStateAssistCKKSTLKUploadCKKSReset = (OctagonState*) @"assist_ckks_tlk_upload_ckks_reset";
 OctagonState* const OctagonStateAssistCKKSTLKUploadAfterCKKSReset = (OctagonState*) @"assist_ckks_tlk_upload_after_ckks_reset";
 
 OctagonState* const OctagonStateAssistCKKSTLKUploadCKKSReset = (OctagonState*) @"assist_ckks_tlk_upload_ckks_reset";
 OctagonState* const OctagonStateAssistCKKSTLKUploadAfterCKKSReset = (OctagonState*) @"assist_ckks_tlk_upload_after_ckks_reset";
 
+OctagonState* const OctagonStateHealthCheckLeaveClique = (OctagonState*) @"leave_clique";
+
 /* escrow */
 OctagonState* const OctagonStateEscrowTriggerUpdate = (OctagonState*) @"escrow-trigger-update";
 
 /* escrow */
 OctagonState* const OctagonStateEscrowTriggerUpdate = (OctagonState*) @"escrow-trigger-update";
 
@@ -145,8 +155,8 @@ NSDictionary<OctagonState*, NSNumber*>* OctagonStateMap(void) {
                 OctagonStateReEnactPrepare:                     @14U,
                 OctagonStateReEnactReadyToEstablish:            @15U,
                 OctagonStateNoAccountDoReset:                   @16U,
                 OctagonStateReEnactPrepare:                     @14U,
                 OctagonStateReEnactReadyToEstablish:            @15U,
                 OctagonStateNoAccountDoReset:                   @16U,
-                OctagonStateInitiatorVouchWithBottle:           @17U,
-                OctagonStateInitiatorCreateIdentity:            @18U,
+                OctagonStateBottleJoinVouchWithBottle:          @17U,
+                OctagonStateBottleJoinCreateIdentity:           @18U,
                 OctagonStateCloudKitNewlyAvailable:             @19U,
                 OctagonStateCheckTrustState:                    @20U,
                 OctagonStateBecomeUntrusted:                    @21U,
                 OctagonStateCloudKitNewlyAvailable:             @19U,
                 OctagonStateCheckTrustState:                    @20U,
                 OctagonStateBecomeUntrusted:                    @21U,
@@ -181,6 +191,14 @@ NSDictionary<OctagonState*, NSNumber*>* OctagonStateMap(void) {
                 OctagonStateHealthCheckReset:                   @50U,
                 OctagonStateAssistCKKSTLKUploadCKKSReset:       @51U,
                 OctagonStateAssistCKKSTLKUploadAfterCKKSReset:  @52U,
                 OctagonStateHealthCheckReset:                   @50U,
                 OctagonStateAssistCKKSTLKUploadCKKSReset:       @51U,
                 OctagonStateAssistCKKSTLKUploadAfterCKKSReset:  @52U,
+                OctagonStateWaitForCDP:                         @53U,
+                OctagonStateDetermineCDPState:                  @54U,
+                OctagonStateWaitForCDPUpdated:                  @55U,
+                OctagonStateEstablishEnableCDPBit:              @56U,
+                OctagonStateInitiatorSetCDPBit:                 @57U,
+                OctagonStateCDPHealthCheck:                     @58U,
+                OctagonStateHealthCheckLeaveClique:             @59U,
+                OctagonStateRefetchCKKSPolicy:                  @60U,
             };
     });
     return map;
             };
     });
     return map;
@@ -230,6 +248,7 @@ NSSet<OctagonState *>* OctagonHealthSourceStates(void)
         [sourceStates addObject:OctagonStateUntrusted];
         [sourceStates addObject:OctagonStateWaitForHSA2];
         [sourceStates addObject:OctagonStateWaitForUnlock];
         [sourceStates addObject:OctagonStateUntrusted];
         [sourceStates addObject:OctagonStateWaitForHSA2];
         [sourceStates addObject:OctagonStateWaitForUnlock];
+        [sourceStates addObject:OctagonStateWaitForCDP];
 
         s = sourceStates;
     });
 
         s = sourceStates;
     });
@@ -242,6 +261,7 @@ OctagonFlag* const OctagonFlagEgoPeerPreapproved = (OctagonFlag*) @"preapproved"
 OctagonFlag* const OctagonFlagCKKSRequestsTLKUpload  = (OctagonFlag*) @"tlk_upload_needed";
 OctagonFlag* const OctagonFlagCuttlefishNotification = (OctagonFlag*) @"recd_push";
 OctagonFlag* const OctagonFlagAccountIsAvailable = (OctagonFlag*)@"account_available";
 OctagonFlag* const OctagonFlagCKKSRequestsTLKUpload  = (OctagonFlag*) @"tlk_upload_needed";
 OctagonFlag* const OctagonFlagCuttlefishNotification = (OctagonFlag*) @"recd_push";
 OctagonFlag* const OctagonFlagAccountIsAvailable = (OctagonFlag*)@"account_available";
+OctagonFlag* const OctagonFlagCDPEnabled = (OctagonFlag*) @"cdp_enabled";
 OctagonFlag* const OctagonFlagAttemptSOSUpgrade = (OctagonFlag*)@"attempt_sos_upgrade";
 OctagonFlag* const OctagonFlagFetchAuthKitMachineIDList = (OctagonFlag*)@"attempt_machine_id_list";
 OctagonFlag* const OctagonFlagUnlocked = (OctagonFlag*)@"unlocked";
 OctagonFlag* const OctagonFlagAttemptSOSUpgrade = (OctagonFlag*)@"attempt_sos_upgrade";
 OctagonFlag* const OctagonFlagFetchAuthKitMachineIDList = (OctagonFlag*)@"attempt_machine_id_list";
 OctagonFlag* const OctagonFlagUnlocked = (OctagonFlag*)@"unlocked";
@@ -261,6 +281,7 @@ NSSet<OctagonFlag *>* AllOctagonFlags(void)
         [flags addObject:OctagonFlagCKKSRequestsTLKUpload];
         [flags addObject:OctagonFlagCuttlefishNotification];
         [flags addObject:OctagonFlagAccountIsAvailable];
         [flags addObject:OctagonFlagCKKSRequestsTLKUpload];
         [flags addObject:OctagonFlagCuttlefishNotification];
         [flags addObject:OctagonFlagAccountIsAvailable];
+        [flags addObject:OctagonFlagCDPEnabled];
         [flags addObject:OctagonFlagAttemptSOSUpgrade];
         [flags addObject:OctagonFlagFetchAuthKitMachineIDList];
         [flags addObject:OctagonFlagUnlocked];
         [flags addObject:OctagonFlagAttemptSOSUpgrade];
         [flags addObject:OctagonFlagFetchAuthKitMachineIDList];
         [flags addObject:OctagonFlagUnlocked];
index 6dbc7022576317f9f08b2b34efd45861c81d18d2..18999f229ea267b27910757493dcc535480da97a 100644 (file)
@@ -15,6 +15,7 @@ NS_ASSUME_NONNULL_BEGIN
 
 - (instancetype)initWithDependencies:(OTOperationDependencies*)dependencies
                        intendedState:(OctagonState*)intendedState
 
 - (instancetype)initWithDependencies:(OTOperationDependencies*)dependencies
                        intendedState:(OctagonState*)intendedState
+                    peerUnknownState:(OctagonState*)peerUnknownState
                           errorState:(OctagonState*)errorState
                            retryFlag:(OctagonFlag* _Nullable)retryFlag;
 @end
                           errorState:(OctagonState*)errorState
                            retryFlag:(OctagonFlag* _Nullable)retryFlag;
 @end
index 5a24b829e845ba4191a5c13dad1c6c9f6380f634..c31589f0fa212ac22755494fcfbcfbe4b0aedf32 100644 (file)
@@ -18,6 +18,8 @@
 @interface OTUpdateTPHOperation ()
 @property OTOperationDependencies* deps;
 
 @interface OTUpdateTPHOperation ()
 @property OTOperationDependencies* deps;
 
+@property OctagonState* peerUnknownState;
+
 @property NSOperation* finishedOp;
 
 @property (nullable) OctagonFlag* retryFlag;
 @property NSOperation* finishedOp;
 
 @property (nullable) OctagonFlag* retryFlag;
@@ -29,6 +31,7 @@
 
 - (instancetype)initWithDependencies:(OTOperationDependencies*)dependencies
                        intendedState:(OctagonState*)intendedState
 
 - (instancetype)initWithDependencies:(OTOperationDependencies*)dependencies
                        intendedState:(OctagonState*)intendedState
+                    peerUnknownState:(OctagonState*)peerUnknownState
                           errorState:(OctagonState*)errorState
                            retryFlag:(OctagonFlag* _Nullable)retryFlag
 {
                           errorState:(OctagonState*)errorState
                            retryFlag:(OctagonFlag* _Nullable)retryFlag
 {
@@ -37,6 +40,7 @@
 
         _intendedState = intendedState;
         _nextState = errorState;
 
         _intendedState = intendedState;
         _nextState = errorState;
+        _peerUnknownState = peerUnknownState;
 
         _retryFlag = retryFlag;
     }
 
         _retryFlag = retryFlag;
     }
                 self.nextState = OctagonStateBecomeUntrusted;
 
             } else if(peerState.peerStatus & TPPeerStatusUnknown) {
                 self.nextState = OctagonStateBecomeUntrusted;
 
             } else if(peerState.peerStatus & TPPeerStatusUnknown) {
-                secnotice("octagon", "Self peer (%@) is unknown; moving to untrusted", peerState.peerID);
-                self.nextState = OctagonStateBecomeUntrusted;
+                secnotice("octagon", "Self peer (%@) is unknown; moving to '%@''", peerState.peerID, self.peerUnknownState);
+                self.nextState = self.peerUnknownState;
 
             } else {
                 self.nextState = self.intendedState;
 
             } else {
                 self.nextState = self.intendedState;
index 5f8a8763ee8a5d854d5899bf8f518b3fd8c8a66d..0e6cb278527e6f436e0270c8721765d3d888b44e 100644 (file)
     }];
     [self dependOnBeforeGroupFinished:self.finishedOp];
 
     }];
     [self dependOnBeforeGroupFinished:self.finishedOp];
 
+    NSError* localError = nil;
+    BOOL isAccountDemo = [self.deps.authKitAdapter accountIsDemoAccount:&localError];
+    if(localError) {
+        secerror("octagon-authkit: failed to fetch demo account flag: %@", localError);
+    }
+
     [self.deps.authKitAdapter fetchCurrentDeviceList:^(NSSet<NSString *> * _Nullable machineIDs, NSError * _Nullable error) {
         STRONGIFY(self);
         if(!machineIDs || error) {
     [self.deps.authKitAdapter fetchCurrentDeviceList:^(NSSet<NSString *> * _Nullable machineIDs, NSError * _Nullable error) {
         STRONGIFY(self);
         if(!machineIDs || error) {
             if (self.logForUpgrade) {
                 [[CKKSAnalytics logger] logSuccessForEventNamed:OctagonEventUpgradeFetchDeviceIDs];
             }
             if (self.logForUpgrade) {
                 [[CKKSAnalytics logger] logSuccessForEventNamed:OctagonEventUpgradeFetchDeviceIDs];
             }
-            [self afterAuthKitFetch:machineIDs];
+            [self afterAuthKitFetch:machineIDs accountIsDemo:isAccountDemo];
         }
     }];
 }
 
         }
     }];
 }
 
-- (void)afterAuthKitFetch:(NSSet<NSString *>*)allowedMachineIDs
+- (void)afterAuthKitFetch:(NSSet<NSString *>*)allowedMachineIDs accountIsDemo:(BOOL)accountIsDemo
 {
     WEAKIFY(self);
 {
     WEAKIFY(self);
+    BOOL honorIDMSListChanges = accountIsDemo ? NO : YES;
+
     [self.deps.cuttlefishXPCWrapper setAllowedMachineIDsWithContainer:self.deps.containerName
                                                               context:self.deps.contextID
                                                     allowedMachineIDs:allowedMachineIDs
     [self.deps.cuttlefishXPCWrapper setAllowedMachineIDsWithContainer:self.deps.containerName
                                                               context:self.deps.contextID
                                                     allowedMachineIDs:allowedMachineIDs
+                                                        honorIDMSListChanges:honorIDMSListChanges
                                                                 reply:^(BOOL listDifferences, NSError * _Nullable error) {
             STRONGIFY(self);
 
                                                                 reply:^(BOOL listDifferences, NSError * _Nullable error) {
             STRONGIFY(self);
 
index f54308046aae625c1cdf1927e7b9fc7a81da0f73..5c74b845e008e71f619863b04cb38a006ca89850 100644 (file)
@@ -51,7 +51,8 @@
     NSMutableSet<CKKSKeychainView*>* viewsToUpload = [NSMutableSet set];
 
     // One (or more) of our sub-CKKSes believes it needs to upload new TLKs.
     NSMutableSet<CKKSKeychainView*>* viewsToUpload = [NSMutableSet set];
 
     // One (or more) of our sub-CKKSes believes it needs to upload new TLKs.
-    for(CKKSKeychainView* view in [self.deps.viewManager currentViews]) {
+    CKKSViewManager* viewManager = self.deps.viewManager;
+    for(CKKSKeychainView* view in viewManager.currentViews) {
         if([view.keyHierarchyState isEqualToString:SecCKKSZoneKeyStateWaitForTLKUpload] ||
            [view.keyHierarchyState isEqualToString:SecCKKSZoneKeyStateWaitForTLKCreation]) {
             secnotice("octagon-ckks", "CKKS view %@ needs TLK uploads!", view);
         if([view.keyHierarchyState isEqualToString:SecCKKSZoneKeyStateWaitForTLKUpload] ||
            [view.keyHierarchyState isEqualToString:SecCKKSZoneKeyStateWaitForTLKCreation]) {
             secnotice("octagon-ckks", "CKKS view %@ needs TLK uploads!", view);
index 48a381bcb1bc04cd999688d94eeda8247f075033..d47c452d62cef4191d68547b039fb3271ae4f8ce 100644 (file)
         }
     }
 
         }
     }
 
-    WEAKIFY(self);
-
-    // After a vouch, we also want to acquire all TLKs that the bottled peer might have had
-    OTFetchCKKSKeysOperation* fetchKeysOp = [[OTFetchCKKSKeysOperation alloc] initWithDependencies:self.deps];
-    [self runBeforeGroupFinished:fetchKeysOp];
-
-    CKKSResultOperation* proceedWithKeys = [CKKSResultOperation named:@"bottle-tlks"
-                                                            withBlock:^{
-                                                                STRONGIFY(self);
-                                                                [self proceedWithKeys:fetchKeysOp.viewKeySets tlkShares:fetchKeysOp.tlkShares];
-                                                            }];
-
-    [proceedWithKeys addDependency:fetchKeysOp];
-    [self runBeforeGroupFinished:proceedWithKeys];
-}
-
-- (void)proceedWithKeys:(NSArray<CKKSKeychainBackedKeySet*>*)viewKeySets tlkShares:(NSArray<CKKSTLKShare*>*)tlkShares
-{
     // Preflight the vouch: this will tell us the peerID of the recovering peer.
     // Then, filter the tlkShares array to include only tlks sent to that peer.
     WEAKIFY(self);
     [self.deps.cuttlefishXPCWrapper preflightVouchWithBottleWithContainer:self.deps.containerName
                                                                   context:self.deps.contextID
                                                                  bottleID:self.bottleID
     // Preflight the vouch: this will tell us the peerID of the recovering peer.
     // Then, filter the tlkShares array to include only tlks sent to that peer.
     WEAKIFY(self);
     [self.deps.cuttlefishXPCWrapper preflightVouchWithBottleWithContainer:self.deps.containerName
                                                                   context:self.deps.contextID
                                                                  bottleID:self.bottleID
-                                                                    reply:^(NSString * _Nullable peerID, NSError * _Nullable error) {
+                                                                    reply:^(NSString * _Nullable peerID,
+                                                                            NSSet<NSString*>* peerSyncingViews,
+                                                                            TPPolicy* peerSyncingPolicy,
+                                                                            NSError * _Nullable error) {
         STRONGIFY(self);
         [[CKKSAnalytics logger] logResultForEvent:OctagonEventPreflightVouchWithBottle hardFailure:true result:error];
 
         STRONGIFY(self);
         [[CKKSAnalytics logger] logResultForEvent:OctagonEventPreflightVouchWithBottle hardFailure:true result:error];
 
-        if(error){
+        if(error || !peerID) {
             secerror("octagon: Error preflighting voucher using bottle: %@", error);
             self.error = error;
             [self runBeforeGroupFinished:self.finishedOp];
             secerror("octagon: Error preflighting voucher using bottle: %@", error);
             self.error = error;
             [self runBeforeGroupFinished:self.finishedOp];
 
         secnotice("octagon", "Bottle %@ is for peerID %@", self.bottleID, peerID);
 
 
         secnotice("octagon", "Bottle %@ is for peerID %@", self.bottleID, peerID);
 
+        // Tell CKKS to spin up the new views and policy
+        // But, do not persist this view set! We'll do that when we actually manager to join
+        [self.deps.viewManager setSyncingViews:peerSyncingViews sortingPolicy:peerSyncingPolicy];
+
+        [self proceedWithPeerID:peerID];
+    }];
+}
+
+- (void)proceedWithPeerID:(NSString*)peerID
+{
+    WEAKIFY(self);
+
+    // After a vouch, we also want to acquire all TLKs that the bottled peer might have had
+    OTFetchCKKSKeysOperation* fetchKeysOp = [[OTFetchCKKSKeysOperation alloc] initWithDependencies:self.deps];
+    [self runBeforeGroupFinished:fetchKeysOp];
+
+    CKKSResultOperation* proceedWithKeys = [CKKSResultOperation named:@"bottle-tlks"
+                                                            withBlock:^{
+        STRONGIFY(self);
+
         NSMutableArray<CKKSTLKShare*>* filteredTLKShares = [NSMutableArray array];
         NSMutableArray<CKKSTLKShare*>* filteredTLKShares = [NSMutableArray array];
-        for(CKKSTLKShare* share in tlkShares) {
+        for(CKKSTLKShare* share in fetchKeysOp.tlkShares) {
             // If we didn't get a peerID, just pass every tlkshare and hope for the best
             if(peerID == nil || [share.receiverPeerID isEqualToString:peerID]) {
                 [filteredTLKShares addObject:share];
             }
         }
 
             // If we didn't get a peerID, just pass every tlkshare and hope for the best
             if(peerID == nil || [share.receiverPeerID isEqualToString:peerID]) {
                 [filteredTLKShares addObject:share];
             }
         }
 
-        [self proceedWithKeys:viewKeySets filteredTLKShares:filteredTLKShares];
+        [self proceedWithKeys:fetchKeysOp.viewKeySets filteredTLKShares:filteredTLKShares];
     }];
     }];
+
+    [proceedWithKeys addDependency:fetchKeysOp];
+    [self runBeforeGroupFinished:proceedWithKeys];
+}
+
+
+- (void)noteMetric:(NSString*)metric count:(int64_t)count
+{
+    NSString* metricName = [NSString stringWithFormat:@"%@%lld", metric, count];
+
+    [[CKKSAnalytics logger] logResultForEvent:metricName
+                                  hardFailure:NO
+                                       result:nil];
+
+    [[CKKSAnalytics logger] setDateProperty:[NSDate date] forKey:metricName];
+    [[CKKSAnalytics logger] setNumberProperty:[[NSNumber alloc]initWithLong:count] forKey:metric];
 }
 
 - (void)proceedWithKeys:(NSArray<CKKSKeychainBackedKeySet*>*)viewKeySets filteredTLKShares:(NSArray<CKKSTLKShare*>*)tlkShares
 }
 
 - (void)proceedWithKeys:(NSArray<CKKSKeychainBackedKeySet*>*)viewKeySets filteredTLKShares:(NSArray<CKKSTLKShare*>*)tlkShares
                                                          entropy:self.entropy
                                                       bottleSalt:self.bottleSalt
                                                        tlkShares:tlkShares
                                                          entropy:self.entropy
                                                       bottleSalt:self.bottleSalt
                                                        tlkShares:tlkShares
-                                                           reply:^(NSData * _Nullable voucher, NSData * _Nullable voucherSig, NSError * _Nullable error) {
+                                                           reply:^(NSData * _Nullable voucher,
+                                                                   NSData * _Nullable voucherSig,
+                                                                   int64_t uniqueTLKsRecovered,
+                                                                   int64_t totalTLKSharesRecovered,
+                                                                   NSError * _Nullable error) {
             STRONGIFY(self);
             [[CKKSAnalytics logger] logResultForEvent:OctagonEventVoucherWithBottle hardFailure:true result:error];
 
             STRONGIFY(self);
             [[CKKSAnalytics logger] logResultForEvent:OctagonEventVoucherWithBottle hardFailure:true result:error];
 
                 return;
             }
 
                 return;
             }
 
+            //collect TLK count metrics
+            [self noteMetric:OctagonAnalyticsBottledUniqueTLKsRecovered count:uniqueTLKsRecovered];
+            [self noteMetric:OctagonAnalyticsBottledTotalTLKSharesRecovered count:totalTLKSharesRecovered];
+            [self noteMetric:OctagonAnalyticsBottledTotalTLKShares count:tlkShares.count];
+
+            NSMutableSet<NSString*>* uniqueTLKsWithShares = [NSMutableSet set];
+            for (CKKSTLKShare* share in tlkShares) {
+                [uniqueTLKsWithShares addObject:share.tlkUUID];
+            }
+
+            [self noteMetric:OctagonAnalyticsBottledUniqueTLKsWithSharesCount count:uniqueTLKsWithShares.count];
+
+            NSMutableDictionary *views = [NSMutableDictionary dictionary];
+            for (CKKSTLKShare *share in tlkShares) {
+                views[share.zoneID] = share.zoneID;
+            }
+            [self noteMetric:OctagonAnalyticsBottledTLKUniqueViewCount count:views.count];
+
             secnotice("octagon", "Received bottle voucher");
 
             self.voucher = voucher;
             secnotice("octagon", "Received bottle voucher");
 
             self.voucher = voucher;
index 12712c0ee89d1baa8009435e78dc52f6d550e31f..1203d7156f61f819d0bf58aa51b380adc79cb4da 100644 (file)
@@ -42,8 +42,6 @@ NS_ASSUME_NONNULL_BEGIN
                          recoveryKey:(NSString*)recoveryKey;
 
 @property (weak) OTCuttlefishContext* cuttlefishContext;
                          recoveryKey:(NSString*)recoveryKey;
 
 @property (weak) OTCuttlefishContext* cuttlefishContext;
-@property (nonatomic) NSString* salt;
-@property (nonatomic) NSString* recoveryKey;
 
 @property (nonatomic) NSData* voucher;
 @property (nonatomic) NSData* voucherSig;
 
 @property (nonatomic) NSData* voucher;
 @property (nonatomic) NSData* voucherSig;
index 0bb767fefe56dbdf26df9574bf00aca4a0549330..24809b5c5a3d29609a5e27c050a83844c3d52104 100644 (file)
@@ -36,6 +36,9 @@
 @interface OTVouchWithRecoveryKeyOperation ()
 @property OTOperationDependencies* deps;
 
 @interface OTVouchWithRecoveryKeyOperation ()
 @property OTOperationDependencies* deps;
 
+@property NSString* salt;
+@property NSString* recoveryKey;
+
 @property NSOperation* finishOp;
 @end
 
 @property NSOperation* finishOp;
 @end
 
     self.finishOp = [[NSOperation alloc] init];
     [self dependOnBeforeGroupFinished:self.finishOp];
 
     self.finishOp = [[NSOperation alloc] init];
     [self dependOnBeforeGroupFinished:self.finishOp];
 
-    NSString* salt = nil;
+    NSString *altDSID = [self.deps.authKitAdapter primaryiCloudAccountAltDSID:nil];
+    if(altDSID){
+        secnotice("octagon", "using auth kit adapter, altdsid is: %@", altDSID);
+        self.salt = altDSID;
+    }
+    else {
+        NSError* accountError = nil;
+        OTAccountMetadataClassC* account = [self.deps.stateHolder loadOrCreateAccountMetadata:&accountError];
 
 
-    if(self.salt != nil) {
-        secnotice("octagon", "using passed in altdsid, altdsid is: %@", self.salt);
-        salt = self.salt;
-    } else{
-        NSString *altDSID = [self.deps.authKitAdapter primaryiCloudAccountAltDSID:nil];
-        if(altDSID){
-            secnotice("octagon", "using auth kit adapter, altdsid is: %@", altDSID);
-            salt = altDSID;
+        if(account && !accountError) {
+            secnotice("octagon", "retrieved account, altdsid is: %@", account.altDSID);
+            self.salt = account.altDSID;
         }
         }
-        else {
-            NSError* accountError = nil;
-            OTAccountMetadataClassC* account = [self.deps.stateHolder loadOrCreateAccountMetadata:&accountError];
-
-            if(account && !accountError) {
-                secnotice("octagon", "retrieved account, altdsid is: %@", account.altDSID);
-                salt = account.altDSID;
-            }
-            if(accountError || !account){
-                secerror("failed to rerieve account object: %@", accountError);
-            }
+        if(accountError || !account){
+            secerror("failed to rerieve account object: %@", accountError);
         }
     }
 
         }
     }
 
+    // First, let's preflight the vouch (to receive a policy and view set to use for TLK fetching
+    WEAKIFY(self);
+    [self.deps.cuttlefishXPCWrapper preflightVouchWithRecoveryKeyWithContainer:self.deps.containerName
+                                                                  context:self.deps.contextID
+                                                                   recoveryKey:self.recoveryKey
+                                                                          salt:self.salt
+                                                                    reply:^(NSString * _Nullable recoveryKeyID,
+                                                                            NSSet<NSString*>* peerSyncingViews,
+                                                                            TPPolicy* peerSyncingPolicy,
+                                                                            NSError * _Nullable error) {
+        STRONGIFY(self);
+        [[CKKSAnalytics logger] logResultForEvent:OctagonEventPreflightVouchWithRecoveryKey hardFailure:true result:error];
+
+        if(error || !recoveryKeyID) {
+            secerror("octagon: Error preflighting voucher using recovery key: %@", error);
+            self.error = error;
+            [self runBeforeGroupFinished:self.finishOp];
+            return;
+        }
+
+        secnotice("octagon", "Recovery key ID %@ looks good to go", recoveryKeyID);
+
+        // Tell CKKS to spin up the new views and policy
+        // But, do not persist this view set! We'll do that when we actually manage to join
+        [self.deps.viewManager setSyncingViews:peerSyncingViews sortingPolicy:peerSyncingPolicy];
+
+        [self proceedWithRecoveryKeyID:recoveryKeyID];
+    }];
+}
+
+- (void)proceedWithRecoveryKeyID:(NSString*)recoveryKeyID
+{
     WEAKIFY(self);
 
     // After a vouch, we also want to acquire all TLKs that the bottled peer might have had
     WEAKIFY(self);
 
     // After a vouch, we also want to acquire all TLKs that the bottled peer might have had
 
     CKKSResultOperation* proceedWithKeys = [CKKSResultOperation named:@"recovery-tlks"
                                                             withBlock:^{
 
     CKKSResultOperation* proceedWithKeys = [CKKSResultOperation named:@"recovery-tlks"
                                                             withBlock:^{
-                                                                STRONGIFY(self);
-                                                                [self proceedWithKeys:fetchKeysOp.viewKeySets tlkShares:fetchKeysOp.tlkShares salt:salt];
-                                                            }];
+        STRONGIFY(self);
+
+        NSMutableArray<CKKSTLKShare*>* filteredTLKShares = [NSMutableArray array];
+        for(CKKSTLKShare* share in fetchKeysOp.tlkShares) {
+            // If we didn't get a recoveryKeyID, just pass every tlkshare and hope for the best
+            if(recoveryKeyID == nil || [share.receiverPeerID isEqualToString:recoveryKeyID]) {
+                [filteredTLKShares addObject:share];
+            }
+        }
+
+        [self proceedWithKeys:fetchKeysOp.viewKeySets tlkShares:filteredTLKShares salt:self.salt];
+    }];
 
     [proceedWithKeys addDependency:fetchKeysOp];
     [self runBeforeGroupFinished:proceedWithKeys];
 
     [proceedWithKeys addDependency:fetchKeysOp];
     [self runBeforeGroupFinished:proceedWithKeys];
index dcd693ad5f5ec0dbb8dfe8aed514e475b80c3067..3e4f4efe3381e06621d44846d392832b83b5425c 100644 (file)
@@ -303,8 +303,6 @@ format,
     // Overwrite any existing pending flag!
     self.pendingFlags[pendingFlag.flag] = pendingFlag;
 
     // Overwrite any existing pending flag!
     self.pendingFlags[pendingFlag.flag] = pendingFlag;
 
-    self.currentFlags.flagConditions[pendingFlag.flag] = [[CKKSCondition alloc]init];
-    
     // Do we need to recheck any conditions? Anything which is currently the state of the world needs checking
     OctagonPendingConditions recheck = pendingFlag.conditions & self.currentConditions;
     if(recheck != 0x0) {
     // Do we need to recheck any conditions? Anything which is currently the state of the world needs checking
     OctagonPendingConditions recheck = pendingFlag.conditions & self.currentConditions;
     if(recheck != 0x0) {
index 4278b4996c94d97061777addd1f957342159ed88..b60e47eafb7d1606762febb8b9f2d15c4d9e74b3 100644 (file)
@@ -15,6 +15,12 @@ NS_ASSUME_NONNULL_BEGIN
 + (OTAccountMetadataClassC* _Nullable)loadFromKeychainForContainer:(NSString*)containerName contextID:(NSString*)contextID error:(NSError**)error;
 @end
 
 + (OTAccountMetadataClassC* _Nullable)loadFromKeychainForContainer:(NSString*)containerName contextID:(NSString*)contextID error:(NSError**)error;
 @end
 
+@class TPPolicy;
+@interface OTAccountMetadataClassC (NSSecureCodingSupport)
+- (void)setTPPolicy:(TPPolicy* _Nullable)policy;
+- (TPPolicy* _Nullable)getTPPolicy;
+@end
+
 NS_ASSUME_NONNULL_END
 
 #endif // OCTAGON
 NS_ASSUME_NONNULL_END
 
 #endif // OCTAGON
index 68f33b21562291ecaf169b6b397c5c42da5c55d8..ced8b4d28cacddbe96ea6f4540944e4e09aac4b4 100644 (file)
@@ -12,6 +12,7 @@
 
 #import "keychain/ot/OTDefines.h"
 #import "keychain/ot/OTConstants.h"
 
 #import "keychain/ot/OTDefines.h"
 #import "keychain/ot/OTConstants.h"
+#import <TrustedPeers/TPPolicy.h>
 
 @implementation OTAccountMetadataClassC (KeychainSupport)
 
 
 @implementation OTAccountMetadataClassC (KeychainSupport)
 
     return state;
 }
 
     return state;
 }
 
+#pragma mark - Field Coding support
+
+- (void)setTPPolicy:(TPPolicy*)policy
+{
+    if(policy) {
+        NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc] initRequiringSecureCoding:YES];
+        [policy encodeWithCoder:archiver];
+        self.syncingPolicy = archiver.encodedData;
+    } else {
+        self.syncingPolicy = nil;
+    }
+}
+
+- (TPPolicy* _Nullable)getTPPolicy
+{
+    NSKeyedUnarchiver *coder = [[NSKeyedUnarchiver alloc] initForReadingFromData:self.syncingPolicy error:nil];
+    TPPolicy* policy = [[TPPolicy alloc] initWithCoder:coder];
+    [coder finishDecoding];
+
+    return policy;
+}
+
 @end
 
 #endif // OCTAGON
 @end
 
 #endif // OCTAGON
index 3943683f0feac9f5213da6b8973a1f1cee455820..204e720cd287b2a972024b4c61d5c792f2caa00f 100644 (file)
@@ -8,6 +8,7 @@
 
 @protocol OctagonEscrowRecovererPrococol <NSObject>
 - (NSError*)recoverWithInfo:(NSDictionary*)info results:(NSDictionary**)results;
 
 @protocol OctagonEscrowRecovererPrococol <NSObject>
 - (NSError*)recoverWithInfo:(NSDictionary*)info results:(NSDictionary**)results;
+- (NSError *)disableWithInfo:(NSDictionary *)info;
 @end
 
 @interface SecureBackup (OctagonProtocolConformance) <OctagonEscrowRecovererPrococol>
 @end
 
 @interface SecureBackup (OctagonProtocolConformance) <OctagonEscrowRecovererPrococol>
index a3f0379fb7871ba435080f5fe4ad2503c73a1bdb..d706b6d9dde4d2574e9427c146f4e566c3d0d822 100644 (file)
@@ -13,6 +13,9 @@ message AccountMetadataClassC {
     //  If there is an SA account, we will bt in NO_ACCOUNT and have an altDSID
     //  If there is an HSA2 account, we will be in ACCOUNT_AVAILABLE and have an altDSID
 
     //  If there is an SA account, we will bt in NO_ACCOUNT and have an altDSID
     //  If there is an HSA2 account, we will be in ACCOUNT_AVAILABLE and have an altDSID
 
+    // Once you're in an HSA2 account, CDP might be enabled or disabled. If it's not enabled,
+    // then Octagon shouldn't be active.
+
     enum AccountState {
         UNKNOWN = 0;
         NO_ACCOUNT = 1;
     enum AccountState {
         UNKNOWN = 0;
         NO_ACCOUNT = 1;
@@ -32,6 +35,12 @@ message AccountMetadataClassC {
         ATTEMPTED = 2;
     }
 
         ATTEMPTED = 2;
     }
 
+    enum CDPState {
+        UNKNOWN = 0;
+        DISABLED = 1;
+        ENABLED = 2;
+    }
+
     optional string peerID = 1;
     optional AccountState icloudAccountState = 2;
     optional int64 epoch = 3;
     optional string peerID = 1;
     optional AccountState icloudAccountState = 2;
     optional int64 epoch = 3;
@@ -43,4 +52,10 @@ message AccountMetadataClassC {
     optional uint64 lastHealthCheckup = 6;
 
     optional AttemptedAJoinState attemptedJoin = 7;
     optional uint64 lastHealthCheckup = 6;
 
     optional AttemptedAJoinState attemptedJoin = 7;
+
+    optional CDPState cdpState = 8;
+
+    // These store the current policy and view list, so that we don't need to re-ask TPH every time
+    optional bytes syncingPolicy = 9;
+    repeated string syncingView = 10;
 }
 }
index c6b68a5630399824cb3af66f250cbb42dac2189c..edc46473cb5b85b2d0dff5c37c6055e138e9aadb 100644 (file)
@@ -28,14 +28,8 @@ option objc_class_visibility = "hidden";
 
 package OT;
 
 
 package OT;
 
-message SOSMessage {
-    optional bytes credential = 1;
-    optional bytes peerInfo = 2;
-    optional bytes circleBlob = 3;
-    optional bytes initialSyncItems = 4;
-}
 
 
-message SponsorToApplicantRound1M2{
+message SponsorToApplicantRound1M2 {
     optional uint64 epoch = 1;
 }
 
     optional uint64 epoch = 1;
 }
 
@@ -50,12 +44,14 @@ message ApplicantToSponsorRound2M1 {
 message SponsorToApplicantRound2M2{
     optional bytes voucher = 1;
     optional bytes voucherSignature = 2;
 message SponsorToApplicantRound2M2{
     optional bytes voucher = 1;
     optional bytes voucherSignature = 2;
-    repeated bytes preapprovedKeys = 3;
+    // Claimed for a field, but never used
+    //reserved 3;
 }
 
 message PairingMessage {
 }
 
 message PairingMessage {
-    optional SponsorToApplicantRound1M2 epoch= 1;
+    optional SponsorToApplicantRound1M2 epoch = 1;
     optional ApplicantToSponsorRound2M1 prepare = 2;
     optional SponsorToApplicantRound2M2 voucher = 3;
     optional ApplicantToSponsorRound2M1 prepare = 2;
     optional SponsorToApplicantRound2M2 voucher = 3;
-    optional SOSMessage sosPairingMessage = 4;
+    // <rdar://problem/55465193> reserved is not a keyword (it should be)
+    // reserved 4;
 }
 }
index aeef0ac0aef1d8deba620dd9d79b8c10e426b03a..295d83b6cf0ea23239965667a2df898a5226569a 100644 (file)
@@ -10,6 +10,8 @@
  *  If there is no account, we will be in NO_ACCOUNT and have no altDSID
  *  If there is an SA account, we will bt in NO_ACCOUNT and have an altDSID
  *  If there is an HSA2 account, we will be in ACCOUNT_AVAILABLE and have an altDSID
  *  If there is no account, we will be in NO_ACCOUNT and have no altDSID
  *  If there is an SA account, we will bt in NO_ACCOUNT and have an altDSID
  *  If there is an HSA2 account, we will be in ACCOUNT_AVAILABLE and have an altDSID
+ * Once you're in an HSA2 account, CDP might be enabled or disabled. If it's not enabled,
+ * then Octagon shouldn't be active.
  */
 typedef NS_ENUM(int32_t, OTAccountMetadataClassC_AccountState) {
     OTAccountMetadataClassC_AccountState_UNKNOWN = 0,
  */
 typedef NS_ENUM(int32_t, OTAccountMetadataClassC_AccountState) {
     OTAccountMetadataClassC_AccountState_UNKNOWN = 0,
@@ -92,6 +94,32 @@ NS_INLINE OTAccountMetadataClassC_AttemptedAJoinState StringAsOTAccountMetadataC
     return OTAccountMetadataClassC_AttemptedAJoinState_UNKNOWN;
 }
 #endif /* __OBJC__ */
     return OTAccountMetadataClassC_AttemptedAJoinState_UNKNOWN;
 }
 #endif /* __OBJC__ */
+typedef NS_ENUM(int32_t, OTAccountMetadataClassC_CDPState) {
+    OTAccountMetadataClassC_CDPState_UNKNOWN = 0,
+    OTAccountMetadataClassC_CDPState_DISABLED = 1,
+    OTAccountMetadataClassC_CDPState_ENABLED = 2,
+};
+#ifdef __OBJC__
+NS_INLINE NSString *OTAccountMetadataClassC_CDPStateAsString(OTAccountMetadataClassC_CDPState value)
+{
+    switch (value)
+    {
+        case OTAccountMetadataClassC_CDPState_UNKNOWN: return @"UNKNOWN";
+        case OTAccountMetadataClassC_CDPState_DISABLED: return @"DISABLED";
+        case OTAccountMetadataClassC_CDPState_ENABLED: return @"ENABLED";
+        default: return [NSString stringWithFormat:@"(unknown: %i)", value];
+    }
+}
+#endif /* __OBJC__ */
+#ifdef __OBJC__
+NS_INLINE OTAccountMetadataClassC_CDPState StringAsOTAccountMetadataClassC_CDPState(NSString *value)
+{
+    if ([value isEqualToString:@"UNKNOWN"]) return OTAccountMetadataClassC_CDPState_UNKNOWN;
+    if ([value isEqualToString:@"DISABLED"]) return OTAccountMetadataClassC_CDPState_DISABLED;
+    if ([value isEqualToString:@"ENABLED"]) return OTAccountMetadataClassC_CDPState_ENABLED;
+    return OTAccountMetadataClassC_CDPState_UNKNOWN;
+}
+#endif /* __OBJC__ */
 
 #ifdef __cplusplus
 #define OTACCOUNTMETADATACLASSC_FUNCTION extern "C" __attribute__((visibility("hidden")))
 
 #ifdef __cplusplus
 #define OTACCOUNTMETADATACLASSC_FUNCTION extern "C" __attribute__((visibility("hidden")))
@@ -106,13 +134,17 @@ __attribute__((visibility("hidden")))
     uint64_t _lastHealthCheckup;
     NSString *_altDSID;
     OTAccountMetadataClassC_AttemptedAJoinState _attemptedJoin;
     uint64_t _lastHealthCheckup;
     NSString *_altDSID;
     OTAccountMetadataClassC_AttemptedAJoinState _attemptedJoin;
+    OTAccountMetadataClassC_CDPState _cdpState;
     OTAccountMetadataClassC_AccountState _icloudAccountState;
     NSString *_peerID;
     OTAccountMetadataClassC_AccountState _icloudAccountState;
     NSString *_peerID;
+    NSData *_syncingPolicy;
+    NSMutableArray<NSString *> *_syncingViews;
     OTAccountMetadataClassC_TrustState _trustState;
     struct {
         int epoch:1;
         int lastHealthCheckup:1;
         int attemptedJoin:1;
     OTAccountMetadataClassC_TrustState _trustState;
     struct {
         int epoch:1;
         int lastHealthCheckup:1;
         int attemptedJoin:1;
+        int cdpState:1;
         int icloudAccountState:1;
         int trustState:1;
     } _has;
         int icloudAccountState:1;
         int trustState:1;
     } _has;
@@ -147,6 +179,22 @@ __attribute__((visibility("hidden")))
 - (NSString *)attemptedJoinAsString:(OTAccountMetadataClassC_AttemptedAJoinState)value;
 - (OTAccountMetadataClassC_AttemptedAJoinState)StringAsAttemptedJoin:(NSString *)str;
 
 - (NSString *)attemptedJoinAsString:(OTAccountMetadataClassC_AttemptedAJoinState)value;
 - (OTAccountMetadataClassC_AttemptedAJoinState)StringAsAttemptedJoin:(NSString *)str;
 
+@property (nonatomic) BOOL hasCdpState;
+@property (nonatomic) OTAccountMetadataClassC_CDPState cdpState;
+- (NSString *)cdpStateAsString:(OTAccountMetadataClassC_CDPState)value;
+- (OTAccountMetadataClassC_CDPState)StringAsCdpState:(NSString *)str;
+
+@property (nonatomic, readonly) BOOL hasSyncingPolicy;
+/** These store the current policy and view list, so that we don't need to re-ask TPH every time */
+@property (nonatomic, retain) NSData *syncingPolicy;
+
+@property (nonatomic, retain) NSMutableArray<NSString *> *syncingViews;
+- (void)clearSyncingViews;
+- (void)addSyncingView:(NSString *)i;
+- (NSUInteger)syncingViewsCount;
+- (NSString *)syncingViewAtIndex:(NSUInteger)idx;
++ (Class)syncingViewType;
+
 // Performs a shallow copy into other
 - (void)copyTo:(OTAccountMetadataClassC *)other;
 
 // Performs a shallow copy into other
 - (void)copyTo:(OTAccountMetadataClassC *)other;
 
index c7e41deba9eed62f8871dd0078fee9d4e1194a83..40c4de4635ecb300cc3c5dfa2ff7f031cf254e15 100644 (file)
@@ -34,7 +34,7 @@
 }
 - (BOOL)hasIcloudAccountState
 {
 }
 - (BOOL)hasIcloudAccountState
 {
-    return _has.icloudAccountState;
+    return _has.icloudAccountState != 0;
 }
 - (NSString *)icloudAccountStateAsString:(OTAccountMetadataClassC_AccountState)value
 {
 }
 - (NSString *)icloudAccountStateAsString:(OTAccountMetadataClassC_AccountState)value
 {
@@ -56,7 +56,7 @@
 }
 - (BOOL)hasEpoch
 {
 }
 - (BOOL)hasEpoch
 {
-    return _has.epoch;
+    return _has.epoch != 0;
 }
 - (BOOL)hasAltDSID
 {
 }
 - (BOOL)hasAltDSID
 {
@@ -79,7 +79,7 @@
 }
 - (BOOL)hasTrustState
 {
 }
 - (BOOL)hasTrustState
 {
-    return _has.trustState;
+    return _has.trustState != 0;
 }
 - (NSString *)trustStateAsString:(OTAccountMetadataClassC_TrustState)value
 {
 }
 - (NSString *)trustStateAsString:(OTAccountMetadataClassC_TrustState)value
 {
 }
 - (BOOL)hasLastHealthCheckup
 {
 }
 - (BOOL)hasLastHealthCheckup
 {
-    return _has.lastHealthCheckup;
+    return _has.lastHealthCheckup != 0;
 }
 @synthesize attemptedJoin = _attemptedJoin;
 - (OTAccountMetadataClassC_AttemptedAJoinState)attemptedJoin
 }
 @synthesize attemptedJoin = _attemptedJoin;
 - (OTAccountMetadataClassC_AttemptedAJoinState)attemptedJoin
 }
 - (BOOL)hasAttemptedJoin
 {
 }
 - (BOOL)hasAttemptedJoin
 {
-    return _has.attemptedJoin;
+    return _has.attemptedJoin != 0;
 }
 - (NSString *)attemptedJoinAsString:(OTAccountMetadataClassC_AttemptedAJoinState)value
 {
 }
 - (NSString *)attemptedJoinAsString:(OTAccountMetadataClassC_AttemptedAJoinState)value
 {
 {
     return StringAsOTAccountMetadataClassC_AttemptedAJoinState(str);
 }
 {
     return StringAsOTAccountMetadataClassC_AttemptedAJoinState(str);
 }
+@synthesize cdpState = _cdpState;
+- (OTAccountMetadataClassC_CDPState)cdpState
+{
+    return _has.cdpState ? _cdpState : OTAccountMetadataClassC_CDPState_UNKNOWN;
+}
+- (void)setCdpState:(OTAccountMetadataClassC_CDPState)v
+{
+    _has.cdpState = YES;
+    _cdpState = v;
+}
+- (void)setHasCdpState:(BOOL)f
+{
+    _has.cdpState = f;
+}
+- (BOOL)hasCdpState
+{
+    return _has.cdpState != 0;
+}
+- (NSString *)cdpStateAsString:(OTAccountMetadataClassC_CDPState)value
+{
+    return OTAccountMetadataClassC_CDPStateAsString(value);
+}
+- (OTAccountMetadataClassC_CDPState)StringAsCdpState:(NSString *)str
+{
+    return StringAsOTAccountMetadataClassC_CDPState(str);
+}
+- (BOOL)hasSyncingPolicy
+{
+    return _syncingPolicy != nil;
+}
+@synthesize syncingPolicy = _syncingPolicy;
+@synthesize syncingViews = _syncingViews;
+- (void)clearSyncingViews
+{
+    [_syncingViews removeAllObjects];
+}
+- (void)addSyncingView:(NSString *)i
+{
+    if (!_syncingViews)
+    {
+        _syncingViews = [[NSMutableArray alloc] init];
+    }
+    [_syncingViews addObject:i];
+}
+- (NSUInteger)syncingViewsCount
+{
+    return [_syncingViews count];
+}
+- (NSString *)syncingViewAtIndex:(NSUInteger)idx
+{
+    return [_syncingViews objectAtIndex:idx];
+}
++ (Class)syncingViewType
+{
+    return [NSString class];
+}
 
 - (NSString *)description
 {
 
 - (NSString *)description
 {
     {
         [dict setObject:OTAccountMetadataClassC_AttemptedAJoinStateAsString(self->_attemptedJoin) forKey:@"attemptedJoin"];
     }
     {
         [dict setObject:OTAccountMetadataClassC_AttemptedAJoinStateAsString(self->_attemptedJoin) forKey:@"attemptedJoin"];
     }
+    if (self->_has.cdpState)
+    {
+        [dict setObject:OTAccountMetadataClassC_CDPStateAsString(self->_cdpState) forKey:@"cdpState"];
+    }
+    if (self->_syncingPolicy)
+    {
+        [dict setObject:self->_syncingPolicy forKey:@"syncingPolicy"];
+    }
+    if (self->_syncingViews)
+    {
+        [dict setObject:self->_syncingViews forKey:@"syncingView"];
+    }
     return dict;
 }
 
     return dict;
 }
 
@@ -227,6 +295,27 @@ BOOL OTAccountMetadataClassCReadFrom(__unsafe_unretained OTAccountMetadataClassC
                 self->_attemptedJoin = PBReaderReadInt32(reader);
             }
             break;
                 self->_attemptedJoin = PBReaderReadInt32(reader);
             }
             break;
+            case 8 /* cdpState */:
+            {
+                self->_has.cdpState = YES;
+                self->_cdpState = PBReaderReadInt32(reader);
+            }
+            break;
+            case 9 /* syncingPolicy */:
+            {
+                NSData *new_syncingPolicy = PBReaderReadData(reader);
+                self->_syncingPolicy = new_syncingPolicy;
+            }
+            break;
+            case 10 /* syncingViews */:
+            {
+                NSString *new_syncingViews = PBReaderReadString(reader);
+                if (new_syncingViews)
+                {
+                    [self addSyncingView:new_syncingViews];
+                }
+            }
+            break;
             default:
                 if (!PBReaderSkipValueWithTag(reader, tag, aType))
                     return NO;
             default:
                 if (!PBReaderSkipValueWithTag(reader, tag, aType))
                     return NO;
@@ -291,6 +380,27 @@ BOOL OTAccountMetadataClassCReadFrom(__unsafe_unretained OTAccountMetadataClassC
             PBDataWriterWriteInt32Field(writer, self->_attemptedJoin, 7);
         }
     }
             PBDataWriterWriteInt32Field(writer, self->_attemptedJoin, 7);
         }
     }
+    /* cdpState */
+    {
+        if (self->_has.cdpState)
+        {
+            PBDataWriterWriteInt32Field(writer, self->_cdpState, 8);
+        }
+    }
+    /* syncingPolicy */
+    {
+        if (self->_syncingPolicy)
+        {
+            PBDataWriterWriteDataField(writer, self->_syncingPolicy, 9);
+        }
+    }
+    /* syncingViews */
+    {
+        for (NSString *s_syncingViews in self->_syncingViews)
+        {
+            PBDataWriterWriteStringField(writer, s_syncingViews, 10);
+        }
+    }
 }
 
 - (void)copyTo:(OTAccountMetadataClassC *)other
 }
 
 - (void)copyTo:(OTAccountMetadataClassC *)other
@@ -328,6 +438,24 @@ BOOL OTAccountMetadataClassCReadFrom(__unsafe_unretained OTAccountMetadataClassC
         other->_attemptedJoin = _attemptedJoin;
         other->_has.attemptedJoin = YES;
     }
         other->_attemptedJoin = _attemptedJoin;
         other->_has.attemptedJoin = YES;
     }
+    if (self->_has.cdpState)
+    {
+        other->_cdpState = _cdpState;
+        other->_has.cdpState = YES;
+    }
+    if (_syncingPolicy)
+    {
+        other.syncingPolicy = _syncingPolicy;
+    }
+    if ([self syncingViewsCount])
+    {
+        [other clearSyncingViews];
+        NSUInteger syncingViewsCnt = [self syncingViewsCount];
+        for (NSUInteger i = 0; i < syncingViewsCnt; i++)
+        {
+            [other addSyncingView:[self syncingViewAtIndex:i]];
+        }
+    }
 }
 
 - (id)copyWithZone:(NSZone *)zone
 }
 
 - (id)copyWithZone:(NSZone *)zone
@@ -360,6 +488,17 @@ BOOL OTAccountMetadataClassCReadFrom(__unsafe_unretained OTAccountMetadataClassC
         copy->_attemptedJoin = _attemptedJoin;
         copy->_has.attemptedJoin = YES;
     }
         copy->_attemptedJoin = _attemptedJoin;
         copy->_has.attemptedJoin = YES;
     }
+    if (self->_has.cdpState)
+    {
+        copy->_cdpState = _cdpState;
+        copy->_has.cdpState = YES;
+    }
+    copy->_syncingPolicy = [_syncingPolicy copyWithZone:zone];
+    for (NSString *v in _syncingViews)
+    {
+        NSString *vCopy = [v copyWithZone:zone];
+        [copy addSyncingView:vCopy];
+    }
     return copy;
 }
 
     return copy;
 }
 
@@ -381,6 +520,12 @@ BOOL OTAccountMetadataClassCReadFrom(__unsafe_unretained OTAccountMetadataClassC
     ((self->_has.lastHealthCheckup && other->_has.lastHealthCheckup && self->_lastHealthCheckup == other->_lastHealthCheckup) || (!self->_has.lastHealthCheckup && !other->_has.lastHealthCheckup))
     &&
     ((self->_has.attemptedJoin && other->_has.attemptedJoin && self->_attemptedJoin == other->_attemptedJoin) || (!self->_has.attemptedJoin && !other->_has.attemptedJoin))
     ((self->_has.lastHealthCheckup && other->_has.lastHealthCheckup && self->_lastHealthCheckup == other->_lastHealthCheckup) || (!self->_has.lastHealthCheckup && !other->_has.lastHealthCheckup))
     &&
     ((self->_has.attemptedJoin && other->_has.attemptedJoin && self->_attemptedJoin == other->_attemptedJoin) || (!self->_has.attemptedJoin && !other->_has.attemptedJoin))
+    &&
+    ((self->_has.cdpState && other->_has.cdpState && self->_cdpState == other->_cdpState) || (!self->_has.cdpState && !other->_has.cdpState))
+    &&
+    ((!self->_syncingPolicy && !other->_syncingPolicy) || [self->_syncingPolicy isEqual:other->_syncingPolicy])
+    &&
+    ((!self->_syncingViews && !other->_syncingViews) || [self->_syncingViews isEqual:other->_syncingViews])
     ;
 }
 
     ;
 }
 
@@ -401,6 +546,12 @@ BOOL OTAccountMetadataClassCReadFrom(__unsafe_unretained OTAccountMetadataClassC
     (self->_has.lastHealthCheckup ? PBHashInt((NSUInteger)self->_lastHealthCheckup) : 0)
     ^
     (self->_has.attemptedJoin ? PBHashInt((NSUInteger)self->_attemptedJoin) : 0)
     (self->_has.lastHealthCheckup ? PBHashInt((NSUInteger)self->_lastHealthCheckup) : 0)
     ^
     (self->_has.attemptedJoin ? PBHashInt((NSUInteger)self->_attemptedJoin) : 0)
+    ^
+    (self->_has.cdpState ? PBHashInt((NSUInteger)self->_cdpState) : 0)
+    ^
+    [self->_syncingPolicy hash]
+    ^
+    [self->_syncingViews hash]
     ;
 }
 
     ;
 }
 
@@ -439,6 +590,19 @@ BOOL OTAccountMetadataClassCReadFrom(__unsafe_unretained OTAccountMetadataClassC
         self->_attemptedJoin = other->_attemptedJoin;
         self->_has.attemptedJoin = YES;
     }
         self->_attemptedJoin = other->_attemptedJoin;
         self->_has.attemptedJoin = YES;
     }
+    if (other->_has.cdpState)
+    {
+        self->_cdpState = other->_cdpState;
+        self->_has.cdpState = YES;
+    }
+    if (other->_syncingPolicy)
+    {
+        [self setSyncingPolicy:other->_syncingPolicy];
+    }
+    for (NSString *iter_syncingViews in other->_syncingViews)
+    {
+        [self addSyncingView:iter_syncingViews];
+    }
 }
 
 @end
 }
 
 @end
index f2fad2f14446c23de9666771b96de10955ff073c..e8c27edd467aa87ceb37f9c272a60bb8775b1c4f 100644 (file)
@@ -8,7 +8,6 @@
 @class OTSponsorToApplicantRound1M2;
 @class OTApplicantToSponsorRound2M1;
 @class OTSponsorToApplicantRound2M2;
 @class OTSponsorToApplicantRound1M2;
 @class OTApplicantToSponsorRound2M1;
 @class OTSponsorToApplicantRound2M2;
-@class OTSOSMessage;
 
 #ifdef __cplusplus
 #define OTPAIRINGMESSAGE_FUNCTION extern "C" __attribute__((visibility("hidden")))
 
 #ifdef __cplusplus
 #define OTPAIRINGMESSAGE_FUNCTION extern "C" __attribute__((visibility("hidden")))
 #define OTPAIRINGMESSAGE_FUNCTION extern __attribute__((visibility("hidden")))
 #endif
 
 #define OTPAIRINGMESSAGE_FUNCTION extern __attribute__((visibility("hidden")))
 #endif
 
+/**
+ * Claimed for a field, but never used
+ * reserved 3;
+ */
 __attribute__((visibility("hidden")))
 @interface OTPairingMessage : PBCodable <NSCopying>
 {
     OTSponsorToApplicantRound1M2 *_epoch;
     OTApplicantToSponsorRound2M1 *_prepare;
 __attribute__((visibility("hidden")))
 @interface OTPairingMessage : PBCodable <NSCopying>
 {
     OTSponsorToApplicantRound1M2 *_epoch;
     OTApplicantToSponsorRound2M1 *_prepare;
-    OTSOSMessage *_sosPairingMessage;
     OTSponsorToApplicantRound2M2 *_voucher;
 }
 
     OTSponsorToApplicantRound2M2 *_voucher;
 }
 
@@ -35,9 +37,6 @@ __attribute__((visibility("hidden")))
 @property (nonatomic, readonly) BOOL hasVoucher;
 @property (nonatomic, retain) OTSponsorToApplicantRound2M2 *voucher;
 
 @property (nonatomic, readonly) BOOL hasVoucher;
 @property (nonatomic, retain) OTSponsorToApplicantRound2M2 *voucher;
 
-@property (nonatomic, readonly) BOOL hasSosPairingMessage;
-@property (nonatomic, retain) OTSOSMessage *sosPairingMessage;
-
 // Performs a shallow copy into other
 - (void)copyTo:(OTPairingMessage *)other;
 
 // Performs a shallow copy into other
 - (void)copyTo:(OTPairingMessage *)other;
 
index 3416a4f952bdf5a78ac2b944d4faf1f315f1952c..a7c8f89b28faf48b41b3fb712869f50476e1e2cf 100644 (file)
@@ -8,7 +8,6 @@
 #import <ProtocolBuffer/PBDataReader.h>
 
 #import "OTApplicantToSponsorRound2M1.h"
 #import <ProtocolBuffer/PBDataReader.h>
 
 #import "OTApplicantToSponsorRound2M1.h"
-#import "OTSOSMessage.h"
 #import "OTSponsorToApplicantRound1M2.h"
 #import "OTSponsorToApplicantRound2M2.h"
 
 #import "OTSponsorToApplicantRound1M2.h"
 #import "OTSponsorToApplicantRound2M2.h"
 
     return _voucher != nil;
 }
 @synthesize voucher = _voucher;
     return _voucher != nil;
 }
 @synthesize voucher = _voucher;
-- (BOOL)hasSosPairingMessage
-{
-    return _sosPairingMessage != nil;
-}
-@synthesize sosPairingMessage = _sosPairingMessage;
 
 - (NSString *)description
 {
 
 - (NSString *)description
 {
     {
         [dict setObject:[_voucher dictionaryRepresentation] forKey:@"voucher"];
     }
     {
         [dict setObject:[_voucher dictionaryRepresentation] forKey:@"voucher"];
     }
-    if (self->_sosPairingMessage)
-    {
-        [dict setObject:[_sosPairingMessage dictionaryRepresentation] forKey:@"sosPairingMessage"];
-    }
     return dict;
 }
 
     return dict;
 }
 
@@ -136,24 +126,6 @@ BOOL OTPairingMessageReadFrom(__unsafe_unretained OTPairingMessage *self, __unsa
                 PBReaderRecallMark(reader, &mark_voucher);
             }
             break;
                 PBReaderRecallMark(reader, &mark_voucher);
             }
             break;
-            case 4 /* sosPairingMessage */:
-            {
-                OTSOSMessage *new_sosPairingMessage = [[OTSOSMessage alloc] init];
-                self->_sosPairingMessage = new_sosPairingMessage;
-                PBDataReaderMark mark_sosPairingMessage;
-                BOOL markError = !PBReaderPlaceMark(reader, &mark_sosPairingMessage);
-                if (markError)
-                {
-                    return NO;
-                }
-                BOOL inError = !OTSOSMessageReadFrom(new_sosPairingMessage, reader);
-                if (inError)
-                {
-                    return NO;
-                }
-                PBReaderRecallMark(reader, &mark_sosPairingMessage);
-            }
-            break;
             default:
                 if (!PBReaderSkipValueWithTag(reader, tag, aType))
                     return NO;
             default:
                 if (!PBReaderSkipValueWithTag(reader, tag, aType))
                     return NO;
@@ -190,13 +162,6 @@ BOOL OTPairingMessageReadFrom(__unsafe_unretained OTPairingMessage *self, __unsa
             PBDataWriterWriteSubmessage(writer, self->_voucher, 3);
         }
     }
             PBDataWriterWriteSubmessage(writer, self->_voucher, 3);
         }
     }
-    /* sosPairingMessage */
-    {
-        if (self->_sosPairingMessage != nil)
-        {
-            PBDataWriterWriteSubmessage(writer, self->_sosPairingMessage, 4);
-        }
-    }
 }
 
 - (void)copyTo:(OTPairingMessage *)other
 }
 
 - (void)copyTo:(OTPairingMessage *)other
@@ -213,10 +178,6 @@ BOOL OTPairingMessageReadFrom(__unsafe_unretained OTPairingMessage *self, __unsa
     {
         other.voucher = _voucher;
     }
     {
         other.voucher = _voucher;
     }
-    if (_sosPairingMessage)
-    {
-        other.sosPairingMessage = _sosPairingMessage;
-    }
 }
 
 - (id)copyWithZone:(NSZone *)zone
 }
 
 - (id)copyWithZone:(NSZone *)zone
@@ -225,7 +186,6 @@ BOOL OTPairingMessageReadFrom(__unsafe_unretained OTPairingMessage *self, __unsa
     copy->_epoch = [_epoch copyWithZone:zone];
     copy->_prepare = [_prepare copyWithZone:zone];
     copy->_voucher = [_voucher copyWithZone:zone];
     copy->_epoch = [_epoch copyWithZone:zone];
     copy->_prepare = [_prepare copyWithZone:zone];
     copy->_voucher = [_voucher copyWithZone:zone];
-    copy->_sosPairingMessage = [_sosPairingMessage copyWithZone:zone];
     return copy;
 }
 
     return copy;
 }
 
@@ -239,8 +199,6 @@ BOOL OTPairingMessageReadFrom(__unsafe_unretained OTPairingMessage *self, __unsa
     ((!self->_prepare && !other->_prepare) || [self->_prepare isEqual:other->_prepare])
     &&
     ((!self->_voucher && !other->_voucher) || [self->_voucher isEqual:other->_voucher])
     ((!self->_prepare && !other->_prepare) || [self->_prepare isEqual:other->_prepare])
     &&
     ((!self->_voucher && !other->_voucher) || [self->_voucher isEqual:other->_voucher])
-    &&
-    ((!self->_sosPairingMessage && !other->_sosPairingMessage) || [self->_sosPairingMessage isEqual:other->_sosPairingMessage])
     ;
 }
 
     ;
 }
 
@@ -253,8 +211,6 @@ BOOL OTPairingMessageReadFrom(__unsafe_unretained OTPairingMessage *self, __unsa
     [self->_prepare hash]
     ^
     [self->_voucher hash]
     [self->_prepare hash]
     ^
     [self->_voucher hash]
-    ^
-    [self->_sosPairingMessage hash]
     ;
 }
 
     ;
 }
 
@@ -284,14 +240,6 @@ BOOL OTPairingMessageReadFrom(__unsafe_unretained OTPairingMessage *self, __unsa
     {
         [self setVoucher:other->_voucher];
     }
     {
         [self setVoucher:other->_voucher];
     }
-    if (self->_sosPairingMessage && other->_sosPairingMessage)
-    {
-        [self->_sosPairingMessage mergeFrom:other->_sosPairingMessage];
-    }
-    else if (!self->_sosPairingMessage && other->_sosPairingMessage)
-    {
-        [self setSosPairingMessage:other->_sosPairingMessage];
-    }
 }
 
 @end
 }
 
 @end
diff --git a/keychain/ot/proto/generated_source/OTSOSMessage.h b/keychain/ot/proto/generated_source/OTSOSMessage.h
deleted file mode 100644 (file)
index e515077..0000000
+++ /dev/null
@@ -1,48 +0,0 @@
-// This file was automatically generated by protocompiler
-// DO NOT EDIT!
-// Compiled from OTPairingMessage.proto
-
-#import <Foundation/Foundation.h>
-#import <ProtocolBuffer/PBCodable.h>
-
-#ifdef __cplusplus
-#define OTSOSMESSAGE_FUNCTION extern "C" __attribute__((visibility("hidden")))
-#else
-#define OTSOSMESSAGE_FUNCTION extern __attribute__((visibility("hidden")))
-#endif
-
-__attribute__((visibility("hidden")))
-@interface OTSOSMessage : PBCodable <NSCopying>
-{
-    NSData *_circleBlob;
-    NSData *_credential;
-    NSData *_initialSyncItems;
-    NSData *_peerInfo;
-}
-
-
-@property (nonatomic, readonly) BOOL hasCredential;
-@property (nonatomic, retain) NSData *credential;
-
-@property (nonatomic, readonly) BOOL hasPeerInfo;
-@property (nonatomic, retain) NSData *peerInfo;
-
-@property (nonatomic, readonly) BOOL hasCircleBlob;
-@property (nonatomic, retain) NSData *circleBlob;
-
-@property (nonatomic, readonly) BOOL hasInitialSyncItems;
-@property (nonatomic, retain) NSData *initialSyncItems;
-
-// Performs a shallow copy into other
-- (void)copyTo:(OTSOSMessage *)other;
-
-// Performs a deep merge from other into self
-// If set in other, singular values in self are replaced in self
-// Singular composite values are recursively merged
-// Repeated values from other are appended to repeated values in self
-- (void)mergeFrom:(OTSOSMessage *)other;
-
-OTSOSMESSAGE_FUNCTION BOOL OTSOSMessageReadFrom(__unsafe_unretained OTSOSMessage *self, __unsafe_unretained PBDataReader *reader);
-
-@end
-
diff --git a/keychain/ot/proto/generated_source/OTSOSMessage.m b/keychain/ot/proto/generated_source/OTSOSMessage.m
deleted file mode 100644 (file)
index e4a1f2a..0000000
+++ /dev/null
@@ -1,229 +0,0 @@
-// This file was automatically generated by protocompiler
-// DO NOT EDIT!
-// Compiled from OTPairingMessage.proto
-
-#import "OTSOSMessage.h"
-#import <ProtocolBuffer/PBConstants.h>
-#import <ProtocolBuffer/PBHashUtil.h>
-#import <ProtocolBuffer/PBDataReader.h>
-
-#if !__has_feature(objc_arc)
-# error This generated file depends on ARC but it is not enabled; turn on ARC, or use 'objc_use_arc' option to generate non-ARC code.
-#endif
-
-@implementation OTSOSMessage
-
-- (BOOL)hasCredential
-{
-    return _credential != nil;
-}
-@synthesize credential = _credential;
-- (BOOL)hasPeerInfo
-{
-    return _peerInfo != nil;
-}
-@synthesize peerInfo = _peerInfo;
-- (BOOL)hasCircleBlob
-{
-    return _circleBlob != nil;
-}
-@synthesize circleBlob = _circleBlob;
-- (BOOL)hasInitialSyncItems
-{
-    return _initialSyncItems != nil;
-}
-@synthesize initialSyncItems = _initialSyncItems;
-
-- (NSString *)description
-{
-    return [NSString stringWithFormat:@"%@ %@", [super description], [self dictionaryRepresentation]];
-}
-
-- (NSDictionary *)dictionaryRepresentation
-{
-    NSMutableDictionary *dict = [NSMutableDictionary dictionary];
-    if (self->_credential)
-    {
-        [dict setObject:self->_credential forKey:@"credential"];
-    }
-    if (self->_peerInfo)
-    {
-        [dict setObject:self->_peerInfo forKey:@"peerInfo"];
-    }
-    if (self->_circleBlob)
-    {
-        [dict setObject:self->_circleBlob forKey:@"circleBlob"];
-    }
-    if (self->_initialSyncItems)
-    {
-        [dict setObject:self->_initialSyncItems forKey:@"initialSyncItems"];
-    }
-    return dict;
-}
-
-BOOL OTSOSMessageReadFrom(__unsafe_unretained OTSOSMessage *self, __unsafe_unretained PBDataReader *reader) {
-    while (PBReaderHasMoreData(reader)) {
-        uint32_t tag = 0;
-        uint8_t aType = 0;
-
-        PBReaderReadTag32AndType(reader, &tag, &aType);
-
-        if (PBReaderHasError(reader))
-            break;
-
-        if (aType == TYPE_END_GROUP) {
-            break;
-        }
-
-        switch (tag) {
-
-            case 1 /* credential */:
-            {
-                NSData *new_credential = PBReaderReadData(reader);
-                self->_credential = new_credential;
-            }
-            break;
-            case 2 /* peerInfo */:
-            {
-                NSData *new_peerInfo = PBReaderReadData(reader);
-                self->_peerInfo = new_peerInfo;
-            }
-            break;
-            case 3 /* circleBlob */:
-            {
-                NSData *new_circleBlob = PBReaderReadData(reader);
-                self->_circleBlob = new_circleBlob;
-            }
-            break;
-            case 4 /* initialSyncItems */:
-            {
-                NSData *new_initialSyncItems = PBReaderReadData(reader);
-                self->_initialSyncItems = new_initialSyncItems;
-            }
-            break;
-            default:
-                if (!PBReaderSkipValueWithTag(reader, tag, aType))
-                    return NO;
-                break;
-        }
-    }
-    return !PBReaderHasError(reader);
-}
-
-- (BOOL)readFrom:(PBDataReader *)reader
-{
-    return OTSOSMessageReadFrom(self, reader);
-}
-- (void)writeTo:(PBDataWriter *)writer
-{
-    /* credential */
-    {
-        if (self->_credential)
-        {
-            PBDataWriterWriteDataField(writer, self->_credential, 1);
-        }
-    }
-    /* peerInfo */
-    {
-        if (self->_peerInfo)
-        {
-            PBDataWriterWriteDataField(writer, self->_peerInfo, 2);
-        }
-    }
-    /* circleBlob */
-    {
-        if (self->_circleBlob)
-        {
-            PBDataWriterWriteDataField(writer, self->_circleBlob, 3);
-        }
-    }
-    /* initialSyncItems */
-    {
-        if (self->_initialSyncItems)
-        {
-            PBDataWriterWriteDataField(writer, self->_initialSyncItems, 4);
-        }
-    }
-}
-
-- (void)copyTo:(OTSOSMessage *)other
-{
-    if (_credential)
-    {
-        other.credential = _credential;
-    }
-    if (_peerInfo)
-    {
-        other.peerInfo = _peerInfo;
-    }
-    if (_circleBlob)
-    {
-        other.circleBlob = _circleBlob;
-    }
-    if (_initialSyncItems)
-    {
-        other.initialSyncItems = _initialSyncItems;
-    }
-}
-
-- (id)copyWithZone:(NSZone *)zone
-{
-    OTSOSMessage *copy = [[[self class] allocWithZone:zone] init];
-    copy->_credential = [_credential copyWithZone:zone];
-    copy->_peerInfo = [_peerInfo copyWithZone:zone];
-    copy->_circleBlob = [_circleBlob copyWithZone:zone];
-    copy->_initialSyncItems = [_initialSyncItems copyWithZone:zone];
-    return copy;
-}
-
-- (BOOL)isEqual:(id)object
-{
-    OTSOSMessage *other = (OTSOSMessage *)object;
-    return [other isMemberOfClass:[self class]]
-    &&
-    ((!self->_credential && !other->_credential) || [self->_credential isEqual:other->_credential])
-    &&
-    ((!self->_peerInfo && !other->_peerInfo) || [self->_peerInfo isEqual:other->_peerInfo])
-    &&
-    ((!self->_circleBlob && !other->_circleBlob) || [self->_circleBlob isEqual:other->_circleBlob])
-    &&
-    ((!self->_initialSyncItems && !other->_initialSyncItems) || [self->_initialSyncItems isEqual:other->_initialSyncItems])
-    ;
-}
-
-- (NSUInteger)hash
-{
-    return 0
-    ^
-    [self->_credential hash]
-    ^
-    [self->_peerInfo hash]
-    ^
-    [self->_circleBlob hash]
-    ^
-    [self->_initialSyncItems hash]
-    ;
-}
-
-- (void)mergeFrom:(OTSOSMessage *)other
-{
-    if (other->_credential)
-    {
-        [self setCredential:other->_credential];
-    }
-    if (other->_peerInfo)
-    {
-        [self setPeerInfo:other->_peerInfo];
-    }
-    if (other->_circleBlob)
-    {
-        [self setCircleBlob:other->_circleBlob];
-    }
-    if (other->_initialSyncItems)
-    {
-        [self setInitialSyncItems:other->_initialSyncItems];
-    }
-}
-
-@end
-
index b7495329a312f53c3e193b3b897705b480375b38..a0a32e482abb718f37248b60299f0ba3c0d49167 100644 (file)
@@ -14,7 +14,6 @@
 __attribute__((visibility("hidden")))
 @interface OTSponsorToApplicantRound2M2 : PBCodable <NSCopying>
 {
 __attribute__((visibility("hidden")))
 @interface OTSponsorToApplicantRound2M2 : PBCodable <NSCopying>
 {
-    NSMutableArray<NSData *> *_preapprovedKeys;
     NSData *_voucher;
     NSData *_voucherSignature;
 }
     NSData *_voucher;
     NSData *_voucherSignature;
 }
@@ -26,13 +25,6 @@ __attribute__((visibility("hidden")))
 @property (nonatomic, readonly) BOOL hasVoucherSignature;
 @property (nonatomic, retain) NSData *voucherSignature;
 
 @property (nonatomic, readonly) BOOL hasVoucherSignature;
 @property (nonatomic, retain) NSData *voucherSignature;
 
-@property (nonatomic, retain) NSMutableArray<NSData *> *preapprovedKeys;
-- (void)clearPreapprovedKeys;
-- (void)addPreapprovedKeys:(NSData *)i;
-- (NSUInteger)preapprovedKeysCount;
-- (NSData *)preapprovedKeysAtIndex:(NSUInteger)idx;
-+ (Class)preapprovedKeysType;
-
 // Performs a shallow copy into other
 - (void)copyTo:(OTSponsorToApplicantRound2M2 *)other;
 
 // Performs a shallow copy into other
 - (void)copyTo:(OTSponsorToApplicantRound2M2 *)other;
 
index 6737b64112abe9a90e41a54a00cad4f78cd30879..c51eaced45326b167af2349d11bc56baa418d13b 100644 (file)
     return _voucherSignature != nil;
 }
 @synthesize voucherSignature = _voucherSignature;
     return _voucherSignature != nil;
 }
 @synthesize voucherSignature = _voucherSignature;
-@synthesize preapprovedKeys = _preapprovedKeys;
-- (void)clearPreapprovedKeys
-{
-    [_preapprovedKeys removeAllObjects];
-}
-- (void)addPreapprovedKeys:(NSData *)i
-{
-    if (!_preapprovedKeys)
-    {
-        _preapprovedKeys = [[NSMutableArray alloc] init];
-    }
-    [_preapprovedKeys addObject:i];
-}
-- (NSUInteger)preapprovedKeysCount
-{
-    return [_preapprovedKeys count];
-}
-- (NSData *)preapprovedKeysAtIndex:(NSUInteger)idx
-{
-    return [_preapprovedKeys objectAtIndex:idx];
-}
-+ (Class)preapprovedKeysType
-{
-    return [NSData class];
-}
 
 - (NSString *)description
 {
 
 - (NSString *)description
 {
     {
         [dict setObject:self->_voucherSignature forKey:@"voucherSignature"];
     }
     {
         [dict setObject:self->_voucherSignature forKey:@"voucherSignature"];
     }
-    if (self->_preapprovedKeys)
-    {
-        [dict setObject:self->_preapprovedKeys forKey:@"preapprovedKeys"];
-    }
     return dict;
 }
 
     return dict;
 }
 
@@ -100,15 +71,6 @@ BOOL OTSponsorToApplicantRound2M2ReadFrom(__unsafe_unretained OTSponsorToApplica
                 self->_voucherSignature = new_voucherSignature;
             }
             break;
                 self->_voucherSignature = new_voucherSignature;
             }
             break;
-            case 3 /* preapprovedKeys */:
-            {
-                NSData *new_preapprovedKeys = PBReaderReadData(reader);
-                if (new_preapprovedKeys)
-                {
-                    [self addPreapprovedKeys:new_preapprovedKeys];
-                }
-            }
-            break;
             default:
                 if (!PBReaderSkipValueWithTag(reader, tag, aType))
                     return NO;
             default:
                 if (!PBReaderSkipValueWithTag(reader, tag, aType))
                     return NO;
@@ -138,13 +100,6 @@ BOOL OTSponsorToApplicantRound2M2ReadFrom(__unsafe_unretained OTSponsorToApplica
             PBDataWriterWriteDataField(writer, self->_voucherSignature, 2);
         }
     }
             PBDataWriterWriteDataField(writer, self->_voucherSignature, 2);
         }
     }
-    /* preapprovedKeys */
-    {
-        for (NSData *s_preapprovedKeys in self->_preapprovedKeys)
-        {
-            PBDataWriterWriteDataField(writer, s_preapprovedKeys, 3);
-        }
-    }
 }
 
 - (void)copyTo:(OTSponsorToApplicantRound2M2 *)other
 }
 
 - (void)copyTo:(OTSponsorToApplicantRound2M2 *)other
@@ -157,15 +112,6 @@ BOOL OTSponsorToApplicantRound2M2ReadFrom(__unsafe_unretained OTSponsorToApplica
     {
         other.voucherSignature = _voucherSignature;
     }
     {
         other.voucherSignature = _voucherSignature;
     }
-    if ([self preapprovedKeysCount])
-    {
-        [other clearPreapprovedKeys];
-        NSUInteger preapprovedKeysCnt = [self preapprovedKeysCount];
-        for (NSUInteger i = 0; i < preapprovedKeysCnt; i++)
-        {
-            [other addPreapprovedKeys:[self preapprovedKeysAtIndex:i]];
-        }
-    }
 }
 
 - (id)copyWithZone:(NSZone *)zone
 }
 
 - (id)copyWithZone:(NSZone *)zone
@@ -173,11 +119,6 @@ BOOL OTSponsorToApplicantRound2M2ReadFrom(__unsafe_unretained OTSponsorToApplica
     OTSponsorToApplicantRound2M2 *copy = [[[self class] allocWithZone:zone] init];
     copy->_voucher = [_voucher copyWithZone:zone];
     copy->_voucherSignature = [_voucherSignature copyWithZone:zone];
     OTSponsorToApplicantRound2M2 *copy = [[[self class] allocWithZone:zone] init];
     copy->_voucher = [_voucher copyWithZone:zone];
     copy->_voucherSignature = [_voucherSignature copyWithZone:zone];
-    for (NSData *v in _preapprovedKeys)
-    {
-        NSData *vCopy = [v copyWithZone:zone];
-        [copy addPreapprovedKeys:vCopy];
-    }
     return copy;
 }
 
     return copy;
 }
 
@@ -189,8 +130,6 @@ BOOL OTSponsorToApplicantRound2M2ReadFrom(__unsafe_unretained OTSponsorToApplica
     ((!self->_voucher && !other->_voucher) || [self->_voucher isEqual:other->_voucher])
     &&
     ((!self->_voucherSignature && !other->_voucherSignature) || [self->_voucherSignature isEqual:other->_voucherSignature])
     ((!self->_voucher && !other->_voucher) || [self->_voucher isEqual:other->_voucher])
     &&
     ((!self->_voucherSignature && !other->_voucherSignature) || [self->_voucherSignature isEqual:other->_voucherSignature])
-    &&
-    ((!self->_preapprovedKeys && !other->_preapprovedKeys) || [self->_preapprovedKeys isEqual:other->_preapprovedKeys])
     ;
 }
 
     ;
 }
 
@@ -201,8 +140,6 @@ BOOL OTSponsorToApplicantRound2M2ReadFrom(__unsafe_unretained OTSponsorToApplica
     [self->_voucher hash]
     ^
     [self->_voucherSignature hash]
     [self->_voucher hash]
     ^
     [self->_voucherSignature hash]
-    ^
-    [self->_preapprovedKeys hash]
     ;
 }
 
     ;
 }
 
@@ -216,10 +153,6 @@ BOOL OTSponsorToApplicantRound2M2ReadFrom(__unsafe_unretained OTSponsorToApplica
     {
         [self setVoucherSignature:other->_voucherSignature];
     }
     {
         [self setVoucherSignature:other->_voucherSignature];
     }
-    for (NSData *iter_preapprovedKeys in other->_preapprovedKeys)
-    {
-        [self addPreapprovedKeys:iter_preapprovedKeys];
-    }
 }
 
 @end
 }
 
 @end
diff --git a/keychain/ot/proto/source/OTSOSMessage.m b/keychain/ot/proto/source/OTSOSMessage.m
deleted file mode 100644 (file)
index e4a1f2a..0000000
+++ /dev/null
@@ -1,229 +0,0 @@
-// This file was automatically generated by protocompiler
-// DO NOT EDIT!
-// Compiled from OTPairingMessage.proto
-
-#import "OTSOSMessage.h"
-#import <ProtocolBuffer/PBConstants.h>
-#import <ProtocolBuffer/PBHashUtil.h>
-#import <ProtocolBuffer/PBDataReader.h>
-
-#if !__has_feature(objc_arc)
-# error This generated file depends on ARC but it is not enabled; turn on ARC, or use 'objc_use_arc' option to generate non-ARC code.
-#endif
-
-@implementation OTSOSMessage
-
-- (BOOL)hasCredential
-{
-    return _credential != nil;
-}
-@synthesize credential = _credential;
-- (BOOL)hasPeerInfo
-{
-    return _peerInfo != nil;
-}
-@synthesize peerInfo = _peerInfo;
-- (BOOL)hasCircleBlob
-{
-    return _circleBlob != nil;
-}
-@synthesize circleBlob = _circleBlob;
-- (BOOL)hasInitialSyncItems
-{
-    return _initialSyncItems != nil;
-}
-@synthesize initialSyncItems = _initialSyncItems;
-
-- (NSString *)description
-{
-    return [NSString stringWithFormat:@"%@ %@", [super description], [self dictionaryRepresentation]];
-}
-
-- (NSDictionary *)dictionaryRepresentation
-{
-    NSMutableDictionary *dict = [NSMutableDictionary dictionary];
-    if (self->_credential)
-    {
-        [dict setObject:self->_credential forKey:@"credential"];
-    }
-    if (self->_peerInfo)
-    {
-        [dict setObject:self->_peerInfo forKey:@"peerInfo"];
-    }
-    if (self->_circleBlob)
-    {
-        [dict setObject:self->_circleBlob forKey:@"circleBlob"];
-    }
-    if (self->_initialSyncItems)
-    {
-        [dict setObject:self->_initialSyncItems forKey:@"initialSyncItems"];
-    }
-    return dict;
-}
-
-BOOL OTSOSMessageReadFrom(__unsafe_unretained OTSOSMessage *self, __unsafe_unretained PBDataReader *reader) {
-    while (PBReaderHasMoreData(reader)) {
-        uint32_t tag = 0;
-        uint8_t aType = 0;
-
-        PBReaderReadTag32AndType(reader, &tag, &aType);
-
-        if (PBReaderHasError(reader))
-            break;
-
-        if (aType == TYPE_END_GROUP) {
-            break;
-        }
-
-        switch (tag) {
-
-            case 1 /* credential */:
-            {
-                NSData *new_credential = PBReaderReadData(reader);
-                self->_credential = new_credential;
-            }
-            break;
-            case 2 /* peerInfo */:
-            {
-                NSData *new_peerInfo = PBReaderReadData(reader);
-                self->_peerInfo = new_peerInfo;
-            }
-            break;
-            case 3 /* circleBlob */:
-            {
-                NSData *new_circleBlob = PBReaderReadData(reader);
-                self->_circleBlob = new_circleBlob;
-            }
-            break;
-            case 4 /* initialSyncItems */:
-            {
-                NSData *new_initialSyncItems = PBReaderReadData(reader);
-                self->_initialSyncItems = new_initialSyncItems;
-            }
-            break;
-            default:
-                if (!PBReaderSkipValueWithTag(reader, tag, aType))
-                    return NO;
-                break;
-        }
-    }
-    return !PBReaderHasError(reader);
-}
-
-- (BOOL)readFrom:(PBDataReader *)reader
-{
-    return OTSOSMessageReadFrom(self, reader);
-}
-- (void)writeTo:(PBDataWriter *)writer
-{
-    /* credential */
-    {
-        if (self->_credential)
-        {
-            PBDataWriterWriteDataField(writer, self->_credential, 1);
-        }
-    }
-    /* peerInfo */
-    {
-        if (self->_peerInfo)
-        {
-            PBDataWriterWriteDataField(writer, self->_peerInfo, 2);
-        }
-    }
-    /* circleBlob */
-    {
-        if (self->_circleBlob)
-        {
-            PBDataWriterWriteDataField(writer, self->_circleBlob, 3);
-        }
-    }
-    /* initialSyncItems */
-    {
-        if (self->_initialSyncItems)
-        {
-            PBDataWriterWriteDataField(writer, self->_initialSyncItems, 4);
-        }
-    }
-}
-
-- (void)copyTo:(OTSOSMessage *)other
-{
-    if (_credential)
-    {
-        other.credential = _credential;
-    }
-    if (_peerInfo)
-    {
-        other.peerInfo = _peerInfo;
-    }
-    if (_circleBlob)
-    {
-        other.circleBlob = _circleBlob;
-    }
-    if (_initialSyncItems)
-    {
-        other.initialSyncItems = _initialSyncItems;
-    }
-}
-
-- (id)copyWithZone:(NSZone *)zone
-{
-    OTSOSMessage *copy = [[[self class] allocWithZone:zone] init];
-    copy->_credential = [_credential copyWithZone:zone];
-    copy->_peerInfo = [_peerInfo copyWithZone:zone];
-    copy->_circleBlob = [_circleBlob copyWithZone:zone];
-    copy->_initialSyncItems = [_initialSyncItems copyWithZone:zone];
-    return copy;
-}
-
-- (BOOL)isEqual:(id)object
-{
-    OTSOSMessage *other = (OTSOSMessage *)object;
-    return [other isMemberOfClass:[self class]]
-    &&
-    ((!self->_credential && !other->_credential) || [self->_credential isEqual:other->_credential])
-    &&
-    ((!self->_peerInfo && !other->_peerInfo) || [self->_peerInfo isEqual:other->_peerInfo])
-    &&
-    ((!self->_circleBlob && !other->_circleBlob) || [self->_circleBlob isEqual:other->_circleBlob])
-    &&
-    ((!self->_initialSyncItems && !other->_initialSyncItems) || [self->_initialSyncItems isEqual:other->_initialSyncItems])
-    ;
-}
-
-- (NSUInteger)hash
-{
-    return 0
-    ^
-    [self->_credential hash]
-    ^
-    [self->_peerInfo hash]
-    ^
-    [self->_circleBlob hash]
-    ^
-    [self->_initialSyncItems hash]
-    ;
-}
-
-- (void)mergeFrom:(OTSOSMessage *)other
-{
-    if (other->_credential)
-    {
-        [self setCredential:other->_credential];
-    }
-    if (other->_peerInfo)
-    {
-        [self setPeerInfo:other->_peerInfo];
-    }
-    if (other->_circleBlob)
-    {
-        [self setCircleBlob:other->_circleBlob];
-    }
-    if (other->_initialSyncItems)
-    {
-        [self setInitialSyncItems:other->_initialSyncItems];
-    }
-}
-
-@end
-
diff --git a/keychain/ot/tests/octagon/.swiftlint.yml b/keychain/ot/tests/octagon/.swiftlint.yml
new file mode 100644 (file)
index 0000000..9ddd0d1
--- /dev/null
@@ -0,0 +1,3 @@
+disabled_rules:
+    - force_cast
+    - force_try
index 0b457d231af78a482a1dfa84d2632a1c95e78405..82e22519249dd8ef5c5b7f984622fa264c1278b5 100644 (file)
@@ -19,6 +19,7 @@ class OctagonAccountMetadataClassCPersistenceTests: CloudKitKeychainSyncingMockX
         state.peerID = "asdf"
         state.icloudAccountState = .ACCOUNT_AVAILABLE
         state.trustState = .TRUSTED
         state.peerID = "asdf"
         state.icloudAccountState = .ACCOUNT_AVAILABLE
         state.trustState = .TRUSTED
+        state.cdpState = .ENABLED
 
         XCTAssertNoThrow(try state.saveToKeychain(forContainer: OTCKContainerName, contextID: OTDefaultContext), "saving to the keychain should work")
 
 
         XCTAssertNoThrow(try state.saveToKeychain(forContainer: OTCKContainerName, contextID: OTDefaultContext), "saving to the keychain should work")
 
@@ -28,6 +29,7 @@ class OctagonAccountMetadataClassCPersistenceTests: CloudKitKeychainSyncingMockX
             XCTAssertEqual(state2.peerID, state.peerID, "peer ID persists through keychain")
             XCTAssertEqual(state2.icloudAccountState, state.icloudAccountState, "account state persists through keychain")
             XCTAssertEqual(state2.trustState, state.trustState, "trust state persists through keychain")
             XCTAssertEqual(state2.peerID, state.peerID, "peer ID persists through keychain")
             XCTAssertEqual(state2.icloudAccountState, state.icloudAccountState, "account state persists through keychain")
             XCTAssertEqual(state2.trustState, state.trustState, "trust state persists through keychain")
+            XCTAssertEqual(state2.cdpState, state.cdpState, "cdp state persists through keychain")
         } catch {
             XCTFail("error loading from keychain: \(error)")
         }
         } catch {
             XCTFail("error loading from keychain: \(error)")
         }
index a9d31759e0329062f7620451df526ca5bf5c30e6..ef94770e4f37aeb4bee228d1344c3cf83bb94c67 100644 (file)
@@ -37,6 +37,10 @@ class OTMockSecureBackup: NSObject, OctagonEscrowRecovererPrococol {
         }
         return nil
     }
         }
         return nil
     }
+
+    func disable(withInfo info: [AnyHashable: Any]!) -> Error? {
+        return nil
+    }
 }
 
 class OTMockFollowUpController: NSObject, OctagonFollowUpControllerProtocol {
 }
 
 class OTMockFollowUpController: NSObject, OctagonFollowUpControllerProtocol {
diff --git a/keychain/ot/tests/octagon/OctagonTests+Account.swift b/keychain/ot/tests/octagon/OctagonTests+Account.swift
new file mode 100644 (file)
index 0000000..fa1e387
--- /dev/null
@@ -0,0 +1,568 @@
+/*
+* Copyright (c) 2019 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
+
+#if OCTAGON
+
+class OctagonAccountTests: OctagonTestsBase {
+    func testAccountSave() throws {
+        let contextName = OTDefaultContext
+        let containerName = OTCKContainerName
+
+        self.startCKAccountStatusMock()
+
+        // Before resetAndEstablish, there shouldn't be any stored account state
+        XCTAssertThrowsError(try OTAccountMetadataClassC.loadFromKeychain(forContainer: containerName, contextID: contextName), "Before doing anything, loading a non-existent account state should fail")
+
+        let resetAndEstablishExpectation = self.expectation(description: "resetAndEstablish callback occurs")
+        self.manager.resetAndEstablish(containerName,
+                                       context: contextName,
+                                       altDSID: "new altDSID",
+                                       resetReason: .testGenerated) { resetError in
+                                        XCTAssertNil(resetError, "Should be no error calling resetAndEstablish")
+                                        resetAndEstablishExpectation.fulfill()
+        }
+
+        self.wait(for: [resetAndEstablishExpectation], timeout: 10)
+        self.assertEnters(context: self.cuttlefishContext, state: OctagonStateReady, within: 10 * NSEC_PER_SEC)
+
+        let selfPeerID = try self.cuttlefishContext.accountMetadataStore.loadOrCreateAccountMetadata().peerID
+
+        // After resetAndEstablish, you should be able to see the persisted account state
+        do {
+            let accountState = try OTAccountMetadataClassC.loadFromKeychain(forContainer: containerName, contextID: contextName)
+            XCTAssertEqual(selfPeerID, accountState.peerID, "Saved account state should have the same peer ID that prepare returned")
+            XCTAssertEqual(accountState.cdpState, .ENABLED, "Saved CDP status should be 'enabled' after a resetAndEstablish")
+        } catch {
+            XCTFail("error loading account state: \(error)")
+        }
+    }
+
+    func testLoadToNoAccount() throws {
+        // No CloudKit account, either
+        self.accountStatus = .noAccount
+        self.startCKAccountStatusMock()
+
+        // Tell SOS that it is absent, so we don't enable CDP on bringup
+        self.mockSOSAdapter.circleStatus = SOSCCStatus(kSOSCCCircleAbsent)
+
+        // With no identity and AuthKit reporting no iCloud account, Octagon should go directly into 'no account'
+        self.mockAuthKit.altDSID = nil
+
+        let asyncExpectation = self.expectation(description: "dispatch works")
+        let quiescentExpectation = self.expectation(description: "quiescence has been determined")
+        DispatchQueue.global(qos: .userInitiated).async { [weak self] in
+            asyncExpectation.fulfill()
+
+            let c = self!.cuttlefishContext.stateMachine.paused
+            XCTAssertEqual(0, c.wait(10 * NSEC_PER_SEC), "State machine should become quiescent")
+            quiescentExpectation.fulfill()
+        }
+        // Wait for the block above to fire before continuing
+        self.wait(for: [asyncExpectation], timeout: 10)
+
+        self.cuttlefishContext.startOctagonStateMachine()
+
+        self.assertEnters(context: self.cuttlefishContext, state: OctagonStateNoAccount, within: 10 * NSEC_PER_SEC)
+        XCTAssertTrue(self.cuttlefishContext.stateMachine.isPaused(), "State machine should be stopped")
+        self.assertNoAccount(context: self.cuttlefishContext)
+
+        XCTAssertEqual(0, self.cuttlefishContext.stateMachine.paused.wait(10 * NSEC_PER_SEC), "State machine should be quiescent")
+
+        self.wait(for: [quiescentExpectation], timeout: 10)
+
+        // CKKS should also be logged out, since Octagon believes there's no account
+        self.assertAllCKKSViews(enter: SecCKKSZoneKeyStateLoggedOut, within: 10 * NSEC_PER_SEC)
+    }
+
+    func testNoAccountLeadsToInitialize() throws {
+        self.startCKAccountStatusMock()
+
+        // Tell SOS that it is absent, so we don't enable CDP on bringup
+        self.mockSOSAdapter.circleStatus = SOSCCStatus(kSOSCCCircleAbsent)
+
+        // With no identity and AuthKit reporting no iCloud account, Octagon should go directly into 'no account'
+        self.mockAuthKit.altDSID = nil
+
+        self.cuttlefishContext.startOctagonStateMachine()
+
+        self.assertEnters(context: self.cuttlefishContext, state: OctagonStateNoAccount, within: 10 * NSEC_PER_SEC)
+
+        self.mockAuthKit.altDSID = "1234"
+        let signinExpectation = self.expectation(description: "sign in returns")
+        self.otControl.sign(in: "1234", container: nil, context: OTDefaultContext) { error in
+            XCTAssertNil(error, "error should be nil")
+            signinExpectation.fulfill()
+        }
+        self.wait(for: [signinExpectation], timeout: 10)
+
+        self.assertEnters(context: self.cuttlefishContext, state: OctagonStateWaitForCDP, within: 10 * NSEC_PER_SEC)
+
+        // And now the CDP bit is set...
+        XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
+
+        self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
+    }
+
+    func testSignIn() throws {
+        self.startCKAccountStatusMock()
+
+        // Tell SOS that it is absent, so we don't enable CDP on bringup
+        self.mockSOSAdapter.circleStatus = SOSCCStatus(kSOSCCCircleAbsent)
+
+        // Device is signed out
+        self.mockAuthKit.altDSID = nil
+        self.mockAuthKit.hsa2 = false
+
+        // With no account, Octagon should go directly into 'NoAccount'
+        self.cuttlefishContext.startOctagonStateMachine()
+        self.assertEnters(context: self.cuttlefishContext, state: OctagonStateNoAccount, within: 10 * NSEC_PER_SEC)
+
+        // Sign in occurs
+        let newAltDSID = UUID().uuidString
+        self.mockAuthKit.altDSID = newAltDSID
+        self.mockAuthKit.hsa2 = true
+        XCTAssertNoThrow(try self.cuttlefishContext.accountAvailable(newAltDSID), "Sign-in shouldn't error")
+
+        // Octagon should go into 'waitforcdp'
+        self.assertEnters(context: self.cuttlefishContext, state: OctagonStateWaitForCDP, within: 10 * NSEC_PER_SEC)
+        self.assertConsidersSelfWaitingForCDP(context: self.cuttlefishContext)
+        XCTAssertEqual(self.fetchCDPStatus(context: self.cuttlefishContext), .disabled, "CDP status should be 'disabled'")
+        XCTAssertFalse(self.cuttlefishContext.followupHandler.hasPosted(.stateRepair), "Octagon should have not posted a repair CFU while waiting for CDP")
+
+        // And CDP is enabled:
+        XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
+
+        self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
+        self.assertConsidersSelfUntrusted(context: self.cuttlefishContext)
+        XCTAssertEqual(self.fetchCDPStatus(context: self.cuttlefishContext), .enabled, "CDP status should be 'enabled'")
+
+        #if !os(tvOS)
+        XCTAssertTrue(self.cuttlefishContext.followupHandler.hasPosted(.stateRepair), "Octagon should have posted a repair CFU")
+        #else
+        XCTAssertFalse(self.cuttlefishContext.followupHandler.hasPosted(.stateRepair), "posted should be false on tvOS; there aren't any devices around to repair it")
+        #endif
+
+        self.assertAllCKKSViews(enter: SecCKKSZoneKeyStateWaitForTLKCreation, within: 10 * NSEC_PER_SEC)
+
+        // On sign-out, octagon should go back to 'no account'
+        self.mockAuthKit.altDSID = nil
+        XCTAssertNoThrow(try self.cuttlefishContext.accountNoLongerAvailable(), "sign-out shouldn't error")
+        self.assertEnters(context: self.cuttlefishContext, state: OctagonStateNoAccount, within: 10 * NSEC_PER_SEC)
+        self.assertNoAccount(context: self.cuttlefishContext)
+        XCTAssertEqual(self.fetchCDPStatus(context: self.cuttlefishContext), .unknown, "CDP status should be 'unknown'")
+
+        self.assertAllCKKSViews(enter: SecCKKSZoneKeyStateWaitForTLKCreation, within: 10 * NSEC_PER_SEC)
+    }
+
+    func testSignInWithDelayedHSA2Status() throws {
+        self.startCKAccountStatusMock()
+
+        // Tell SOS that it is absent, so we don't enable CDP on bringup
+        self.mockSOSAdapter.circleStatus = SOSCCStatus(kSOSCCCircleAbsent)
+
+        // Device is signed out
+        self.mockAuthKit.altDSID = nil
+        self.mockAuthKit.hsa2 = false
+
+        // With no account, Octagon should go directly into 'NoAccount'
+        self.cuttlefishContext.startOctagonStateMachine()
+        self.assertEnters(context: self.cuttlefishContext, state: OctagonStateNoAccount, within: 10 * NSEC_PER_SEC)
+
+        // Sign in occurs, but HSA2 status isn't here yet
+        let newAltDSID = UUID().uuidString
+        self.mockAuthKit.altDSID = newAltDSID
+        XCTAssertNoThrow(try self.cuttlefishContext.accountAvailable(newAltDSID), "Sign-in shouldn't error")
+
+        // Octagon should go into 'waitforhsa2'
+        self.assertEnters(context: self.cuttlefishContext, state: OctagonStateWaitForHSA2, within: 10 * NSEC_PER_SEC)
+
+        self.mockAuthKit.hsa2 = true
+        XCTAssertNoThrow(try self.cuttlefishContext.idmsTrustLevelChanged(), "Notification of IDMS trust level shouldn't error")
+
+        self.assertEnters(context: self.cuttlefishContext, state: OctagonStateWaitForCDP, within: 10 * NSEC_PER_SEC)
+        self.assertConsidersSelfWaitingForCDP(context: self.cuttlefishContext)
+
+        XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
+
+        self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
+        self.assertConsidersSelfUntrusted(context: self.cuttlefishContext)
+
+        self.assertAllCKKSViews(enter: SecCKKSZoneKeyStateWaitForTLKCreation, within: 10 * NSEC_PER_SEC)
+
+        // On sign-out, octagon should go back to 'no account'
+        self.mockAuthKit.altDSID = nil
+        XCTAssertNoThrow(try self.cuttlefishContext.accountNoLongerAvailable(), "sign-out shouldn't error")
+        self.assertEnters(context: self.cuttlefishContext, state: OctagonStateNoAccount, within: 10 * NSEC_PER_SEC)
+        self.assertNoAccount(context: self.cuttlefishContext)
+
+        self.assertAllCKKSViews(enter: SecCKKSZoneKeyStateWaitForTLKCreation, within: 10 * NSEC_PER_SEC)
+    }
+
+    func testSignInWithCDPStateBeforeDelayedHSA2Status() throws {
+        self.startCKAccountStatusMock()
+
+        // Device is signed out
+        self.mockAuthKit.altDSID = nil
+        self.mockAuthKit.hsa2 = false
+
+        // With no account, Octagon should go directly into 'NoAccount'
+        self.cuttlefishContext.startOctagonStateMachine()
+        self.assertEnters(context: self.cuttlefishContext, state: OctagonStateNoAccount, within: 10 * NSEC_PER_SEC)
+
+        // CDP state is set. Cool?
+        XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
+
+        // Sign in occurs, but HSA2 status isn't here yet
+        let newAltDSID = UUID().uuidString
+        self.mockAuthKit.altDSID = newAltDSID
+        self.mockAuthKit.hsa2 = true
+        XCTAssertNoThrow(try self.cuttlefishContext.accountAvailable(newAltDSID), "Sign-in shouldn't error")
+
+        // Octagon should go into 'untrusted', as everything is in place
+        self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
+        self.assertAllCKKSViews(enter: SecCKKSZoneKeyStateWaitForTLKCreation, within: 10 * NSEC_PER_SEC)
+
+        // On sign-out, octagon should go back to 'no account'
+        self.mockAuthKit.altDSID = nil
+        XCTAssertNoThrow(try self.cuttlefishContext.accountNoLongerAvailable(), "sign-out shouldn't error")
+        self.assertEnters(context: self.cuttlefishContext, state: OctagonStateNoAccount, within: 10 * NSEC_PER_SEC)
+        self.assertNoAccount(context: self.cuttlefishContext)
+
+        self.assertAllCKKSViews(enter: SecCKKSZoneKeyStateWaitForTLKCreation, within: 10 * NSEC_PER_SEC)
+    }
+
+    func testSetCDPStateWithUnconfiguredArguments() throws {
+        self.startCKAccountStatusMock()
+
+        // Tell SOS that it is absent, so we don't enable CDP on bringup
+        self.mockSOSAdapter.circleStatus = SOSCCStatus(kSOSCCCircleAbsent)
+
+        // Device is signed out
+        self.mockAuthKit.altDSID = nil
+        self.mockAuthKit.hsa2 = false
+
+        // With no account, Octagon should go directly into 'NoAccount'
+        self.cuttlefishContext.startOctagonStateMachine()
+        self.assertEnters(context: self.cuttlefishContext, state: OctagonStateNoAccount, within: 10 * NSEC_PER_SEC)
+
+        // Now set the CDP state, but using the OTClique API (and not configuring the context)
+        let unconfigured = OTConfigurationContext()
+        unconfigured.otControl = self.otControl
+        XCTAssertNoThrow(try OTClique.setCDPEnabled(unconfigured))
+
+        // Sign in occurs, but HSA2 status isn't here yet
+        let newAltDSID = UUID().uuidString
+        self.mockAuthKit.altDSID = newAltDSID
+        XCTAssertNoThrow(try self.cuttlefishContext.accountAvailable(newAltDSID), "Sign-in shouldn't error")
+
+        // Octagon should go into 'waitforhsa2'
+        self.assertEnters(context: self.cuttlefishContext, state: OctagonStateWaitForHSA2, within: 10 * NSEC_PER_SEC)
+
+        self.mockAuthKit.hsa2 = true
+        XCTAssertNoThrow(try self.cuttlefishContext.idmsTrustLevelChanged(), "Notification of IDMS trust level shouldn't error")
+
+        // and we should skip waiting for CDP, as it's already set
+        self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
+        self.assertConsidersSelfUntrusted(context: self.cuttlefishContext)
+
+        self.assertAllCKKSViews(enter: SecCKKSZoneKeyStateWaitForTLKCreation, within: 10 * NSEC_PER_SEC)
+
+        // On sign-out, octagon should go back to 'no account'
+        self.mockAuthKit.altDSID = nil
+        XCTAssertNoThrow(try self.cuttlefishContext.accountNoLongerAvailable(), "sign-out shouldn't error")
+        self.assertEnters(context: self.cuttlefishContext, state: OctagonStateNoAccount, within: 10 * NSEC_PER_SEC)
+        self.assertNoAccount(context: self.cuttlefishContext)
+
+        self.assertAllCKKSViews(enter: SecCKKSZoneKeyStateWaitForTLKCreation, within: 10 * NSEC_PER_SEC)
+    }
+
+    func testSignInWithExistingCuttlefishRecordsSetsCDPStatus() throws {
+        self.startCKAccountStatusMock()
+
+        // Tell SOS that it is absent, so we don't enable CDP on bringup
+        self.mockSOSAdapter.circleStatus = SOSCCStatus(kSOSCCCircleAbsent)
+
+        // signing in to an account with pre-existing Octagon data should turn on the CDP bit by default:
+        // no setting needed.
+
+        let remote = self.makeInitiatorContext(contextID: "remote")
+        self.assertResetAndBecomeTrusted(context: remote)
+
+        // when this context boots up, it should go straight into untrusted, and set its CDP bit
+        // since there's already CDP data in the account
+        self.cuttlefishContext.startOctagonStateMachine()
+        self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
+        XCTAssertEqual(self.fetchCDPStatus(context: self.cuttlefishContext), .enabled, "CDP status should be 'enabled'")
+
+        #if !os(tvOS)
+        XCTAssertTrue(self.cuttlefishContext.followupHandler.hasPosted(.stateRepair), "Octagon should have posted a repair CFU after the CDP bit was set")
+        #else
+        XCTAssertFalse(self.cuttlefishContext.followupHandler.hasPosted(.stateRepair), "Octagon should not have posted on tvOS; there aren't any iphones around to repair it")
+        #endif
+    }
+
+    func testEnableCDPStatusIfNotificationArrives() throws {
+        self.startCKAccountStatusMock()
+
+        // Tell SOS that it is absent, so we don't enable CDP on bringup
+        self.mockSOSAdapter.circleStatus = SOSCCStatus(kSOSCCCircleAbsent)
+
+        // default context comes up, but CDP is not enabled
+        self.cuttlefishContext.startOctagonStateMachine()
+        self.assertEnters(context: self.cuttlefishContext, state: OctagonStateWaitForCDP, within: 10 * NSEC_PER_SEC)
+        self.assertConsidersSelfWaitingForCDP(context: self.cuttlefishContext)
+        XCTAssertEqual(self.fetchCDPStatus(context: self.cuttlefishContext), .disabled, "CDP status should be 'disabled'")
+        XCTAssertFalse(self.cuttlefishContext.followupHandler.hasPosted(.stateRepair), "Octagon should have not posted a repair CFU while waiting for CDP")
+
+        // If a cuttlefish push occurs without any data existing, the CDP bit should stay off
+        self.sendContainerChangeWaitForFetchForStates(context: self.cuttlefishContext, states: [OctagonStateWaitForCDPUpdated, OctagonStateDetermineCDPState])
+        self.assertEnters(context: self.cuttlefishContext, state: OctagonStateWaitForCDP, within: 10 * NSEC_PER_SEC)
+        self.assertConsidersSelfWaitingForCDP(context: self.cuttlefishContext)
+        XCTAssertEqual(self.fetchCDPStatus(context: self.cuttlefishContext), .disabled, "CDP status should be 'disabled'")
+        XCTAssertFalse(self.cuttlefishContext.followupHandler.hasPosted(.stateRepair), "Octagon should have not posted a repair CFU while waiting for CDP")
+
+        // Another peer comes along and performs Octagon operations
+        let remote = self.makeInitiatorContext(contextID: "remote")
+        self.assertResetAndBecomeTrusted(context: remote)
+
+        // And some SOS operations. SOS now returns "error" when asked its state
+        self.mockSOSAdapter.circleStatusError = NSError(domain: kSOSErrorDomain as String, code: kSOSErrorPublicKeyAbsent, userInfo: nil)
+        self.mockSOSAdapter.circleStatus = SOSCCStatus(kSOSCCError)
+
+        // And after the update, the context should go into 'untrusted'
+        self.sendContainerChangeWaitForFetchForStates(context: self.cuttlefishContext, states: [OctagonStateWaitForCDPUpdated, OctagonStateDetermineCDPState])
+        self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
+        XCTAssertEqual(self.fetchCDPStatus(context: self.cuttlefishContext), .enabled, "CDP status should be 'enabled'")
+
+        #if !os(tvOS)
+        XCTAssertTrue(self.cuttlefishContext.followupHandler.hasPosted(.stateRepair), "Octagon should have posted a repair CFU")
+        #else
+        XCTAssertFalse(self.cuttlefishContext.followupHandler.hasPosted(.stateRepair), "Octagon should not have posted on tvOS; there aren't any iphones around to repair it")
+        #endif
+    }
+
+    func testEnableCDPStatusIfNotificationArrivesWithoutCreatingSOSCircle() throws {
+        self.startCKAccountStatusMock()
+
+        // Tell SOS that it is absent, so we don't enable CDP on bringup
+        self.mockSOSAdapter.circleStatus = SOSCCStatus(kSOSCCCircleAbsent)
+
+        // default context comes up, but CDP is not enabled
+        self.cuttlefishContext.startOctagonStateMachine()
+        self.assertEnters(context: self.cuttlefishContext, state: OctagonStateWaitForCDP, within: 10 * NSEC_PER_SEC)
+        self.assertConsidersSelfWaitingForCDP(context: self.cuttlefishContext)
+        XCTAssertEqual(self.fetchCDPStatus(context: self.cuttlefishContext), .disabled, "CDP status should be 'disabled'")
+        XCTAssertFalse(self.cuttlefishContext.followupHandler.hasPosted(.stateRepair), "Octagon should have not posted a repair CFU while waiting for CDP")
+
+        // If a cuttlefish push occurs without any data existing, the CDP bit should stay off
+        self.sendContainerChangeWaitForFetchForStates(context: self.cuttlefishContext, states: [OctagonStateWaitForCDPUpdated, OctagonStateDetermineCDPState])
+        self.assertEnters(context: self.cuttlefishContext, state: OctagonStateWaitForCDP, within: 10 * NSEC_PER_SEC)
+        self.assertConsidersSelfWaitingForCDP(context: self.cuttlefishContext)
+        XCTAssertEqual(self.fetchCDPStatus(context: self.cuttlefishContext), .disabled, "CDP status should be 'disabled'")
+        XCTAssertFalse(self.cuttlefishContext.followupHandler.hasPosted(.stateRepair), "Octagon should have not posted a repair CFU while waiting for CDP")
+
+        // Another peer comes along and performs Octagon operations
+        let remote = self.makeInitiatorContext(contextID: "remote")
+        self.assertResetAndBecomeTrusted(context: remote)
+
+        // Unlike testEnableCDPStatusIfNotificationArrives, SOS remains in 'absent', simulating a non-sos platform creating Octagon
+
+        // And after the update, the context should go into 'untrusted'
+        self.sendContainerChangeWaitForFetchForStates(context: self.cuttlefishContext, states: [OctagonStateWaitForCDPUpdated, OctagonStateDetermineCDPState])
+        self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
+        XCTAssertEqual(self.fetchCDPStatus(context: self.cuttlefishContext), .enabled, "CDP status should be 'enabled'")
+
+        #if !os(tvOS)
+        XCTAssertTrue(self.cuttlefishContext.followupHandler.hasPosted(.stateRepair), "Octagon should have posted a repair CFU")
+        #else
+        XCTAssertFalse(self.cuttlefishContext.followupHandler.hasPosted(.stateRepair), "Octagon should not have posted on tvOS; there aren't any iphones around to repair it")
+        #endif
+    }
+
+    func testCDPEnableAPIRaceWithCDPStateDetermination() throws {
+        // The API call to enable CDP might occur while Octagon is figuring out that it should be disabled
+        // Octagon should respect the API call
+
+        // SOS is absent
+        self.mockSOSAdapter.circleStatus = SOSCCStatus(kSOSCCCircleAbsent)
+
+        self.startCKAccountStatusMock()
+
+        // The initial CDP-enable API call happens while Octagon is fetching the world's state
+        let fetchExpectation = self.expectation(description: "fetchChanges called")
+        self.fakeCuttlefishServer.fetchChangesListener = { [unowned self] _ in
+            do {
+                try self.cuttlefishContext.setCDPEnabled()
+            } catch {
+                XCTFail("Expected to be able to set CDP status without error: \(error)")
+            }
+            fetchExpectation.fulfill()
+            return nil
+        }
+
+        self.cuttlefishContext.startOctagonStateMachine()
+
+        // Octagon should go into 'untrusted', as the API call said that CDP was there
+        self.wait(for: [fetchExpectation], timeout: 10)
+        self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
+        self.assertConsidersSelfUntrusted(context: self.cuttlefishContext)
+
+        self.assertAllCKKSViews(enter: SecCKKSZoneKeyStateWaitForTLKCreation, within: 10 * NSEC_PER_SEC)
+    }
+
+    func testSignOut() throws {
+        self.startCKAccountStatusMock()
+
+        // Tell SOS that it is absent, so we don't enable CDP on bringup
+        self.mockSOSAdapter.circleStatus = SOSCCStatus(kSOSCCCircleAbsent)
+
+        self.cuttlefishContext.startOctagonStateMachine()
+        self.assertEnters(context: self.cuttlefishContext, state: OctagonStateWaitForCDP, within: 10 * NSEC_PER_SEC)
+
+        XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
+        self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
+
+        do {
+            let clique = try OTClique.newFriends(withContextData: self.otcliqueContext, resetReason: .testGenerated)
+            XCTAssertNotNil(clique, "Clique should not be nil")
+        } catch {
+            XCTFail("Shouldn't have errored making new friends: \(error)")
+        }
+
+        // Now, we should be in 'ready'
+        self.assertEnters(context: self.cuttlefishContext, state: OctagonStateReady, within: 10 * NSEC_PER_SEC)
+        self.assertConsidersSelfTrusted(context: self.cuttlefishContext)
+        self.verifyDatabaseMocks()
+        self.assertAllCKKSViews(enter: SecCKKSZoneKeyStateReady, within: 10 * NSEC_PER_SEC)
+
+        // And 'dump' should show some information
+        let dumpExpectation = self.expectation(description: "dump callback occurs")
+        self.tphClient.dump(withContainer: self.cuttlefishContext.containerName, context: self.cuttlefishContext.contextID) { dump, error in
+            XCTAssertNil(error, "Should be no error dumping data")
+            XCTAssertNotNil(dump, "dump should not be nil")
+            let egoSelf = dump!["self"] as? [String: AnyObject]
+            XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
+            let peerID = egoSelf!["peerID"] as? String
+            XCTAssertNotNil(peerID, "peerID should not be nil")
+
+            dumpExpectation.fulfill()
+        }
+        self.wait(for: [dumpExpectation], timeout: 10)
+
+        // Turn off the CK account too
+        self.accountStatus = .noAccount
+        self.accountStateTracker.notifyCKAccountStatusChangeAndWaitForSignal()
+
+        XCTAssertNoThrow(try self.cuttlefishContext.accountNoLongerAvailable(), "Should be no issue signing out")
+        self.assertEnters(context: self.cuttlefishContext, state: OctagonStateNoAccount, within: 10 * NSEC_PER_SEC)
+        self.assertNoAccount(context: self.cuttlefishContext)
+
+        // And 'dump' should show nothing
+        let signedOutDumpExpectation = self.expectation(description: "dump callback occurs")
+        self.tphClient.dump(withContainer: self.cuttlefishContext.containerName, context: self.cuttlefishContext.contextID) { dump, error in
+            XCTAssertNil(error, "Should be no error dumping data")
+            XCTAssertNotNil(dump, "dump should not be nil")
+            let egoSelf = dump!["self"] as? [String: AnyObject]
+            XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
+            XCTAssertEqual(egoSelf!.count, 0, "egoSelf should have zero elements")
+
+            signedOutDumpExpectation.fulfill()
+        }
+        self.wait(for: [signedOutDumpExpectation], timeout: 10)
+        self.assertAllCKKSViews(enter: SecCKKSZoneKeyStateLoggedOut, within: 10 * NSEC_PER_SEC)
+
+        //check trust status
+        let checkTrustExpectation = self.expectation(description: "checkTrustExpectation callback occurs")
+        let configuration = OTOperationConfiguration()
+        self.cuttlefishContext.rpcTrustStatus(configuration) { _, _, _, _, _ in
+            checkTrustExpectation.fulfill()
+        }
+        self.wait(for: [checkTrustExpectation], timeout: 10)
+
+        self.assertEnters(context: self.cuttlefishContext, state: OctagonStateNoAccount, within: 10 * NSEC_PER_SEC)
+
+        // And 'dump' should show nothing
+        let signedOutDumpExpectationAfterCheckTrustStatus = self.expectation(description: "dump callback occurs")
+        self.tphClient.dump(withContainer: self.cuttlefishContext.containerName, context: self.cuttlefishContext.contextID) { dump, error in
+            XCTAssertNil(error, "Should be no error dumping data")
+            XCTAssertNotNil(dump, "dump should not be nil")
+            let egoSelf = dump!["self"] as? [String: AnyObject]
+            XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
+            XCTAssertEqual(egoSelf!.count, 0, "egoSelf should have zero elements")
+
+            signedOutDumpExpectationAfterCheckTrustStatus.fulfill()
+        }
+        self.wait(for: [signedOutDumpExpectationAfterCheckTrustStatus], timeout: 10)
+        self.assertEnters(context: self.cuttlefishContext, state: OctagonStateNoAccount, within: 10 * NSEC_PER_SEC)
+        self.assertNoAccount(context: self.cuttlefishContext)
+    }
+
+    func testSignOutFromWaitForCDP() throws {
+        self.startCKAccountStatusMock()
+
+        // Tell SOS that it is absent, so we don't enable CDP on bringup
+        self.mockSOSAdapter.circleStatus = SOSCCStatus(kSOSCCCircleAbsent)
+
+        self.cuttlefishContext.startOctagonStateMachine()
+        self.assertEnters(context: self.cuttlefishContext, state: OctagonStateWaitForCDP, within: 10 * NSEC_PER_SEC)
+
+        // Turn off the CK account too
+        self.accountStatus = .noAccount
+        self.accountStateTracker.notifyCKAccountStatusChangeAndWaitForSignal()
+
+        XCTAssertNoThrow(try self.cuttlefishContext.accountNoLongerAvailable(), "Should be no issue signing out")
+        self.assertEnters(context: self.cuttlefishContext, state: OctagonStateNoAccount, within: 10 * NSEC_PER_SEC)
+        self.assertNoAccount(context: self.cuttlefishContext)
+        XCTAssertEqual(self.fetchCDPStatus(context: self.cuttlefishContext), .unknown, "CDP status should be 'unknown'")
+    }
+
+    func testNoAccountTimeoutTransitionWatcher() throws {
+        self.startCKAccountStatusMock()
+
+        // Tell SOS that it is absent, so we don't enable CDP on bringup
+        self.mockSOSAdapter.circleStatus = SOSCCStatus(kSOSCCCircleAbsent)
+
+        // With no identity and AuthKit reporting no iCloud account, Octagon should go directly into 'no account'
+        self.mockAuthKit.altDSID = nil
+
+        self.cuttlefishContext.startOctagonStateMachine()
+        self.cuttlefishContext.stateMachine.setWatcherTimeout(2 * NSEC_PER_SEC)
+
+        self.assertEnters(context: self.cuttlefishContext, state: OctagonStateNoAccount, within: 10 * NSEC_PER_SEC)
+        XCTAssertTrue(self.cuttlefishContext.stateMachine.isPaused(), "State machine should be stopped")
+        self.assertNoAccount(context: self.cuttlefishContext)
+        XCTAssertEqual(0, self.cuttlefishContext.stateMachine.paused.wait(10 * NSEC_PER_SEC), "State machine should be quiescent")
+
+        let joinWithBottleExpectation = self.expectation(description: "joinWithBottle callback occurs")
+        self.cuttlefishContext.join(withBottle: "bottleID", entropy: Data(), bottleSalt: "peer2AltDSID") { error in
+            XCTAssertNotNil(error, "error should not be nil")
+            joinWithBottleExpectation.fulfill()
+        }
+        self.wait(for: [joinWithBottleExpectation], timeout: 3)
+    }
+
+}
+
+#endif
index d088ab714e80c1a8d4b9a242207eadc828633bb6..4888f6e160c442137e0f6159e8a40ad7d5e9ebe9 100644 (file)
@@ -6,11 +6,11 @@ class OctagonCKKSTests: OctagonTestsBase {
 
         // Right after CKKS fetches for the first time, insert a new key hierarchy into CloudKit
         self.silentFetchesAllowed = false
 
         // Right after CKKS fetches for the first time, insert a new key hierarchy into CloudKit
         self.silentFetchesAllowed = false
-        self.expectCKFetchAndRun(beforeFinished: {
-            self.putFakeKeyHierarchy(inCloudKit: self.manateeZoneID)
-            self.putFakeDeviceStatus(inCloudKit: self.manateeZoneID)
+        self.expectCKFetchAndRun {
+            self.putFakeKeyHierarchiesInCloudKit()
+            self.putFakeDeviceStatusesInCloudKit()
             self.silentFetchesAllowed = true
             self.silentFetchesAllowed = true
-        })
+        }
 
         self.cuttlefishContext.startOctagonStateMachine()
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
         self.cuttlefishContext.startOctagonStateMachine()
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
@@ -37,12 +37,12 @@ class OctagonCKKSTests: OctagonTestsBase {
 
         // Right after CKKS fetches for the first time, insert a new key hierarchy into CloudKit
         self.silentFetchesAllowed = false
 
         // Right after CKKS fetches for the first time, insert a new key hierarchy into CloudKit
         self.silentFetchesAllowed = false
-        self.expectCKFetchAndRun(beforeFinished: {
-            self.putFakeKeyHierarchy(inCloudKit: self.manateeZoneID)
-            self.putFakeDeviceStatus(inCloudKit: self.manateeZoneID)
-            self.saveTLKMaterial(toKeychain: self.manateeZoneID)
+        self.expectCKFetchAndRun {
+            self.putFakeKeyHierarchiesInCloudKit()
+            self.putFakeDeviceStatusesInCloudKit()
+            self.saveTLKMaterialToKeychain()
             self.silentFetchesAllowed = true
             self.silentFetchesAllowed = true
-        })
+        }
 
         self.cuttlefishContext.startOctagonStateMachine()
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
         self.cuttlefishContext.startOctagonStateMachine()
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
diff --git a/keychain/ot/tests/octagon/OctagonTests+CKKSConfiguration.swift b/keychain/ot/tests/octagon/OctagonTests+CKKSConfiguration.swift
new file mode 100644 (file)
index 0000000..67e9a82
--- /dev/null
@@ -0,0 +1,149 @@
+import Foundation
+
+class OctagonCKKSConfigurationTestsPolicyDisabled: OctagonTestsBase {
+    // Pre-configure some things, so the OctagonTests will only operate on these views
+    override func setUp() {
+        if self.mockDeviceInfo == nil {
+            let actualDeviceAdapter = OTDeviceInformationActualAdapter()
+            self.mockDeviceInfo = OTMockDeviceInfoAdapter(modelID: actualDeviceAdapter.modelID(),
+                                                          deviceName: actualDeviceAdapter.deviceName(),
+                                                          serialNumber: NSUUID().uuidString,
+                                                          osVersion: actualDeviceAdapter.osVersion())
+        }
+
+        // With the policy disabled, we only want to operate on a few zones
+        if self.mockDeviceInfo.mockModelID.contains("AppleTV") {
+            self.intendedCKKSZones = Set([
+                CKRecordZone.ID(zoneName: "LimitedPeersAllowed"),
+            ])
+        } else {
+            self.intendedCKKSZones = Set([
+                CKRecordZone.ID(zoneName: "LimitedPeersAllowed"),
+                CKRecordZone.ID(zoneName: "Manatee"),
+            ])
+        }
+
+        self.setCKKSViewsFromPolicyToNo = true
+
+        super.setUp()
+
+        XCTAssertFalse(self.cuttlefishContext.viewManager!.useCKKSViewsFromPolicy(), "CKKS should not be configured to listen to policy-based views")
+    }
+
+    func testMergedViewListOff() throws {
+        XCTAssertFalse(self.cuttlefishContext.viewManager!.useCKKSViewsFromPolicy(), "CKKS should not be configured to listen to policy-based views")
+
+        self.startCKAccountStatusMock()
+        self.assertResetAndBecomeTrustedInDefaultContext()
+
+        let viewList = self.cuttlefishContext.viewManager!.viewList()
+        #if !os(tvOS)
+        let expected = Set<String>(["Manatee", "LimitedPeersAllowed"])
+        #else
+        let expected = Set<String>(["LimitedPeersAllowed"])
+        #endif
+        XCTAssertEqual(expected, viewList)
+    }
+}
+
+class OctagonCKKSConfigurationTestsPolicyEnabled: OctagonTestsBase {
+    override func setUp() {
+
+        if self.mockDeviceInfo == nil {
+            let actualDeviceAdapter = OTDeviceInformationActualAdapter()
+            self.mockDeviceInfo = OTMockDeviceInfoAdapter(modelID: actualDeviceAdapter.modelID(),
+                                                          deviceName: actualDeviceAdapter.deviceName(),
+                                                          serialNumber: NSUUID().uuidString,
+                                                          osVersion: actualDeviceAdapter.osVersion())
+        }
+
+        // Most tests will use a much smaller list of views. But not us! Go wild!
+        if self.mockDeviceInfo.mockModelID.contains("AppleTV") {
+            self.intendedCKKSZones = Set([
+                CKRecordZone.ID(zoneName: "Home"),
+                self.limitedPeersAllowedZoneID!,
+                CKRecordZone.ID(zoneName: "WiFi"),
+            ])
+        } else {
+            self.intendedCKKSZones = Set([
+                CKRecordZone.ID(zoneName: "ApplePay"),
+                CKRecordZone.ID(zoneName: "Applications"),
+                CKRecordZone.ID(zoneName: "AutoUnlock"),
+                // <rdar://problem/57771098> Octagon: create final policy for CKKS4All
+                // CKRecordZone.ID(zoneName: "Backstop"),
+                // <rdar://problem/57810109> Cuttlefish: remove Safari prefix from view names
+                CKRecordZone.ID(zoneName: "SafariCreditCards"),
+                CKRecordZone.ID(zoneName: "DevicePairing"),
+                CKRecordZone.ID(zoneName: "Engram"),
+                CKRecordZone.ID(zoneName: "Health"),
+                CKRecordZone.ID(zoneName: "Home"),
+                CKRecordZone.ID(zoneName: "LimitedPeersAllowed"),
+                CKRecordZone.ID(zoneName: "Manatee"),
+                // <rdar://problem/57810109> Cuttlefish: remove Safari prefix from view names
+                CKRecordZone.ID(zoneName: "SafariPasswords"),
+                CKRecordZone.ID(zoneName: "ProtectedCloudStorage"),
+                CKRecordZone.ID(zoneName: "SecureObjectSync"),
+                CKRecordZone.ID(zoneName: "WiFi"),
+             ])
+         }
+
+        super.setUp()
+    }
+
+    func testMergedViewListOn() throws {
+        XCTAssertTrue(self.cuttlefishContext.viewManager!.useCKKSViewsFromPolicy(), "CKKS should be configured to listen to policy-based views")
+
+        self.startCKAccountStatusMock()
+        self.assertResetAndBecomeTrustedInDefaultContext()
+
+        let viewList = self.cuttlefishContext.viewManager!.viewList()
+
+        #if !os(tvOS)
+        let expected = Set<String>([
+                                    "ApplePay",
+                                    "Applications",
+                                    "AutoUnlock",
+                                    // <rdar://problem/57771098> Octagon: create final policy for CKKS4All
+                                    //"Backstop",
+                                    // <rdar://problem/57810109> Cuttlefish: remove Safari prefix from view names
+                                    "SafariCreditCards",
+                                    "DevicePairing",
+                                    "Engram",
+                                    "Health",
+                                    "Home",
+                                    "LimitedPeersAllowed",
+                                    "Manatee",
+                                    // <rdar://problem/57810109> Cuttlefish: remove Safari prefix from view names
+                                    "SafariPasswords",
+                                    "ProtectedCloudStorage",
+                                    "SecureObjectSync",
+                                    "WiFi",
+                                    ])
+        #else
+        let expected = Set<String>(["LimitedPeersAllowed",
+                                    "Home",
+                                    "WiFi", ])
+        #endif
+        XCTAssertEqual(expected, viewList)
+    }
+
+    func testPolicyResetRPC() throws {
+        XCTAssertTrue(self.cuttlefishContext.viewManager!.useCKKSViewsFromPolicy(), "CKKS should be configured to listen to policy-based views")
+
+        self.startCKAccountStatusMock()
+        self.assertResetAndBecomeTrustedInDefaultContext()
+
+        XCTAssertNotNil(self.injectedManager?.policy, "Should have given CKKS a TPPolicy during initialization")
+        XCTAssertEqual(self.injectedManager?.policy?.version, prevailingPolicyVersion, "Policy given to CKKS should be prevailing policy")
+
+        self.injectedManager!.resetSyncingPolicy()
+        XCTAssertNil(self.injectedManager?.policy, "CKKS policy should be reset (by the test)")
+
+        self.otControl.refetchCKKSPolicy(nil, contextID: self.cuttlefishContext.contextID) { error in
+            XCTAssertNil(error, "Should be no error refetching the CKKS policy")
+        }
+
+        XCTAssertNotNil(self.injectedManager?.policy, "Should have given CKKS a TPPolicy during refetch")
+        XCTAssertEqual(self.injectedManager?.policy?.version, prevailingPolicyVersion, "Policy given to CKKS should be prevailing policy")
+    }
+}
index 5a43ca5487c8a180343e3139ff3f81b76f9565d6..fd35bd09f36b23d740b4a785d8ff42950ab179f9 100644 (file)
@@ -10,6 +10,9 @@ class OctagonCloudKitAccountTests: OctagonTestsBase {
         self.startCKAccountStatusMock()
         self.accountStateTracker.notifyCKAccountStatusChangeAndWaitForSignal()
 
         self.startCKAccountStatusMock()
         self.accountStateTracker.notifyCKAccountStatusChangeAndWaitForSignal()
 
+        // Tell SOS that it is absent, so we don't enable CDP on bringup
+        self.mockSOSAdapter.circleStatus = SOSCCStatus(kSOSCCCircleAbsent)
+
         // With no account, Octagon should go directly into 'NoAccount'
         self.cuttlefishContext.startOctagonStateMachine()
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateNoAccount, within: 10 * NSEC_PER_SEC)
         // With no account, Octagon should go directly into 'NoAccount'
         self.cuttlefishContext.startOctagonStateMachine()
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateNoAccount, within: 10 * NSEC_PER_SEC)
@@ -19,9 +22,9 @@ class OctagonCloudKitAccountTests: OctagonTestsBase {
         self.mockAuthKit.altDSID = newAltDSID
         XCTAssertNoThrow(try self.cuttlefishContext.accountAvailable(newAltDSID), "Sign-in shouldn't error")
 
         self.mockAuthKit.altDSID = newAltDSID
         XCTAssertNoThrow(try self.cuttlefishContext.accountAvailable(newAltDSID), "Sign-in shouldn't error")
 
-        // We should reach 'untrusted', as we cached the CK value
-        self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
-        self.assertConsidersSelfUntrusted(context: self.cuttlefishContext)
+        // We should reach 'waitforcdp', as we cached the CK value
+        self.assertEnters(context: self.cuttlefishContext, state: OctagonStateWaitForCDP, within: 10 * NSEC_PER_SEC)
+        self.assertConsidersSelfWaitingForCDP(context: self.cuttlefishContext)
     }
 
     func testSignInPausesForCloudKit() throws {
     }
 
     func testSignInPausesForCloudKit() throws {
@@ -32,6 +35,9 @@ class OctagonCloudKitAccountTests: OctagonTestsBase {
         self.accountStatus = .noAccount
         self.startCKAccountStatusMock()
 
         self.accountStatus = .noAccount
         self.startCKAccountStatusMock()
 
+        // Tell SOS that it is absent, so we don't enable CDP on bringup
+        self.mockSOSAdapter.circleStatus = SOSCCStatus(kSOSCCCircleAbsent)
+
         // With no account, Octagon should go directly into 'NoAccount'
         self.cuttlefishContext.startOctagonStateMachine()
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateNoAccount, within: 10 * NSEC_PER_SEC)
         // With no account, Octagon should go directly into 'NoAccount'
         self.cuttlefishContext.startOctagonStateMachine()
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateNoAccount, within: 10 * NSEC_PER_SEC)
@@ -48,10 +54,16 @@ class OctagonCloudKitAccountTests: OctagonTestsBase {
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateWaitingForCloudKitAccount, within: 10 * NSEC_PER_SEC)
         assertAllCKKSViews(enter: SecCKKSZoneKeyStateLoggedOut, within: 10 * NSEC_PER_SEC)
 
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateWaitingForCloudKitAccount, within: 10 * NSEC_PER_SEC)
         assertAllCKKSViews(enter: SecCKKSZoneKeyStateLoggedOut, within: 10 * NSEC_PER_SEC)
 
-        // And when CK shows up, we should go into 'untrusted'
+        // And when CK shows up, we should go into 'waitforcdp'
         self.accountStatus = .available
         self.accountStateTracker.notifyCKAccountStatusChangeAndWaitForSignal()
 
         self.accountStatus = .available
         self.accountStateTracker.notifyCKAccountStatusChangeAndWaitForSignal()
 
+        self.assertEnters(context: self.cuttlefishContext, state: OctagonStateWaitForCDP, within: 10 * NSEC_PER_SEC)
+        self.assertConsidersSelfWaitingForCDP(context: self.cuttlefishContext)
+
+        // And then CDP is set to be on
+        XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
+
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
         self.assertConsidersSelfUntrusted(context: self.cuttlefishContext)
         assertAllCKKSViews(enter: SecCKKSZoneKeyStateWaitForTLKCreation, within: 10 * NSEC_PER_SEC)
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
         self.assertConsidersSelfUntrusted(context: self.cuttlefishContext)
         assertAllCKKSViews(enter: SecCKKSZoneKeyStateWaitForTLKCreation, within: 10 * NSEC_PER_SEC)
@@ -108,6 +120,9 @@ class OctagonCloudKitAccountTests: OctagonTestsBase {
         self.accountStatus = .noAccount
         self.startCKAccountStatusMock()
 
         self.accountStatus = .noAccount
         self.startCKAccountStatusMock()
 
+        // Tell SOS that it is absent, so we don't enable CDP on bringup
+        self.mockSOSAdapter.circleStatus = SOSCCStatus(kSOSCCCircleAbsent)
+
         // With no account, Octagon should go directly into 'NoAccount'
         self.cuttlefishContext.startOctagonStateMachine()
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateNoAccount, within: 10 * NSEC_PER_SEC)
         // With no account, Octagon should go directly into 'NoAccount'
         self.cuttlefishContext.startOctagonStateMachine()
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateNoAccount, within: 10 * NSEC_PER_SEC)
@@ -120,10 +135,16 @@ class OctagonCloudKitAccountTests: OctagonTestsBase {
         // Octagon should go into 'wait for cloudkit account'
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateWaitingForCloudKitAccount, within: 10 * NSEC_PER_SEC)
 
         // Octagon should go into 'wait for cloudkit account'
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateWaitingForCloudKitAccount, within: 10 * NSEC_PER_SEC)
 
-        // And when CK shows up, we should go into 'untrusted'
+        // And when CK shows up, we should go into 'waitforcdp'
         self.accountStatus = .available
         self.accountStateTracker.notifyCKAccountStatusChangeAndWaitForSignal()
 
         self.accountStatus = .available
         self.accountStateTracker.notifyCKAccountStatusChangeAndWaitForSignal()
 
+        self.assertEnters(context: self.cuttlefishContext, state: OctagonStateWaitForCDP, within: 10 * NSEC_PER_SEC)
+        self.assertConsidersSelfWaitingForCDP(context: self.cuttlefishContext)
+
+        // and then, when CDP tells us, we should go into 'untrusted'
+        XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
+
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
         self.assertConsidersSelfUntrusted(context: self.cuttlefishContext)
 
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
         self.assertConsidersSelfUntrusted(context: self.cuttlefishContext)
 
@@ -194,6 +215,68 @@ class OctagonCloudKitAccountTests: OctagonTestsBase {
         self.accountStatus = .noAccount
         self.accountStateTracker.notifyCKAccountStatusChangeAndWaitForSignal()
 
         self.accountStatus = .noAccount
         self.accountStateTracker.notifyCKAccountStatusChangeAndWaitForSignal()
 
+        // Tell SOS that it is absent, so we don't enable CDP on bringup
+        self.mockSOSAdapter.circleStatus = SOSCCStatus(kSOSCCCircleAbsent)
+
+        // With no account, Octagon should go directly into 'NoAccount'
+        self.cuttlefishContext.startOctagonStateMachine()
+        self.assertEnters(context: self.cuttlefishContext, state: OctagonStateNoAccount, within: 10 * NSEC_PER_SEC)
+
+        // Account signs in as SA.
+        let newAltDSID = UUID().uuidString
+        self.mockAuthKit.altDSID = newAltDSID
+
+        XCTAssertNoThrow(try self.cuttlefishContext.idmsTrustLevelChanged(), "Notification of IDMS trust level shouldn't error")
+        XCTAssertNoThrow(try self.cuttlefishContext.accountAvailable(newAltDSID), "Sign-in shouldn't error")
+
+        self.assertEnters(context: self.cuttlefishContext, state: OctagonStateWaitForHSA2, within: 10 * NSEC_PER_SEC)
+        self.assertNoAccount(context: self.cuttlefishContext)
+
+        // Account promotes to HSA2
+        self.mockAuthKit.hsa2 = true
+        XCTAssertNoThrow(try self.cuttlefishContext.idmsTrustLevelChanged(), "Notification of IDMS trust level shouldn't error")
+
+        // Octagon should go into 'waitforcloudkit'
+        self.assertEnters(context: self.cuttlefishContext, state: OctagonStateWaitingForCloudKitAccount, within: 10 * NSEC_PER_SEC)
+        self.assertAccountAvailable(context: self.cuttlefishContext)
+
+        self.assertAllCKKSViews(enter: SecCKKSZoneKeyStateLoggedOut, within: 10 * NSEC_PER_SEC)
+
+        // On CK account sign-in, Octagon should race to 'waitforcdp'
+        self.accountStatus = .available
+        self.accountStateTracker.notifyCKAccountStatusChangeAndWaitForSignal()
+
+        self.assertEnters(context: self.cuttlefishContext, state: OctagonStateWaitForCDP, within: 10 * NSEC_PER_SEC)
+        self.assertConsidersSelfWaitingForCDP(context: self.cuttlefishContext)
+
+        XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
+        self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
+        self.assertAllCKKSViews(enter: SecCKKSZoneKeyStateWaitForTLKCreation, within: 10 * NSEC_PER_SEC)
+
+        // On sign-out, octagon should go back to 'no account'
+        self.mockAuthKit.altDSID = nil
+        XCTAssertNoThrow(try self.cuttlefishContext.accountNoLongerAvailable(), "sign-out shouldn't error")
+        self.assertEnters(context: self.cuttlefishContext, state: OctagonStateNoAccount, within: 10 * NSEC_PER_SEC)
+        self.assertNoAccount(context: self.cuttlefishContext)
+
+        // But CKKS is listening for the CK account removal, not the accountNoLongerAvailable call
+        self.assertAllCKKSViews(enter: SecCKKSZoneKeyStateWaitForTLKCreation, within: 10 * NSEC_PER_SEC)
+
+        self.accountStatus = .noAccount
+        self.accountStateTracker.notifyCKAccountStatusChangeAndWaitForSignal()
+        self.assertAllCKKSViews(enter: SecCKKSZoneKeyStateLoggedOut, within: 10 * NSEC_PER_SEC)
+    }
+
+    func testSAtoHSA2PromotionandCDPWithoutCloudKit() throws {
+        self.startCKAccountStatusMock()
+
+        // Device is signed out
+        self.mockAuthKit.altDSID = nil
+        self.mockAuthKit.hsa2 = false
+
+        self.accountStatus = .noAccount
+        self.accountStateTracker.notifyCKAccountStatusChangeAndWaitForSignal()
+
         // With no account, Octagon should go directly into 'NoAccount'
         self.cuttlefishContext.startOctagonStateMachine()
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateNoAccount, within: 10 * NSEC_PER_SEC)
         // With no account, Octagon should go directly into 'NoAccount'
         self.cuttlefishContext.startOctagonStateMachine()
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateNoAccount, within: 10 * NSEC_PER_SEC)
@@ -218,6 +301,10 @@ class OctagonCloudKitAccountTests: OctagonTestsBase {
 
         self.assertAllCKKSViews(enter: SecCKKSZoneKeyStateLoggedOut, within: 10 * NSEC_PER_SEC)
 
 
         self.assertAllCKKSViews(enter: SecCKKSZoneKeyStateLoggedOut, within: 10 * NSEC_PER_SEC)
 
+        // And setting CDP bit now should be successful, but not move the state machine
+        XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
+        self.assertEnters(context: self.cuttlefishContext, state: OctagonStateWaitingForCloudKitAccount, within: 10 * NSEC_PER_SEC)
+
         // On CK account sign-in, Octagon should race to 'untrusted'
         self.accountStatus = .available
         self.accountStateTracker.notifyCKAccountStatusChangeAndWaitForSignal()
         // On CK account sign-in, Octagon should race to 'untrusted'
         self.accountStatus = .available
         self.accountStateTracker.notifyCKAccountStatusChangeAndWaitForSignal()
@@ -274,6 +361,71 @@ class OctagonCloudKitAccountTests: OctagonTestsBase {
 
         self.assertAllCKKSViews(enter: SecCKKSZoneKeyStateLoggedOut, within: 10 * NSEC_PER_SEC)
     }
 
         self.assertAllCKKSViews(enter: SecCKKSZoneKeyStateLoggedOut, within: 10 * NSEC_PER_SEC)
     }
+
+    func testStatusRPCsWithUnknownCloudKitAccount() throws {
+        // If CloudKit isn't returning our calls, we should still return something reasonable...
+        let statusexpectation = self.expectation(description: "trust status returns")
+        let configuration = OTOperationConfiguration()
+        configuration.timeoutWaitForCKAccount = 500 * NSEC_PER_MSEC
+        self.cuttlefishContext.rpcTrustStatus(configuration) { egoStatus, _, _, _, _ in
+            XCTAssertTrue([.absent].contains(egoStatus), "Self peer should be in the 'absent' state")
+            statusexpectation.fulfill()
+        }
+        self.wait(for: [statusexpectation], timeout: 10)
+
+        // Now sign in to 'untrusted'
+        self.startCKAccountStatusMock()
+
+        self.cuttlefishContext.startOctagonStateMachine()
+        XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
+        self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
+        self.assertConsidersSelfUntrusted(context: self.cuttlefishContext)
+
+        // And restart, without any idea of the cloudkit state
+        self.ckaccountHoldOperation = BlockOperation()
+        self.manager.accountStateTracker = CKKSAccountStateTracker(self.manager.cloudKitContainer,
+                                                                   nsnotificationCenterClass: FakeNSNotificationCenter.self as CKKSNSNotificationCenter.Type)
+        self.injectedManager!.accountTracker = self.manager.accountStateTracker
+
+        self.manager.removeContext(forContainerName: OTCKContainerName, contextID: OTDefaultContext)
+        self.restartCKKSViews()
+        self.cuttlefishContext = self.manager.context(forContainerName: OTCKContainerName, contextID: OTDefaultContext)
+
+        // Should know it's untrusted
+        self.assertConsidersSelfUntrusted(context: self.cuttlefishContext)
+        self.startCKAccountStatusMock()
+
+        // Now become ready
+        do {
+            let clique = try OTClique.newFriends(withContextData: self.otcliqueContext, resetReason: .testGenerated)
+            XCTAssertNotNil(clique, "Clique should not be nil")
+        } catch {
+            XCTFail("Shouldn't have errored making new friends: \(error)")
+        }
+
+        // Now, we should be in 'ready'
+        self.assertEnters(context: self.cuttlefishContext, state: OctagonStateReady, within: 10 * NSEC_PER_SEC)
+        self.assertConsidersSelfTrusted(context: self.cuttlefishContext)
+        self.assertConsidersSelfTrustedCachedAccountStatus(context: self.cuttlefishContext)
+
+        // and all subCKKSes should enter ready...
+        assertAllCKKSViews(enter: SecCKKSZoneKeyStateReady, within: 10 * NSEC_PER_SEC)
+        self.verifyDatabaseMocks()
+
+        // Restart one more time:
+
+        self.ckaccountHoldOperation = BlockOperation()
+        self.manager.accountStateTracker = CKKSAccountStateTracker(self.manager.cloudKitContainer,
+                                                                   nsnotificationCenterClass: FakeNSNotificationCenter.self as CKKSNSNotificationCenter.Type)
+        self.injectedManager!.accountTracker = self.manager.accountStateTracker
+
+        self.manager.removeContext(forContainerName: OTCKContainerName, contextID: OTDefaultContext)
+        self.restartCKKSViews()
+        self.cuttlefishContext = self.manager.context(forContainerName: OTCKContainerName, contextID: OTDefaultContext)
+
+        self.assertConsidersSelfTrusted(context: self.cuttlefishContext)
+        self.assertConsidersSelfTrustedCachedAccountStatus(context: self.cuttlefishContext)
+    }
 }
 
 #endif // OCTAGON
 }
 
 #endif // OCTAGON
index c26920374b6dc49453b1c5c3b0a2ceb72c60bf71..b639f749d35506a411d62da1b3857dab2cefe389 100644 (file)
@@ -16,14 +16,14 @@ class OctagonCoreFollowUpTests: OctagonTestsBase {
                           machineID: "asdf",
                           bottleSalt: "123456789",
                           bottleID: UUID().uuidString,
                           machineID: "asdf",
                           bottleSalt: "123456789",
                           bottleID: UUID().uuidString,
-                          modelID: "asdf",
+                          modelID: "iPhone9,1",
                           deviceName: "asdf",
                           serialNumber: "1234",
                           osVersion: "asdf",
                           policyVersion: nil,
                           policySecrets: nil,
                           signingPrivKeyPersistentRef: nil,
                           deviceName: "asdf",
                           serialNumber: "1234",
                           osVersion: "asdf",
                           policyVersion: nil,
                           policySecrets: nil,
                           signingPrivKeyPersistentRef: nil,
-                          encPrivKeyPersistentRef: nil) { peerID, permanentInfo, permanentInfoSig, stableInfo, stableInfoSig, error in
+                          encPrivKeyPersistentRef: nil) { peerID, permanentInfo, permanentInfoSig, stableInfo, stableInfoSig, _, _, error in
                             XCTAssertNil(error, "Should be no error preparing identity")
                             XCTAssertNotNil(peerID, "Should be a peer ID")
                             XCTAssertNotNil(permanentInfo, "Should have a permenent info")
                             XCTAssertNil(error, "Should be no error preparing identity")
                             XCTAssertNotNil(peerID, "Should be a peer ID")
                             XCTAssertNotNil(permanentInfo, "Should have a permenent info")
@@ -44,8 +44,9 @@ class OctagonCoreFollowUpTests: OctagonTestsBase {
 
         XCTAssertNoThrow(try account.saveToKeychain(forContainer: containerName, contextID: contextName), "Should be no error saving fake account metadata")
 
 
         XCTAssertNoThrow(try account.saveToKeychain(forContainer: containerName, contextID: contextName), "Should be no error saving fake account metadata")
 
-        OctagonInitialize()
+        self.cuttlefishContext.startOctagonStateMachine()
 
 
+        XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
         self.assertConsidersSelfUntrusted(context: self.cuttlefishContext)
 
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
         self.assertConsidersSelfUntrusted(context: self.cuttlefishContext)
 
@@ -53,10 +54,10 @@ class OctagonCoreFollowUpTests: OctagonTestsBase {
         assertAllCKKSViews(enter: SecCKKSZoneKeyStateWaitForTLKCreation, within: 10 * NSEC_PER_SEC)
 
         #if !os(tvOS)
         assertAllCKKSViews(enter: SecCKKSZoneKeyStateWaitForTLKCreation, within: 10 * NSEC_PER_SEC)
 
         #if !os(tvOS)
-        XCTAssertTrue(self.cuttlefishContext.postedRepairCFU, "should have posted an repair CFU")
+        XCTAssertTrue(self.cuttlefishContext.followupHandler.hasPosted(.stateRepair), "should have posted an repair CFU")
         #else
         // Apple TV should not post a CFU, as there's no peers to join
         #else
         // Apple TV should not post a CFU, as there's no peers to join
-        XCTAssertFalse(self.cuttlefishContext.postedRepairCFU, "appleTV should not have posted a repair CFU")
+        XCTAssertFalse(self.cuttlefishContext.followupHandler.hasPosted(.stateRepair), "appleTV should not have posted a repair CFU")
         #endif
     }
 
         #endif
     }
 
@@ -78,14 +79,14 @@ class OctagonCoreFollowUpTests: OctagonTestsBase {
                           machineID: "asdf",
                           bottleSalt: "123456789",
                           bottleID: UUID().uuidString,
                           machineID: "asdf",
                           bottleSalt: "123456789",
                           bottleID: UUID().uuidString,
-                          modelID: "asdf",
+                          modelID: "iPhone9,1",
                           deviceName: "asdf",
                           serialNumber: "1234",
                           osVersion: "asdf",
                           policyVersion: nil,
                           policySecrets: nil,
                           signingPrivKeyPersistentRef: nil,
                           deviceName: "asdf",
                           serialNumber: "1234",
                           osVersion: "asdf",
                           policyVersion: nil,
                           policySecrets: nil,
                           signingPrivKeyPersistentRef: nil,
-                          encPrivKeyPersistentRef: nil) { peerID, permanentInfo, permanentInfoSig, stableInfo, stableInfoSig, error in
+                          encPrivKeyPersistentRef: nil) { peerID, permanentInfo, permanentInfoSig, stableInfo, stableInfoSig, _, _, error in
                             XCTAssertNil(error, "Should be no error preparing identity")
                             XCTAssertNotNil(peerID, "Should be a peer ID")
                             XCTAssertNotNil(permanentInfo, "Should have a permenent info")
                             XCTAssertNil(error, "Should be no error preparing identity")
                             XCTAssertNotNil(peerID, "Should be a peer ID")
                             XCTAssertNotNil(permanentInfo, "Should have a permenent info")
@@ -106,8 +107,9 @@ class OctagonCoreFollowUpTests: OctagonTestsBase {
 
         XCTAssertNoThrow(try account.saveToKeychain(forContainer: containerName, contextID: contextName), "Should be no error saving fake account metadata")
 
 
         XCTAssertNoThrow(try account.saveToKeychain(forContainer: containerName, contextID: contextName), "Should be no error saving fake account metadata")
 
-        OctagonInitialize()
+        self.cuttlefishContext.startOctagonStateMachine()
 
 
+        XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
         self.assertConsidersSelfUntrusted(context: self.cuttlefishContext)
 
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
         self.assertConsidersSelfUntrusted(context: self.cuttlefishContext)
 
@@ -116,17 +118,19 @@ class OctagonCoreFollowUpTests: OctagonTestsBase {
 
         // Since SOS isn't around to help, Octagon should post a CFU
         #if os(tvOS)
 
         // Since SOS isn't around to help, Octagon should post a CFU
         #if os(tvOS)
-        XCTAssertEqual(self.cuttlefishContext.postedRepairCFU, false, "Should not have posted a CFU on aTV (due to having no peers to join)")
+        XCTAssertEqual(self.cuttlefishContext.followupHandler.hasPosted(.stateRepair), false, "Should not have posted a CFU on aTV (due to having no peers to join)")
         #else
         #else
-        XCTAssertTrue(self.cuttlefishContext.postedRepairCFU, "should have posted an repair CFU, as SOS can't help")
+        XCTAssertTrue(self.cuttlefishContext.followupHandler.hasPosted(.stateRepair), "should have posted an repair CFU, as SOS can't help")
         #endif
     }
 
     func testAttemptedJoinNotAttemptedStateSOSError() throws {
         self.startCKAccountStatusMock()
 
         #endif
     }
 
     func testAttemptedJoinNotAttemptedStateSOSError() throws {
         self.startCKAccountStatusMock()
 
+        // Note that some errors mean "out of circle", so use NotReady here to avoid that
         self.mockSOSAdapter.sosEnabled = true
         self.mockSOSAdapter.circleStatus = SOSCCStatus(kSOSCCError)
         self.mockSOSAdapter.sosEnabled = true
         self.mockSOSAdapter.circleStatus = SOSCCStatus(kSOSCCError)
+        self.mockSOSAdapter.circleStatusError = NSError(domain: kSOSErrorDomain as String, code: kSOSErrorNotReady, userInfo: nil)
 
         // Prepare an identity, then pretend like securityd thought it was in the right account
         let containerName = OTCKContainerName
 
         // Prepare an identity, then pretend like securityd thought it was in the right account
         let containerName = OTCKContainerName
@@ -140,14 +144,14 @@ class OctagonCoreFollowUpTests: OctagonTestsBase {
                           machineID: "asdf",
                           bottleSalt: "123456789",
                           bottleID: UUID().uuidString,
                           machineID: "asdf",
                           bottleSalt: "123456789",
                           bottleID: UUID().uuidString,
-                          modelID: "asdf",
+                          modelID: "iPhone9,1",
                           deviceName: "asdf",
                           serialNumber: "1234",
                           osVersion: "asdf",
                           policyVersion: nil,
                           policySecrets: nil,
                           signingPrivKeyPersistentRef: nil,
                           deviceName: "asdf",
                           serialNumber: "1234",
                           osVersion: "asdf",
                           policyVersion: nil,
                           policySecrets: nil,
                           signingPrivKeyPersistentRef: nil,
-                          encPrivKeyPersistentRef: nil) { peerID, permanentInfo, permanentInfoSig, stableInfo, stableInfoSig, error in
+                          encPrivKeyPersistentRef: nil) { peerID, permanentInfo, permanentInfoSig, stableInfo, stableInfoSig, _, _, error in
                             XCTAssertNil(error, "Should be no error preparing identity")
                             XCTAssertNotNil(peerID, "Should be a peer ID")
                             XCTAssertNotNil(permanentInfo, "Should have a permenent info")
                             XCTAssertNil(error, "Should be no error preparing identity")
                             XCTAssertNotNil(peerID, "Should be a peer ID")
                             XCTAssertNotNil(permanentInfo, "Should have a permenent info")
@@ -168,8 +172,9 @@ class OctagonCoreFollowUpTests: OctagonTestsBase {
 
         XCTAssertNoThrow(try account.saveToKeychain(forContainer: containerName, contextID: contextName), "Should be no error saving fake account metadata")
 
 
         XCTAssertNoThrow(try account.saveToKeychain(forContainer: containerName, contextID: contextName), "Should be no error saving fake account metadata")
 
-        OctagonInitialize()
+        self.cuttlefishContext.startOctagonStateMachine()
 
 
+        XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
         self.assertConsidersSelfUntrusted(context: self.cuttlefishContext)
 
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
         self.assertConsidersSelfUntrusted(context: self.cuttlefishContext)
 
@@ -177,7 +182,7 @@ class OctagonCoreFollowUpTests: OctagonTestsBase {
         self.assertAllCKKSViews(enter: SecCKKSZoneKeyStateWaitForTLKCreation, within: 10 * NSEC_PER_SEC)
 
         // Since SOS is in 'error', octagon shouldn't post until SOS can say y/n
         self.assertAllCKKSViews(enter: SecCKKSZoneKeyStateWaitForTLKCreation, within: 10 * NSEC_PER_SEC)
 
         // Since SOS is in 'error', octagon shouldn't post until SOS can say y/n
-        XCTAssertFalse(self.cuttlefishContext.postedRepairCFU, "should NOT have posted an repair CFU")
+        XCTAssertFalse(self.cuttlefishContext.followupHandler.hasPosted(.stateRepair), "should NOT have posted an repair CFU")
     }
 
     func testAttemptedJoinNotAttemptedStateSOSDisabled() throws {
     }
 
     func testAttemptedJoinNotAttemptedStateSOSDisabled() throws {
@@ -188,6 +193,7 @@ class OctagonCoreFollowUpTests: OctagonTestsBase {
         // No need to mock not joining; Octagon won't have attempted a join if we just start it
         self.cuttlefishContext.startOctagonStateMachine()
 
         // No need to mock not joining; Octagon won't have attempted a join if we just start it
         self.cuttlefishContext.startOctagonStateMachine()
 
+        XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
         self.assertConsidersSelfUntrusted(context: self.cuttlefishContext)
 
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
         self.assertConsidersSelfUntrusted(context: self.cuttlefishContext)
 
@@ -195,10 +201,10 @@ class OctagonCoreFollowUpTests: OctagonTestsBase {
         assertAllCKKSViews(enter: SecCKKSZoneKeyStateWaitForTLKCreation, within: 10 * NSEC_PER_SEC)
 
         #if !os(tvOS)
         assertAllCKKSViews(enter: SecCKKSZoneKeyStateWaitForTLKCreation, within: 10 * NSEC_PER_SEC)
 
         #if !os(tvOS)
-        XCTAssertTrue(self.cuttlefishContext.postedRepairCFU, "should have posted an repair CFU, as SOS is disabled")
+        XCTAssertTrue(self.cuttlefishContext.followupHandler.hasPosted(.stateRepair), "should have posted an repair CFU, as SOS is disabled")
         #else
         // Apple TV should not post a CFU, as there's no peers to join
         #else
         // Apple TV should not post a CFU, as there's no peers to join
-        XCTAssertFalse(self.cuttlefishContext.postedRepairCFU, "appleTV should not have posted a repair CFU")
+        XCTAssertFalse(self.cuttlefishContext.followupHandler.hasPosted(.stateRepair), "appleTV should not have posted a repair CFU")
         #endif
     }
 
         #endif
     }
 
@@ -217,14 +223,14 @@ class OctagonCoreFollowUpTests: OctagonTestsBase {
                           machineID: "asdf",
                           bottleSalt: "123456789",
                           bottleID: UUID().uuidString,
                           machineID: "asdf",
                           bottleSalt: "123456789",
                           bottleID: UUID().uuidString,
-                          modelID: "asdf",
+                          modelID: "iPhone9,1",
                           deviceName: "asdf",
                           serialNumber: "1234",
                           osVersion: "asdf",
                           policyVersion: nil,
                           policySecrets: nil,
                           signingPrivKeyPersistentRef: nil,
                           deviceName: "asdf",
                           serialNumber: "1234",
                           osVersion: "asdf",
                           policyVersion: nil,
                           policySecrets: nil,
                           signingPrivKeyPersistentRef: nil,
-                          encPrivKeyPersistentRef: nil) { peerID, permanentInfo, permanentInfoSig, stableInfo, stableInfoSig, error in
+                          encPrivKeyPersistentRef: nil) { peerID, permanentInfo, permanentInfoSig, stableInfo, stableInfoSig, _, _, error in
                             XCTAssertNil(error, "Should be no error preparing identity")
                             XCTAssertNotNil(peerID, "Should be a peer ID")
                             XCTAssertNotNil(permanentInfo, "Should have a permenent info")
                             XCTAssertNil(error, "Should be no error preparing identity")
                             XCTAssertNotNil(peerID, "Should be a peer ID")
                             XCTAssertNotNil(permanentInfo, "Should have a permenent info")
@@ -245,8 +251,9 @@ class OctagonCoreFollowUpTests: OctagonTestsBase {
 
         XCTAssertNoThrow(try account.saveToKeychain(forContainer: containerName, contextID: contextName), "Should be no error saving fake account metadata")
 
 
         XCTAssertNoThrow(try account.saveToKeychain(forContainer: containerName, contextID: contextName), "Should be no error saving fake account metadata")
 
-        OctagonInitialize()
+        self.cuttlefishContext.startOctagonStateMachine()
 
 
+        XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
         self.assertConsidersSelfUntrusted(context: self.cuttlefishContext)
 
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
         self.assertConsidersSelfUntrusted(context: self.cuttlefishContext)
 
@@ -254,10 +261,10 @@ class OctagonCoreFollowUpTests: OctagonTestsBase {
         assertAllCKKSViews(enter: SecCKKSZoneKeyStateWaitForTLKCreation, within: 10 * NSEC_PER_SEC)
 
         #if !os(tvOS)
         assertAllCKKSViews(enter: SecCKKSZoneKeyStateWaitForTLKCreation, within: 10 * NSEC_PER_SEC)
 
         #if !os(tvOS)
-        XCTAssertTrue(self.cuttlefishContext.postedRepairCFU, "should have posted an repair CFU")
+        XCTAssertTrue(self.cuttlefishContext.followupHandler.hasPosted(.stateRepair), "should have posted an repair CFU")
         #else
         // Apple TV should not post a CFU, as there's no peers to join
         #else
         // Apple TV should not post a CFU, as there's no peers to join
-        XCTAssertFalse(self.cuttlefishContext.postedRepairCFU, "appleTV should not have posted a repair CFU")
+        XCTAssertFalse(self.cuttlefishContext.followupHandler.hasPosted(.stateRepair), "appleTV should not have posted a repair CFU")
         #endif
     }
 
         #endif
     }
 
@@ -268,12 +275,13 @@ class OctagonCoreFollowUpTests: OctagonTestsBase {
         self.mockSOSAdapter.sosEnabled = false
 
         self.cuttlefishContext.startOctagonStateMachine()
         self.mockSOSAdapter.sosEnabled = false
 
         self.cuttlefishContext.startOctagonStateMachine()
+        XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
         self.assertConsidersSelfUntrusted(context: self.cuttlefishContext)
         self.assertAllCKKSViews(enter: SecCKKSZoneKeyStateWaitForTLKCreation, within: 10 * NSEC_PER_SEC)
 
         // Apple TV should not post a CFU, as there's no peers to join
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
         self.assertConsidersSelfUntrusted(context: self.cuttlefishContext)
         self.assertAllCKKSViews(enter: SecCKKSZoneKeyStateWaitForTLKCreation, within: 10 * NSEC_PER_SEC)
 
         // Apple TV should not post a CFU, as there's no peers to join
-        XCTAssertFalse(self.cuttlefishContext.postedRepairCFU, "appleTV should not have posted a repair CFU")
+        XCTAssertFalse(self.cuttlefishContext.followupHandler.hasPosted(.stateRepair), "appleTV should not have posted a repair CFU")
 
         // Now, an iphone appears!
         let iphone = self.manager.context(forContainerName: OTCKContainerName,
 
         // Now, an iphone appears!
         let iphone = self.manager.context(forContainerName: OTCKContainerName,
@@ -296,7 +304,7 @@ class OctagonCoreFollowUpTests: OctagonTestsBase {
 
         // The TV should now post a CFU, as there's an iphone that can repair it
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
         // The TV should now post a CFU, as there's an iphone that can repair it
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
-        XCTAssertTrue(self.cuttlefishContext.postedRepairCFU, "appleTV should have posted a repair CFU")
+        XCTAssertTrue(self.cuttlefishContext.followupHandler.hasPosted(.stateRepair), "appleTV should have posted a repair CFU")
     }
 
     func testDontPostCFUWhenApprovalIncapablePeerJoins() throws {
     }
 
     func testDontPostCFUWhenApprovalIncapablePeerJoins() throws {
@@ -305,12 +313,13 @@ class OctagonCoreFollowUpTests: OctagonTestsBase {
         self.mockSOSAdapter.sosEnabled = false
 
         self.cuttlefishContext.startOctagonStateMachine()
         self.mockSOSAdapter.sosEnabled = false
 
         self.cuttlefishContext.startOctagonStateMachine()
+        XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
         self.assertConsidersSelfUntrusted(context: self.cuttlefishContext)
         self.assertAllCKKSViews(enter: SecCKKSZoneKeyStateWaitForTLKCreation, within: 10 * NSEC_PER_SEC)
 
         // Apple TV should not post a CFU, as there's no peers to join
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
         self.assertConsidersSelfUntrusted(context: self.cuttlefishContext)
         self.assertAllCKKSViews(enter: SecCKKSZoneKeyStateWaitForTLKCreation, within: 10 * NSEC_PER_SEC)
 
         // Apple TV should not post a CFU, as there's no peers to join
-        XCTAssertFalse(self.cuttlefishContext.postedRepairCFU, "appleTV should not have posted a repair CFU")
+        XCTAssertFalse(self.cuttlefishContext.followupHandler.hasPosted(.stateRepair), "appleTV should not have posted a repair CFU")
 
         // Now, a mac appears! macs cannot fix apple TVs.
         let mac = self.manager.context(forContainerName: OTCKContainerName,
 
         // Now, a mac appears! macs cannot fix apple TVs.
         let mac = self.manager.context(forContainerName: OTCKContainerName,
@@ -333,7 +342,7 @@ class OctagonCoreFollowUpTests: OctagonTestsBase {
 
         // The TV should not post a CFU, as there's still no iPhone to repair it
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
         // The TV should not post a CFU, as there's still no iPhone to repair it
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
-        XCTAssertFalse(self.cuttlefishContext.postedRepairCFU, "appleTV should not have posted a repair CFU; no devices present can repair it")
+        XCTAssertFalse(self.cuttlefishContext.followupHandler.hasPosted(.stateRepair), "appleTV should not have posted a repair CFU; no devices present can repair it")
     }
 
     func testDontPostCFUWhenCapablePeersAreUntrusted() throws {
     }
 
     func testDontPostCFUWhenCapablePeersAreUntrusted() throws {
@@ -373,6 +382,7 @@ class OctagonCoreFollowUpTests: OctagonTestsBase {
                       "iphone should distrust itself")
 
         self.cuttlefishContext.startOctagonStateMachine()
                       "iphone should distrust itself")
 
         self.cuttlefishContext.startOctagonStateMachine()
+        XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
         // Ensure that the aTV has fetched properly
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
         // Ensure that the aTV has fetched properly
@@ -382,7 +392,7 @@ class OctagonCoreFollowUpTests: OctagonTestsBase {
         self.assertAllCKKSViews(enter: SecCKKSZoneKeyStateWaitForTLKCreation, within: 10 * NSEC_PER_SEC)
 
         // Apple TV should not post a CFU, as the only iPhone around is untrusted
         self.assertAllCKKSViews(enter: SecCKKSZoneKeyStateWaitForTLKCreation, within: 10 * NSEC_PER_SEC)
 
         // Apple TV should not post a CFU, as the only iPhone around is untrusted
-        XCTAssertFalse(self.cuttlefishContext.postedRepairCFU, "appleTV should not have posted a repair CFU")
+        XCTAssertFalse(self.cuttlefishContext.followupHandler.hasPosted(.stateRepair), "appleTV should not have posted a repair CFU")
 
         // Another iPhone resets the world
         let iphone2 = self.manager.context(forContainerName: OTCKContainerName,
 
         // Another iPhone resets the world
         let iphone2 = self.manager.context(forContainerName: OTCKContainerName,
@@ -403,7 +413,7 @@ class OctagonCoreFollowUpTests: OctagonTestsBase {
 
         // The aTV is notified, and now posts a CFU
         self.sendContainerChangeWaitForUntrustedFetch(context: self.cuttlefishContext)
 
         // The aTV is notified, and now posts a CFU
         self.sendContainerChangeWaitForUntrustedFetch(context: self.cuttlefishContext)
-        XCTAssertTrue(self.cuttlefishContext.postedRepairCFU, "appleTV should have posted a repair CFU")
+        XCTAssertTrue(self.cuttlefishContext.followupHandler.hasPosted(.stateRepair), "appleTV should have posted a repair CFU")
     }
     #endif
 
     }
     #endif
 
@@ -413,14 +423,15 @@ class OctagonCoreFollowUpTests: OctagonTestsBase {
         self.mockSOSAdapter.circleStatus = SOSCCStatus(kSOSCCNotInCircle)
 
         self.cuttlefishContext.startOctagonStateMachine()
         self.mockSOSAdapter.circleStatus = SOSCCStatus(kSOSCCNotInCircle)
 
         self.cuttlefishContext.startOctagonStateMachine()
+        XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
         self.assertConsidersSelfUntrusted(context: self.cuttlefishContext)
         self.assertAllCKKSViews(enter: SecCKKSZoneKeyStateWaitForTLKCreation, within: 10 * NSEC_PER_SEC)
 
         #if os(tvOS)
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
         self.assertConsidersSelfUntrusted(context: self.cuttlefishContext)
         self.assertAllCKKSViews(enter: SecCKKSZoneKeyStateWaitForTLKCreation, within: 10 * NSEC_PER_SEC)
 
         #if os(tvOS)
-        XCTAssertEqual(self.cuttlefishContext.postedRepairCFU, false, "Should not have posted a CFU on aTV (due to having no peers to join)")
+        XCTAssertEqual(self.cuttlefishContext.followupHandler.hasPosted(.stateRepair), false, "Should not have posted a CFU on aTV (due to having no peers to join)")
         #else
         #else
-        XCTAssertTrue(self.cuttlefishContext.postedRepairCFU, "should have posted an repair CFU, as SOS can't help")
+        XCTAssertTrue(self.cuttlefishContext.followupHandler.hasPosted(.stateRepair), "should have posted an repair CFU, as SOS can't help")
         #endif
     }
 }
         #endif
     }
 }
index 848bb213e12bff37a75f00b400971ece52f46c9c..110b7e30ab4007d36afcd01a76189bbd8dd5ad2f 100644 (file)
@@ -35,6 +35,7 @@ class OctagonDeviceListTests: OctagonTestsBase {
         self.startCKAccountStatusMock()
 
         self.mockAuthKit.excludeDevices.formUnion(self.mockAuthKit.currentDeviceList())
         self.startCKAccountStatusMock()
 
         self.mockAuthKit.excludeDevices.formUnion(self.mockAuthKit.currentDeviceList())
+        self.mockAuthKit.isDemoAccount = true
         XCTAssertTrue(self.mockAuthKit.currentDeviceList().isEmpty, "should have zero devices")
 
         self.cuttlefishContext.startOctagonStateMachine()
         XCTAssertTrue(self.mockAuthKit.currentDeviceList().isEmpty, "should have zero devices")
 
         self.cuttlefishContext.startOctagonStateMachine()
@@ -450,14 +451,13 @@ class OctagonDeviceListTests: OctagonTestsBase {
         let container = try self.tphClient.getContainer(withContainer: self.cuttlefishContext.containerName, context: self.cuttlefishContext.contextID)
         container.moc.performAndWait {
             var foundPeer2 = false
         let container = try self.tphClient.getContainer(withContainer: self.cuttlefishContext.containerName, context: self.cuttlefishContext.contextID)
         container.moc.performAndWait {
             var foundPeer2 = false
-            for machinemo in container.containerMO.machines as? Set<MachineMO> ?? Set() {
-                if machinemo.machineID == self.mockAuthKit2.currentMachineID {
+            for machinemo in container.containerMO.machines as? Set<MachineMO> ?? Set()
+                where machinemo.machineID == self.mockAuthKit2.currentMachineID {
                     foundPeer2 = true
                     //
                     machinemo.modified = Date(timeIntervalSinceNow: -60 * 60 * TimeInterval(72))
                     XCTAssertEqual(machinemo.status, Int64(TPMachineIDStatus.unknown.rawValue), "peer2's MID entry should be 'unknown'")
                 }
                     foundPeer2 = true
                     //
                     machinemo.modified = Date(timeIntervalSinceNow: -60 * 60 * TimeInterval(72))
                     XCTAssertEqual(machinemo.status, Int64(TPMachineIDStatus.unknown.rawValue), "peer2's MID entry should be 'unknown'")
                 }
-            }
 
             XCTAssertTrue(foundPeer2, "Should have found an entry for peer2")
             try! container.moc.save()
 
             XCTAssertTrue(foundPeer2, "Should have found an entry for peer2")
             try! container.moc.save()
@@ -534,6 +534,103 @@ class OctagonDeviceListTests: OctagonTestsBase {
         XCTAssertTrue(self.fakeCuttlefishServer.assertCuttlefishState(FakeCuttlefishAssertion(peer: peer1ID, opinion: .trusts, target: peer2ID)),
                       "peer 1 should trust peer 2 after update")
     }
         XCTAssertTrue(self.fakeCuttlefishServer.assertCuttlefishState(FakeCuttlefishAssertion(peer: peer1ID, opinion: .trusts, target: peer2ID)),
                       "peer 1 should trust peer 2 after update")
     }
+
+    func testDemoAccountBypassIDMSTrustedDeviceList() throws {
+        // Check that we can bypass IdMS trusted device list (needed for demo accounts)
+
+        self.startCKAccountStatusMock()
+
+        self.mockAuthKit.excludeDevices.formUnion(self.mockAuthKit.currentDeviceList())
+        self.mockAuthKit.isDemoAccount = true
+        XCTAssertTrue(self.mockAuthKit.currentDeviceList().isEmpty, "should have zero devices")
+
+        self.cuttlefishContext.startOctagonStateMachine()
+        self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
+
+        do {
+            let clique = try OTClique.newFriends(withContextData: self.otcliqueContext, resetReason: .testGenerated)
+            XCTAssertNotNil(clique, "Clique should not be nil")
+        } catch {
+            XCTFail("Shouldn't have errored making new friends: \(error)")
+        }
+
+        // Now, we should be in 'ready'
+        self.assertEnters(context: self.cuttlefishContext, state: OctagonStateReady, within: 10 * NSEC_PER_SEC)
+        self.assertConsidersSelfTrusted(context: self.cuttlefishContext)
+        self.assertConsidersSelfTrustedCachedAccountStatus(context: self.cuttlefishContext)
+
+        // and all subCKKSes should enter ready...
+        self.assertAllCKKSViews(enter: SecCKKSZoneKeyStateReady, within: 10 * NSEC_PER_SEC)
+        self.verifyDatabaseMocks()
+
+        self.assertSelfTLKSharesInCloudKit(context: self.cuttlefishContext)
+
+        // And we haven't helpfully added anything to the MID list
+        self.assertMIDList(context: self.cuttlefishContext, allowed: Set(), disallowed: Set(), unknown: Set())
+    }
+
+    func testDemoAccountTrustPeerWhenMissingFromDeviceList() throws {
+        self.startCKAccountStatusMock()
+
+        self.mockAuthKit.otherDevices.removeAll()
+        self.mockAuthKit.isDemoAccount = true
+        self.mockAuthKit2.isDemoAccount = true
+        
+        XCTAssertEqual(self.mockAuthKit.currentDeviceList(), Set([self.mockAuthKit.currentMachineID]), "AuthKit should have exactly one device on the list")
+        XCTAssertFalse(self.mockAuthKit.currentDeviceList().contains(self.mockAuthKit2.currentMachineID), "AuthKit should not already have device 2 on the list")
+
+        let peer1ID = self.assertResetAndBecomeTrustedInDefaultContext()
+
+        let joiningContext = self.makeInitiatorContext(contextID: "joiner", authKitAdapter: self.mockAuthKit2)
+        let peer2ID = self.assertJoinViaEscrowRecovery(joiningContext: joiningContext, sponsor: self.cuttlefishContext)
+
+        // Now, tell peer1 about the change. It will trust the peer, despite it missing from the list, and upload some TLK shares
+        self.assertAllCKKSViewsUpload(tlkShares: 1)
+        self.sendContainerChangeWaitForFetch(context: self.cuttlefishContext)
+        self.assertAllCKKSViews(enter: SecCKKSZoneKeyStateReady, within: 10 * NSEC_PER_SEC)
+        self.verifyDatabaseMocks()
+
+        XCTAssertTrue(self.fakeCuttlefishServer.assertCuttlefishState(FakeCuttlefishAssertion(peer: peer1ID, opinion: .trusts, target: peer2ID)),
+                     "peer 1 should trust peer 2 after update")
+        self.assertMIDList(context: self.cuttlefishContext,
+                           allowed: self.mockAuthKit.currentDeviceList(),
+                           disallowed: Set(),
+                           unknown: Set())
+
+        // On a follow-up update, peer1 should _not_ hit IDMS, even though there's an unknown peer ID in its DB
+        let currentCount = self.mockAuthKit.fetchInvocations
+        self.sendContainerChangeWaitForFetchForStates(context: self.cuttlefishContext, states: [OctagonStateReadyUpdated, OctagonStateReady])
+        self.assertMIDList(context: self.cuttlefishContext,
+                           allowed: self.mockAuthKit.currentDeviceList(),
+                           disallowed: Set(),
+                           unknown: Set())
+        XCTAssertEqual(currentCount, self.mockAuthKit.fetchInvocations, "Receving a push while having an unknown peer MID should not cause an AuthKit fetch")
+
+        ////////
+
+        // Then peer2 arrives on the device list. Peer 1 should update its dynamic info to no longer have a disposition for peer2.
+        let updateTrustExpectation = self.expectation(description: "updateTrust")
+        self.fakeCuttlefishServer.updateListener = { request in
+            XCTAssertTrue(request.hasDynamicInfoAndSig, "updateTrust request should have a dynamic info")
+            let newDynamicInfo = TPPeerDynamicInfo(data: request.dynamicInfoAndSig.peerDynamicInfo,
+                                                   sig: request.dynamicInfoAndSig.sig)
+            XCTAssertNotNil(newDynamicInfo, "should be able to make a dynamic info from protobuf")
+
+            // TODO: swift refuses to see the dispositions object on newDynamicInfo; ah well
+            updateTrustExpectation.fulfill()
+            return nil
+        }
+
+        self.mockAuthKit.addAndSendNotification(machineID: try! self.mockAuthKit2.machineID())
+
+        self.wait(for: [updateTrustExpectation], timeout: 10)
+        self.assertEnters(context: self.cuttlefishContext, state: OctagonStateReady, within: 10 * NSEC_PER_SEC)
+        self.assertAllCKKSViews(enter: SecCKKSZoneKeyStateReady, within: 10 * NSEC_PER_SEC)
+
+        XCTAssertTrue(self.fakeCuttlefishServer.assertCuttlefishState(FakeCuttlefishAssertion(peer: peer1ID, opinion: .trusts, target: peer2ID)),
+                     "peer 1 should trust peer 2 after update")
+    }
+
 }
 
 #endif
 }
 
 #endif
index b5069a737c11ff9fafc3451bcbf540a62581aa60..da28130fdf630882a853b68bf43987f93c098eaf 100644 (file)
@@ -2,6 +2,30 @@
 
 class OctagonErrorHandlingTests: OctagonTestsBase {
 
 
 class OctagonErrorHandlingTests: OctagonTestsBase {
 
+    func testEstablishFailedError() throws {
+        self.startCKAccountStatusMock()
+
+        let establishExpectation = self.expectation(description: "establishExpectation")
+
+        self.fakeCuttlefishServer.establishListener = {  [unowned self] request in
+            self.fakeCuttlefishServer.establishListener = nil
+
+            self.fakeCuttlefishServer.establish(request) {
+                response, error in
+                XCTAssertNil(error, "should be no error from establish")
+                XCTAssertNotNil(response, "should get a response from establish")
+                // drop the response on the floor
+            }
+
+            establishExpectation.fulfill()
+
+            return FakeCuttlefishServer.makeCloudKitCuttlefishError(code: .establishFailed)
+        }
+
+        _ = self.assertResetAndBecomeTrustedInDefaultContext()
+        self.wait(for: [establishExpectation], timeout: 10)
+    }
+
     func testRecoverFromImmediateTimeoutDuringEstablish() throws {
         self.startCKAccountStatusMock()
 
     func testRecoverFromImmediateTimeoutDuringEstablish() throws {
         self.startCKAccountStatusMock()
 
@@ -75,6 +99,7 @@ class OctagonErrorHandlingTests: OctagonTestsBase {
         self.startCKAccountStatusMock()
 
         self.cuttlefishContext.startOctagonStateMachine()
         self.startCKAccountStatusMock()
 
         self.cuttlefishContext.startOctagonStateMachine()
+        XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
         self.aksLockState = true
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
         self.aksLockState = true
@@ -90,6 +115,7 @@ class OctagonErrorHandlingTests: OctagonTestsBase {
         self.startCKAccountStatusMock()
 
         self.cuttlefishContext.startOctagonStateMachine()
         self.startCKAccountStatusMock()
 
         self.cuttlefishContext.startOctagonStateMachine()
+        XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
         do {
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
         do {
@@ -146,6 +172,7 @@ class OctagonErrorHandlingTests: OctagonTestsBase {
         self.startCKAccountStatusMock()
 
         self.cuttlefishContext.startOctagonStateMachine()
         self.startCKAccountStatusMock()
 
         self.cuttlefishContext.startOctagonStateMachine()
+        XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
         do {
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
         do {
@@ -178,6 +205,7 @@ class OctagonErrorHandlingTests: OctagonTestsBase {
         self.startCKAccountStatusMock()
 
         self.cuttlefishContext.startOctagonStateMachine()
         self.startCKAccountStatusMock()
 
         self.cuttlefishContext.startOctagonStateMachine()
+        XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
         do {
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
         do {
@@ -225,6 +253,7 @@ class OctagonErrorHandlingTests: OctagonTestsBase {
         self.startCKAccountStatusMock()
 
         self.cuttlefishContext.startOctagonStateMachine()
         self.startCKAccountStatusMock()
 
         self.cuttlefishContext.startOctagonStateMachine()
+        XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
         do {
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
         do {
@@ -258,9 +287,9 @@ class OctagonErrorHandlingTests: OctagonTestsBase {
 
     func testPreapprovedPushWhileLocked() throws {
         // Peer 1 becomes SOS+Octagon
 
     func testPreapprovedPushWhileLocked() throws {
         // Peer 1 becomes SOS+Octagon
-        self.putFakeKeyHierarchy(inCloudKit: self.manateeZoneID)
-        self.putSelfTLKShares(inCloudKit: self.manateeZoneID)
-        self.saveTLKMaterial(toKeychain: self.manateeZoneID)
+        self.putFakeKeyHierarchiesInCloudKit()
+        self.putSelfTLKSharesInCloudKit()
+        self.saveTLKMaterialToKeychain()
 
         XCTAssertTrue(OctagonPerformSOSUpgrade(), "SOS upgrade should be on")
 
 
         XCTAssertTrue(OctagonPerformSOSUpgrade(), "SOS upgrade should be on")
 
@@ -277,7 +306,7 @@ class OctagonErrorHandlingTests: OctagonTestsBase {
 
         assertAllCKKSViews(enter: SecCKKSZoneKeyStateReady, within: 10 * NSEC_PER_SEC)
 
 
         assertAllCKKSViews(enter: SecCKKSZoneKeyStateReady, within: 10 * NSEC_PER_SEC)
 
-        // Peer 2 attempts to join via preapprovalh
+        // Peer 2 attempts to join via preapproval
         let peer2SOSMockPeer = self.createSOSPeer(peerID: "peer2ID")
         let peer2contextID = "peer2"
         let peer2mockSOS = CKKSMockSOSPresentAdapter(selfPeer: peer2SOSMockPeer, trustedPeers: self.mockSOSAdapter.allPeers(), essential: false)
         let peer2SOSMockPeer = self.createSOSPeer(peerID: "peer2ID")
         let peer2contextID = "peer2"
         let peer2mockSOS = CKKSMockSOSPresentAdapter(selfPeer: peer2SOSMockPeer, trustedPeers: self.mockSOSAdapter.allPeers(), essential: false)
@@ -324,31 +353,36 @@ class OctagonErrorHandlingTests: OctagonTestsBase {
         self.wait(for: [updateTrustExpectation], timeout: 100)
         self.fakeCuttlefishServer.updateListener = nil
 
         self.wait(for: [updateTrustExpectation], timeout: 100)
         self.fakeCuttlefishServer.updateListener = nil
 
+        self.assertEnters(context: self.cuttlefishContext, state: OctagonStateReady, within: 10 * NSEC_PER_SEC)
+
         // Now, peer 2 should lock and receive an Octagon push
         self.aksLockState = true
         self.lockStateTracker.recheck()
 
         // Now, peer 2 should lock and receive an Octagon push
         self.aksLockState = true
         self.lockStateTracker.recheck()
 
-        // Now, peer2 should receive an Octagon push, try to realize it is preapproved, and get stuck
+        // Now, peer2 should receive an Octagon push, try to realize it is preapproved, and get stuck waiting for an unlock
+        let flagCondition = peer2.stateMachine.flags.condition(forFlag: OctagonFlagCuttlefishNotification)
+
         self.sendContainerChange(context: peer2)
         self.assertEnters(context: peer2, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
         self.sendContainerChange(context: peer2)
         self.assertEnters(context: peer2, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
-        sleep(1)
 
 
-        XCTAssertTrue(peer2.stateMachine.possiblePendingFlags().contains(OctagonFlagCuttlefishNotification), "Should have recd_push pending flag")
+        // The pending flag should become a real flag after the lock state changes
+        // But it should not fire for a bit
+        XCTAssertNotEqual(0, flagCondition.wait(1 * NSEC_PER_SEC), "Cuttlefish Notification flag should not be removed while locked")
 
         self.aksLockState = false
         self.lockStateTracker.recheck()
 
 
         self.aksLockState = false
         self.lockStateTracker.recheck()
 
-        XCTAssertEqual(self.cuttlefishContext.stateMachine.possiblePendingFlags(), [], "Should have 0 pending flags")
-        self.assertEnters(context: self.cuttlefishContext, state: OctagonStateReady, within: 10 * NSEC_PER_SEC)
+        XCTAssertEqual(0, flagCondition.wait(10 * NSEC_PER_SEC), "Cuttlefish Notification flag should be removed")
+        XCTAssertEqual(peer2.stateMachine.possiblePendingFlags(), [], "Should have 0 pending flags")
 
         self.assertEnters(context: peer2, state: OctagonStateReady, within: 10 * NSEC_PER_SEC)
     }
 
     func testReceiveMachineListUpdateWhileReadyAndLocked() throws {
         // Peer 1 becomes SOS+Octagon
 
         self.assertEnters(context: peer2, state: OctagonStateReady, within: 10 * NSEC_PER_SEC)
     }
 
     func testReceiveMachineListUpdateWhileReadyAndLocked() throws {
         // Peer 1 becomes SOS+Octagon
-        self.putFakeKeyHierarchy(inCloudKit: self.manateeZoneID)
-        self.putSelfTLKShares(inCloudKit: self.manateeZoneID)
-        self.saveTLKMaterial(toKeychain: self.manateeZoneID)
+        self.putFakeKeyHierarchiesInCloudKit()
+        self.putSelfTLKSharesInCloudKit()
+        self.saveTLKMaterialToKeychain()
 
         XCTAssertTrue(OctagonPerformSOSUpgrade(), "SOS upgrade should be on")
 
 
         XCTAssertTrue(OctagonPerformSOSUpgrade(), "SOS upgrade should be on")
 
@@ -383,12 +417,12 @@ class OctagonErrorHandlingTests: OctagonTestsBase {
         bNewOTCliqueContext.otControl = self.otcliqueContext.otControl
         bNewOTCliqueContext.sbd = OTMockSecureBackup(bottleID: bottle.bottleID, entropy: entropy!)
 
         bNewOTCliqueContext.otControl = self.otcliqueContext.otControl
         bNewOTCliqueContext.sbd = OTMockSecureBackup(bottleID: bottle.bottleID, entropy: entropy!)
 
-        let deviceBmockAuthKit = OTMockAuthKitAdapter(altDSID: self.otcliqueContext.altDSID,
+        let deviceBmockAuthKit = OTMockAuthKitAdapter(altDSID: self.otcliqueContext.altDSID!,
                                                       machineID: "b-machine-id",
                                                       otherDevices: [self.mockAuthKit.currentMachineID])
 
         let bRestoreContext = self.manager.context(forContainerName: OTCKContainerName,
                                                       machineID: "b-machine-id",
                                                       otherDevices: [self.mockAuthKit.currentMachineID])
 
         let bRestoreContext = self.manager.context(forContainerName: OTCKContainerName,
-                                                   contextID: bNewOTCliqueContext.context!,
+                                                   contextID: bNewOTCliqueContext.context,
                                                    sosAdapter: OTSOSMissingAdapter(),
                                                    authKitAdapter: deviceBmockAuthKit,
                                                    lockStateTracker: self.lockStateTracker,
                                                    sosAdapter: OTSOSMissingAdapter(),
                                                    authKitAdapter: deviceBmockAuthKit,
                                                    lockStateTracker: self.lockStateTracker,
@@ -447,13 +481,14 @@ class OctagonErrorHandlingTests: OctagonTestsBase {
     }
 
     func testCKKSResetRecoverFromCKKSConflict() throws {
     }
 
     func testCKKSResetRecoverFromCKKSConflict() throws {
-        self.putFakeKeyHierarchy(inCloudKit: self.manateeZoneID)
-        self.putFakeDeviceStatus(inCloudKit: self.manateeZoneID)
+        self.putFakeKeyHierarchiesInCloudKit()
+        self.putFakeDeviceStatusesInCloudKit()
         // But do NOT add them to the keychain
 
         // CKKS should get stuck in waitfortlk
         self.startCKAccountStatusMock()
         self.cuttlefishContext.startOctagonStateMachine()
         // But do NOT add them to the keychain
 
         // CKKS should get stuck in waitfortlk
         self.startCKAccountStatusMock()
         self.cuttlefishContext.startOctagonStateMachine()
+        XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
         do {
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
         do {
@@ -473,23 +508,18 @@ class OctagonErrorHandlingTests: OctagonTestsBase {
         var tlkUUIDs: [CKRecordZone.ID: String] = [:]
 
         self.silentFetchesAllowed = false
         var tlkUUIDs: [CKRecordZone.ID: String] = [:]
 
         self.silentFetchesAllowed = false
-        self.expectCKFetchAndRun(beforeFinished: {
-            self.putFakeKeyHierarchy(inCloudKit: self.manateeZoneID)
-            self.putFakeDeviceStatus(inCloudKit: self.manateeZoneID)
+        self.expectCKFetchAndRun {
+            self.putFakeKeyHierarchiesInCloudKit()
+            self.putFakeDeviceStatusesInCloudKit()
             self.silentFetchesAllowed = true
 
             self.silentFetchesAllowed = true
 
-            // Use the commented version below when multi-zone support is readded to the tets
-            tlkUUIDs[self.manateeZoneID!] = (self.keys![self.manateeZoneID!] as? ZoneKeys)?.tlk?.uuid
-            /*
             for zoneID in self.ckksZones {
                 tlkUUIDs[zoneID as! CKRecordZone.ID] = (self.keys![zoneID] as? ZoneKeys)?.tlk?.uuid
             }
             for zoneID in self.ckksZones {
                 tlkUUIDs[zoneID as! CKRecordZone.ID] = (self.keys![zoneID] as? ZoneKeys)?.tlk?.uuid
             }
-             */
-        })
+        }
 
         let resetExepctation = self.expectation(description: "reset callback is called")
 
         let resetExepctation = self.expectation(description: "reset callback is called")
-        self.cuttlefishContext.viewManager!.rpcResetCloudKit(nil, reason: "unit-test") {
-            error in
+        self.cuttlefishContext.viewManager!.rpcResetCloudKit(nil, reason: "unit-test") { error in
             XCTAssertNil(error, "should be no error resetting cloudkit")
             resetExepctation.fulfill()
         }
             XCTAssertNil(error, "should be no error resetting cloudkit")
             resetExepctation.fulfill()
         }
index 64a4006fb7df4359ab2a6869c681bc31af835ffd..d79263f8e3ed491b255b914f5e89ffd72a2091f7 100644 (file)
@@ -1,6 +1,7 @@
 #if OCTAGON
 
 #if OCTAGON
 
-@objcMembers class OctagonEscrowRecoveryTests: OctagonTestsBase {
+@objcMembers
+class OctagonEscrowRecoveryTests: OctagonTestsBase {
     override func setUp() {
         super.setUp()
     }
     override func setUp() {
         super.setUp()
     }
@@ -16,6 +17,7 @@
         ckacctinfo.accountPartition = .production
 
         bottlerContext.cloudkitAccountStateChange(nil, to: ckacctinfo)
         ckacctinfo.accountPartition = .production
 
         bottlerContext.cloudkitAccountStateChange(nil, to: ckacctinfo)
+        XCTAssertNoThrow(try bottlerContext.setCDPEnabled())
         self.assertEnters(context: bottlerContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
         let clique: OTClique
         self.assertEnters(context: bottlerContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
         let clique: OTClique
@@ -40,8 +42,8 @@
         XCTAssertNotNil(entropy, "entropy should not be nil")
 
         // Fake that this peer also created some TLKShares for itself
         XCTAssertNotNil(entropy, "entropy should not be nil")
 
         // Fake that this peer also created some TLKShares for itself
-        self.putFakeKeyHierarchy(inCloudKit: self.manateeZoneID)
-        try self.putSelfTLKShareInCloudKit(context: bottlerContext, zoneID: self.manateeZoneID)
+        self.putFakeKeyHierarchiesInCloudKit()
+        try self.putSelfTLKSharesInCloudKit(context: bottlerContext)
 
         let bottle = self.fakeCuttlefishServer.state.bottles[0]
 
 
         let bottle = self.fakeCuttlefishServer.state.bottles[0]
 
@@ -53,7 +55,7 @@
         self.holdCloudKitFetches()
 
         // Note: CKKS will want to upload a TLKShare for its self
         self.holdCloudKitFetches()
 
         // Note: CKKS will want to upload a TLKShare for its self
-        self.expectCKModifyKeyRecords(0, currentKeyPointerRecords: 0, tlkShareRecords: 1, zoneID: self.manateeZoneID)
+        self.assertAllCKKSViewsUpload(tlkShares: 1)
 
         // Before you call joinWithBottle, you need to call fetchViableBottles.
         let fetchViableExpectation = self.expectation(description: "fetchViableBottles callback occurs")
 
         // Before you call joinWithBottle, you need to call fetchViableBottles.
         let fetchViableExpectation = self.expectation(description: "fetchViableBottles callback occurs")
@@ -65,7 +67,7 @@
         self.wait(for: [fetchViableExpectation], timeout: 10)
 
         let joinWithBottleExpectation = self.expectation(description: "joinWithBottle callback occurs")
         self.wait(for: [fetchViableExpectation], timeout: 10)
 
         let joinWithBottleExpectation = self.expectation(description: "joinWithBottle callback occurs")
-        self.cuttlefishContext.join(withBottle: bottle.bottleID, entropy: entropy!, bottleSalt: self.otcliqueContext.altDSID) { error in
+        self.cuttlefishContext.join(withBottle: bottle.bottleID, entropy: entropy!, bottleSalt: self.otcliqueContext.altDSID!) { error in
             XCTAssertNil(error, "error should be nil")
             joinWithBottleExpectation.fulfill()
         }
             XCTAssertNil(error, "error should be nil")
             joinWithBottleExpectation.fulfill()
         }
         self.wait(for: [joinWithBottleExpectation], timeout: 100)
 
         let dumpCallback = self.expectation(description: "dumpCallback callback occurs")
         self.wait(for: [joinWithBottleExpectation], timeout: 100)
 
         let dumpCallback = self.expectation(description: "dumpCallback callback occurs")
-        self.tphClient.dump(withContainer: OTCKContainerName, context: OTDefaultContext) {
-            dump, _ in
+        self.tphClient.dump(withContainer: OTCKContainerName, context: OTDefaultContext) { dump, _ in
             XCTAssertNotNil(dump, "dump should not be nil")
             XCTAssertNotNil(dump, "dump should not be nil")
-            let egoSelf = dump!["self"] as? Dictionary<String, AnyObject>
+            let egoSelf = dump!["self"] as? [String: AnyObject]
             XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
             XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
-            let dynamicInfo = egoSelf!["dynamicInfo"] as? Dictionary<String, AnyObject>
+            let dynamicInfo = egoSelf!["dynamicInfo"] as? [String: AnyObject]
             XCTAssertNotNil(dynamicInfo, "dynamicInfo should not be nil")
             XCTAssertNotNil(dynamicInfo, "dynamicInfo should not be nil")
-            let included = dynamicInfo!["included"] as? Array<String>
+            let included = dynamicInfo!["included"] as? [String]
             XCTAssertNotNil(included, "included should not be nil")
             XCTAssertEqual(included!.count, 2, "should be 2 peer ids")
             dumpCallback.fulfill()
             XCTAssertNotNil(included, "included should not be nil")
             XCTAssertEqual(included!.count, 2, "should be 2 peer ids")
             dumpCallback.fulfill()
 
         let initiatorContextID = "initiator-context-id"
         self.cuttlefishContext.startOctagonStateMachine()
 
         let initiatorContextID = "initiator-context-id"
         self.cuttlefishContext.startOctagonStateMachine()
+        XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
         let clique: OTClique
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
         let clique: OTClique
         initiatorContext.startOctagonStateMachine()
         let restoreExpectation = self.expectation(description: "restore returns")
 
         initiatorContext.startOctagonStateMachine()
         let restoreExpectation = self.expectation(description: "restore returns")
 
-        self.manager!.restore(OTCKContainerName, contextID: initiatorContextID, bottleSalt: self.otcliqueContext.altDSID, entropy: entropy!, bottleID: bottle.bottleID) { error in
+        self.manager!.restore(OTCKContainerName, contextID: initiatorContextID, bottleSalt: self.otcliqueContext.altDSID!, entropy: entropy!, bottleID: bottle.bottleID) { error in
             XCTAssertNil(error, "error should be nil")
             restoreExpectation.fulfill()
         }
             XCTAssertNil(error, "error should be nil")
             restoreExpectation.fulfill()
         }
         self.assertEnters(context: initiatorContext, state: OctagonStateReady, within: 10 * NSEC_PER_SEC)
 
         let initiatorDumpCallback = self.expectation(description: "initiatorDumpCallback callback occurs")
         self.assertEnters(context: initiatorContext, state: OctagonStateReady, within: 10 * NSEC_PER_SEC)
 
         let initiatorDumpCallback = self.expectation(description: "initiatorDumpCallback callback occurs")
-        self.tphClient.dump(withContainer: OTCKContainerName, context: initiatorContextID) {
-            dump, _ in
+        self.tphClient.dump(withContainer: OTCKContainerName, context: initiatorContextID) { dump, _ in
             XCTAssertNotNil(dump, "dump should not be nil")
             XCTAssertNotNil(dump, "dump should not be nil")
-            let egoSelf = dump!["self"] as? Dictionary<String, AnyObject>
+            let egoSelf = dump!["self"] as? [String: AnyObject]
             XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
             XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
-            let dynamicInfo = egoSelf!["dynamicInfo"] as? Dictionary<String, AnyObject>
+            let dynamicInfo = egoSelf!["dynamicInfo"] as? [String: AnyObject]
             XCTAssertNotNil(dynamicInfo, "dynamicInfo should not be nil")
             XCTAssertNotNil(dynamicInfo, "dynamicInfo should not be nil")
-            let included = dynamicInfo!["included"] as? Array<String>
+            let included = dynamicInfo!["included"] as? [String]
             XCTAssertNotNil(included, "included should not be nil")
             XCTAssertEqual(included!.count, 2, "should be 2 peer ids")
 
             XCTAssertNotNil(included, "included should not be nil")
             XCTAssertEqual(included!.count, 2, "should be 2 peer ids")
 
         ckacctinfo.accountPartition = .production
 
         bottlerContext.cloudkitAccountStateChange(nil, to: ckacctinfo)
         ckacctinfo.accountPartition = .production
 
         bottlerContext.cloudkitAccountStateChange(nil, to: ckacctinfo)
+        XCTAssertNoThrow(try bottlerContext.setCDPEnabled())
         self.assertEnters(context: bottlerContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
         let clique: OTClique
         self.assertEnters(context: bottlerContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
         let clique: OTClique
 
         // During the join, there's a CKKS key race
         self.silentFetchesAllowed = false
 
         // During the join, there's a CKKS key race
         self.silentFetchesAllowed = false
-        self.expectCKFetchAndRun(beforeFinished: {
-            self.putFakeKeyHierarchy(inCloudKit: self.manateeZoneID)
-            self.putFakeDeviceStatus(inCloudKit: self.manateeZoneID)
+        self.expectCKFetchAndRun {
+            self.putFakeKeyHierarchiesInCloudKit()
+            self.putFakeDeviceStatusesInCloudKit()
             self.silentFetchesAllowed = true
             self.silentFetchesAllowed = true
-        })
+        }
 
         self.cuttlefishContext.startOctagonStateMachine()
         self.startCKAccountStatusMock()
 
         self.cuttlefishContext.startOctagonStateMachine()
         self.startCKAccountStatusMock()
         self.wait(for: [fetchViableExpectation], timeout: 10)
 
         let joinWithBottleExpectation = self.expectation(description: "joinWithBottle callback occurs")
         self.wait(for: [fetchViableExpectation], timeout: 10)
 
         let joinWithBottleExpectation = self.expectation(description: "joinWithBottle callback occurs")
-        self.cuttlefishContext.join(withBottle: bottle.bottleID, entropy: entropy!, bottleSalt: self.otcliqueContext.altDSID) { error in
+        self.cuttlefishContext.join(withBottle: bottle.bottleID, entropy: entropy!, bottleSalt: self.otcliqueContext.altDSID!) { error in
             XCTAssertNil(error, "error should be nil")
             joinWithBottleExpectation.fulfill()
         }
             XCTAssertNil(error, "error should be nil")
             joinWithBottleExpectation.fulfill()
         }
         self.startCKAccountStatusMock()
 
         self.cuttlefishContext.startOctagonStateMachine()
         self.startCKAccountStatusMock()
 
         self.cuttlefishContext.startOctagonStateMachine()
+        XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
         let clique: OTClique
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
         let clique: OTClique
         differentDevice.startOctagonStateMachine()
         differentDevice.join(withBottle: bottle.bottleID,
                             entropy: entropy!,
         differentDevice.startOctagonStateMachine()
         differentDevice.join(withBottle: bottle.bottleID,
                             entropy: entropy!,
-                            bottleSalt: self.otcliqueContext.altDSID) { error in
+                            bottleSalt: self.otcliqueContext.altDSID!) { error in
                                 XCTAssertNil(error, "error should be nil")
                                 differentRestoreExpectation.fulfill()
         }
                                 XCTAssertNil(error, "error should be nil")
                                 differentRestoreExpectation.fulfill()
         }
         let restoreExpectation = self.expectation(description: "restore returns")
         restoreContext.join(withBottle: bottle.bottleID,
                             entropy: entropy!,
         let restoreExpectation = self.expectation(description: "restore returns")
         restoreContext.join(withBottle: bottle.bottleID,
                             entropy: entropy!,
-                            bottleSalt: self.otcliqueContext.altDSID) { error in
+                            bottleSalt: self.otcliqueContext.altDSID!) { error in
             XCTAssertNil(error, "error should be nil")
             restoreExpectation.fulfill()
         }
             XCTAssertNil(error, "error should be nil")
             restoreExpectation.fulfill()
         }
 
         let initiatorContextID = "initiator-context-id"
         self.cuttlefishContext.startOctagonStateMachine()
 
         let initiatorContextID = "initiator-context-id"
         self.cuttlefishContext.startOctagonStateMachine()
+        XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
         let clique: OTClique
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
         let clique: OTClique
 
         initiatorContext.startOctagonStateMachine()
 
 
         initiatorContext.startOctagonStateMachine()
 
+        XCTAssertNoThrow(try initiatorContext.setCDPEnabled())
         self.assertEnters(context: initiatorContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
         let restoreExpectation = self.expectation(description: "restore returns")
 
         self.assertEnters(context: initiatorContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
         let restoreExpectation = self.expectation(description: "restore returns")
 
-        self.manager!.restore(OTCKContainerName, contextID: initiatorContextID, bottleSalt: self.otcliqueContext.altDSID, entropy: entropy!, bottleID: bottle.bottleID) { error in
+        self.manager!.restore(OTCKContainerName, contextID: initiatorContextID, bottleSalt: self.otcliqueContext.altDSID!, entropy: entropy!, bottleID: bottle.bottleID) { error in
             XCTAssertNil(error, "error should be nil")
             restoreExpectation.fulfill()
         }
         self.wait(for: [restoreExpectation], timeout: 10)
 
         let initiatorDumpCallback = self.expectation(description: "initiatorDumpCallback callback occurs")
             XCTAssertNil(error, "error should be nil")
             restoreExpectation.fulfill()
         }
         self.wait(for: [restoreExpectation], timeout: 10)
 
         let initiatorDumpCallback = self.expectation(description: "initiatorDumpCallback callback occurs")
-        self.tphClient.dump(withContainer: OTCKContainerName, context: initiatorContextID) {
-            dump, _ in
+        self.tphClient.dump(withContainer: OTCKContainerName, context: initiatorContextID) { dump, _ in
             XCTAssertNotNil(dump, "dump should not be nil")
             XCTAssertNotNil(dump, "dump should not be nil")
-            let egoSelf = dump!["self"] as? Dictionary<String, AnyObject>
+            let egoSelf = dump!["self"] as? [String: AnyObject]
             XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
             XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
-            let dynamicInfo = egoSelf!["dynamicInfo"] as? Dictionary<String, AnyObject>
+            let dynamicInfo = egoSelf!["dynamicInfo"] as? [String: AnyObject]
             XCTAssertNotNil(dynamicInfo, "dynamicInfo should not be nil")
             XCTAssertNotNil(dynamicInfo, "dynamicInfo should not be nil")
-            let included = dynamicInfo!["included"] as? Array<String>
+            let included = dynamicInfo!["included"] as? [String]
             XCTAssertNotNil(included, "included should not be nil")
             XCTAssertEqual(included!.count, 2, "should be 2 peer ids")
 
             XCTAssertNotNil(included, "included should not be nil")
             XCTAssertEqual(included!.count, 2, "should be 2 peer ids")
 
 
         let initiatorContextID = "initiator-context-id"
         self.cuttlefishContext.startOctagonStateMachine()
 
         let initiatorContextID = "initiator-context-id"
         self.cuttlefishContext.startOctagonStateMachine()
+        XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
         let clique: OTClique
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
         let clique: OTClique
 
         let restoreExpectation = self.expectation(description: "restore returns")
 
 
         let restoreExpectation = self.expectation(description: "restore returns")
 
-        self.manager!.restore(OTCKContainerName, contextID: initiatorContextID, bottleSalt: self.otcliqueContext.altDSID, entropy: entropy!, bottleID: "bad escrow record ID") { error in
+        self.manager!.restore(OTCKContainerName, contextID: initiatorContextID, bottleSalt: self.otcliqueContext.altDSID!, entropy: entropy!, bottleID: "bad escrow record ID") { error in
             XCTAssertNotNil(error, "error should not be nil")
             restoreExpectation.fulfill()
         }
             XCTAssertNotNil(error, "error should not be nil")
             restoreExpectation.fulfill()
         }
         self.assertEnters(context: initiatorContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
         let acceptorDumpCallback = self.expectation(description: "acceptorDumpCallback callback occurs")
         self.assertEnters(context: initiatorContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
         let acceptorDumpCallback = self.expectation(description: "acceptorDumpCallback callback occurs")
-        self.tphClient.dump(withContainer: OTCKContainerName, context: OTDefaultContext) {
-            dump, _ in
+        self.tphClient.dump(withContainer: OTCKContainerName, context: OTDefaultContext) { dump, _ in
             XCTAssertNotNil(dump, "dump should not be nil")
             XCTAssertNotNil(dump, "dump should not be nil")
-            let egoSelf = dump!["self"] as? Dictionary<String, AnyObject>
+            let egoSelf = dump!["self"] as? [String: AnyObject]
             XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
             XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
-            let dynamicInfo = egoSelf!["dynamicInfo"] as? Dictionary<String, AnyObject>
+            let dynamicInfo = egoSelf!["dynamicInfo"] as? [String: AnyObject]
             XCTAssertNotNil(dynamicInfo, "dynamicInfo should not be nil")
             XCTAssertNotNil(dynamicInfo, "dynamicInfo should not be nil")
-            let included = dynamicInfo!["included"] as? Array<String>
+            let included = dynamicInfo!["included"] as? [String]
             XCTAssertNotNil(included, "included should not be nil")
             XCTAssertEqual(included!.count, 1, "should be 1 peer ids")
 
             XCTAssertNotNil(included, "included should not be nil")
             XCTAssertEqual(included!.count, 1, "should be 1 peer ids")
 
         // We will upload a new TLK for the new peer
         self.assertAllCKKSViewsUpload(tlkShares: 1)
         self.sendContainerChangeWaitForFetch(context: self.cuttlefishContext)
         // We will upload a new TLK for the new peer
         self.assertAllCKKSViewsUpload(tlkShares: 1)
         self.sendContainerChangeWaitForFetch(context: self.cuttlefishContext)
-        assertAllCKKSViews(enter: SecCKKSZoneKeyStateReady, within: 10 * NSEC_PER_SEC)
+        self.assertAllCKKSViews(enter: SecCKKSZoneKeyStateReady, within: 10 * NSEC_PER_SEC)
+        self.verifyDatabaseMocks()
 
         self.sendContainerChangeWaitForFetch(context: initiatorContext)
 
 
         self.sendContainerChangeWaitForFetch(context: initiatorContext)
 
         XCTAssertEqual(bottleIDs.partialRecoveryBottleIDs.count, 0, "partialRecoveryBottleIDs should be empty")
 
         let dumpExpectation = self.expectation(description: "dump callback occurs")
         XCTAssertEqual(bottleIDs.partialRecoveryBottleIDs.count, 0, "partialRecoveryBottleIDs should be empty")
 
         let dumpExpectation = self.expectation(description: "dump callback occurs")
-        self.tphClient.dump(withContainer: self.cuttlefishContext.containerName, context: self.cuttlefishContext.contextID) {
-            dump, error in
+        self.tphClient.dump(withContainer: self.cuttlefishContext.containerName, context: self.cuttlefishContext.contextID) { dump, error in
             XCTAssertNil(error, "Should be no error dumping data")
             XCTAssertNotNil(dump, "dump should not be nil")
             XCTAssertNil(error, "Should be no error dumping data")
             XCTAssertNotNil(dump, "dump should not be nil")
-            let egoSelf = dump!["self"] as? Dictionary<String, AnyObject>
+            let egoSelf = dump!["self"] as? [String: AnyObject]
             XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
             let peerID = egoSelf!["peerID"] as? String
             XCTAssertNotNil(peerID, "peerID should not be nil")
             XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
             let peerID = egoSelf!["peerID"] as? String
             XCTAssertNotNil(peerID, "peerID should not be nil")
         self.wait(for: [dumpExpectation], timeout: 10)
 
         self.otControlCLI.status(OTCKContainerName,
         self.wait(for: [dumpExpectation], timeout: 10)
 
         self.otControlCLI.status(OTCKContainerName,
-                                 context: newOTCliqueContext.context!,
+                                 context: newOTCliqueContext.context,
                                  json: false)
     }
 
                                  json: false)
     }
 
 
         let initiatorContextID = "initiator-context-id"
         self.cuttlefishContext.startOctagonStateMachine()
 
         let initiatorContextID = "initiator-context-id"
         self.cuttlefishContext.startOctagonStateMachine()
+        XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
         let clique: OTClique
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
         let clique: OTClique
         initiatorContext.startOctagonStateMachine()
 
         let joinWithBottleExpectation = self.expectation(description: "joinWithBottle callback occurs")
         initiatorContext.startOctagonStateMachine()
 
         let joinWithBottleExpectation = self.expectation(description: "joinWithBottle callback occurs")
-        initiatorContext.join(withBottle: bottle.bottleID, entropy: entropy!, bottleSalt: self.otcliqueContext.altDSID) { error in
+        initiatorContext.join(withBottle: bottle.bottleID, entropy: entropy!, bottleSalt: self.otcliqueContext.altDSID!) { error in
             XCTAssertNotNil(error, "error should not be nil")
             joinWithBottleExpectation.fulfill()
         }
             XCTAssertNotNil(error, "error should not be nil")
             joinWithBottleExpectation.fulfill()
         }
         let initiatorContextID = "initiator-context-id"
 
         self.cuttlefishContext.startOctagonStateMachine()
         let initiatorContextID = "initiator-context-id"
 
         self.cuttlefishContext.startOctagonStateMachine()
+        XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
         let clique: OTClique
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
         let clique: OTClique
         initiatorContext.startOctagonStateMachine()
 
         let joinWithBottleExpectation = self.expectation(description: "joinWithBottle callback occurs")
         initiatorContext.startOctagonStateMachine()
 
         let joinWithBottleExpectation = self.expectation(description: "joinWithBottle callback occurs")
-        initiatorContext.join(withBottle: "sos peer id", entropy: entropy!, bottleSalt: self.otcliqueContext.altDSID) { error in
+        initiatorContext.join(withBottle: "sos peer id", entropy: entropy!, bottleSalt: self.otcliqueContext.altDSID!) { error in
             XCTAssertNotNil(error, "error should be nil")
             joinWithBottleExpectation.fulfill()
         }
             XCTAssertNotNil(error, "error should be nil")
             joinWithBottleExpectation.fulfill()
         }
 
         let initiatorContextID = "initiator-context-id"
         self.cuttlefishContext.startOctagonStateMachine()
 
         let initiatorContextID = "initiator-context-id"
         self.cuttlefishContext.startOctagonStateMachine()
+        XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
         let clique: OTClique
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
         let clique: OTClique
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateReady, within: 10 * NSEC_PER_SEC)
 
         let joinWithBottleExpectation = self.expectation(description: "joinWithBottle callback occurs")
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateReady, within: 10 * NSEC_PER_SEC)
 
         let joinWithBottleExpectation = self.expectation(description: "joinWithBottle callback occurs")
-        initiatorContext.join(withBottle: bottle.bottleID, entropy: Data(count: 72), bottleSalt: self.otcliqueContext.altDSID) { error in
+        initiatorContext.join(withBottle: bottle.bottleID, entropy: Data(count: 72), bottleSalt: self.otcliqueContext.altDSID!) { error in
             XCTAssertNotNil(error, "error should not be nil, when entropy is missing")
             joinWithBottleExpectation.fulfill()
         }
             XCTAssertNotNil(error, "error should not be nil, when entropy is missing")
             joinWithBottleExpectation.fulfill()
         }
 
         let initiatorContextID = "initiator-context-id"
         self.cuttlefishContext.startOctagonStateMachine()
 
         let initiatorContextID = "initiator-context-id"
         self.cuttlefishContext.startOctagonStateMachine()
+        XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
         let clique: OTClique
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
         let clique: OTClique
         bNewOTCliqueContext.otControl = self.otcliqueContext.otControl
         bNewOTCliqueContext.sbd = OTMockSecureBackup(bottleID: bottle.bottleID, entropy: entropy!)
 
         bNewOTCliqueContext.otControl = self.otcliqueContext.otControl
         bNewOTCliqueContext.sbd = OTMockSecureBackup(bottleID: bottle.bottleID, entropy: entropy!)
 
-        let deviceBmockAuthKit = OTMockAuthKitAdapter(altDSID: self.otcliqueContext.altDSID,
+        let deviceBmockAuthKit = OTMockAuthKitAdapter(altDSID: self.otcliqueContext.altDSID!,
                                                       machineID: "b-machine-id",
                                                       otherDevices: [self.mockAuthKit.currentMachineID])
 
         let bRestoreContext = self.manager.context(forContainerName: OTCKContainerName,
                                                       machineID: "b-machine-id",
                                                       otherDevices: [self.mockAuthKit.currentMachineID])
 
         let bRestoreContext = self.manager.context(forContainerName: OTCKContainerName,
-                                                   contextID: bNewOTCliqueContext.context!,
+                                                   contextID: bNewOTCliqueContext.context,
                                                    sosAdapter: OTSOSMissingAdapter(),
                                                    authKitAdapter: deviceBmockAuthKit,
                                                    lockStateTracker: self.lockStateTracker,
                                                    sosAdapter: OTSOSMissingAdapter(),
                                                    authKitAdapter: deviceBmockAuthKit,
                                                    lockStateTracker: self.lockStateTracker,
 
         // And introduce C, which will kick out A
         // During the next sign in, the machine ID list has changed to just the new one
 
         // And introduce C, which will kick out A
         // During the next sign in, the machine ID list has changed to just the new one
-        let restoremockAuthKit = OTMockAuthKitAdapter(altDSID: self.otcliqueContext.altDSID,
+        let restoremockAuthKit = OTMockAuthKitAdapter(altDSID: self.otcliqueContext.altDSID!,
                                                       machineID: "c-machine-id",
                                                       otherDevices: [self.mockAuthKit.currentMachineID, deviceBmockAuthKit.currentMachineID])
         let restoreContext = self.manager.context(forContainerName: OTCKContainerName,
                                                       machineID: "c-machine-id",
                                                       otherDevices: [self.mockAuthKit.currentMachineID, deviceBmockAuthKit.currentMachineID])
         let restoreContext = self.manager.context(forContainerName: OTCKContainerName,
         self.assertConsidersSelfTrusted(context: restoreContext)
 
         let restoreDumpCallback = self.expectation(description: "acceptorDumpCallback callback occurs")
         self.assertConsidersSelfTrusted(context: restoreContext)
 
         let restoreDumpCallback = self.expectation(description: "acceptorDumpCallback callback occurs")
-        self.tphClient.dump(withContainer: OTCKContainerName, context: newOTCliqueContext.context!) {
-            dump, _ in
+        self.tphClient.dump(withContainer: OTCKContainerName, context: newOTCliqueContext.context) { dump, _ in
             XCTAssertNotNil(dump, "dump should not be nil")
             XCTAssertNotNil(dump, "dump should not be nil")
-            let egoSelf = dump!["self"] as? Dictionary<String, AnyObject>
+            let egoSelf = dump!["self"] as? [String: AnyObject]
             XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
             XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
-            let dynamicInfo = egoSelf!["dynamicInfo"] as? Dictionary<String, AnyObject>
+            let dynamicInfo = egoSelf!["dynamicInfo"] as? [String: AnyObject]
             XCTAssertNotNil(dynamicInfo, "dynamicInfo should not be nil")
             XCTAssertNotNil(dynamicInfo, "dynamicInfo should not be nil")
-            let included = dynamicInfo!["included"] as? Array<String>
+            let included = dynamicInfo!["included"] as? [String]
             XCTAssertNotNil(included, "included should not be nil")
             XCTAssertEqual(included!.count, 3, "should be 3 peer id included")
 
             XCTAssertNotNil(included, "included should not be nil")
             XCTAssertEqual(included!.count, 3, "should be 3 peer id included")
 
         ckacctinfo.accountPartition = .production
 
         bottlerContext.cloudkitAccountStateChange(nil, to: ckacctinfo)
         ckacctinfo.accountPartition = .production
 
         bottlerContext.cloudkitAccountStateChange(nil, to: ckacctinfo)
+        XCTAssertNoThrow(try bottlerContext.setCDPEnabled())
         self.assertEnters(context: bottlerContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
         let clique: OTClique
         self.assertEnters(context: bottlerContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
         let clique: OTClique
         XCTAssertNotNil(entropy, "entropy should not be nil")
 
         // Fake that this peer also created some TLKShares for itself
         XCTAssertNotNil(entropy, "entropy should not be nil")
 
         // Fake that this peer also created some TLKShares for itself
-        self.putFakeKeyHierarchy(inCloudKit: self.manateeZoneID)
-        try self.putSelfTLKShareInCloudKit(context: bottlerContext, zoneID: self.manateeZoneID)
+        self.putFakeKeyHierarchiesInCloudKit()
+        try self.putSelfTLKSharesInCloudKit(context: bottlerContext)
 
         let bottle = self.fakeCuttlefishServer.state.bottles[0]
 
 
         let bottle = self.fakeCuttlefishServer.state.bottles[0]
 
         self.holdCloudKitFetches()
 
         // Note: CKKS will want to upload a TLKShare for its self
         self.holdCloudKitFetches()
 
         // Note: CKKS will want to upload a TLKShare for its self
-        self.expectCKModifyKeyRecords(0, currentKeyPointerRecords: 0, tlkShareRecords: 1, zoneID: self.manateeZoneID)
+        self.assertAllCKKSViewsUpload(tlkShares: 1)
 
         let joinWithBottleExpectation = self.expectation(description: "joinWithBottle callback occurs")
 
         let joinWithBottleExpectation = self.expectation(description: "joinWithBottle callback occurs")
-        self.cuttlefishContext.join(withBottle: bottle.bottleID, entropy: entropy!, bottleSalt: self.otcliqueContext.altDSID) { error in
+        self.cuttlefishContext.join(withBottle: bottle.bottleID, entropy: entropy!, bottleSalt: self.otcliqueContext.altDSID!) { error in
             XCTAssertNil(error, "error should be nil")
             joinWithBottleExpectation.fulfill()
         }
             XCTAssertNil(error, "error should be nil")
             joinWithBottleExpectation.fulfill()
         }
         self.wait(for: [joinWithBottleExpectation], timeout: 100)
 
         let dumpCallback = self.expectation(description: "dumpCallback callback occurs")
         self.wait(for: [joinWithBottleExpectation], timeout: 100)
 
         let dumpCallback = self.expectation(description: "dumpCallback callback occurs")
-        self.tphClient.dump(withContainer: OTCKContainerName, context: OTDefaultContext) {
-            dump, _ in
+        self.tphClient.dump(withContainer: OTCKContainerName, context: OTDefaultContext) { dump, _ in
             XCTAssertNotNil(dump, "dump should not be nil")
             XCTAssertNotNil(dump, "dump should not be nil")
-            let egoSelf = dump!["self"] as? Dictionary<String, AnyObject>
+            let egoSelf = dump!["self"] as? [String: AnyObject]
             XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
             XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
-            let dynamicInfo = egoSelf!["dynamicInfo"] as? Dictionary<String, AnyObject>
+            let dynamicInfo = egoSelf!["dynamicInfo"] as? [String: AnyObject]
             XCTAssertNotNil(dynamicInfo, "dynamicInfo should not be nil")
             XCTAssertNotNil(dynamicInfo, "dynamicInfo should not be nil")
-            let included = dynamicInfo!["included"] as? Array<String>
+            let included = dynamicInfo!["included"] as? [String]
             XCTAssertNotNil(included, "included should not be nil")
             XCTAssertEqual(included!.count, 2, "should be 2 peer ids")
             dumpCallback.fulfill()
             XCTAssertNotNil(included, "included should not be nil")
             XCTAssertEqual(included!.count, 2, "should be 2 peer ids")
             dumpCallback.fulfill()
         ckacctinfo.accountPartition = .production
 
         bottlerContext.cloudkitAccountStateChange(nil, to: ckacctinfo)
         ckacctinfo.accountPartition = .production
 
         bottlerContext.cloudkitAccountStateChange(nil, to: ckacctinfo)
+        XCTAssertNoThrow(try bottlerContext.setCDPEnabled())
         self.assertEnters(context: bottlerContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
         let clique: OTClique
         self.assertEnters(context: bottlerContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
         let clique: OTClique
         XCTAssertNotNil(entropy, "entropy should not be nil")
 
         // Fake that this peer also created some TLKShares for itself
         XCTAssertNotNil(entropy, "entropy should not be nil")
 
         // Fake that this peer also created some TLKShares for itself
-        self.putFakeKeyHierarchy(inCloudKit: self.manateeZoneID)
-        try self.putSelfTLKShareInCloudKit(context: bottlerContext, zoneID: self.manateeZoneID)
+        self.putFakeKeyHierarchiesInCloudKit()
+        try self.putSelfTLKSharesInCloudKit(context: bottlerContext)
 
         let bottle = self.fakeCuttlefishServer.state.bottles[0]
 
         self.cuttlefishContext.startOctagonStateMachine()
         self.startCKAccountStatusMock()
 
         let bottle = self.fakeCuttlefishServer.state.bottles[0]
 
         self.cuttlefishContext.startOctagonStateMachine()
         self.startCKAccountStatusMock()
+        XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
         // Try to enforce that that CKKS doesn't know about the key hierarchy until Octagon asks it
         self.holdCloudKitFetches()
 
         // Note: CKKS will want to upload a TLKShare for its self
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
         // Try to enforce that that CKKS doesn't know about the key hierarchy until Octagon asks it
         self.holdCloudKitFetches()
 
         // Note: CKKS will want to upload a TLKShare for its self
-        self.expectCKModifyKeyRecords(0, currentKeyPointerRecords: 0, tlkShareRecords: 1, zoneID: self.manateeZoneID)
+        self.assertAllCKKSViewsUpload(tlkShares: 1)
 
         let joinWithBottleExpectation = self.expectation(description: "joinWithBottle callback occurs")
 
         let joinWithBottleExpectation = self.expectation(description: "joinWithBottle callback occurs")
-        self.cuttlefishContext.join(withBottle: bottle.bottleID, entropy: entropy!, bottleSalt: self.otcliqueContext.altDSID) { error in
+        self.cuttlefishContext.join(withBottle: bottle.bottleID, entropy: entropy!, bottleSalt: self.otcliqueContext.altDSID!) { error in
             XCTAssertNil(error, "error should be nil")
             joinWithBottleExpectation.fulfill()
         }
             XCTAssertNil(error, "error should be nil")
             joinWithBottleExpectation.fulfill()
         }
         self.wait(for: [joinWithBottleExpectation], timeout: 100)
 
         let dumpCallback = self.expectation(description: "dumpCallback callback occurs")
         self.wait(for: [joinWithBottleExpectation], timeout: 100)
 
         let dumpCallback = self.expectation(description: "dumpCallback callback occurs")
-        self.tphClient.dump(withContainer: OTCKContainerName, context: OTDefaultContext) {
-            dump, _ in
+        self.tphClient.dump(withContainer: OTCKContainerName, context: OTDefaultContext) { dump, _ in
             XCTAssertNotNil(dump, "dump should not be nil")
             XCTAssertNotNil(dump, "dump should not be nil")
-            let egoSelf = dump!["self"] as? Dictionary<String, AnyObject>
+            let egoSelf = dump!["self"] as? [String: AnyObject]
             XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
             XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
-            let dynamicInfo = egoSelf!["dynamicInfo"] as? Dictionary<String, AnyObject>
+            let dynamicInfo = egoSelf!["dynamicInfo"] as? [String: AnyObject]
             XCTAssertNotNil(dynamicInfo, "dynamicInfo should not be nil")
             XCTAssertNotNil(dynamicInfo, "dynamicInfo should not be nil")
-            let included = dynamicInfo!["included"] as? Array<String>
+            let included = dynamicInfo!["included"] as? [String]
             XCTAssertNotNil(included, "included should not be nil")
             XCTAssertEqual(included!.count, 2, "should be 2 peer ids")
             dumpCallback.fulfill()
             XCTAssertNotNil(included, "included should not be nil")
             XCTAssertEqual(included!.count, 2, "should be 2 peer ids")
             dumpCallback.fulfill()
         ckacctinfo.accountPartition = .production
 
         bottlerContext.cloudkitAccountStateChange(nil, to: ckacctinfo)
         ckacctinfo.accountPartition = .production
 
         bottlerContext.cloudkitAccountStateChange(nil, to: ckacctinfo)
+        XCTAssertNoThrow(try bottlerContext.setCDPEnabled())
         self.assertEnters(context: bottlerContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
         let clique: OTClique
         self.assertEnters(context: bottlerContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
         let clique: OTClique
         XCTAssertNotNil(entropy, "entropy should not be nil")
 
         // Fake that this peer also created some TLKShares for itself
         XCTAssertNotNil(entropy, "entropy should not be nil")
 
         // Fake that this peer also created some TLKShares for itself
-        self.putFakeKeyHierarchy(inCloudKit: self.manateeZoneID)
-        try self.putSelfTLKShareInCloudKit(context: bottlerContext, zoneID: self.manateeZoneID)
+        self.putFakeKeyHierarchiesInCloudKit()
+        try self.putSelfTLKSharesInCloudKit(context: bottlerContext)
 
         let bottle = self.fakeCuttlefishServer.state.bottles[0]
 
 
         let bottle = self.fakeCuttlefishServer.state.bottles[0]
 
         self.holdCloudKitFetches()
 
         // Note: CKKS will want to upload a TLKShare for its self
         self.holdCloudKitFetches()
 
         // Note: CKKS will want to upload a TLKShare for its self
-        self.expectCKModifyKeyRecords(0, currentKeyPointerRecords: 0, tlkShareRecords: 1, zoneID: self.manateeZoneID)
+        self.assertAllCKKSViewsUpload(tlkShares: 1)
 
         let joinWithBottleExpectation = self.expectation(description: "joinWithBottle callback occurs")
 
         let joinWithBottleExpectation = self.expectation(description: "joinWithBottle callback occurs")
-        self.cuttlefishContext.join(withBottle: bottle.bottleID, entropy: entropy!, bottleSalt: self.otcliqueContext.altDSID) { error in
+        self.cuttlefishContext.join(withBottle: bottle.bottleID, entropy: entropy!, bottleSalt: self.otcliqueContext.altDSID!) { error in
             XCTAssertNil(error, "error should be nil")
             joinWithBottleExpectation.fulfill()
         }
             XCTAssertNil(error, "error should be nil")
             joinWithBottleExpectation.fulfill()
         }
         var egoPeerID: String?
 
         let dumpCallback = self.expectation(description: "dumpCallback callback occurs")
         var egoPeerID: String?
 
         let dumpCallback = self.expectation(description: "dumpCallback callback occurs")
-        self.tphClient.dump(withContainer: OTCKContainerName, context: OTDefaultContext) {
-            dump, _ in
+        self.tphClient.dump(withContainer: OTCKContainerName, context: OTDefaultContext) { dump, _ in
             XCTAssertNotNil(dump, "dump should not be nil")
             XCTAssertNotNil(dump, "dump should not be nil")
-            let egoSelf = dump!["self"] as? Dictionary<String, AnyObject>
+            let egoSelf = dump!["self"] as? [String: AnyObject]
             XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
             egoPeerID = egoSelf!["peerID"] as? String
             XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
             egoPeerID = egoSelf!["peerID"] as? String
-            let dynamicInfo = egoSelf!["dynamicInfo"] as? Dictionary<String, AnyObject>
+            let dynamicInfo = egoSelf!["dynamicInfo"] as? [String: AnyObject]
             XCTAssertNotNil(dynamicInfo, "dynamicInfo should not be nil")
             XCTAssertNotNil(dynamicInfo, "dynamicInfo should not be nil")
-            let included = dynamicInfo!["included"] as? Array<String>
+            let included = dynamicInfo!["included"] as? [String]
             XCTAssertNotNil(included, "included should not be nil")
             XCTAssertEqual(included!.count, 2, "should be 2 peer ids")
             dumpCallback.fulfill()
             XCTAssertNotNil(included, "included should not be nil")
             XCTAssertEqual(included!.count, 2, "should be 2 peer ids")
             dumpCallback.fulfill()
         self.wait(for: [FetchAllViableBottles], timeout: 10)
         self.wait(for: [fetchUnCachedViableBottlesExpectation], timeout: 10)
     }
         self.wait(for: [FetchAllViableBottles], timeout: 10)
         self.wait(for: [fetchUnCachedViableBottlesExpectation], timeout: 10)
     }
+
+    func testRecoverTLKSharesSendByPeers() throws {
+        // First, set up the world: two peers, one of which has sent TLKs tto itself and the other
+        let noSelfSharesContext = self.makeInitiatorContext(contextID: "noShares", authKitAdapter: self.mockAuthKit2)
+        let allSharesContext = self.makeInitiatorContext(contextID: "allShares", authKitAdapter: self.mockAuthKit3)
+
+        self.startCKAccountStatusMock()
+        let noSelfSharesPeerID = self.assertResetAndBecomeTrusted(context: noSelfSharesContext)
+        let allSharesPeerID = self.assertJoinViaEscrowRecovery(joiningContext: allSharesContext, sponsor: noSelfSharesContext)
+
+        self.sendContainerChangeWaitForFetch(context: noSelfSharesContext)
+        self.assertEnters(context: noSelfSharesContext, state: OctagonStateReady, within: 10 * NSEC_PER_SEC)
+
+        XCTAssertTrue(self.fakeCuttlefishServer.assertCuttlefishState(FakeCuttlefishAssertion(peer: noSelfSharesPeerID, opinion: .trusts, target: allSharesPeerID)),
+                       "noShares should trust allShares")
+        XCTAssertTrue(self.fakeCuttlefishServer.assertCuttlefishState(FakeCuttlefishAssertion(peer: noSelfSharesPeerID, opinion: .trusts, target: noSelfSharesPeerID)),
+                      "No shares should trust itself")
+
+        self.putFakeKeyHierarchiesInCloudKit()
+        try self.putSelfTLKSharesInCloudKit(context: allSharesContext)
+        try self.putAllTLKSharesInCloudKit(to: noSelfSharesContext, from: allSharesContext)
+
+        try self.ckksZones.forEach { zone in
+            XCTAssertFalse(try self.tlkShareInCloudKit(receiverPeerID: noSelfSharesPeerID, senderPeerID: noSelfSharesPeerID, zoneID: zone as! CKRecordZone.ID), "Should not have self shares for noSelfShares")
+            XCTAssertTrue(try self.tlkShareInCloudKit(receiverPeerID: noSelfSharesPeerID, senderPeerID: allSharesPeerID, zoneID: zone as! CKRecordZone.ID), "Should have a share for noSelfShares from allShares")
+        }
+
+        // Now, recover from noSelfShares
+        self.assertAllCKKSViewsUpload(tlkShares: 1)
+        self.assertJoinViaEscrowRecovery(joiningContext: self.cuttlefishContext, sponsor: noSelfSharesContext)
+        // And CKKS should enter ready!
+        self.assertAllCKKSViews(enter: SecCKKSZoneKeyStateReady, within: 10 * NSEC_PER_SEC)
+        self.verifyDatabaseMocks()
+    }
 }
 
 #endif
 }
 
 #endif
diff --git a/keychain/ot/tests/octagon/OctagonTests+ForwardCompatibility.swift b/keychain/ot/tests/octagon/OctagonTests+ForwardCompatibility.swift
new file mode 100644 (file)
index 0000000..67c3928
--- /dev/null
@@ -0,0 +1,555 @@
+#if OCTAGON
+
+import Foundation
+
+class OctagonForwardCompatibilityTests: OctagonTestsBase {
+    func testApprovePeerWithNewPolicy() throws {
+        self.startCKAccountStatusMock()
+        let peer1ID = self.assertResetAndBecomeTrustedInDefaultContext()
+
+        // Now, we'll approve a new peer with a new policy! First, make that new policy.
+        let currentPolicyOptional = builtInPolicyDocuments().filter { $0.version.versionNumber == prevailingPolicyVersion.versionNumber }.first
+        XCTAssertNotNil(currentPolicyOptional, "Should have one current policy")
+        let currentPolicy = currentPolicyOptional!
+
+        let newPolicy = currentPolicy.clone(withVersionNumber: currentPolicy.version.versionNumber + 1)!
+        self.fakeCuttlefishServer.policyOverlay.append(newPolicy)
+
+        let peer2ContextID = "asdf"
+
+        // Assist the other client here: it'll likely already have the policy built-in
+        let fetchExpectation = self.expectation(description: "fetch callback occurs")
+        self.tphClient.fetchPolicyDocuments(withContainer: OTCKContainerName,
+                                            context: peer2ContextID,
+                                            versions: Set([newPolicy.version])) { _, error in
+                                                XCTAssertNil(error, "Should have no error")
+                                                fetchExpectation.fulfill()
+        }
+        self.wait(for: [fetchExpectation], timeout: 10)
+
+        var peer2ID: String = "not initialized"
+
+        let prepareExpectation = self.expectation(description: "prepare callback occurs")
+        let vouchExpectation = self.expectation(description: "vouch callback occurs")
+        let joinExpectation = self.expectation(description: "join callback occurs")
+        let serverJoinExpectation = self.expectation(description: "joinWithVoucher is called")
+
+        self.tphClient.prepare(withContainer: OTCKContainerName,
+                               context: peer2ContextID,
+                               epoch: 1,
+                               machineID: self.mockAuthKit2.currentMachineID,
+                               bottleSalt: self.mockAuthKit2.altDSID!,
+                               bottleID: "why-is-this-nonnil",
+                               modelID: self.mockDeviceInfo.modelID(),
+                               deviceName: "new-policy-peer",
+                               serialNumber: "1234",
+                               osVersion: "something",
+                               policyVersion: newPolicy.version,
+                               policySecrets: nil,
+                               signingPrivKeyPersistentRef: nil,
+                               encPrivKeyPersistentRef: nil) { peerID, permanentInfo, permanentInfoSig, stableInfo, stableInfoSig, _, _, error in
+                                XCTAssertNil(error, "Should be no error preparing the second peer")
+                                XCTAssertNotNil(peerID, "Should have a peerID")
+                                peer2ID = peerID!
+
+                                XCTAssertNotNil(stableInfo, "Should have a stable info")
+                                XCTAssertNotNil(stableInfoSig, "Should have a stable info signature")
+
+                                let newStableInfo = TPPeerStableInfo(data: stableInfo!, sig: stableInfoSig!)
+                                XCTAssertNotNil(newStableInfo, "should be able to make a stableInfo info from protobuf")
+
+                                XCTAssertEqual(newStableInfo?.frozenPolicyVersion, frozenPolicyVersion, "Frozen policy version in new identity should match frozen policy version")
+                                XCTAssertEqual(newStableInfo?.flexiblePolicyVersion, newPolicy.version, "Flexible policy version in new identity should match new policy version")
+
+                                self.tphClient.vouch(withContainer: self.cuttlefishContext.containerName,
+                                                     context: self.cuttlefishContext.contextID,
+                                                     peerID: peerID!,
+                                                     permanentInfo: permanentInfo!,
+                                                     permanentInfoSig: permanentInfoSig!,
+                                                     stableInfo: stableInfo!,
+                                                     stableInfoSig: stableInfoSig!,
+                                                     ckksKeys: []) { voucher, voucherSig, error in
+                                                        XCTAssertNil(error, "Should be no error vouching")
+                                                        XCTAssertNotNil(voucher, "Should have a voucher")
+                                                        XCTAssertNotNil(voucherSig, "Should have a voucher signature")
+
+                                                        self.fakeCuttlefishServer.joinListener = { joinRequest in
+                                                            XCTAssertEqual(peer2ID, joinRequest.peer.peerID, "joinWithVoucher request should be for peer 2")
+                                                            XCTAssertTrue(joinRequest.peer.hasStableInfoAndSig, "Joining peer should have a stable info")
+                                                            let newStableInfo = joinRequest.peer.stableInfoAndSig.stableInfo()
+
+                                                            XCTAssertEqual(newStableInfo.frozenPolicyVersion, frozenPolicyVersion, "Frozen policy version in new identity should match frozen policy version")
+                                                            XCTAssertEqual(newStableInfo.flexiblePolicyVersion, newPolicy.version, "Flexible policy version in new identity should match new policy version (as provided by new peer)")
+
+                                                            serverJoinExpectation.fulfill()
+                                                            return nil
+                                                        }
+
+                                                        self.tphClient.join(withContainer: OTCKContainerName,
+                                                                            context: peer2ContextID,
+                                                                            voucherData: voucher!,
+                                                                            voucherSig: voucherSig!,
+                                                                            ckksKeys: [],
+                                                                            tlkShares: [],
+                                                                            preapprovedKeys: []) { peerID, _, _, _, error in
+                                                                                XCTAssertNil(error, "Should be no error joining")
+                                                                                XCTAssertNotNil(peerID, "Should have a peerID")
+                                                                                joinExpectation.fulfill()
+                                                        }
+                                                        vouchExpectation.fulfill()
+                                }
+                                prepareExpectation.fulfill()
+        }
+        self.wait(for: [prepareExpectation, vouchExpectation, joinExpectation, serverJoinExpectation], timeout: 10)
+
+        // Then, after the remote peer joins, the original peer should realize it trusts the new peer and update its own stableinfo to use the new policy
+        let updateTrustExpectation = self.expectation(description: "updateTrust")
+        self.fakeCuttlefishServer.updateListener = { request in
+            XCTAssertEqual(peer1ID, request.peerID, "updateTrust request should be for peer 1")
+            let newDynamicInfo = request.dynamicInfoAndSig.dynamicInfo()
+            XCTAssert(newDynamicInfo.includedPeerIDs.contains(peer2ID), "Peer1 should trust peer2")
+
+            let newStableInfo = request.stableInfoAndSig.stableInfo()
+
+            XCTAssertEqual(newStableInfo.frozenPolicyVersion, frozenPolicyVersion, "Policy version in peer should match frozen policy version")
+            XCTAssertEqual(newStableInfo.flexiblePolicyVersion, newPolicy.version, "Prevailing policy version in peer should match new policy version")
+
+            updateTrustExpectation.fulfill()
+            return nil
+        }
+
+        self.sendContainerChangeWaitForFetch(context: self.cuttlefishContext)
+        self.wait(for: [updateTrustExpectation], timeout: 10)
+        self.assertEnters(context: self.cuttlefishContext, state: OctagonStateReady, within: 10 * NSEC_PER_SEC)
+    }
+
+    func testRejectVouchingForPeerWithUnknownNewPolicy() throws {
+        self.startCKAccountStatusMock()
+        _ = self.assertResetAndBecomeTrustedInDefaultContext()
+
+        // Now, a new peer joins with a policy we can't fetch
+        let currentPolicyOptional = builtInPolicyDocuments().filter { $0.version.versionNumber == prevailingPolicyVersion.versionNumber }.first
+        XCTAssertNotNil(currentPolicyOptional, "Should have one current policy")
+        let currentPolicy = currentPolicyOptional!
+
+        let newPolicy = currentPolicy.clone(withVersionNumber: currentPolicy.version.versionNumber + 1)!
+
+        let peer2ContextID = "asdf"
+
+        // Assist the other client here: it'll likely already have this built-in
+        self.fakeCuttlefishServer.policyOverlay.append(newPolicy)
+
+        let fetchExpectation = self.expectation(description: "fetch callback occurs")
+        self.tphClient.fetchPolicyDocuments(withContainer: OTCKContainerName,
+                                            context: peer2ContextID,
+                                            versions: Set([newPolicy.version])) { _, error in
+                                                XCTAssertNil(error, "Should have no error")
+                                                fetchExpectation.fulfill()
+        }
+        self.wait(for: [fetchExpectation], timeout: 10)
+
+        // Remove the policy, now that peer2 has it
+        self.fakeCuttlefishServer.policyOverlay.removeAll()
+
+        let prepareExpectation = self.expectation(description: "prepare callback occurs")
+        let vouchExpectation = self.expectation(description: "vouch callback occurs")
+
+        self.tphClient.prepare(withContainer: OTCKContainerName,
+                               context: peer2ContextID,
+                               epoch: 1,
+                               machineID: self.mockAuthKit2.currentMachineID,
+                               bottleSalt: self.mockAuthKit2.altDSID!,
+                               bottleID: "why-is-this-nonnil",
+                               modelID: self.mockDeviceInfo.modelID(),
+                               deviceName: "new-policy-peer",
+                               serialNumber: "1234",
+                               osVersion: "something",
+                               policyVersion: newPolicy.version,
+                               policySecrets: nil,
+                               signingPrivKeyPersistentRef: nil,
+                               encPrivKeyPersistentRef: nil) { peerID, permanentInfo, permanentInfoSig, stableInfo, stableInfoSig, _, _, error in
+                                XCTAssertNil(error, "Should be no error preparing the second peer")
+                                XCTAssertNotNil(peerID, "Should have a peerID")
+
+                                XCTAssertNotNil(stableInfo, "Should have a stable info")
+                                XCTAssertNotNil(stableInfoSig, "Should have a stable info signature")
+
+                                let newStableInfo = TPPeerStableInfo(data: stableInfo!, sig: stableInfoSig!)
+                                XCTAssertNotNil(newStableInfo, "should be able to make a stableInfo info from protobuf")
+
+                                XCTAssertEqual(newStableInfo?.frozenPolicyVersion, frozenPolicyVersion, "Frozen policy version in new identity should match frozen policy version")
+                                XCTAssertEqual(newStableInfo?.flexiblePolicyVersion, newPolicy.version, "Flexible policy version in new identity should match new policy version")
+
+                                self.tphClient.vouch(withContainer: self.cuttlefishContext.containerName,
+                                                     context: self.cuttlefishContext.contextID,
+                                                     peerID: peerID!,
+                                                     permanentInfo: permanentInfo!,
+                                                     permanentInfoSig: permanentInfoSig!,
+                                                     stableInfo: stableInfo!,
+                                                     stableInfoSig: stableInfoSig!,
+                                                     ckksKeys: []) { voucher, voucherSig, error in
+                                                        XCTAssertNotNil(error, "should be an error vouching for a peer with an unknown policy")
+                                                        XCTAssertNil(voucher, "Should have no voucher")
+                                                        XCTAssertNil(voucherSig, "Should have no voucher signature")
+
+                                                        vouchExpectation.fulfill()
+                                }
+                                prepareExpectation.fulfill()
+        }
+        self.wait(for: [prepareExpectation, vouchExpectation], timeout: 10)
+    }
+
+    func testIgnoreAlreadyJoinedPeerWithUnknownNewPolicy() throws {
+        self.startCKAccountStatusMock()
+        let peer1ID = self.assertResetAndBecomeTrustedInDefaultContext()
+
+        // Now, a new peer joins with a policy we can't fetch
+        let currentPolicyOptional = builtInPolicyDocuments().filter { $0.version.versionNumber == prevailingPolicyVersion.versionNumber }.first
+        XCTAssertNotNil(currentPolicyOptional, "Should have one current policy")
+        let currentPolicy = currentPolicyOptional!
+
+        let newPolicy = currentPolicy.clone(withVersionNumber: currentPolicy.version.versionNumber + 1)!
+
+        let peer2ContextID = "asdf"
+
+        // Assist the other client here: it'll likely already have this built-in
+        self.fakeCuttlefishServer.policyOverlay.append(newPolicy)
+
+        let fetchExpectation = self.expectation(description: "fetch callback occurs")
+        self.tphClient.fetchPolicyDocuments(withContainer: OTCKContainerName,
+                                            context: peer2ContextID,
+                                            versions: Set([newPolicy.version])) { _, error in
+                                                XCTAssertNil(error, "Should have no error")
+                                                fetchExpectation.fulfill()
+        }
+        self.wait(for: [fetchExpectation], timeout: 10)
+
+        // Remove the policy, now that peer2 has it
+        self.fakeCuttlefishServer.policyOverlay.removeAll()
+
+        let joiningContext = self.makeInitiatorContext(contextID: peer2ContextID, authKitAdapter: self.mockAuthKit2)
+        joiningContext.policyOverride = newPolicy.version
+
+        let serverJoinExpectation = self.expectation(description: "peer2 joins successfully")
+        self.fakeCuttlefishServer.joinListener = { joinRequest in
+            XCTAssertTrue(joinRequest.peer.hasStableInfoAndSig, "Joining peer should have a stable info")
+            let newStableInfo = joinRequest.peer.stableInfoAndSig.stableInfo()
+
+            XCTAssertEqual(newStableInfo.frozenPolicyVersion, frozenPolicyVersion, "Frozen policy version in new identity should match frozen policy version")
+            XCTAssertEqual(newStableInfo.flexiblePolicyVersion, newPolicy.version, "Flexible policy version in new identity should match new policy version (as provided by new peer)")
+
+            serverJoinExpectation.fulfill()
+            return nil
+        }
+
+        _ = self.assertJoinViaEscrowRecovery(joiningContext: joiningContext, sponsor: self.cuttlefishContext)
+        self.wait(for: [serverJoinExpectation], timeout: 10)
+
+        // Then, after the remote peer joins, the original peer should ignore it entirely: peer1 has no idea what this new policy is about
+        // That means it won't update its trust in response to the join
+        self.fakeCuttlefishServer.updateListener = { request in
+            XCTFail("Expected no updateTrust after peer1 joins")
+            XCTAssertEqual(peer1ID, request.peerID, "updateTrust request should be for peer 1")
+            /*
+             * But, if it did update its trust, here's what we would expect:
+            let newDynamicInfo = request.dynamicInfoAndSig.dynamicInfo()
+            XCTAssertFalse(newDynamicInfo.includedPeerIDs.contains(peer2ID), "Peer1 should not trust peer2")
+            XCTAssertFalse(newDynamicInfo.excludedPeerIDs.contains(peer2ID), "Peer1 should not distrust peer2")
+
+            let newStableInfo = request.stableInfoAndSig.stableInfo()
+
+            XCTAssertEqual(newStableInfo.frozenPolicyVersion, frozenPolicyVersion, "Policy version in peer should match frozen policy version")
+            XCTAssertEqual(newStableInfo.flexiblePolicyVersion, prevailingPolicyVersion, "Prevailing policy version in peer should match current prevailing policy version")
+
+            updateTrustExpectation.fulfill()
+             */
+            return nil
+        }
+
+        self.sendContainerChangeWaitForFetch(context: self.cuttlefishContext)
+        self.assertEnters(context: self.cuttlefishContext, state: OctagonStateReady, within: 10 * NSEC_PER_SEC)
+    }
+
+    func createOctagonAndCKKSUsingFuturePolicy() throws -> (TPPolicyDocument, CKRecordZone.ID) {
+        // We want to set up a world with a peer, in Octagon, with TLKs for zones that don't even exist in our current policy.
+        // First, make a new policy.
+        let currentPolicyOptional = builtInPolicyDocuments().filter { $0.version.versionNumber == prevailingPolicyVersion.versionNumber }.first
+        XCTAssertNotNil(currentPolicyOptional, "Should have one current policy")
+        let currentPolicyDocument = currentPolicyOptional!
+
+        let futureViewName = "FutureView"
+        let futureViewMapping = TPPBPolicyKeyViewMapping()!
+
+        let futureViewZoneID = CKRecordZone.ID(zoneName: futureViewName)
+        self.ckksZones.add(futureViewZoneID)
+        self.injectedManager!.setSyncingViewsAllowList(Set((self.intendedCKKSZones.union([futureViewZoneID])).map { $0.zoneName }))
+
+        self.zones![futureViewZoneID] = FakeCKZone(zone: futureViewZoneID)
+
+        XCTAssertFalse(currentPolicyDocument.categoriesByView.keys.contains(futureViewName), "Current policy should not include future view")
+
+        let newPolicyDocument = try TPPolicyDocument(internalVersion: currentPolicyDocument.version.versionNumber + 1,
+            modelToCategory: currentPolicyDocument.modelToCategory,
+            categoriesByView: currentPolicyDocument.categoriesByView.merging([futureViewName: Set(["watch", "full", "tv"])]) { _, new in new },
+            introducersByCategory: currentPolicyDocument.introducersByCategory,
+            redactions: [:],
+            keyViewMapping: [futureViewMapping] + currentPolicyDocument.keyViewMapping,
+            hashAlgo: .SHA256)
+
+        self.fakeCuttlefishServer.policyOverlay.append(newPolicyDocument)
+
+        return (newPolicyDocument, futureViewZoneID)
+    }
+
+    func testRestoreBottledPeerUsingFuturePolicy() throws {
+        let (newPolicyDocument, futureViewZoneID) = try self.createOctagonAndCKKSUsingFuturePolicy()
+
+        let futurePeerContext = self.makeInitiatorContext(contextID: "futurePeer")
+        futurePeerContext.policyOverride = newPolicyDocument.version
+
+        self.startCKAccountStatusMock()
+        let futurePeerID = self.assertResetAndBecomeTrusted(context: futurePeerContext)
+
+        self.putFakeKeyHierarchiesInCloudKit()
+        try self.putSelfTLKSharesInCloudKit(context: futurePeerContext)
+        XCTAssertTrue(try self.tlkShareInCloudKit(receiverPeerID: futurePeerID, senderPeerID: futurePeerID, zoneID: futureViewZoneID))
+
+        // Now, our peer (with no inbuilt knowledge of newPolicyDocument) joins via escrow recovery.
+        // It should be able to recover the FutureView TLK
+        self.assertAllCKKSViewsUpload(tlkShares: 1)
+
+        let serverJoinExpectation = self.expectation(description: "peer1 joins successfully")
+        self.fakeCuttlefishServer.joinListener = { joinRequest in
+            XCTAssertTrue(joinRequest.peer.hasStableInfoAndSig, "Joining peer should have a stable info")
+            let newStableInfo = joinRequest.peer.stableInfoAndSig.stableInfo()
+
+            XCTAssertEqual(newStableInfo.frozenPolicyVersion, frozenPolicyVersion, "Policy version in peer should match frozen policy version")
+            XCTAssertEqual(newStableInfo.flexiblePolicyVersion, newPolicyDocument.version, "Prevailing policy version in peer should match new policy version")
+
+            serverJoinExpectation.fulfill()
+            return nil
+        }
+
+        let peerID = self.assertJoinViaEscrowRecovery(joiningContext: self.cuttlefishContext, sponsor: futurePeerContext)
+        self.assertAllCKKSViews(enter: SecCKKSZoneKeyStateReady, within: 10 * NSEC_PER_SEC)
+        XCTAssertEqual(self.injectedManager!.policy?.version, newPolicyDocument.version, "CKKS should be configured with new policy")
+        self.verifyDatabaseMocks()
+
+        self.wait(for: [serverJoinExpectation], timeout: 10)
+
+        // And the joined peer should have recovered the TLK, and uploaded itself a share
+        XCTAssertTrue(try self.tlkShareInCloudKit(receiverPeerID: peerID, senderPeerID: peerID, zoneID: futureViewZoneID))
+    }
+
+    func testPairingJoinUsingFuturePolicy() throws {
+        let (newPolicyDocument, futureViewZoneID) = try self.createOctagonAndCKKSUsingFuturePolicy()
+
+        let futurePeerContext = self.makeInitiatorContext(contextID: "futurePeer")
+        futurePeerContext.policyOverride = newPolicyDocument.version
+
+        self.startCKAccountStatusMock()
+        let futurePeerID = self.assertResetAndBecomeTrusted(context: futurePeerContext)
+
+        self.putFakeKeyHierarchiesInCloudKit()
+        try self.putSelfTLKSharesInCloudKit(context: futurePeerContext)
+        XCTAssertTrue(try self.tlkShareInCloudKit(receiverPeerID: futurePeerID, senderPeerID: futurePeerID, zoneID: futureViewZoneID))
+
+        // Now, our peer (with no inbuilt knowledge of newPolicyDocument) joins via pairing
+        // It should be able to recover the FutureView TLK
+        self.assertAllCKKSViewsUpload(tlkShares: 1)
+
+        let serverJoinExpectation = self.expectation(description: "peer1 joins successfully")
+        self.fakeCuttlefishServer.joinListener = { joinRequest in
+            XCTAssertTrue(joinRequest.peer.hasStableInfoAndSig, "Joining peer should have a stable info")
+            let newStableInfo = joinRequest.peer.stableInfoAndSig.stableInfo()
+
+            XCTAssertEqual(newStableInfo.frozenPolicyVersion, frozenPolicyVersion, "Policy version in peer should match frozen policy version")
+            XCTAssertEqual(newStableInfo.flexiblePolicyVersion, newPolicyDocument.version, "Prevailing policy version in peer should match new policy version")
+
+            serverJoinExpectation.fulfill()
+            return nil
+        }
+
+        let peerID = self.assertJoinViaProximitySetup(joiningContext: self.cuttlefishContext, sponsor: futurePeerContext)
+
+        // And then fake like the other peer uploaded TLKShares after the join succeeded (it would normally happen during, but that's okay)
+        try self.putAllTLKSharesInCloudKit(to: self.cuttlefishContext, from: futurePeerContext)
+        self.sendAllCKKSViewsZoneChanged()
+
+        self.assertAllCKKSViews(enter: SecCKKSZoneKeyStateReady, within: 10 * NSEC_PER_SEC)
+        XCTAssertEqual(self.injectedManager!.policy?.version, newPolicyDocument.version, "CKKS should be configured with new policy")
+        self.verifyDatabaseMocks()
+
+        self.wait(for: [serverJoinExpectation], timeout: 10)
+
+        // And the joined peer should have recovered the TLK, and uploaded itself a share
+        XCTAssertTrue(try self.tlkShareInCloudKit(receiverPeerID: peerID, senderPeerID: peerID, zoneID: futureViewZoneID))
+    }
+
+    func testRecoveryKeyJoinUsingFuturePolicy() throws {
+        let (newPolicyDocument, futureViewZoneID) = try self.createOctagonAndCKKSUsingFuturePolicy()
+
+        let futurePeerContext = self.makeInitiatorContext(contextID: "futurePeer")
+        futurePeerContext.policyOverride = newPolicyDocument.version
+
+        self.startCKAccountStatusMock()
+        let futurePeerID = self.assertResetAndBecomeTrusted(context: futurePeerContext)
+
+        self.putFakeKeyHierarchiesInCloudKit()
+        try self.putSelfTLKSharesInCloudKit(context: futurePeerContext)
+        XCTAssertTrue(try self.tlkShareInCloudKit(receiverPeerID: futurePeerID, senderPeerID: futurePeerID, zoneID: futureViewZoneID))
+
+        // Create the recovery key
+        let recoveryKey = SecPasswordGenerate(SecPasswordType(kSecPasswordTypeiCloudRecoveryKey), nil, nil)! as String
+        XCTAssertNotNil(recoveryKey, "recoveryKey should not be nil")
+
+        self.manager.setSOSEnabledForPlatformFlag(true)
+
+        let createRecoveryExpectation = self.expectation(description: "createRecoveryExpectation returns")
+        self.manager.createRecoveryKey(OTCKContainerName, contextID: futurePeerContext.contextID, recoveryKey: recoveryKey) { error in
+            XCTAssertNil(error, "error should be nil")
+            createRecoveryExpectation.fulfill()
+        }
+        self.wait(for: [createRecoveryExpectation], timeout: 10)
+
+        // Setting the RK will make TLKShares to the RK, so help that out too
+        try self.putRecoveryKeyTLKSharesInCloudKit(recoveryKey: recoveryKey, salt: self.mockAuthKit.altDSID!)
+
+        // Now, our peer (with no inbuilt knowledge of newPolicyDocument) joins via RecoveryKey
+        let serverJoinExpectation = self.expectation(description: "peer1 joins successfully")
+        self.fakeCuttlefishServer.joinListener = { joinRequest in
+            XCTAssertTrue(joinRequest.peer.hasStableInfoAndSig, "Joining peer should have a stable info")
+            let newStableInfo = joinRequest.peer.stableInfoAndSig.stableInfo()
+
+            XCTAssertEqual(newStableInfo.frozenPolicyVersion, frozenPolicyVersion, "Policy version in peer should match frozen policy version")
+            XCTAssertEqual(newStableInfo.flexiblePolicyVersion, newPolicyDocument.version, "Prevailing policy version in peer should match new policy version")
+
+            serverJoinExpectation.fulfill()
+            return nil
+        }
+
+        // It should recover and upload the FutureView TLK
+        self.assertAllCKKSViewsUpload(tlkShares: 1)
+
+        self.cuttlefishContext.startOctagonStateMachine()
+        self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
+
+        let joinWithRecoveryKeyExpectation = self.expectation(description: "joinWithRecoveryKey callback occurs")
+        self.cuttlefishContext.join(withRecoveryKey: recoveryKey) { error in
+            XCTAssertNil(error, "error should be nil")
+            joinWithRecoveryKeyExpectation.fulfill()
+        }
+        self.wait(for: [joinWithRecoveryKeyExpectation], timeout: 10)
+
+        self.assertAllCKKSViews(enter: SecCKKSZoneKeyStateReady, within: 10 * NSEC_PER_SEC)
+        XCTAssertEqual(self.injectedManager!.policy?.version, newPolicyDocument.version, "CKKS should be configured with new policy")
+        self.verifyDatabaseMocks()
+
+        self.wait(for: [serverJoinExpectation], timeout: 10)
+    }
+
+    func testPreapprovedJoinUsingFuturePolicy() throws {
+        let peer2mockSOS = CKKSMockSOSPresentAdapter(selfPeer: self.createSOSPeer(peerID: "peer2ID"), trustedPeers: self.mockSOSAdapter.allPeers(), essential: false)
+        print(peer2mockSOS.allPeers())
+        self.mockSOSAdapter.trustedPeers.add(peer2mockSOS.selfPeer)
+
+        let futurePeerContext = self.manager.context(forContainerName: OTCKContainerName,
+                                                     contextID: "futurePeer",
+                                                     sosAdapter: peer2mockSOS,
+                                                     authKitAdapter: self.mockAuthKit2,
+                                                     lockStateTracker: self.lockStateTracker,
+                                                     accountStateTracker: self.accountStateTracker,
+                                                     deviceInformationAdapter: OTMockDeviceInfoAdapter(modelID: "iPhone9,1", deviceName: "test-SOS-iphone", serialNumber: "456", osVersion: "iOS (fake version)"))
+
+        let (newPolicyDocument, _) = try self.createOctagonAndCKKSUsingFuturePolicy()
+        futurePeerContext.policyOverride = newPolicyDocument.version
+
+        let serverEstablishExpectation = self.expectation(description: "futurePeer establishes successfully")
+        self.fakeCuttlefishServer.establishListener = { establishRequest in
+            XCTAssertTrue(establishRequest.peer.hasStableInfoAndSig, "Establishing peer should have a stable info")
+            let newStableInfo = establishRequest.peer.stableInfoAndSig.stableInfo()
+
+            XCTAssertEqual(newStableInfo.frozenPolicyVersion, frozenPolicyVersion, "Policy version in peer should match frozen policy version")
+            XCTAssertEqual(newStableInfo.flexiblePolicyVersion, newPolicyDocument.version, "Prevailing policy version in peer should match new policy version")
+            serverEstablishExpectation.fulfill()
+            return nil
+        }
+
+        // Setup is complete. Join using a new peer
+        self.startCKAccountStatusMock()
+        futurePeerContext.startOctagonStateMachine()
+        self.assertEnters(context: futurePeerContext, state: OctagonStateReady, within: 10 * NSEC_PER_SEC)
+        self.wait(for: [serverEstablishExpectation], timeout: 10)
+
+        self.putFakeKeyHierarchiesInCloudKit()
+        try self.putSelfTLKSharesInCloudKit(context: futurePeerContext)
+
+        // Now, the default peer joins via SOS preapproval
+        let serverJoinExpectation = self.expectation(description: "peer1 joins successfully")
+        self.fakeCuttlefishServer.joinListener = { joinRequest in
+            XCTAssertTrue(joinRequest.peer.hasStableInfoAndSig, "Joining peer should have a stable info")
+            let newStableInfo = joinRequest.peer.stableInfoAndSig.stableInfo()
+
+            XCTAssertEqual(newStableInfo.frozenPolicyVersion, frozenPolicyVersion, "Policy version in peer should match frozen policy version")
+            XCTAssertEqual(newStableInfo.flexiblePolicyVersion, newPolicyDocument.version, "Prevailing policy version in peer should match new policy version")
+            serverJoinExpectation.fulfill()
+            return nil
+        }
+
+        self.mockSOSAdapter.circleStatus = SOSCCStatus(kSOSCCInCircle)
+        self.cuttlefishContext.startOctagonStateMachine()
+        self.assertEnters(context: self.cuttlefishContext, state: OctagonStateReady, within: 10 * NSEC_PER_SEC)
+        self.assertConsidersSelfTrusted(context: self.cuttlefishContext)
+        self.wait(for: [serverJoinExpectation], timeout: 10)
+
+        // But, since we're not mocking the remote peer sharing the TLKs, ckks should get stuck
+        self.assertAllCKKSViews(enter: SecCKKSZoneKeyStateWaitForTLK, within: 10 * NSEC_PER_SEC)
+        XCTAssertEqual(self.injectedManager!.policy?.version, newPolicyDocument.version, "CKKS should be configured with new policy")
+        self.verifyDatabaseMocks()
+    }
+
+    func testRespondToFuturePoliciesInPeerUpdates() throws {
+        self.startCKAccountStatusMock()
+        self.assertResetAndBecomeTrustedInDefaultContext()
+
+        // Now, another peer comes along and joins via BP recovery, using a new policy
+        let (newPolicyDocument, _) = try self.createOctagonAndCKKSUsingFuturePolicy()
+
+        let futurePeerContext = self.makeInitiatorContext(contextID: "futurePeer")
+        futurePeerContext.policyOverride = newPolicyDocument.version
+
+        let serverJoinExpectation = self.expectation(description: "futurePeer joins successfully")
+         self.fakeCuttlefishServer.joinListener = { joinRequest in
+             XCTAssertTrue(joinRequest.peer.hasStableInfoAndSig, "Joining peer should have a stable info")
+             let newStableInfo = joinRequest.peer.stableInfoAndSig.stableInfo()
+
+             XCTAssertEqual(newStableInfo.frozenPolicyVersion, frozenPolicyVersion, "Policy version in peer should match frozen policy version")
+             XCTAssertEqual(newStableInfo.flexiblePolicyVersion, newPolicyDocument.version, "Prevailing policy version in peer should match new policy version")
+             serverJoinExpectation.fulfill()
+             return nil
+         }
+
+        let peer2ID = self.assertJoinViaEscrowRecovery(joiningContext: futurePeerContext, sponsor: self.cuttlefishContext)
+        self.wait(for: [serverJoinExpectation], timeout: 10)
+
+        // Now, tell our first peer about the new changes. It should trust the new peer, and update its policy
+        let updateTrustExpectation = self.expectation(description: "updateTrustExpectation successfully")
+        self.fakeCuttlefishServer.updateListener = { request in
+            let newStableInfo = request.stableInfoAndSig.stableInfo()
+
+            XCTAssertEqual(newStableInfo.frozenPolicyVersion, frozenPolicyVersion, "Policy version in peer should match frozen policy version")
+            XCTAssertEqual(newStableInfo.flexiblePolicyVersion, newPolicyDocument.version, "Prevailing policy version in peer should match new policy version")
+
+            let newDynamicInfo = request.dynamicInfoAndSig.dynamicInfo()
+            XCTAssert(newDynamicInfo.includedPeerIDs.contains(peer2ID), "Peer1 should trust peer2")
+
+            updateTrustExpectation.fulfill()
+            return nil
+        }
+
+        self.sendContainerChangeWaitForFetch(context: self.cuttlefishContext)
+        self.wait(for: [updateTrustExpectation], timeout: 10)
+    }
+}
+
+#endif
index e1fd51bd0ddab33142e68a79f91ecbc565d8c6ee..6591eb61e3c18076d7b3eff847d308fccade6fa3 100644 (file)
@@ -9,6 +9,7 @@ class OctagonHealthCheckTests: OctagonTestsBase {
         self.cuttlefishContext.startOctagonStateMachine()
         self.startCKAccountStatusMock()
 
         self.cuttlefishContext.startOctagonStateMachine()
         self.startCKAccountStatusMock()
 
+        XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
         let clique: OTClique
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
         let clique: OTClique
@@ -39,10 +40,9 @@ class OctagonHealthCheckTests: OctagonTestsBase {
         self.wait(for: [healthCheckCallback], timeout: 10)
 
         let dumpCallback = self.expectation(description: "dumpCallback callback occurs")
         self.wait(for: [healthCheckCallback], timeout: 10)
 
         let dumpCallback = self.expectation(description: "dumpCallback callback occurs")
-        self.tphClient.dump(withContainer: containerName, context: contextName) {
-            dump, _ in
+        self.tphClient.dump(withContainer: containerName, context: contextName) { dump, _ in
             XCTAssertNotNil(dump, "dump should not be nil")
             XCTAssertNotNil(dump, "dump should not be nil")
-            let egoSelf = dump!["self"] as? Dictionary<String, AnyObject>
+            let egoSelf = dump!["self"] as? [String: AnyObject]
             XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
             dumpCallback.fulfill()
         }
             XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
             dumpCallback.fulfill()
         }
@@ -56,6 +56,7 @@ class OctagonHealthCheckTests: OctagonTestsBase {
         self.cuttlefishContext.startOctagonStateMachine()
         self.startCKAccountStatusMock()
 
         self.cuttlefishContext.startOctagonStateMachine()
         self.startCKAccountStatusMock()
 
+        XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
         let healthCheckCallback = self.expectation(description: "healthCheckCallback callback occurs")
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
         let healthCheckCallback = self.expectation(description: "healthCheckCallback callback occurs")
@@ -69,16 +70,16 @@ class OctagonHealthCheckTests: OctagonTestsBase {
         self.assertConsidersSelfUntrusted(context: self.cuttlefishContext)
 
         #if os(tvOS)
         self.assertConsidersSelfUntrusted(context: self.cuttlefishContext)
 
         #if os(tvOS)
-        XCTAssertEqual(self.cuttlefishContext.postedRepairCFU, false, "Should not have posted a CFU on aTV")
+        XCTAssertEqual(self.cuttlefishContext.followupHandler.hasPosted(.stateRepair), false, "Should not have posted a CFU on aTV")
         #else
         #else
-        XCTAssertEqual(self.cuttlefishContext.postedRepairCFU, true, "Should have posted a CFU (due to being untrusted)")
+        XCTAssertEqual(self.cuttlefishContext.followupHandler.hasPosted(.stateRepair), true, "Should have posted a CFU (due to being untrusted)")
         #endif
 
         self.verifyDatabaseMocks()
         self.assertEnters(context: cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
         // Reset flag for remainder of test
         #endif
 
         self.verifyDatabaseMocks()
         self.assertEnters(context: cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
         // Reset flag for remainder of test
-        self.cuttlefishContext.setPostedBool(false)
+        self.cuttlefishContext.followupHandler.clearAllPostedFlags()
 
         // Set the "have I attempted to join" bit; TVs should still not CFU, but other devices should
         try! self.cuttlefishContext.accountMetadataStore.persistOctagonJoinAttempt(.ATTEMPTED)
 
         // Set the "have I attempted to join" bit; TVs should still not CFU, but other devices should
         try! self.cuttlefishContext.accountMetadataStore.persistOctagonJoinAttempt(.ATTEMPTED)
@@ -91,9 +92,9 @@ class OctagonHealthCheckTests: OctagonTestsBase {
         self.wait(for: [healthCheckCallback2], timeout: 10)
 
         #if os(tvOS)
         self.wait(for: [healthCheckCallback2], timeout: 10)
 
         #if os(tvOS)
-        XCTAssertEqual(self.cuttlefishContext.postedRepairCFU, false, "Should not have posted a CFU on aTV")
+        XCTAssertEqual(self.cuttlefishContext.followupHandler.hasPosted(.stateRepair), false, "Should not have posted a CFU on aTV")
         #else
         #else
-        XCTAssertEqual(self.cuttlefishContext.postedRepairCFU, true, "Should have posted a CFU")
+        XCTAssertEqual(self.cuttlefishContext.followupHandler.hasPosted(.stateRepair), true, "Should have posted a CFU")
         #endif
     }
 
         #endif
     }
 
@@ -104,6 +105,7 @@ class OctagonHealthCheckTests: OctagonTestsBase {
         self.cuttlefishContext.startOctagonStateMachine()
         self.startCKAccountStatusMock()
 
         self.cuttlefishContext.startOctagonStateMachine()
         self.startCKAccountStatusMock()
 
+        XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
         let clique: OTClique
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
         let clique: OTClique
@@ -140,14 +142,13 @@ class OctagonHealthCheckTests: OctagonTestsBase {
         self.assertConsidersSelfTrusted(context: cuttlefishContext)
 
         let dumpCallback = self.expectation(description: "dumpCallback callback occurs")
         self.assertConsidersSelfTrusted(context: cuttlefishContext)
 
         let dumpCallback = self.expectation(description: "dumpCallback callback occurs")
-        self.tphClient.dump(withContainer: containerName, context: contextName) {
-            dump, _ in
+        self.tphClient.dump(withContainer: containerName, context: contextName) { dump, _ in
             XCTAssertNotNil(dump, "dump should not be nil")
             XCTAssertNotNil(dump, "dump should not be nil")
-            let egoSelf = dump!["self"] as? Dictionary<String, AnyObject>
+            let egoSelf = dump!["self"] as? [String: AnyObject]
             XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
             XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
-            let dynamicInfo = egoSelf!["dynamicInfo"] as? Dictionary<String, AnyObject>
+            let dynamicInfo = egoSelf!["dynamicInfo"] as? [String: AnyObject]
             XCTAssertNotNil(dynamicInfo, "dynamicInfo should not be nil")
             XCTAssertNotNil(dynamicInfo, "dynamicInfo should not be nil")
-            let included = dynamicInfo!["included"] as? Array<String>
+            let included = dynamicInfo!["included"] as? [String]
             XCTAssertNotNil(included, "included should not be nil")
             XCTAssertEqual(included!.count, 1, "should be 1 peer ids")
             dumpCallback.fulfill()
             XCTAssertNotNil(included, "included should not be nil")
             XCTAssertEqual(included!.count, 1, "should be 1 peer ids")
             dumpCallback.fulfill()
@@ -165,6 +166,7 @@ class OctagonHealthCheckTests: OctagonTestsBase {
         self.cuttlefishContext.startOctagonStateMachine()
         self.startCKAccountStatusMock()
 
         self.cuttlefishContext.startOctagonStateMachine()
         self.startCKAccountStatusMock()
 
+        XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
         let clique: OTClique
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
         let clique: OTClique
@@ -210,19 +212,20 @@ class OctagonHealthCheckTests: OctagonTestsBase {
         self.wait(for: [healthCheckCallback], timeout: 10)
 
         #if !os(tvOS)
         self.wait(for: [healthCheckCallback], timeout: 10)
 
         #if !os(tvOS)
-        XCTAssertEqual(cuttlefishContext.postedRepairCFU, true, "Should have posted a CFU")
+        XCTAssertTrue(cuttlefishContext.followupHandler.hasPosted(.stateRepair), "Should have posted a CFU")
         #else
         #else
-        XCTAssertFalse(cuttlefishContext.postedRepairCFU, "aTV should not have posted a CFU, as there's no iphone to recover from")
+        XCTAssertFalse(cuttlefishContext.followupHandler.hasPosted(.stateRepair), "aTV should not have posted a CFU, as there's no iphone to recover from")
         #endif
     }
 
         #endif
     }
 
-    func responseTestsSetup() throws -> (OTCuttlefishContext, String) {
+    func responseTestsSetup(expectedState: String) throws -> (OTCuttlefishContext, String) {
         let containerName = OTCKContainerName
         let contextName = OTDefaultContext
 
         self.cuttlefishContext.startOctagonStateMachine()
         self.startCKAccountStatusMock()
 
         let containerName = OTCKContainerName
         let contextName = OTDefaultContext
 
         self.cuttlefishContext.startOctagonStateMachine()
         self.startCKAccountStatusMock()
 
+        XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
         let clique: OTClique
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
         let clique: OTClique
@@ -249,6 +252,7 @@ class OctagonHealthCheckTests: OctagonTestsBase {
 
         // Reset any CFUs we've done so far
         self.otFollowUpController.postedFollowUp = false
 
         // Reset any CFUs we've done so far
         self.otFollowUpController.postedFollowUp = false
+        self.cuttlefishContext.followupHandler.clearAllPostedFlags()
 
         let healthCheckCallback = self.expectation(description: "healthCheckCallback callback occurs")
         self.manager.healthCheck(containerName, context: contextName, skipRateLimitingCheck: false) { error in
 
         let healthCheckCallback = self.expectation(description: "healthCheckCallback callback occurs")
         self.manager.healthCheck(containerName, context: contextName, skipRateLimitingCheck: false) { error in
@@ -258,57 +262,55 @@ class OctagonHealthCheckTests: OctagonTestsBase {
         self.wait(for: [healthCheckCallback], timeout: 10)
 
         let dumpCallback = self.expectation(description: "dumpCallback callback occurs")
         self.wait(for: [healthCheckCallback], timeout: 10)
 
         let dumpCallback = self.expectation(description: "dumpCallback callback occurs")
-        self.tphClient.dump(withContainer: containerName, context: contextName) {
-            dump, _ in
+        self.tphClient.dump(withContainer: containerName, context: contextName) { dump, _ in
             XCTAssertNotNil(dump, "dump should not be nil")
             XCTAssertNotNil(dump, "dump should not be nil")
-            let egoSelf = dump!["self"] as? Dictionary<String, AnyObject>
+            let egoSelf = dump!["self"] as? [String: AnyObject]
             XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
             dumpCallback.fulfill()
         }
         self.wait(for: [dumpCallback], timeout: 10)
 
         self.verifyDatabaseMocks()
             XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
             dumpCallback.fulfill()
         }
         self.wait(for: [dumpCallback], timeout: 10)
 
         self.verifyDatabaseMocks()
-        self.assertEnters(context: cuttlefishContext, state: OctagonStateReady, within: 10 * NSEC_PER_SEC)
+        self.assertEnters(context: cuttlefishContext, state: expectedState, within: 10 * NSEC_PER_SEC)
 
         return (cuttlefishContext, originalCliqueIdentifier!)
     }
 
     func testCuttlefishResponseNoAction() throws {
         self.fakeCuttlefishServer.returnNoActionResponse = true
 
         return (cuttlefishContext, originalCliqueIdentifier!)
     }
 
     func testCuttlefishResponseNoAction() throws {
         self.fakeCuttlefishServer.returnNoActionResponse = true
-        let (cuttlefishContext, _) = try responseTestsSetup()
+        let (cuttlefishContext, _) = try responseTestsSetup(expectedState: OctagonStateReady)
         XCTAssertFalse(self.otFollowUpController.postedFollowUp, "should not have posted a CFU")
         XCTAssertFalse(self.otFollowUpController.postedFollowUp, "should not have posted a CFU")
-        XCTAssertEqual(cuttlefishContext.postedRepairCFU, false, "should not have posted a CFU")
+        XCTAssertFalse(cuttlefishContext.followupHandler.hasPosted(.stateRepair), "should not have posted a Repair CFU")
     }
 
     func testCuttlefishResponseRepairAccount() throws {
         self.fakeCuttlefishServer.returnRepairAccountResponse = true
     }
 
     func testCuttlefishResponseRepairAccount() throws {
         self.fakeCuttlefishServer.returnRepairAccountResponse = true
-        let (_, _) = try responseTestsSetup()
-        XCTAssertTrue(self.otFollowUpController.postedFollowUp, "should have posted a CFU")
+        let (_, _) = try responseTestsSetup(expectedState: OctagonStateReady)
+        XCTAssertTrue(self.cuttlefishContext.followupHandler.hasPosted(.stateRepair), "should have posted a Repair CFU")
     }
 
     func testCuttlefishResponseRepairEscrow() throws {
         self.fakeCuttlefishServer.returnRepairEscrowResponse = true
         OTMockSecEscrowRequest.self.populateStatuses = false
     }
 
     func testCuttlefishResponseRepairEscrow() throws {
         self.fakeCuttlefishServer.returnRepairEscrowResponse = true
         OTMockSecEscrowRequest.self.populateStatuses = false
-        let (cuttlefishContext, _) = try responseTestsSetup()
+        let (cuttlefishContext, _) = try responseTestsSetup(expectedState: OctagonStateReady)
         XCTAssertTrue(self.otFollowUpController.postedFollowUp, "should have posted a CFU")
         XCTAssertTrue(self.otFollowUpController.postedFollowUp, "should have posted a CFU")
-        XCTAssertEqual(cuttlefishContext.postedEscrowRepairCFU, true, "should have posted an escrow CFU")
+        XCTAssertTrue(cuttlefishContext.followupHandler.hasPosted(.offlinePasscodeChange), "should have posted an escrow CFU")
     }
 
     func testCuttlefishResponseResetOctagon() throws {
         let contextName = OTDefaultContext
         let containerName = OTCKContainerName
         self.fakeCuttlefishServer.returnResetOctagonResponse = true
     }
 
     func testCuttlefishResponseResetOctagon() throws {
         let contextName = OTDefaultContext
         let containerName = OTCKContainerName
         self.fakeCuttlefishServer.returnResetOctagonResponse = true
-        let (cuttlefishContext, cliqueIdentifier) = try responseTestsSetup()
+        let (cuttlefishContext, cliqueIdentifier) = try responseTestsSetup(expectedState: OctagonStateReady)
 
         assertAllCKKSViews(enter: SecCKKSZoneKeyStateReady, within: 10 * NSEC_PER_SEC)
         self.verifyDatabaseMocks()
 
         var newCliqueIdentifier: String?
         let dumpCallback = self.expectation(description: "dumpCallback callback occurs")
 
         assertAllCKKSViews(enter: SecCKKSZoneKeyStateReady, within: 10 * NSEC_PER_SEC)
         self.verifyDatabaseMocks()
 
         var newCliqueIdentifier: String?
         let dumpCallback = self.expectation(description: "dumpCallback callback occurs")
-        self.tphClient.dump(withContainer: containerName, context: contextName) {
-            dump, _ in
+        self.tphClient.dump(withContainer: containerName, context: contextName) { dump, _ in
             XCTAssertNotNil(dump, "dump should not be nil")
             XCTAssertNotNil(dump, "dump should not be nil")
-            let egoSelf = dump!["self"] as? Dictionary<String, AnyObject>
+            let egoSelf = dump!["self"] as? [String: AnyObject]
             newCliqueIdentifier = egoSelf!["peerID"]! as? String
             XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
             dumpCallback.fulfill()
             newCliqueIdentifier = egoSelf!["peerID"]! as? String
             XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
             dumpCallback.fulfill()
@@ -321,12 +323,22 @@ class OctagonHealthCheckTests: OctagonTestsBase {
         assertAllCKKSViews(enter: SecCKKSZoneKeyStateReady, within: 10 * NSEC_PER_SEC)
     }
 
         assertAllCKKSViews(enter: SecCKKSZoneKeyStateReady, within: 10 * NSEC_PER_SEC)
     }
 
+    func testCuttlefishResponseLeaveTrust() throws {
+        OctagonSetSOSFeatureEnabled(false)
+
+        self.fakeCuttlefishServer.returnLeaveTrustResponse = true
+        let (_, _) = try responseTestsSetup(expectedState: OctagonStateUntrusted)
+
+        self.verifyDatabaseMocks()
+        assertAllCKKSViews(enter: SecCKKSZoneKeyStateWaitForTrust, within: 10 * NSEC_PER_SEC)
+    }
+
     func testCuttlefishResponseError() throws {
         self.fakeCuttlefishServer.returnRepairErrorResponse = FakeCuttlefishServer.makeCloudKitCuttlefishError(code: .changeTokenExpired)
 
     func testCuttlefishResponseError() throws {
         self.fakeCuttlefishServer.returnRepairErrorResponse = FakeCuttlefishServer.makeCloudKitCuttlefishError(code: .changeTokenExpired)
 
-        let (cuttlefishContext, _) = try responseTestsSetup()
-        XCTAssertEqual(cuttlefishContext.postedRepairCFU, false, "should not have posted an account repair CFU")
-        XCTAssertEqual(cuttlefishContext.postedEscrowRepairCFU, false, "should not have posted an escrow repair CFU")
+        let (cuttlefishContext, _) = try responseTestsSetup(expectedState: OctagonStateReady)
+        XCTAssertFalse(cuttlefishContext.followupHandler.hasPosted(.stateRepair), "should not have posted an account repair CFU")
+        XCTAssertFalse(cuttlefishContext.followupHandler.hasPosted(.offlinePasscodeChange), "should not have posted an escrow repair CFU")
     }
 
     func testHealthCheckBeforeStateMachineStarts() throws {
     }
 
     func testHealthCheckBeforeStateMachineStarts() throws {
@@ -350,6 +362,7 @@ class OctagonHealthCheckTests: OctagonTestsBase {
 
         cuttlefishContext.startOctagonStateMachine()
 
 
         cuttlefishContext.startOctagonStateMachine()
 
+        XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
         self.assertEnters(context: cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
         let clique: OTClique
         self.assertEnters(context: cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
         let clique: OTClique
@@ -378,10 +391,9 @@ class OctagonHealthCheckTests: OctagonTestsBase {
         }
 
         let dumpCallback = self.expectation(description: "dumpCallback callback occurs")
         }
 
         let dumpCallback = self.expectation(description: "dumpCallback callback occurs")
-        self.tphClient.dump(withContainer: containerName, context: contextName) {
-            dump, _ in
+        self.tphClient.dump(withContainer: containerName, context: contextName) { dump, _ in
             XCTAssertNotNil(dump, "dump should not be nil")
             XCTAssertNotNil(dump, "dump should not be nil")
-            let egoSelf = dump!["self"] as? Dictionary<String, AnyObject>
+            let egoSelf = dump!["self"] as? [String: AnyObject]
             XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
             dumpCallback.fulfill()
         }
             XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
             dumpCallback.fulfill()
         }
@@ -395,6 +407,7 @@ class OctagonHealthCheckTests: OctagonTestsBase {
         let containerName = OTCKContainerName
         let contextName = OTDefaultContext
 
         let containerName = OTCKContainerName
         let contextName = OTDefaultContext
 
+        XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
         self.cuttlefishContext.startOctagonStateMachine()
         self.startCKAccountStatusMock()
 
         self.cuttlefishContext.startOctagonStateMachine()
         self.startCKAccountStatusMock()
 
@@ -430,10 +443,9 @@ class OctagonHealthCheckTests: OctagonTestsBase {
         }
 
         let dumpCallback = self.expectation(description: "dumpCallback callback occurs")
         }
 
         let dumpCallback = self.expectation(description: "dumpCallback callback occurs")
-        self.tphClient.dump(withContainer: containerName, context: contextName) {
-            dump, _ in
+        self.tphClient.dump(withContainer: containerName, context: contextName) { dump, _ in
             XCTAssertNotNil(dump, "dump should not be nil")
             XCTAssertNotNil(dump, "dump should not be nil")
-            let egoSelf = dump!["self"] as? Dictionary<String, AnyObject>
+            let egoSelf = dump!["self"] as? [String: AnyObject]
             XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
             dumpCallback.fulfill()
         }
             XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
             dumpCallback.fulfill()
         }
@@ -450,6 +462,7 @@ class OctagonHealthCheckTests: OctagonTestsBase {
         self.cuttlefishContext.startOctagonStateMachine()
         self.startCKAccountStatusMock()
 
         self.cuttlefishContext.startOctagonStateMachine()
         self.startCKAccountStatusMock()
 
+        XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
         let healthCheckCallback = self.expectation(description: "healthCheckCallback callback occurs")
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
         let healthCheckCallback = self.expectation(description: "healthCheckCallback callback occurs")
@@ -481,10 +494,9 @@ class OctagonHealthCheckTests: OctagonTestsBase {
         }
 
         let dumpCallback = self.expectation(description: "dumpCallback callback occurs")
         }
 
         let dumpCallback = self.expectation(description: "dumpCallback callback occurs")
-        self.tphClient.dump(withContainer: containerName, context: contextName) {
-            dump, _ in
+        self.tphClient.dump(withContainer: containerName, context: contextName) { dump, _ in
             XCTAssertNotNil(dump, "dump should not be nil")
             XCTAssertNotNil(dump, "dump should not be nil")
-            let egoSelf = dump!["self"] as? Dictionary<String, AnyObject>
+            let egoSelf = dump!["self"] as? [String: AnyObject]
             XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
             dumpCallback.fulfill()
         }
             XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
             dumpCallback.fulfill()
         }
@@ -523,6 +535,59 @@ class OctagonHealthCheckTests: OctagonTestsBase {
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateNoAccount, within: 10 * NSEC_PER_SEC)
     }
 
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateNoAccount, within: 10 * NSEC_PER_SEC)
     }
 
+    func testHealthCheckWaitingForCDP() throws {
+        // Tell SOS that it is absent, so we don't enable CDP on bringup
+        self.mockSOSAdapter.circleStatus = SOSCCStatus(kSOSCCCircleAbsent)
+
+        self.cuttlefishContext.startOctagonStateMachine()
+        self.startCKAccountStatusMock()
+
+        self.assertEnters(context: self.cuttlefishContext, state: OctagonStateWaitForCDP, within: 10 * NSEC_PER_SEC)
+
+        self.cuttlefishContext.stateMachine.setWatcherTimeout(2 * NSEC_PER_SEC)
+
+        let healthCheckCallback = self.expectation(description: "healthCheckCallback callback occurs")
+        self.manager.healthCheck(OTCKContainerName, context: self.cuttlefishContext.contextID, skipRateLimitingCheck: false) { error in
+            XCTAssertNil(error, "error should be nil")
+            healthCheckCallback.fulfill()
+        }
+        self.wait(for: [healthCheckCallback], timeout: 10)
+
+        self.assertEnters(context: self.cuttlefishContext, state: OctagonStateWaitForCDP, within: 10 * NSEC_PER_SEC)
+    }
+
+    func testHealthCheckRecoversFromWrongWaitingForCDP() throws {
+        // Tell SOS that it is absent, so we don't enable CDP on bringup
+        self.mockSOSAdapter.circleStatus = SOSCCStatus(kSOSCCCircleAbsent)
+
+        self.cuttlefishContext.startOctagonStateMachine()
+        self.startCKAccountStatusMock()
+
+        self.assertEnters(context: self.cuttlefishContext, state: OctagonStateWaitForCDP, within: 10 * NSEC_PER_SEC)
+
+        // Now, another client creates the circle, but we miss the push
+        let remote = self.makeInitiatorContext(contextID: "remote")
+        self.assertResetAndBecomeTrusted(context: remote)
+
+        // Now, does the health check get us into Untrusted?
+        self.cuttlefishContext.stateMachine.setWatcherTimeout(2 * NSEC_PER_SEC)
+
+        let healthCheckCallback = self.expectation(description: "healthCheckCallback callback occurs")
+        self.manager.healthCheck(OTCKContainerName, context: self.cuttlefishContext.contextID, skipRateLimitingCheck: false) { error in
+            XCTAssertNil(error, "error should be nil")
+            healthCheckCallback.fulfill()
+        }
+        self.wait(for: [healthCheckCallback], timeout: 10)
+
+        self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
+
+        #if !os(tvOS)
+        XCTAssertTrue(self.cuttlefishContext.followupHandler.hasPosted(.stateRepair), "Octagon should have posted a repair CFU after the health check")
+        #else
+        XCTAssertFalse(self.cuttlefishContext.followupHandler.hasPosted(.stateRepair), "posted should be false on tvOS; there aren't any iphones around to repair it")
+        #endif
+    }
+
     func testHealthCheckWhenLocked() throws {
 
         let containerName = OTCKContainerName
     func testHealthCheckWhenLocked() throws {
 
         let containerName = OTCKContainerName
@@ -531,6 +596,7 @@ class OctagonHealthCheckTests: OctagonTestsBase {
         self.cuttlefishContext.startOctagonStateMachine()
         self.startCKAccountStatusMock()
 
         self.cuttlefishContext.startOctagonStateMachine()
         self.startCKAccountStatusMock()
 
+        XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
         let clique: OTClique
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
         let clique: OTClique
@@ -564,10 +630,9 @@ class OctagonHealthCheckTests: OctagonTestsBase {
         self.wait(for: [healthCheckCallback], timeout: 10)
 
         let dumpCallback = self.expectation(description: "dumpCallback callback occurs")
         self.wait(for: [healthCheckCallback], timeout: 10)
 
         let dumpCallback = self.expectation(description: "dumpCallback callback occurs")
-        self.tphClient.dump(withContainer: containerName, context: contextName) {
-            dump, _ in
+        self.tphClient.dump(withContainer: containerName, context: contextName) { dump, _ in
             XCTAssertNotNil(dump, "dump should not be nil")
             XCTAssertNotNil(dump, "dump should not be nil")
-            let egoSelf = dump!["self"] as? Dictionary<String, AnyObject>
+            let egoSelf = dump!["self"] as? [String: AnyObject]
             XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
             dumpCallback.fulfill()
         }
             XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
             dumpCallback.fulfill()
         }
@@ -584,6 +649,7 @@ class OctagonHealthCheckTests: OctagonTestsBase {
         self.cuttlefishContext.startOctagonStateMachine()
         self.startCKAccountStatusMock()
 
         self.cuttlefishContext.startOctagonStateMachine()
         self.startCKAccountStatusMock()
 
+        XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
         let clique: OTClique
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
         let clique: OTClique
@@ -623,10 +689,9 @@ class OctagonHealthCheckTests: OctagonTestsBase {
         self.wait(for: [healthCheckCallback], timeout: 10)
 
         let dumpCallback = self.expectation(description: "dumpCallback callback occurs")
         self.wait(for: [healthCheckCallback], timeout: 10)
 
         let dumpCallback = self.expectation(description: "dumpCallback callback occurs")
-        self.tphClient.dump(withContainer: containerName, context: contextName) {
-            dump, _ in
+        self.tphClient.dump(withContainer: containerName, context: contextName) { dump, _ in
             XCTAssertNotNil(dump, "dump should not be nil")
             XCTAssertNotNil(dump, "dump should not be nil")
-            let egoSelf = dump!["self"] as? Dictionary<String, AnyObject>
+            let egoSelf = dump!["self"] as? [String: AnyObject]
             XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
             dumpCallback.fulfill()
         }
             XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
             dumpCallback.fulfill()
         }
@@ -660,7 +725,7 @@ class OctagonHealthCheckTests: OctagonTestsBase {
         //update the last health check to something way in the past
         do {
             let state = try OTAccountMetadataClassC.loadFromKeychain(forContainer: OTCKContainerName, contextID: OTDefaultContext)
         //update the last health check to something way in the past
         do {
             let state = try OTAccountMetadataClassC.loadFromKeychain(forContainer: OTCKContainerName, contextID: OTDefaultContext)
-            state.lastHealthCheckup = state.lastHealthCheckup - 172800000 /* 2 days of seconds * 1000*/
+            state.lastHealthCheckup -= 172800000 /* 2 days of seconds * 1000*/
             healthCheckMinusTwoDays = state.lastHealthCheckup
             XCTAssertNoThrow(try state.saveToKeychain(forContainer: OTCKContainerName, contextID: OTDefaultContext), "saving to the keychain should work")
         } catch {
             healthCheckMinusTwoDays = state.lastHealthCheckup
             XCTAssertNoThrow(try state.saveToKeychain(forContainer: OTCKContainerName, contextID: OTDefaultContext), "saving to the keychain should work")
         } catch {
@@ -705,6 +770,7 @@ class OctagonHealthCheckTests: OctagonTestsBase {
         bottlerContext.startOctagonStateMachine()
         self.startCKAccountStatusMock()
 
         bottlerContext.startOctagonStateMachine()
         self.startCKAccountStatusMock()
 
+        XCTAssertNoThrow(try bottlerContext.setCDPEnabled())
         self.assertEnters(context: bottlerContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
         let clique: OTClique
         self.assertEnters(context: bottlerContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
         let clique: OTClique
@@ -729,13 +795,14 @@ class OctagonHealthCheckTests: OctagonTestsBase {
         XCTAssertNotNil(entropy, "entropy should not be nil")
 
         // Fake that this peer also created some TLKShares for itself
         XCTAssertNotNil(entropy, "entropy should not be nil")
 
         // Fake that this peer also created some TLKShares for itself
-        self.putFakeKeyHierarchy(inCloudKit: self.manateeZoneID)
-        try self.putSelfTLKShareInCloudKit(context: bottlerContext, zoneID: self.manateeZoneID)
+        self.putFakeKeyHierarchiesInCloudKit()
+        try self.putSelfTLKSharesInCloudKit(context: bottlerContext)
 
         let bottle = self.fakeCuttlefishServer.state.bottles[0]
 
         self.cuttlefishContext.startOctagonStateMachine()
         self.startCKAccountStatusMock()
 
         let bottle = self.fakeCuttlefishServer.state.bottles[0]
 
         self.cuttlefishContext.startOctagonStateMachine()
         self.startCKAccountStatusMock()
+        XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
         // cheat: a bottle restore can only succeed after a fetch occurs
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
         // cheat: a bottle restore can only succeed after a fetch occurs
@@ -766,7 +833,7 @@ class OctagonHealthCheckTests: OctagonTestsBase {
         }
 
         let joinWithBottleExpectation = self.expectation(description: "joinWithBottle callback occurs")
         }
 
         let joinWithBottleExpectation = self.expectation(description: "joinWithBottle callback occurs")
-        self.cuttlefishContext.join(withBottle: bottle.bottleID, entropy: entropy!, bottleSalt: self.otcliqueContext.altDSID) { error in
+        self.cuttlefishContext.join(withBottle: bottle.bottleID, entropy: entropy!, bottleSalt: self.otcliqueContext.altDSID!) { error in
             XCTAssertNotNil(error, "error should not be nil")
             joinWithBottleExpectation.fulfill()
         }
             XCTAssertNotNil(error, "error should not be nil")
             joinWithBottleExpectation.fulfill()
         }
@@ -783,8 +850,8 @@ class OctagonHealthCheckTests: OctagonTestsBase {
     func testCuttlefishDontPostEscrowCFUDueToPendingPrecord() throws {
         self.fakeCuttlefishServer.returnRepairEscrowResponse = true
         OTMockSecEscrowRequest.self.populateStatuses = true
     func testCuttlefishDontPostEscrowCFUDueToPendingPrecord() throws {
         self.fakeCuttlefishServer.returnRepairEscrowResponse = true
         OTMockSecEscrowRequest.self.populateStatuses = true
-        let (cuttlefishContext, _) = try responseTestsSetup()
-        XCTAssertEqual(cuttlefishContext.postedEscrowRepairCFU, false, "should NOT have posted an escrow CFU")
+        let (cuttlefishContext, _) = try responseTestsSetup(expectedState: OctagonStateReady)
+        XCTAssertFalse(cuttlefishContext.followupHandler.hasPosted(.offlinePasscodeChange), "should NOT have posted an escrow CFU")
     }
 
     func testHealthCheckWhileLocked() throws {
     }
 
     func testHealthCheckWhileLocked() throws {
@@ -794,6 +861,7 @@ class OctagonHealthCheckTests: OctagonTestsBase {
         self.cuttlefishContext.startOctagonStateMachine()
         self.startCKAccountStatusMock()
 
         self.cuttlefishContext.startOctagonStateMachine()
         self.startCKAccountStatusMock()
 
+        XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
         let clique: OTClique
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
         let clique: OTClique
diff --git a/keychain/ot/tests/octagon/OctagonTests+Helpers.swift b/keychain/ot/tests/octagon/OctagonTests+Helpers.swift
new file mode 100644 (file)
index 0000000..70ed71e
--- /dev/null
@@ -0,0 +1,33 @@
+#if OCTAGON
+import Foundation
+
+extension SignedPeerStableInfo {
+    func stableInfo() -> TPPeerStableInfo {
+        let newStableInfo = TPPeerStableInfo(data: self.peerStableInfo, sig: self.sig)
+        XCTAssertNotNil(newStableInfo, "should be able to make a stableInfo from protobuf")
+        return newStableInfo!
+    }
+}
+
+extension SignedPeerDynamicInfo {
+    func dynamicInfo() -> TPPeerDynamicInfo {
+        let newDynamicInfo = TPPeerDynamicInfo(data: self.peerDynamicInfo, sig: self.sig)
+        XCTAssertNotNil(newDynamicInfo, "should be able to make a dynamicInfo from protobuf")
+        return newDynamicInfo!
+    }
+}
+
+extension EstablishRequest {
+    func permanentInfo() -> TPPeerPermanentInfo {
+        XCTAssertTrue(self.hasPeer, "establish request should have a peer")
+        XCTAssertTrue(self.peer.hasPermanentInfoAndSig, "establish request should have a permanentInfo")
+        let newPermanentInfo = TPPeerPermanentInfo(peerID: self.peer.peerID,
+                                                   data: self.peer.permanentInfoAndSig.peerPermanentInfo,
+                                                   sig: self.peer.permanentInfoAndSig.sig,
+                                                   keyFactory: TPECPublicKeyFactory())
+        XCTAssertNotNil(newPermanentInfo, "should be able to make a permanantInfo from protobuf")
+        return newPermanentInfo!
+    }
+}
+
+#endif
index ad69de5b3b09a010995c1e411e6a3d5fd65c1cee..10172e3514cd8b8282f02fe43b70079f3d603cbd 100644 (file)
@@ -1,6 +1,18 @@
 #if OCTAGON
 
 #if OCTAGON
 
-@objcMembers class OctagonRecoveryKeyTests: OctagonTestsBase {
+extension Container {
+    func removeRKFromContainer() {
+        self.moc.performAndWait {
+            self.containerMO.recoveryKeySigningSPKI = nil
+            self.containerMO.recoveryKeyEncryptionSPKI = nil
+
+            try! self.moc.save()
+        }
+    }
+}
+
+@objcMembers
+class OctagonRecoveryKeyTests: OctagonTestsBase {
     override func setUp() {
         super.setUp()
     }
     override func setUp() {
         super.setUp()
     }
@@ -10,6 +22,7 @@
         self.manager.setSOSEnabledForPlatformFlag(false)
 
         self.cuttlefishContext.startOctagonStateMachine()
         self.manager.setSOSEnabledForPlatformFlag(false)
 
         self.cuttlefishContext.startOctagonStateMachine()
+        XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
         XCTAssertFalse(self.mockAuthKit.currentDeviceList().isEmpty, "should not have zero devices")
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
         XCTAssertFalse(self.mockAuthKit.currentDeviceList().isEmpty, "should not have zero devices")
@@ -34,7 +47,7 @@
         self.manager.setSOSEnabledForPlatformFlag(true)
 
         let createKeyExpectation = self.expectation(description: "createKeyExpectation returns")
         self.manager.setSOSEnabledForPlatformFlag(true)
 
         let createKeyExpectation = self.expectation(description: "createKeyExpectation returns")
-        self.manager.createRecoveryKey(OTCKContainerName, contextID: self.otcliqueContext.context ?? "defaultContext", recoveryKey: recoveryKey) { error in
+        self.manager.createRecoveryKey(OTCKContainerName, contextID: self.otcliqueContext.context, recoveryKey: recoveryKey) { error in
             XCTAssertNil(error, "error should be nil")
             createKeyExpectation.fulfill()
         }
             XCTAssertNil(error, "error should be nil")
             createKeyExpectation.fulfill()
         }
@@ -46,6 +59,7 @@
         self.manager.setSOSEnabledForPlatformFlag(false)
 
         self.cuttlefishContext.startOctagonStateMachine()
         self.manager.setSOSEnabledForPlatformFlag(false)
 
         self.cuttlefishContext.startOctagonStateMachine()
+        XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
         let clique: OTClique
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
         let clique: OTClique
@@ -66,7 +80,7 @@
         self.manager.setSOSEnabledForPlatformFlag(true)
 
         let createKeyExpectation = self.expectation(description: "createKeyExpectation returns")
         self.manager.setSOSEnabledForPlatformFlag(true)
 
         let createKeyExpectation = self.expectation(description: "createKeyExpectation returns")
-        self.manager.createRecoveryKey(OTCKContainerName, contextID: self.otcliqueContext.context ?? "defaultContext", recoveryKey: recoveryKey) { error in
+        self.manager.createRecoveryKey(OTCKContainerName, contextID: self.otcliqueContext.context, recoveryKey: recoveryKey) { error in
             XCTAssertNil(error, "error should be nil")
             createKeyExpectation.fulfill()
         }
             XCTAssertNil(error, "error should be nil")
             createKeyExpectation.fulfill()
         }
         self.sendContainerChange(context: initiatorContext)
 
         let joinWithBottleExpectation = self.expectation(description: "joinWithBottle callback occurs")
         self.sendContainerChange(context: initiatorContext)
 
         let joinWithBottleExpectation = self.expectation(description: "joinWithBottle callback occurs")
-        initiatorContext.join(withBottle: bottle.bottleID, entropy: entropy!, bottleSalt: self.otcliqueContext.altDSID) { error in
+        initiatorContext.join(withBottle: bottle.bottleID, entropy: entropy!, bottleSalt: self.otcliqueContext.altDSID!) { error in
             XCTAssertNil(error, "error should be nil")
             joinWithBottleExpectation.fulfill()
         }
             XCTAssertNil(error, "error should be nil")
             joinWithBottleExpectation.fulfill()
         }
         self.verifyDatabaseMocks()
 
         let stableInfoCheckDumpCallback = self.expectation(description: "stableInfoCheckDumpCallback callback occurs")
         self.verifyDatabaseMocks()
 
         let stableInfoCheckDumpCallback = self.expectation(description: "stableInfoCheckDumpCallback callback occurs")
-        self.tphClient.dump(withContainer: OTCKContainerName, context: initiatorContextID) {
-            dump, _ in
+        self.tphClient.dump(withContainer: OTCKContainerName, context: initiatorContextID) { dump, _ in
             XCTAssertNotNil(dump, "dump should not be nil")
             XCTAssertNotNil(dump, "dump should not be nil")
-            let egoSelf = dump!["self"] as? Dictionary<String, AnyObject>
+            let egoSelf = dump!["self"] as? [String: AnyObject]
             XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
             XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
-            let dynamicInfo = egoSelf!["dynamicInfo"] as? Dictionary<String, AnyObject>
+            let dynamicInfo = egoSelf!["dynamicInfo"] as? [String: AnyObject]
             XCTAssertNotNil(dynamicInfo, "dynamicInfo should not be nil")
 
             XCTAssertNotNil(dynamicInfo, "dynamicInfo should not be nil")
 
-            let stableInfo = egoSelf!["stableInfo"] as? Dictionary<String, AnyObject>
+            let stableInfo = egoSelf!["stableInfo"] as? [String: AnyObject]
             XCTAssertNotNil(stableInfo, "stableInfo should not be nil")
             XCTAssertNotNil(stableInfo!["recovery_signing_public_key"], "recoverySigningPublicKey should not be nil")
             XCTAssertNotNil(stableInfo!["recovery_encryption_public_key"], "recoveryEncryptionPublicKey should not be nil")
 
             XCTAssertNotNil(stableInfo, "stableInfo should not be nil")
             XCTAssertNotNil(stableInfo!["recovery_signing_public_key"], "recoverySigningPublicKey should not be nil")
             XCTAssertNotNil(stableInfo!["recovery_encryption_public_key"], "recoveryEncryptionPublicKey should not be nil")
 
-            let included = dynamicInfo!["included"] as? Array<String>
+            let included = dynamicInfo!["included"] as? [String]
             XCTAssertNotNil(included, "included should not be nil")
             XCTAssertEqual(included!.count, 2, "should be 2 peer ids")
 
             XCTAssertNotNil(included, "included should not be nil")
             XCTAssertEqual(included!.count, 2, "should be 2 peer ids")
 
         self.wait(for: [stableInfoCheckDumpCallback], timeout: 10)
 
         let stableInfoAcceptorCheckDumpCallback = self.expectation(description: "stableInfoAcceptorCheckDumpCallback callback occurs")
         self.wait(for: [stableInfoCheckDumpCallback], timeout: 10)
 
         let stableInfoAcceptorCheckDumpCallback = self.expectation(description: "stableInfoAcceptorCheckDumpCallback callback occurs")
-        self.tphClient.dump(withContainer: OTCKContainerName, context: self.otcliqueContext.context ?? "defaultContext") {
-            dump, _ in
+        self.tphClient.dump(withContainer: OTCKContainerName, context: self.otcliqueContext.context) { dump, _ in
             XCTAssertNotNil(dump, "dump should not be nil")
             XCTAssertNotNil(dump, "dump should not be nil")
-            let egoSelf = dump!["self"] as? Dictionary<String, AnyObject>
+            let egoSelf = dump!["self"] as? [String: AnyObject]
             XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
             XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
-            let dynamicInfo = egoSelf!["dynamicInfo"] as? Dictionary<String, AnyObject>
+            let dynamicInfo = egoSelf!["dynamicInfo"] as? [String: AnyObject]
             XCTAssertNotNil(dynamicInfo, "dynamicInfo should not be nil")
 
             XCTAssertNotNil(dynamicInfo, "dynamicInfo should not be nil")
 
-            let stableInfo = egoSelf!["stableInfo"] as? Dictionary<String, AnyObject>
+            let stableInfo = egoSelf!["stableInfo"] as? [String: AnyObject]
             XCTAssertNotNil(stableInfo, "stableInfo should not be nil")
             XCTAssertNotNil(stableInfo!["recovery_signing_public_key"], "recoverySigningPublicKey should not be nil")
             XCTAssertNotNil(stableInfo!["recovery_encryption_public_key"], "recoveryEncryptionPublicKey should not be nil")
 
             XCTAssertNotNil(stableInfo, "stableInfo should not be nil")
             XCTAssertNotNil(stableInfo!["recovery_signing_public_key"], "recoverySigningPublicKey should not be nil")
             XCTAssertNotNil(stableInfo!["recovery_encryption_public_key"], "recoveryEncryptionPublicKey should not be nil")
 
-            let included = dynamicInfo!["included"] as? Array<String>
+            let included = dynamicInfo!["included"] as? [String]
             XCTAssertNotNil(included, "included should not be nil")
             XCTAssertEqual(included!.count, 2, "should be 2 peer ids")
 
             XCTAssertNotNil(included, "included should not be nil")
             XCTAssertEqual(included!.count, 2, "should be 2 peer ids")
 
         self.manager.setSOSEnabledForPlatformFlag(false)
 
         self.cuttlefishContext.startOctagonStateMachine()
         self.manager.setSOSEnabledForPlatformFlag(false)
 
         self.cuttlefishContext.startOctagonStateMachine()
+        XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
         let clique: OTClique
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
         let clique: OTClique
         self.manager.setSOSEnabledForPlatformFlag(true)
 
         let createKeyExpectation = self.expectation(description: "createKeyExpectation returns")
         self.manager.setSOSEnabledForPlatformFlag(true)
 
         let createKeyExpectation = self.expectation(description: "createKeyExpectation returns")
-        self.manager.createRecoveryKey(OTCKContainerName, contextID: self.otcliqueContext.context ?? "defaultContext", recoveryKey: recoveryKey) { error in
+        self.manager.createRecoveryKey(OTCKContainerName, contextID: self.otcliqueContext.context, recoveryKey: recoveryKey) { error in
             XCTAssertNil(error, "error should be nil")
             createKeyExpectation.fulfill()
         }
             XCTAssertNil(error, "error should be nil")
             createKeyExpectation.fulfill()
         }
         self.assertEnters(context: initiatorContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
         let joinWithBottleExpectation = self.expectation(description: "joinWithBottle callback occurs")
         self.assertEnters(context: initiatorContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
         let joinWithBottleExpectation = self.expectation(description: "joinWithBottle callback occurs")
-        initiatorContext.join(withBottle: bottle.bottleID, entropy: entropy!, bottleSalt: self.otcliqueContext.altDSID) { error in
+        initiatorContext.join(withBottle: bottle.bottleID, entropy: entropy!, bottleSalt: self.otcliqueContext.altDSID!) { error in
             XCTAssertNil(error, "error should be nil")
             joinWithBottleExpectation.fulfill()
         }
             XCTAssertNil(error, "error should be nil")
             joinWithBottleExpectation.fulfill()
         }
         self.verifyDatabaseMocks()
 
         let stableInfoCheckDumpCallback = self.expectation(description: "stableInfoCheckDumpCallback callback occurs")
         self.verifyDatabaseMocks()
 
         let stableInfoCheckDumpCallback = self.expectation(description: "stableInfoCheckDumpCallback callback occurs")
-        self.tphClient.dump(withContainer: OTCKContainerName, context: initiatorContextID) {
-            dump, _ in
+        self.tphClient.dump(withContainer: OTCKContainerName, context: initiatorContextID) { dump, _ in
             XCTAssertNotNil(dump, "dump should not be nil")
             XCTAssertNotNil(dump, "dump should not be nil")
-            let egoSelf = dump!["self"] as? Dictionary<String, AnyObject>
+            let egoSelf = dump!["self"] as? [String: AnyObject]
             XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
             XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
-            let dynamicInfo = egoSelf!["dynamicInfo"] as? Dictionary<String, AnyObject>
+            let dynamicInfo = egoSelf!["dynamicInfo"] as? [String: AnyObject]
             XCTAssertNotNil(dynamicInfo, "dynamicInfo should not be nil")
 
             XCTAssertNotNil(dynamicInfo, "dynamicInfo should not be nil")
 
-            let stableInfo = egoSelf!["stableInfo"] as? Dictionary<String, AnyObject>
+            let stableInfo = egoSelf!["stableInfo"] as? [String: AnyObject]
             XCTAssertNotNil(stableInfo, "stableInfo should not be nil")
             XCTAssertNotNil(stableInfo!["recovery_signing_public_key"], "recoverySigningPublicKey should not be nil")
             XCTAssertNotNil(stableInfo!["recovery_encryption_public_key"], "recoveryEncryptionPublicKey should not be nil")
 
             XCTAssertNotNil(stableInfo, "stableInfo should not be nil")
             XCTAssertNotNil(stableInfo!["recovery_signing_public_key"], "recoverySigningPublicKey should not be nil")
             XCTAssertNotNil(stableInfo!["recovery_encryption_public_key"], "recoveryEncryptionPublicKey should not be nil")
 
-            let included = dynamicInfo!["included"] as? Array<String>
+            let included = dynamicInfo!["included"] as? [String]
             XCTAssertNotNil(included, "included should not be nil")
             XCTAssertEqual(included!.count, 2, "should be 2 peer ids")
 
             XCTAssertNotNil(included, "included should not be nil")
             XCTAssertEqual(included!.count, 2, "should be 2 peer ids")
 
         self.wait(for: [stableInfoCheckDumpCallback], timeout: 10)
 
         let stableInfoAcceptorCheckDumpCallback = self.expectation(description: "stableInfoAcceptorCheckDumpCallback callback occurs")
         self.wait(for: [stableInfoCheckDumpCallback], timeout: 10)
 
         let stableInfoAcceptorCheckDumpCallback = self.expectation(description: "stableInfoAcceptorCheckDumpCallback callback occurs")
-        self.tphClient.dump(withContainer: OTCKContainerName, context: self.otcliqueContext.context ?? "defaultContext") {
-            dump, _ in
+        self.tphClient.dump(withContainer: OTCKContainerName, context: self.otcliqueContext.context) { dump, _ in
             XCTAssertNotNil(dump, "dump should not be nil")
             XCTAssertNotNil(dump, "dump should not be nil")
-            let egoSelf = dump!["self"] as? Dictionary<String, AnyObject>
+            let egoSelf = dump!["self"] as? [String: AnyObject]
             XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
             XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
-            let dynamicInfo = egoSelf!["dynamicInfo"] as? Dictionary<String, AnyObject>
+            let dynamicInfo = egoSelf!["dynamicInfo"] as? [String: AnyObject]
             XCTAssertNotNil(dynamicInfo, "dynamicInfo should not be nil")
 
             XCTAssertNotNil(dynamicInfo, "dynamicInfo should not be nil")
 
-            let stableInfo = egoSelf!["stableInfo"] as? Dictionary<String, AnyObject>
+            let stableInfo = egoSelf!["stableInfo"] as? [String: AnyObject]
             XCTAssertNotNil(stableInfo, "stableInfo should not be nil")
             XCTAssertNotNil(stableInfo!["recovery_signing_public_key"], "recoverySigningPublicKey should not be nil")
             XCTAssertNotNil(stableInfo!["recovery_encryption_public_key"], "recoveryEncryptionPublicKey should not be nil")
 
             XCTAssertNotNil(stableInfo, "stableInfo should not be nil")
             XCTAssertNotNil(stableInfo!["recovery_signing_public_key"], "recoverySigningPublicKey should not be nil")
             XCTAssertNotNil(stableInfo!["recovery_encryption_public_key"], "recoveryEncryptionPublicKey should not be nil")
 
-            let included = dynamicInfo!["included"] as? Array<String>
+            let included = dynamicInfo!["included"] as? [String]
             XCTAssertNotNil(included, "included should not be nil")
             XCTAssertEqual(included!.count, 2, "should be 2 peer ids")
 
             XCTAssertNotNil(included, "included should not be nil")
             XCTAssertEqual(included!.count, 2, "should be 2 peer ids")
 
 
         self.sendContainerChange(context: thirdPeerContext)
         let thirdPeerJoinWithBottleExpectation = self.expectation(description: "thirdPeerJoinWithBottleExpectation callback occurs")
 
         self.sendContainerChange(context: thirdPeerContext)
         let thirdPeerJoinWithBottleExpectation = self.expectation(description: "thirdPeerJoinWithBottleExpectation callback occurs")
-        thirdPeerContext.join(withBottle: bottle.bottleID, entropy: entropy!, bottleSalt: self.otcliqueContext.altDSID) { error in
+        thirdPeerContext.join(withBottle: bottle.bottleID, entropy: entropy!, bottleSalt: self.otcliqueContext.altDSID!) { error in
             XCTAssertNil(error, "error should be nil")
             thirdPeerJoinWithBottleExpectation.fulfill()
         }
             XCTAssertNil(error, "error should be nil")
             thirdPeerJoinWithBottleExpectation.fulfill()
         }
 
         self.sendContainerChangeWaitForFetch(context: thirdPeerContext)
         let thirdPeerStableInfoCheckDumpCallback = self.expectation(description: "thirdPeerStableInfoCheckDumpCallback callback occurs")
 
         self.sendContainerChangeWaitForFetch(context: thirdPeerContext)
         let thirdPeerStableInfoCheckDumpCallback = self.expectation(description: "thirdPeerStableInfoCheckDumpCallback callback occurs")
-        self.tphClient.dump(withContainer: OTCKContainerName, context: thirdPeerContextID) {
-            dump, _ in
+        self.tphClient.dump(withContainer: OTCKContainerName, context: thirdPeerContextID) { dump, _ in
             XCTAssertNotNil(dump, "dump should not be nil")
             XCTAssertNotNil(dump, "dump should not be nil")
-            let egoSelf = dump!["self"] as? Dictionary<String, AnyObject>
+            let egoSelf = dump!["self"] as? [String: AnyObject]
             XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
             XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
-            let dynamicInfo = egoSelf!["dynamicInfo"] as? Dictionary<String, AnyObject>
+            let dynamicInfo = egoSelf!["dynamicInfo"] as? [String: AnyObject]
             XCTAssertNotNil(dynamicInfo, "dynamicInfo should not be nil")
 
             XCTAssertNotNil(dynamicInfo, "dynamicInfo should not be nil")
 
-            let stableInfo = egoSelf!["stableInfo"] as? Dictionary<String, AnyObject>
+            let stableInfo = egoSelf!["stableInfo"] as? [String: AnyObject]
             XCTAssertNotNil(stableInfo, "stableInfo should not be nil")
             XCTAssertNotNil(stableInfo!["recovery_signing_public_key"], "recoverySigningPublicKey should not be nil")
             XCTAssertNotNil(stableInfo!["recovery_encryption_public_key"], "recoveryEncryptionPublicKey should not be nil")
 
             XCTAssertNotNil(stableInfo, "stableInfo should not be nil")
             XCTAssertNotNil(stableInfo!["recovery_signing_public_key"], "recoverySigningPublicKey should not be nil")
             XCTAssertNotNil(stableInfo!["recovery_encryption_public_key"], "recoveryEncryptionPublicKey should not be nil")
 
-            let included = dynamicInfo!["included"] as? Array<String>
+            let included = dynamicInfo!["included"] as? [String]
             XCTAssertNotNil(included, "included should not be nil")
             XCTAssertEqual(included!.count, 3, "should be 3df peer ids")
 
             XCTAssertNotNil(included, "included should not be nil")
             XCTAssertEqual(included!.count, 3, "should be 3df peer ids")
 
         let establishContext = self.createEstablishContext(contextID: establishContextID)
 
         establishContext.startOctagonStateMachine()
         let establishContext = self.createEstablishContext(contextID: establishContextID)
 
         establishContext.startOctagonStateMachine()
+        XCTAssertNoThrow(try establishContext.setCDPEnabled())
         self.assertEnters(context: establishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
         let clique: OTClique
         self.assertEnters(context: establishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
         let clique: OTClique
         self.assertConsidersSelfTrusted(context: establishContext)
 
         // Fake that this peer also created some TLKShares for itself
         self.assertConsidersSelfTrusted(context: establishContext)
 
         // Fake that this peer also created some TLKShares for itself
-        self.putFakeKeyHierarchy(inCloudKit: self.manateeZoneID)
-        try self.putSelfTLKShareInCloudKit(context: establishContext, zoneID: self.manateeZoneID)
+        self.putFakeKeyHierarchiesInCloudKit()
+        try self.putSelfTLKSharesInCloudKit(context: establishContext)
 
         self.assertSelfTLKSharesInCloudKit(context: establishContext)
 
 
         self.assertSelfTLKSharesInCloudKit(context: establishContext)
 
         self.sendContainerChangeWaitForFetch(context: recoveryContext)
 
         let stableInfoCheckDumpCallback = self.expectation(description: "stableInfoCheckDumpCallback callback occurs")
         self.sendContainerChangeWaitForFetch(context: recoveryContext)
 
         let stableInfoCheckDumpCallback = self.expectation(description: "stableInfoCheckDumpCallback callback occurs")
-        self.tphClient.dump(withContainer: OTCKContainerName, context: OTDefaultContext) {
-            dump, _ in
+        self.tphClient.dump(withContainer: OTCKContainerName, context: OTDefaultContext) { dump, _ in
             XCTAssertNotNil(dump, "dump should not be nil")
             XCTAssertNotNil(dump, "dump should not be nil")
-            let egoSelf = dump!["self"] as? Dictionary<String, AnyObject>
+            let egoSelf = dump!["self"] as? [String: AnyObject]
             XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
             XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
-            let dynamicInfo = egoSelf!["dynamicInfo"] as? Dictionary<String, AnyObject>
+            let dynamicInfo = egoSelf!["dynamicInfo"] as? [String: AnyObject]
             XCTAssertNotNil(dynamicInfo, "dynamicInfo should not be nil")
 
             XCTAssertNotNil(dynamicInfo, "dynamicInfo should not be nil")
 
-            let stableInfo = egoSelf!["stableInfo"] as? Dictionary<String, AnyObject>
+            let stableInfo = egoSelf!["stableInfo"] as? [String: AnyObject]
             XCTAssertNotNil(stableInfo, "stableInfo should not be nil")
             XCTAssertNotNil(stableInfo!["recovery_signing_public_key"], "recoverySigningPublicKey should not be nil")
             XCTAssertNotNil(stableInfo!["recovery_encryption_public_key"], "recoveryEncryptionPublicKey should not be nil")
 
             XCTAssertNotNil(stableInfo, "stableInfo should not be nil")
             XCTAssertNotNil(stableInfo!["recovery_signing_public_key"], "recoverySigningPublicKey should not be nil")
             XCTAssertNotNil(stableInfo!["recovery_encryption_public_key"], "recoveryEncryptionPublicKey should not be nil")
 
-            let included = dynamicInfo!["included"] as? Array<String>
+            let included = dynamicInfo!["included"] as? [String]
             XCTAssertNotNil(included, "included should not be nil")
             XCTAssertEqual(included!.count, 2, "should be 2 peer ids")
             let vouchers = dump!["vouchers"]
             XCTAssertNotNil(included, "included should not be nil")
             XCTAssertEqual(included!.count, 2, "should be 2 peer ids")
             let vouchers = dump!["vouchers"]
         self.sendContainerChangeWaitForFetch(context: establishContext)
 
         let stableInfoAcceptorCheckDumpCallback = self.expectation(description: "stableInfoAcceptorCheckDumpCallback callback occurs")
         self.sendContainerChangeWaitForFetch(context: establishContext)
 
         let stableInfoAcceptorCheckDumpCallback = self.expectation(description: "stableInfoAcceptorCheckDumpCallback callback occurs")
-        self.tphClient.dump(withContainer: OTCKContainerName, context: establishContextID) {
-            dump, _ in
+        self.tphClient.dump(withContainer: OTCKContainerName, context: establishContextID) { dump, _ in
             XCTAssertNotNil(dump, "dump should not be nil")
             XCTAssertNotNil(dump, "dump should not be nil")
-            let egoSelf = dump!["self"] as? Dictionary<String, AnyObject>
+            let egoSelf = dump!["self"] as? [String: AnyObject]
             XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
             XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
-            let dynamicInfo = egoSelf!["dynamicInfo"] as? Dictionary<String, AnyObject>
+            let dynamicInfo = egoSelf!["dynamicInfo"] as? [String: AnyObject]
             XCTAssertNotNil(dynamicInfo, "dynamicInfo should not be nil")
 
             XCTAssertNotNil(dynamicInfo, "dynamicInfo should not be nil")
 
-            let stableInfo = egoSelf!["stableInfo"] as? Dictionary<String, AnyObject>
+            let stableInfo = egoSelf!["stableInfo"] as? [String: AnyObject]
             XCTAssertNotNil(stableInfo, "stableInfo should not be nil")
             XCTAssertNotNil(stableInfo!["recovery_signing_public_key"], "recoverySigningPublicKey should not be nil")
             XCTAssertNotNil(stableInfo!["recovery_encryption_public_key"], "recoveryEncryptionPublicKey should not be nil")
 
             XCTAssertNotNil(stableInfo, "stableInfo should not be nil")
             XCTAssertNotNil(stableInfo!["recovery_signing_public_key"], "recoverySigningPublicKey should not be nil")
             XCTAssertNotNil(stableInfo!["recovery_encryption_public_key"], "recoveryEncryptionPublicKey should not be nil")
 
-            let included = dynamicInfo!["included"] as? Array<String>
+            let included = dynamicInfo!["included"] as? [String]
             XCTAssertNotNil(included, "included should not be nil")
             XCTAssertEqual(included!.count, 2, "should be 2 peer ids")
             let vouchers = dump!["vouchers"]
             XCTAssertNotNil(included, "included should not be nil")
             XCTAssertEqual(included!.count, 2, "should be 2 peer ids")
             let vouchers = dump!["vouchers"]
             stableInfoAcceptorCheckDumpCallback.fulfill()
         }
         self.wait(for: [stableInfoAcceptorCheckDumpCallback], timeout: 10)
             stableInfoAcceptorCheckDumpCallback.fulfill()
         }
         self.wait(for: [stableInfoAcceptorCheckDumpCallback], timeout: 10)
-        try self.putSelfTLKShareInCloudKit(context: recoveryContext, zoneID: self.manateeZoneID)
+        try self.putSelfTLKSharesInCloudKit(context: recoveryContext)
         self.assertSelfTLKSharesInCloudKit(context: recoveryContext)
     }
 
         self.assertSelfTLKSharesInCloudKit(context: recoveryContext)
     }
 
         let establishContext = self.createEstablishContext(contextID: establishContextID)
 
         establishContext.startOctagonStateMachine()
         let establishContext = self.createEstablishContext(contextID: establishContextID)
 
         establishContext.startOctagonStateMachine()
+        XCTAssertNoThrow(try establishContext.setCDPEnabled())
         self.assertEnters(context: establishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
         let clique: OTClique
         self.assertEnters(context: establishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
         let clique: OTClique
         self.sendContainerChangeWaitForFetch(context: establishContext)
 
         self.silentFetchesAllowed = false
         self.sendContainerChangeWaitForFetch(context: establishContext)
 
         self.silentFetchesAllowed = false
-        self.expectCKFetchAndRun(beforeFinished: {
-            self.putFakeKeyHierarchy(inCloudKit: self.manateeZoneID)
-            self.putFakeDeviceStatus(inCloudKit: self.manateeZoneID)
+        self.expectCKFetchAndRun {
+            self.putFakeKeyHierarchiesInCloudKit()
+            self.putFakeDeviceStatusesInCloudKit()
             self.silentFetchesAllowed = true
             self.silentFetchesAllowed = true
-        })
+        }
         let recoveryContext = self.manager.context(forContainerName: OTCKContainerName, contextID: OTDefaultContext)
 
         recoveryContext.startOctagonStateMachine()
         let recoveryContext = self.manager.context(forContainerName: OTCKContainerName, contextID: OTDefaultContext)
 
         recoveryContext.startOctagonStateMachine()
         let establishContext = self.createEstablishContext(contextID: establishContextID)
 
         establishContext.startOctagonStateMachine()
         let establishContext = self.createEstablishContext(contextID: establishContextID)
 
         establishContext.startOctagonStateMachine()
+        XCTAssertNoThrow(try establishContext.setCDPEnabled())
         self.assertEnters(context: establishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
         let clique: OTClique
         self.assertEnters(context: establishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
         let clique: OTClique
         let establishContext = self.createEstablishContext(contextID: establishContextID)
 
         establishContext.startOctagonStateMachine()
         let establishContext = self.createEstablishContext(contextID: establishContextID)
 
         establishContext.startOctagonStateMachine()
+        XCTAssertNoThrow(try establishContext.setCDPEnabled())
         self.assertEnters(context: establishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
         let clique: OTClique
         self.assertEnters(context: establishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
         let clique: OTClique
 
         let initiatorContextID = "initiator-context-id"
         self.cuttlefishContext.startOctagonStateMachine()
 
         let initiatorContextID = "initiator-context-id"
         self.cuttlefishContext.startOctagonStateMachine()
+        XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
         let clique: OTClique
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
         let clique: OTClique
         self.sendContainerChange(context: initiatorContext)
         let restoreExpectation = self.expectation(description: "restore returns")
 
         self.sendContainerChange(context: initiatorContext)
         let restoreExpectation = self.expectation(description: "restore returns")
 
-        self.manager!.restore(OTCKContainerName, contextID: initiatorContextID, bottleSalt: self.otcliqueContext.altDSID, entropy: entropy!, bottleID: bottle.bottleID) { error in
+        self.manager!.restore(OTCKContainerName, contextID: initiatorContextID, bottleSalt: self.otcliqueContext.altDSID!, entropy: entropy!, bottleID: bottle.bottleID) { error in
             XCTAssertNil(error, "error should be nil")
             restoreExpectation.fulfill()
         }
             XCTAssertNil(error, "error should be nil")
             restoreExpectation.fulfill()
         }
         self.assertEnters(context: initiatorContext, state: OctagonStateReady, within: 10 * NSEC_PER_SEC)
 
         var initiatorDumpCallback = self.expectation(description: "initiatorDumpCallback callback occurs")
         self.assertEnters(context: initiatorContext, state: OctagonStateReady, within: 10 * NSEC_PER_SEC)
 
         var initiatorDumpCallback = self.expectation(description: "initiatorDumpCallback callback occurs")
-        self.tphClient.dump(withContainer: OTCKContainerName, context: initiatorContextID) {
-            dump, _ in
+        self.tphClient.dump(withContainer: OTCKContainerName, context: initiatorContextID) { dump, _ in
             XCTAssertNotNil(dump, "dump should not be nil")
             XCTAssertNotNil(dump, "dump should not be nil")
-            let egoSelf = dump!["self"] as? Dictionary<String, AnyObject>
+            let egoSelf = dump!["self"] as? [String: AnyObject]
             XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
             XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
-            let dynamicInfo = egoSelf!["dynamicInfo"] as? Dictionary<String, AnyObject>
+            let dynamicInfo = egoSelf!["dynamicInfo"] as? [String: AnyObject]
             XCTAssertNotNil(dynamicInfo, "dynamicInfo should not be nil")
             XCTAssertNotNil(dynamicInfo, "dynamicInfo should not be nil")
-            let included = dynamicInfo!["included"] as? Array<String>
+            let included = dynamicInfo!["included"] as? [String]
             XCTAssertNotNil(included, "included should not be nil")
             XCTAssertEqual(included!.count, 2, "should be 2 peer ids")
 
             XCTAssertNotNil(included, "included should not be nil")
             XCTAssertEqual(included!.count, 2, "should be 2 peer ids")
 
 
         //now let's ensure recovery keys are set for both the first device and second device
         initiatorDumpCallback = self.expectation(description: "initiatorDumpCallback callback occurs")
 
         //now let's ensure recovery keys are set for both the first device and second device
         initiatorDumpCallback = self.expectation(description: "initiatorDumpCallback callback occurs")
-        self.tphClient.dump(withContainer: OTCKContainerName, context: initiatorContextID) {
-            dump, _ in
+        self.tphClient.dump(withContainer: OTCKContainerName, context: initiatorContextID) { dump, _ in
             XCTAssertNotNil(dump, "dump should not be nil")
             XCTAssertNotNil(dump, "dump should not be nil")
-            let egoSelf = dump!["self"] as? Dictionary<String, AnyObject>
+            let egoSelf = dump!["self"] as? [String: AnyObject]
             XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
             XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
-            let dynamicInfo = egoSelf!["dynamicInfo"] as? Dictionary<String, AnyObject>
+            let dynamicInfo = egoSelf!["dynamicInfo"] as? [String: AnyObject]
             XCTAssertNotNil(dynamicInfo, "dynamicInfo should not be nil")
 
             XCTAssertNotNil(dynamicInfo, "dynamicInfo should not be nil")
 
-            let stableInfo = egoSelf!["stableInfo"] as? Dictionary<String, AnyObject>
+            let stableInfo = egoSelf!["stableInfo"] as? [String: AnyObject]
             XCTAssertNotNil(stableInfo, "stableInfo should not be nil")
             XCTAssertNotNil(stableInfo!["recovery_signing_public_key"], "recoverySigningPublicKey should not be nil")
             XCTAssertNotNil(stableInfo!["recovery_encryption_public_key"], "recoveryEncryptionPublicKey should not be nil")
             XCTAssertNotNil(stableInfo, "stableInfo should not be nil")
             XCTAssertNotNil(stableInfo!["recovery_signing_public_key"], "recoverySigningPublicKey should not be nil")
             XCTAssertNotNil(stableInfo!["recovery_encryption_public_key"], "recoveryEncryptionPublicKey should not be nil")
             initiatorRecoverySigningKey = stableInfo!["recovery_signing_public_key"] as? Data
             initiatorRecoveryEncryptionKey = stableInfo!["recovery_encryption_public_key"] as? Data
 
             initiatorRecoverySigningKey = stableInfo!["recovery_signing_public_key"] as? Data
             initiatorRecoveryEncryptionKey = stableInfo!["recovery_encryption_public_key"] as? Data
 
-            let included = dynamicInfo!["included"] as? Array<String>
+            let included = dynamicInfo!["included"] as? [String]
             XCTAssertNotNil(included, "included should not be nil")
             XCTAssertEqual(included!.count, 2, "should be 2 peer ids")
             let vouchers = dump!["vouchers"]
             XCTAssertNotNil(included, "included should not be nil")
             XCTAssertEqual(included!.count, 2, "should be 2 peer ids")
             let vouchers = dump!["vouchers"]
         self.wait(for: [initiatorDumpCallback], timeout: 10)
 
         let firstDeviceDumpCallback = self.expectation(description: "firstDeviceDumpCallback callback occurs")
         self.wait(for: [initiatorDumpCallback], timeout: 10)
 
         let firstDeviceDumpCallback = self.expectation(description: "firstDeviceDumpCallback callback occurs")
-        self.tphClient.dump(withContainer: OTCKContainerName, context: OTDefaultContext) {
-            dump, _ in
+        self.tphClient.dump(withContainer: OTCKContainerName, context: OTDefaultContext) { dump, _ in
             XCTAssertNotNil(dump, "dump should not be nil")
             XCTAssertNotNil(dump, "dump should not be nil")
-            let egoSelf = dump!["self"] as? Dictionary<String, AnyObject>
+            let egoSelf = dump!["self"] as? [String: AnyObject]
             XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
             XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
-            let dynamicInfo = egoSelf!["dynamicInfo"] as? Dictionary<String, AnyObject>
+            let dynamicInfo = egoSelf!["dynamicInfo"] as? [String: AnyObject]
             XCTAssertNotNil(dynamicInfo, "dynamicInfo should not be nil")
 
             XCTAssertNotNil(dynamicInfo, "dynamicInfo should not be nil")
 
-            let stableInfo = egoSelf!["stableInfo"] as? Dictionary<String, AnyObject>
+            let stableInfo = egoSelf!["stableInfo"] as? [String: AnyObject]
             XCTAssertNotNil(stableInfo, "stableInfo should not be nil")
             XCTAssertNotNil(stableInfo!["recovery_signing_public_key"], "recoverySigningPublicKey should not be nil")
             XCTAssertNotNil(stableInfo!["recovery_encryption_public_key"], "recoveryEncryptionPublicKey should not be nil")
             XCTAssertNotNil(stableInfo, "stableInfo should not be nil")
             XCTAssertNotNil(stableInfo!["recovery_signing_public_key"], "recoverySigningPublicKey should not be nil")
             XCTAssertNotNil(stableInfo!["recovery_encryption_public_key"], "recoveryEncryptionPublicKey should not be nil")
             firstDeviceRecoverySigningKey = stableInfo!["recovery_signing_public_key"] as? Data
             firstDeviceRecoveryEncryptionKey = stableInfo!["recovery_encryption_public_key"] as? Data
 
             firstDeviceRecoverySigningKey = stableInfo!["recovery_signing_public_key"] as? Data
             firstDeviceRecoveryEncryptionKey = stableInfo!["recovery_encryption_public_key"] as? Data
 
-            let included = dynamicInfo!["included"] as? Array<String>
+            let included = dynamicInfo!["included"] as? [String]
             XCTAssertNotNil(included, "included should not be nil")
             XCTAssertEqual(included!.count, 2, "should be 2 peer ids")
             let vouchers = dump!["vouchers"]
             XCTAssertNotNil(included, "included should not be nil")
             XCTAssertEqual(included!.count, 2, "should be 2 peer ids")
             let vouchers = dump!["vouchers"]
         let establishContext = self.createEstablishContext(contextID: establishContextID)
 
         establishContext.startOctagonStateMachine()
         let establishContext = self.createEstablishContext(contextID: establishContextID)
 
         establishContext.startOctagonStateMachine()
+        XCTAssertNoThrow(try establishContext.setCDPEnabled())
         self.assertEnters(context: establishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
         let clique: OTClique
         self.assertEnters(context: establishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
         let clique: OTClique
         self.assertConsidersSelfTrusted(context: establishContext)
 
         // Fake that this peer also created some TLKShares for itself
         self.assertConsidersSelfTrusted(context: establishContext)
 
         // Fake that this peer also created some TLKShares for itself
-        self.putFakeKeyHierarchy(inCloudKit: self.manateeZoneID)
-        try self.putSelfTLKShareInCloudKit(context: establishContext, zoneID: self.manateeZoneID)
+        self.putFakeKeyHierarchiesInCloudKit()
+        try self.putSelfTLKSharesInCloudKit(context: establishContext)
 
         self.assertSelfTLKSharesInCloudKit(context: establishContext)
 
 
         self.assertSelfTLKSharesInCloudKit(context: establishContext)
 
         self.sendContainerChangeWaitForFetch(context: newGuyContext)
 
         let stableInfoAcceptorCheckDumpCallback = self.expectation(description: "stableInfoAcceptorCheckDumpCallback callback occurs")
         self.sendContainerChangeWaitForFetch(context: newGuyContext)
 
         let stableInfoAcceptorCheckDumpCallback = self.expectation(description: "stableInfoAcceptorCheckDumpCallback callback occurs")
-        self.tphClient.dump(withContainer: OTCKContainerName, context: OTDefaultContext) {
-            dump, _ in
+        self.tphClient.dump(withContainer: OTCKContainerName, context: OTDefaultContext) { dump, _ in
             XCTAssertNotNil(dump, "dump should not be nil")
             XCTAssertNotNil(dump, "dump should not be nil")
-            let egoSelf = dump!["self"] as? Dictionary<String, AnyObject>
+            let egoSelf = dump!["self"] as? [String: AnyObject]
             XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
             XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
-            let dynamicInfo = egoSelf!["dynamicInfo"] as? Dictionary<String, AnyObject>
+            let dynamicInfo = egoSelf!["dynamicInfo"] as? [String: AnyObject]
             XCTAssertNotNil(dynamicInfo, "dynamicInfo should not be nil")
 
             XCTAssertNotNil(dynamicInfo, "dynamicInfo should not be nil")
 
-            let stableInfo = egoSelf!["stableInfo"] as? Dictionary<String, AnyObject>
+            let stableInfo = egoSelf!["stableInfo"] as? [String: AnyObject]
             XCTAssertNotNil(stableInfo, "stableInfo should not be nil")
             XCTAssertNotNil(stableInfo!["recovery_signing_public_key"], "recoverySigningPublicKey should not be nil")
             XCTAssertNotNil(stableInfo!["recovery_encryption_public_key"], "recoveryEncryptionPublicKey should not be nil")
 
             XCTAssertNotNil(stableInfo, "stableInfo should not be nil")
             XCTAssertNotNil(stableInfo!["recovery_signing_public_key"], "recoverySigningPublicKey should not be nil")
             XCTAssertNotNil(stableInfo!["recovery_encryption_public_key"], "recoveryEncryptionPublicKey should not be nil")
 
-            let included = dynamicInfo!["included"] as? Array<String>
+            let included = dynamicInfo!["included"] as? [String]
             XCTAssertNotNil(included, "included should not be nil")
             XCTAssertEqual(included!.count, 2, "should be 2 peer ids")
             let vouchers = dump!["vouchers"]
             XCTAssertNotNil(included, "included should not be nil")
             XCTAssertEqual(included!.count, 2, "should be 2 peer ids")
             let vouchers = dump!["vouchers"]
         self.wait(for: [stableInfoAcceptorCheckDumpCallback], timeout: 10)
         self.assertEnters(context: newGuyContext, state: OctagonStateReady, within: 10 * NSEC_PER_SEC)
         self.assertConsidersSelfTrusted(context: newGuyContext)
         self.wait(for: [stableInfoAcceptorCheckDumpCallback], timeout: 10)
         self.assertEnters(context: newGuyContext, state: OctagonStateReady, within: 10 * NSEC_PER_SEC)
         self.assertConsidersSelfTrusted(context: newGuyContext)
-        try self.putSelfTLKShareInCloudKit(context: newGuyContext, zoneID: self.manateeZoneID)
+        try self.putSelfTLKSharesInCloudKit(context: newGuyContext)
         self.assertSelfTLKSharesInCloudKit(context: newGuyContext)
 
         self.sendContainerChangeWaitForFetch(context: establishContext)
 
         let stableInfoCheckDumpCallback = self.expectation(description: "stableInfoCheckDumpCallback callback occurs")
         self.assertSelfTLKSharesInCloudKit(context: newGuyContext)
 
         self.sendContainerChangeWaitForFetch(context: establishContext)
 
         let stableInfoCheckDumpCallback = self.expectation(description: "stableInfoCheckDumpCallback callback occurs")
-        self.tphClient.dump(withContainer: OTCKContainerName, context: establishContextID) {
-            dump, _ in
+        self.tphClient.dump(withContainer: OTCKContainerName, context: establishContextID) { dump, _ in
             XCTAssertNotNil(dump, "dump should not be nil")
             XCTAssertNotNil(dump, "dump should not be nil")
-            let egoSelf = dump!["self"] as? Dictionary<String, AnyObject>
+            let egoSelf = dump!["self"] as? [String: AnyObject]
             XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
             XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
-            let dynamicInfo = egoSelf!["dynamicInfo"] as? Dictionary<String, AnyObject>
+            let dynamicInfo = egoSelf!["dynamicInfo"] as? [String: AnyObject]
             XCTAssertNotNil(dynamicInfo, "dynamicInfo should not be nil")
 
             XCTAssertNotNil(dynamicInfo, "dynamicInfo should not be nil")
 
-            let stableInfo = egoSelf!["stableInfo"] as? Dictionary<String, AnyObject>
+            let stableInfo = egoSelf!["stableInfo"] as? [String: AnyObject]
             XCTAssertNotNil(stableInfo, "stableInfo should not be nil")
             XCTAssertNotNil(stableInfo!["recovery_signing_public_key"], "recoverySigningPublicKey should not be nil")
             XCTAssertNotNil(stableInfo!["recovery_encryption_public_key"], "recoveryEncryptionPublicKey should not be nil")
 
             XCTAssertNotNil(stableInfo, "stableInfo should not be nil")
             XCTAssertNotNil(stableInfo!["recovery_signing_public_key"], "recoverySigningPublicKey should not be nil")
             XCTAssertNotNil(stableInfo!["recovery_encryption_public_key"], "recoveryEncryptionPublicKey should not be nil")
 
-            let included = dynamicInfo!["included"] as? Array<String>
+            let included = dynamicInfo!["included"] as? [String]
             XCTAssertNotNil(included, "included should not be nil")
             XCTAssertEqual(included!.count, 2, "should be 2 peer ids")
             let vouchers = dump!["vouchers"]
             XCTAssertNotNil(included, "included should not be nil")
             XCTAssertEqual(included!.count, 2, "should be 2 peer ids")
             let vouchers = dump!["vouchers"]
         self.sendContainerChange(context: recoveryGuyContext)
 
         let newGuyCheckDumpCallback = self.expectation(description: "newGuyCheckDumpCallback callback occurs")
         self.sendContainerChange(context: recoveryGuyContext)
 
         let newGuyCheckDumpCallback = self.expectation(description: "newGuyCheckDumpCallback callback occurs")
-        self.tphClient.dump(withContainer: OTCKContainerName, context: OTDefaultContext) {
-            dump, _ in
+        self.tphClient.dump(withContainer: OTCKContainerName, context: OTDefaultContext) { dump, _ in
             XCTAssertNotNil(dump, "dump should not be nil")
             XCTAssertNotNil(dump, "dump should not be nil")
-            let egoSelf = dump!["self"] as? Dictionary<String, AnyObject>
+            let egoSelf = dump!["self"] as? [String: AnyObject]
             XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
             XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
-            let dynamicInfo = egoSelf!["dynamicInfo"] as? Dictionary<String, AnyObject>
+            let dynamicInfo = egoSelf!["dynamicInfo"] as? [String: AnyObject]
             XCTAssertNotNil(dynamicInfo, "dynamicInfo should not be nil")
 
             XCTAssertNotNil(dynamicInfo, "dynamicInfo should not be nil")
 
-            let stableInfo = egoSelf!["stableInfo"] as? Dictionary<String, AnyObject>
+            let stableInfo = egoSelf!["stableInfo"] as? [String: AnyObject]
             XCTAssertNotNil(stableInfo, "stableInfo should not be nil")
             XCTAssertNotNil(stableInfo!["recovery_signing_public_key"], "recoverySigningPublicKey should not be nil")
             XCTAssertNotNil(stableInfo!["recovery_encryption_public_key"], "recoveryEncryptionPublicKey should not be nil")
 
             XCTAssertNotNil(stableInfo, "stableInfo should not be nil")
             XCTAssertNotNil(stableInfo!["recovery_signing_public_key"], "recoverySigningPublicKey should not be nil")
             XCTAssertNotNil(stableInfo!["recovery_encryption_public_key"], "recoveryEncryptionPublicKey should not be nil")
 
-            let included = dynamicInfo!["included"] as? Array<String>
+            let included = dynamicInfo!["included"] as? [String]
             XCTAssertNotNil(included, "included should not be nil")
             XCTAssertEqual(included!.count, 1, "should be 1 peer ids")
             let vouchers = dump!["vouchers"]
             XCTAssertNotNil(included, "included should not be nil")
             XCTAssertEqual(included!.count, 1, "should be 1 peer ids")
             let vouchers = dump!["vouchers"]
         self.startCKAccountStatusMock()
 
         self.cuttlefishContext.startOctagonStateMachine()
         self.startCKAccountStatusMock()
 
         self.cuttlefishContext.startOctagonStateMachine()
+        XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
         XCTAssertFalse(self.mockAuthKit.currentDeviceList().isEmpty, "should not have zero devices")
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
         XCTAssertFalse(self.mockAuthKit.currentDeviceList().isEmpty, "should not have zero devices")
         XCTAssertNotNil(recoveryKey, "recoveryKey should not be nil")
 
         let createKeyExpectation = self.expectation(description: "createKeyExpectation returns")
         XCTAssertNotNil(recoveryKey, "recoveryKey should not be nil")
 
         let createKeyExpectation = self.expectation(description: "createKeyExpectation returns")
-        self.manager.createRecoveryKey(OTCKContainerName, contextID: self.otcliqueContext.context ?? "defaultContext", recoveryKey: recoveryKey) { error in
+        self.manager.createRecoveryKey(OTCKContainerName, contextID: self.otcliqueContext.context, recoveryKey: recoveryKey) { error in
             XCTAssertNotNil(error, "error should not be nil")
             XCTAssertEqual((error! as NSError).code, OctagonError.OTErrorLimitedPeer.rawValue, "error code should be limited peer")
             createKeyExpectation.fulfill()
             XCTAssertNotNil(error, "error should not be nil")
             XCTAssertEqual((error! as NSError).code, OctagonError.OTErrorLimitedPeer.rawValue, "error code should be limited peer")
             createKeyExpectation.fulfill()
         let establishContext = self.createEstablishContext(contextID: establishContextID)
 
         establishContext.startOctagonStateMachine()
         let establishContext = self.createEstablishContext(contextID: establishContextID)
 
         establishContext.startOctagonStateMachine()
+        XCTAssertNoThrow(try establishContext.setCDPEnabled())
         self.assertEnters(context: establishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
         let clique: OTClique
         self.assertEnters(context: establishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
         let clique: OTClique
         self.assertConsidersSelfTrusted(context: establishContext)
 
         // Fake that this peer also created some TLKShares for itself
         self.assertConsidersSelfTrusted(context: establishContext)
 
         // Fake that this peer also created some TLKShares for itself
-        self.putFakeKeyHierarchy(inCloudKit: self.manateeZoneID)
-        try self.putSelfTLKShareInCloudKit(context: establishContext, zoneID: self.manateeZoneID)
+        self.putFakeKeyHierarchiesInCloudKit()
+        try self.putSelfTLKSharesInCloudKit(context: establishContext)
 
         self.assertSelfTLKSharesInCloudKit(context: establishContext)
 
 
         self.assertSelfTLKSharesInCloudKit(context: establishContext)
 
         let establishContext = self.createEstablishContext(contextID: establishContextID)
 
         establishContext.startOctagonStateMachine()
         let establishContext = self.createEstablishContext(contextID: establishContextID)
 
         establishContext.startOctagonStateMachine()
+        XCTAssertNoThrow(try establishContext.setCDPEnabled())
         self.assertEnters(context: establishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
         let clique: OTClique
         self.assertEnters(context: establishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
         let clique: OTClique
         self.assertConsidersSelfTrusted(context: establishContext)
 
         // Fake that this peer also created some TLKShares for itself
         self.assertConsidersSelfTrusted(context: establishContext)
 
         // Fake that this peer also created some TLKShares for itself
-        self.putFakeKeyHierarchy(inCloudKit: self.manateeZoneID)
-        try self.putSelfTLKShareInCloudKit(context: establishContext, zoneID: self.manateeZoneID)
+        self.putFakeKeyHierarchiesInCloudKit()
+        try self.putSelfTLKSharesInCloudKit(context: establishContext)
 
         self.assertSelfTLKSharesInCloudKit(context: establishContext)
 
 
         self.assertSelfTLKSharesInCloudKit(context: establishContext)
 
         XCTAssertNotNil(recoveryKey, "recoveryKey should not be nil")
 
         OTClique.recoverOctagon(usingData: newCliqueContext, recoveryKey: recoveryKey!) { error in
         XCTAssertNotNil(recoveryKey, "recoveryKey should not be nil")
 
         OTClique.recoverOctagon(usingData: newCliqueContext, recoveryKey: recoveryKey!) { error in
-            XCTAssertNotNil(error, "error should NOT be nil")
-            XCTAssertEqual((error! as NSError).code, 32, "error code should be 32/untrusted recovery keys")
-            XCTAssertEqual((error! as NSError).domain, "com.apple.security.trustedpeers.container", "error code domain should be com.apple.security.trustedpeers.container")
+            XCTAssertNil(error, "error should be nil")
             joinWithRecoveryKeyExpectation.fulfill()
         }
         self.wait(for: [joinWithRecoveryKeyExpectation], timeout: 10)
             joinWithRecoveryKeyExpectation.fulfill()
         }
         self.wait(for: [joinWithRecoveryKeyExpectation], timeout: 10)
         let establishContext = self.createEstablishContext(contextID: establishContextID)
 
         establishContext.startOctagonStateMachine()
         let establishContext = self.createEstablishContext(contextID: establishContextID)
 
         establishContext.startOctagonStateMachine()
+        XCTAssertNoThrow(try establishContext.setCDPEnabled())
         self.assertEnters(context: establishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
         let clique: OTClique
         self.assertEnters(context: establishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
         let clique: OTClique
         self.assertConsidersSelfTrusted(context: establishContext)
 
         // Fake that this peer also created some TLKShares for itself
         self.assertConsidersSelfTrusted(context: establishContext)
 
         // Fake that this peer also created some TLKShares for itself
-        self.putFakeKeyHierarchy(inCloudKit: self.manateeZoneID)
-        try self.putSelfTLKShareInCloudKit(context: establishContext, zoneID: self.manateeZoneID)
+        self.putFakeKeyHierarchiesInCloudKit()
+        try self.putSelfTLKSharesInCloudKit(context: establishContext)
 
         self.assertSelfTLKSharesInCloudKit(context: establishContext)
 
 
         self.assertSelfTLKSharesInCloudKit(context: establishContext)
 
         let establishContext = self.createEstablishContext(contextID: establishContextID)
 
         establishContext.startOctagonStateMachine()
         let establishContext = self.createEstablishContext(contextID: establishContextID)
 
         establishContext.startOctagonStateMachine()
+        XCTAssertNoThrow(try establishContext.setCDPEnabled())
         self.assertEnters(context: establishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
         let clique: OTClique
         self.assertEnters(context: establishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
         let clique: OTClique
         self.assertConsidersSelfTrusted(context: establishContext)
 
         // Fake that this peer also created some TLKShares for itself
         self.assertConsidersSelfTrusted(context: establishContext)
 
         // Fake that this peer also created some TLKShares for itself
-        self.putFakeKeyHierarchy(inCloudKit: self.manateeZoneID)
-        try self.putSelfTLKShareInCloudKit(context: establishContext, zoneID: self.manateeZoneID)
-
+        self.putFakeKeyHierarchiesInCloudKit()
+        try! self.putSelfTLKSharesInCloudKit(context: establishContext)
         self.assertSelfTLKSharesInCloudKit(context: establishContext)
 
         let recoveryKey = "malformedRecoveryKey"
         self.assertSelfTLKSharesInCloudKit(context: establishContext)
 
         let recoveryKey = "malformedRecoveryKey"
         self.manager.setSOSEnabledForPlatformFlag(true)
 
         let createKeyExpectation = self.expectation(description: "createKeyExpectation returns")
         self.manager.setSOSEnabledForPlatformFlag(true)
 
         let createKeyExpectation = self.expectation(description: "createKeyExpectation returns")
-        self.manager.createRecoveryKey(OTCKContainerName, contextID: self.otcliqueContext.context ?? "defaultContext", recoveryKey: recoveryKey) { error in
+        self.manager.createRecoveryKey(OTCKContainerName, contextID: self.otcliqueContext.context, recoveryKey: recoveryKey) { error in
             XCTAssertNotNil(error, "error should NOT be nil")
             XCTAssertEqual((error! as NSError).code, 41, "error code should be 41/malformed recovery key")
             XCTAssertEqual((error! as NSError).domain, "com.apple.security.octagon", "error code domain should be com.apple.security.octagon")
             XCTAssertNotNil(error, "error should NOT be nil")
             XCTAssertEqual((error! as NSError).code, 41, "error code should be 41/malformed recovery key")
             XCTAssertEqual((error! as NSError).domain, "com.apple.security.octagon", "error code domain should be com.apple.security.octagon")
         }
         self.wait(for: [joinWithRecoveryKeyExpectation], timeout: 10)
     }
         }
         self.wait(for: [joinWithRecoveryKeyExpectation], timeout: 10)
     }
+
+    @discardableResult
+    func createAndSetRecoveryKey(context: OTCuttlefishContext) -> String {
+        let cliqueConfiguration = OTConfigurationContext()
+        cliqueConfiguration.context = context.contextID
+        cliqueConfiguration.altDSID = try! context.authKitAdapter.primaryiCloudAccountAltDSID()
+        cliqueConfiguration.otControl = self.otControl
+
+        let recoveryKey = SecRKCreateRecoveryKeyString(nil)
+        XCTAssertNotNil(recoveryKey, "recoveryKey should not be nil")
+
+        let setRecoveryKeyExpectation = self.expectation(description: "setRecoveryKeyExpectation callback occurs")
+        TestsObjectiveC.setNewRecoveryKeyWithData(cliqueConfiguration, recoveryKey: recoveryKey!) { _, error in
+            XCTAssertNil(error, "error should be nil")
+            setRecoveryKeyExpectation.fulfill()
+        }
+        self.wait(for: [setRecoveryKeyExpectation], timeout: 10)
+
+        return recoveryKey!
+    }
+
+    func testConcurWithTrustedPeer() throws {
+        self.startCKAccountStatusMock()
+        self.manager.setSOSEnabledForPlatformFlag(true)
+
+        self.assertResetAndBecomeTrustedInDefaultContext()
+
+        let peer2Context = self.makeInitiatorContext(contextID: "peer2")
+        let peer2ID = self.assertJoinViaEscrowRecovery(joiningContext: peer2Context, sponsor: self.cuttlefishContext)
+
+        self.assertAllCKKSViewsUpload(tlkShares: 1)
+        self.sendContainerChangeWaitForFetch(context: self.cuttlefishContext)
+        self.assertAllCKKSViews(enter: SecCKKSZoneKeyStateReady, within: 10 * NSEC_PER_SEC)
+        self.verifyDatabaseMocks()
+
+        // peer1 sets a recovery key
+        var rkSigningPubKey : Data? = nil
+        var rkEncryptionPubKey : Data? = nil
+
+        let setRKExpectation = self.expectation(description: "setRecoveryKey")
+        self.fakeCuttlefishServer.setRecoveryKeyListener = { request in
+            XCTAssertNotNil(request.recoverySigningPubKey, "signing public key should be present")
+            XCTAssertNotNil(request.recoveryEncryptionPubKey, "encryption public key should be present")
+
+            rkSigningPubKey = request.recoverySigningPubKey
+            rkEncryptionPubKey = request.recoveryEncryptionPubKey
+
+            setRKExpectation.fulfill()
+            return nil
+        }
+
+        self.createAndSetRecoveryKey(context: self.cuttlefishContext)
+        self.wait(for: [setRKExpectation], timeout: 10)
+
+        // And peer2 concurs with it upon receiving a push
+        let updateTrustExpectation = self.expectation(description: "updateTrust")
+        self.fakeCuttlefishServer.updateListener = { [unowned self] request in
+            XCTAssertEqual(request.peerID, peer2ID, "Update should be for peer2")
+
+            let newStableInfo = request.stableInfoAndSig.stableInfo()
+            XCTAssertEqual(newStableInfo.recoverySigningPublicKey, rkSigningPubKey, "Recovery signing key should match other peer")
+            XCTAssertEqual(newStableInfo.recoveryEncryptionPublicKey, rkEncryptionPubKey, "Recovery encryption key should match other peer")
+            self.fakeCuttlefishServer.updateListener = nil
+            updateTrustExpectation.fulfill()
+
+            return nil
+        }
+
+        self.sendContainerChangeWaitForFetch(context: peer2Context)
+        self.wait(for: [updateTrustExpectation], timeout: 10)
+
+        // Restart TPH, and ensure that more updates succeed
+        self.tphClient.containerMap.removeAllContainers()
+
+        self.sendContainerChangeWaitForFetch(context: self.cuttlefishContext)
+        self.sendContainerChangeWaitForFetch(context: peer2Context)
+    }
+
+    func testRecoveryKeyLoadingOnContainerLoad() throws {
+        self.startCKAccountStatusMock()
+        self.manager.setSOSEnabledForPlatformFlag(true)
+
+        let _ = self.assertResetAndBecomeTrustedInDefaultContext()
+        // peer1 sets a recovery key
+        self.createAndSetRecoveryKey(context: self.cuttlefishContext)
+
+        // Restart TPH
+        self.tphClient.containerMap.removeAllContainers()
+
+        self.sendContainerChangeWaitForFetch(context: self.cuttlefishContext)
+    }
+
+    func testRecoveryKeyLoadingOnContainerLoadEvenIfMissing() throws {
+        self.startCKAccountStatusMock()
+        self.manager.setSOSEnabledForPlatformFlag(true)
+
+        let _ = self.assertResetAndBecomeTrustedInDefaultContext()
+        // peer1 sets a recovery key
+        self.createAndSetRecoveryKey(context: self.cuttlefishContext)
+
+        // Before restarting TPH, emulate a world in which the RK variables were not set on the container
+
+        let containerName = ContainerName(container: self.cuttlefishContext.containerName, context: self.cuttlefishContext.contextID)
+        let container = try self.tphClient.containerMap.findOrCreate(name: containerName)
+        container.removeRKFromContainer()
+
+        // Restart TPH
+        self.tphClient.containerMap.removeAllContainers()
+
+        self.sendContainerChangeWaitForFetch(context: self.cuttlefishContext)
+    }
 }
 #endif
 }
 #endif
index 00037248579768448709953afb8e778a63de59b6..3bc43ec728c0c144dbc3f3e68b7dac8f12f8cd00 100644 (file)
@@ -5,6 +5,7 @@ class OctagonResetTests: OctagonTestsBase {
         self.startCKAccountStatusMock()
 
         self.cuttlefishContext.startOctagonStateMachine()
         self.startCKAccountStatusMock()
 
         self.cuttlefishContext.startOctagonStateMachine()
+        XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
         _ = try self.cuttlefishContext.accountAvailable("13453464")
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
         _ = try self.cuttlefishContext.accountAvailable("13453464")
@@ -21,6 +22,7 @@ class OctagonResetTests: OctagonTestsBase {
         self.startCKAccountStatusMock()
 
         self.cuttlefishContext.startOctagonStateMachine()
         self.startCKAccountStatusMock()
 
         self.cuttlefishContext.startOctagonStateMachine()
+        XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
         self.cuttlefishContext.rpcResetAndEstablish(.testGenerated) { resetError in
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
         self.cuttlefishContext.rpcResetAndEstablish(.testGenerated) { resetError in
@@ -37,6 +39,7 @@ class OctagonResetTests: OctagonTestsBase {
         self.startCKAccountStatusMock()
 
         self.cuttlefishContext.startOctagonStateMachine()
         self.startCKAccountStatusMock()
 
         self.cuttlefishContext.startOctagonStateMachine()
+        XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
         self.cuttlefishContext.rpcResetAndEstablish(.testGenerated) { resetError in
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
         self.cuttlefishContext.rpcResetAndEstablish(.testGenerated) { resetError in
@@ -58,7 +61,7 @@ class OctagonResetTests: OctagonTestsBase {
         self.startCKAccountStatusMock()
 
         // Before resetAndEstablish, there shouldn't be any stored account state
         self.startCKAccountStatusMock()
 
         // Before resetAndEstablish, there shouldn't be any stored account state
-               XCTAssertThrowsError(try OTAccountMetadataClassC.loadFromKeychain(forContainer: containerName, contextID: contextName), "Before doing anything, loading a non-existent account state should fail")
+        XCTAssertThrowsError(try OTAccountMetadataClassC.loadFromKeychain(forContainer: containerName, contextID: contextName), "Before doing anything, loading a non-existent account state should fail")
 
         let resetAndEstablishExpectation = self.expectation(description: "resetAndEstablish callback occurs")
         let escrowRequestNotification = expectation(forNotification: OTMockEscrowRequestNotification,
 
         let resetAndEstablishExpectation = self.expectation(description: "resetAndEstablish callback occurs")
         let escrowRequestNotification = expectation(forNotification: OTMockEscrowRequestNotification,
@@ -94,6 +97,7 @@ class OctagonResetTests: OctagonTestsBase {
         self.startCKAccountStatusMock()
 
         self.cuttlefishContext.startOctagonStateMachine()
         self.startCKAccountStatusMock()
 
         self.cuttlefishContext.startOctagonStateMachine()
+        XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
         do {
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
         do {
@@ -116,7 +120,7 @@ class OctagonResetTests: OctagonTestsBase {
         let waitfortrusts = self.ckksViews.compactMap { view in
             (view as! CKKSKeychainView).keyHierarchyConditions[SecCKKSZoneKeyStateWaitForTrust] as? CKKSCondition
         }
         let waitfortrusts = self.ckksViews.compactMap { view in
             (view as! CKKSKeychainView).keyHierarchyConditions[SecCKKSZoneKeyStateWaitForTrust] as? CKKSCondition
         }
-        XCTAssert(waitfortrusts.count > 0, "Should have at least one waitfortrust condition")
+        XCTAssert(!waitfortrusts.isEmpty, "Should have at least one waitfortrust condition")
 
         let resetAndEstablishExpectation = self.expectation(description: "resetAndEstablish callback occurs")
         let escrowRequestNotification = expectation(forNotification: OTMockEscrowRequestNotification,
 
         let resetAndEstablishExpectation = self.expectation(description: "resetAndEstablish callback occurs")
         let escrowRequestNotification = expectation(forNotification: OTMockEscrowRequestNotification,
@@ -143,14 +147,15 @@ class OctagonResetTests: OctagonTestsBase {
     }
 
     func testOctagonResetAlsoResetsCKKSViewsMissingTLKs() {
     }
 
     func testOctagonResetAlsoResetsCKKSViewsMissingTLKs() {
-        self.putFakeKeyHierarchy(inCloudKit: self.manateeZoneID)
+        self.putFakeKeyHierarchiesInCloudKit()
 
 
-        let zoneKeys = self.keys![self.manateeZoneID!] as? ZoneKeys
+        let zoneKeys = self.keys![self.limitedPeersAllowedZoneID!] as? ZoneKeys
         XCTAssertNotNil(zoneKeys, "Should have some zone keys")
         XCTAssertNotNil(zoneKeys?.tlk, "Should have a tlk in the original key set")
 
         self.startCKAccountStatusMock()
         self.cuttlefishContext.startOctagonStateMachine()
         XCTAssertNotNil(zoneKeys, "Should have some zone keys")
         XCTAssertNotNil(zoneKeys?.tlk, "Should have a tlk in the original key set")
 
         self.startCKAccountStatusMock()
         self.cuttlefishContext.startOctagonStateMachine()
+        XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
         assertAllCKKSViews(enter: SecCKKSZoneKeyStateWaitForTrust, within: 10 * NSEC_PER_SEC)
 
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
         assertAllCKKSViews(enter: SecCKKSZoneKeyStateWaitForTrust, within: 10 * NSEC_PER_SEC)
 
@@ -164,7 +169,7 @@ class OctagonResetTests: OctagonTestsBase {
 
         assertAllCKKSViews(enter: SecCKKSZoneKeyStateReady, within: 10 * NSEC_PER_SEC)
 
 
         assertAllCKKSViews(enter: SecCKKSZoneKeyStateReady, within: 10 * NSEC_PER_SEC)
 
-        let laterZoneKeys = self.keys![self.manateeZoneID!] as? ZoneKeys
+        let laterZoneKeys = self.keys![self.limitedPeersAllowedZoneID!] as? ZoneKeys
         XCTAssertNotNil(laterZoneKeys, "Should have some zone keys")
         XCTAssertNotNil(laterZoneKeys?.tlk, "Should have a tlk in the newly created keyset")
         XCTAssertNotEqual(zoneKeys?.tlk?.uuid, laterZoneKeys?.tlk?.uuid, "CKKS zone should now have different keys")
         XCTAssertNotNil(laterZoneKeys, "Should have some zone keys")
         XCTAssertNotNil(laterZoneKeys?.tlk, "Should have a tlk in the newly created keyset")
         XCTAssertNotEqual(zoneKeys?.tlk?.uuid, laterZoneKeys?.tlk?.uuid, "CKKS zone should now have different keys")
@@ -172,23 +177,39 @@ class OctagonResetTests: OctagonTestsBase {
 
     func testOctagonResetIgnoresOldRemoteDevicesWithKeysAndResetsCKKS() {
         // CKKS has no keys, and there's another device claiming to have them already, but it's old
 
     func testOctagonResetIgnoresOldRemoteDevicesWithKeysAndResetsCKKS() {
         // CKKS has no keys, and there's another device claiming to have them already, but it's old
-        self.putFakeKeyHierarchy(inCloudKit: self.manateeZoneID)
-        self.putFakeDeviceStatus(inCloudKit: self.manateeZoneID)
+        self.putFakeKeyHierarchiesInCloudKit()
+        self.putFakeDeviceStatusesInCloudKit()
 
 
+        #if !os(tvOS)
         (self.zones![self.manateeZoneID!]! as! FakeCKZone).currentDatabase.allValues.forEach { record in
             let r = record as! CKRecord
         (self.zones![self.manateeZoneID!]! as! FakeCKZone).currentDatabase.allValues.forEach { record in
             let r = record as! CKRecord
-            if(r.recordType == SecCKRecordDeviceStateType) {
+            if r.recordType == SecCKRecordDeviceStateType {
+                r.creationDate = NSDate.distantPast
+                r.modificationDate = NSDate.distantPast
+            }
+        }
+        #endif
+        (self.zones![self.limitedPeersAllowedZoneID!]! as! FakeCKZone).currentDatabase.allValues.forEach { record in
+            let r = record as! CKRecord
+            if r.recordType == SecCKRecordDeviceStateType {
                 r.creationDate = NSDate.distantPast
                 r.modificationDate = NSDate.distantPast
             }
         }
 
                 r.creationDate = NSDate.distantPast
                 r.modificationDate = NSDate.distantPast
             }
         }
 
+        #if !os(tvOS)
         let zoneKeys = self.keys![self.manateeZoneID!] as? ZoneKeys
         let zoneKeys = self.keys![self.manateeZoneID!] as? ZoneKeys
-        XCTAssertNotNil(zoneKeys, "Should have some zone keys")
-        XCTAssertNotNil(zoneKeys?.tlk, "Should have a tlk in the original key set")
+        XCTAssertNotNil(zoneKeys, "Should have some zone keys for Manatee")
+        XCTAssertNotNil(zoneKeys?.tlk, "Should have a tlk in the original key set for Manatee")
+        #endif
+
+        let lpZoneKeys = self.keys![self.limitedPeersAllowedZoneID!] as? ZoneKeys
+        XCTAssertNotNil(lpZoneKeys, "Should have some zone keys for LimitedPeers")
+        XCTAssertNotNil(lpZoneKeys?.tlk, "Should have a tlk in the original key set for LimitedPeers")
 
         self.startCKAccountStatusMock()
         self.cuttlefishContext.startOctagonStateMachine()
 
         self.startCKAccountStatusMock()
         self.cuttlefishContext.startOctagonStateMachine()
+        XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
         self.silentZoneDeletesAllowed = true
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
         self.silentZoneDeletesAllowed = true
@@ -199,25 +220,42 @@ class OctagonResetTests: OctagonTestsBase {
             XCTFail("failed to make new friends: \(error)")
         }
 
             XCTFail("failed to make new friends: \(error)")
         }
 
-        assertAllCKKSViews(enter: SecCKKSZoneKeyStateReady, within: 10 * NSEC_PER_SEC)
+        self.assertAllCKKSViews(enter: SecCKKSZoneKeyStateReady, within: 10 * NSEC_PER_SEC)
 
 
+        #if !os(tvOS)
         let laterZoneKeys = self.keys![self.manateeZoneID!] as? ZoneKeys
         let laterZoneKeys = self.keys![self.manateeZoneID!] as? ZoneKeys
-        XCTAssertNotNil(laterZoneKeys, "Should have some zone keys")
-        XCTAssertNotNil(laterZoneKeys?.tlk, "Should have a tlk in the newly created keyset")
-        XCTAssertNotEqual(zoneKeys?.tlk?.uuid, laterZoneKeys?.tlk?.uuid, "CKKS zone should now have different keys")
+        XCTAssertNotNil(laterZoneKeys, "Should have some zone keys for Manatee")
+        XCTAssertNotNil(laterZoneKeys?.tlk, "Should have a tlk in the newly created keyset for Manatee")
+        XCTAssertNotEqual(zoneKeys?.tlk?.uuid, laterZoneKeys?.tlk?.uuid, "CKKS zone should now have different keys for Manatee")
+        #else
+        let laterZoneKeys = self.keys![self.manateeZoneID!] as? ZoneKeys
+        XCTAssertNil(laterZoneKeys, "Should have no Manatee zone keys for aTV")
+        #endif
+
+        let laterLpZoneKeys = self.keys![self.limitedPeersAllowedZoneID!] as? ZoneKeys
+        XCTAssertNotNil(laterLpZoneKeys, "Should have some zone keys for LimitedPeers")
+        XCTAssertNotNil(laterLpZoneKeys?.tlk, "Should have a tlk in the newly created keyset for LimitedPeers")
+        XCTAssertNotEqual(lpZoneKeys?.tlk?.uuid, laterLpZoneKeys?.tlk?.uuid, "CKKS zone should now have different keys for LimitedPeers")
     }
 
     func testOctagonResetWithRemoteDevicesWithKeysDoesNotResetCKKS() {
         // CKKS has no keys, and there's another device claiming to have them already, so CKKS won't immediately reset it
     }
 
     func testOctagonResetWithRemoteDevicesWithKeysDoesNotResetCKKS() {
         // CKKS has no keys, and there's another device claiming to have them already, so CKKS won't immediately reset it
-        self.putFakeKeyHierarchy(inCloudKit: self.manateeZoneID)
-        self.putFakeDeviceStatus(inCloudKit: self.manateeZoneID)
+        self.putFakeKeyHierarchiesInCloudKit()
+        self.putFakeDeviceStatusesInCloudKit()
 
 
+        #if !os(tvOS)
         let zoneKeys = self.keys![self.manateeZoneID!] as? ZoneKeys
         XCTAssertNotNil(zoneKeys, "Should have some zone keys")
         XCTAssertNotNil(zoneKeys?.tlk, "Should have a tlk in the original key set")
         let zoneKeys = self.keys![self.manateeZoneID!] as? ZoneKeys
         XCTAssertNotNil(zoneKeys, "Should have some zone keys")
         XCTAssertNotNil(zoneKeys?.tlk, "Should have a tlk in the original key set")
+        #endif
+
+        let lpZoneKeys = self.keys![self.limitedPeersAllowedZoneID!] as? ZoneKeys
+        XCTAssertNotNil(lpZoneKeys, "Should have some zone keys for LimitedPeers")
+        XCTAssertNotNil(lpZoneKeys?.tlk, "Should have a tlk in the original key set for LimitedPeers")
 
         self.startCKAccountStatusMock()
         self.cuttlefishContext.startOctagonStateMachine()
 
         self.startCKAccountStatusMock()
         self.cuttlefishContext.startOctagonStateMachine()
+        XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
         self.silentZoneDeletesAllowed = true
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
         self.silentZoneDeletesAllowed = true
@@ -230,23 +268,40 @@ class OctagonResetTests: OctagonTestsBase {
 
         assertAllCKKSViews(enter: SecCKKSZoneKeyStateWaitForTLK, within: 10 * NSEC_PER_SEC)
 
 
         assertAllCKKSViews(enter: SecCKKSZoneKeyStateWaitForTLK, within: 10 * NSEC_PER_SEC)
 
+        #if !os(tvOS)
         let laterZoneKeys = self.keys![self.manateeZoneID!] as? ZoneKeys
         XCTAssertNotNil(laterZoneKeys, "Should have some zone keys")
         XCTAssertNotNil(laterZoneKeys?.tlk, "Should have a tlk in the newly created keyset")
         XCTAssertEqual(zoneKeys?.tlk?.uuid, laterZoneKeys?.tlk?.uuid, "CKKS zone should now have the same keys")
         let laterZoneKeys = self.keys![self.manateeZoneID!] as? ZoneKeys
         XCTAssertNotNil(laterZoneKeys, "Should have some zone keys")
         XCTAssertNotNil(laterZoneKeys?.tlk, "Should have a tlk in the newly created keyset")
         XCTAssertEqual(zoneKeys?.tlk?.uuid, laterZoneKeys?.tlk?.uuid, "CKKS zone should now have the same keys")
+        #else
+        let laterZoneKeys = self.keys![self.manateeZoneID!] as? ZoneKeys
+        XCTAssertNil(laterZoneKeys, "Should have no Manatee zone keys for aTV")
+        #endif
+
+        let lpLaterZoneKeys = self.keys![self.limitedPeersAllowedZoneID!] as? ZoneKeys
+        XCTAssertNotNil(lpLaterZoneKeys, "Should have some zone keys for LimitedPeersAllowed")
+        XCTAssertNotNil(lpLaterZoneKeys?.tlk, "Should have a tlk in the newly created keyset for LimitedPeersAllowed")
+        XCTAssertEqual(lpZoneKeys?.tlk?.uuid, lpLaterZoneKeys?.tlk?.uuid, "CKKS zone should now have the same keys for LimitedPeersAllowed")
     }
 
     func testOctagonResetWithTLKsDoesNotResetCKKS() {
         // CKKS has the keys keys
     }
 
     func testOctagonResetWithTLKsDoesNotResetCKKS() {
         // CKKS has the keys keys
-        self.putFakeKeyHierarchy(inCloudKit: self.manateeZoneID)
-        self.saveTLKMaterial(toKeychain: self.manateeZoneID)
+        self.putFakeKeyHierarchiesInCloudKit()
+        self.saveTLKMaterialToKeychain()
 
 
+        #if !os(tvOS)
         let zoneKeys = self.keys![self.manateeZoneID!] as? ZoneKeys
         XCTAssertNotNil(zoneKeys, "Should have some zone keys")
         XCTAssertNotNil(zoneKeys?.tlk, "Should have a tlk in the original key set")
         let zoneKeys = self.keys![self.manateeZoneID!] as? ZoneKeys
         XCTAssertNotNil(zoneKeys, "Should have some zone keys")
         XCTAssertNotNil(zoneKeys?.tlk, "Should have a tlk in the original key set")
+        #endif
+
+        let lpZoneKeys = self.keys![self.limitedPeersAllowedZoneID!] as? ZoneKeys
+        XCTAssertNotNil(lpZoneKeys, "Should have some zone keys for LimitedPeers")
+        XCTAssertNotNil(lpZoneKeys?.tlk, "Should have a tlk in the original key set for LimitedPeers")
 
         self.startCKAccountStatusMock()
         self.cuttlefishContext.startOctagonStateMachine()
 
         self.startCKAccountStatusMock()
         self.cuttlefishContext.startOctagonStateMachine()
+        XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
         do {
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
         do {
@@ -258,10 +313,20 @@ class OctagonResetTests: OctagonTestsBase {
 
         assertAllCKKSViews(enter: SecCKKSZoneKeyStateReady, within: 10 * NSEC_PER_SEC)
 
 
         assertAllCKKSViews(enter: SecCKKSZoneKeyStateReady, within: 10 * NSEC_PER_SEC)
 
+        #if !os(tvOS)
         let laterZoneKeys = self.keys![self.manateeZoneID!] as? ZoneKeys
         XCTAssertNotNil(laterZoneKeys, "Should have some zone keys")
         XCTAssertNotNil(laterZoneKeys?.tlk, "Should have a tlk in the newly created keyset")
         XCTAssertEqual(zoneKeys?.tlk?.uuid, laterZoneKeys?.tlk?.uuid, "CKKS zone should now have the same keys")
         let laterZoneKeys = self.keys![self.manateeZoneID!] as? ZoneKeys
         XCTAssertNotNil(laterZoneKeys, "Should have some zone keys")
         XCTAssertNotNil(laterZoneKeys?.tlk, "Should have a tlk in the newly created keyset")
         XCTAssertEqual(zoneKeys?.tlk?.uuid, laterZoneKeys?.tlk?.uuid, "CKKS zone should now have the same keys")
+        #else
+        let laterZoneKeys = self.keys![self.manateeZoneID!] as? ZoneKeys
+        XCTAssertNil(laterZoneKeys, "Should have no Manatee zone keys for aTV")
+        #endif
+
+        let lpLaterZoneKeys = self.keys![self.limitedPeersAllowedZoneID!] as? ZoneKeys
+        XCTAssertNotNil(lpLaterZoneKeys, "Should have some zone keys for LimitedPeersAllowed")
+        XCTAssertNotNil(lpLaterZoneKeys?.tlk, "Should have a tlk in the newly created keyset for LimitedPeersAllowed")
+        XCTAssertEqual(lpZoneKeys?.tlk?.uuid, lpLaterZoneKeys?.tlk?.uuid, "CKKS zone should now have the same keys for LimitedPeersAllowed")
     }
 
     func testOctagonResetAndEstablishFail() throws {
     }
 
     func testOctagonResetAndEstablishFail() throws {
@@ -269,6 +334,7 @@ class OctagonResetTests: OctagonTestsBase {
         self.startCKAccountStatusMock()
 
         self.cuttlefishContext.startOctagonStateMachine()
         self.startCKAccountStatusMock()
 
         self.cuttlefishContext.startOctagonStateMachine()
+        XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
         _ = try self.cuttlefishContext.accountAvailable("13453464")
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
         _ = try self.cuttlefishContext.accountAvailable("13453464")
@@ -297,6 +363,7 @@ class OctagonResetTests: OctagonTestsBase {
         self.startCKAccountStatusMock()
 
         self.cuttlefishContext.startOctagonStateMachine()
         self.startCKAccountStatusMock()
 
         self.cuttlefishContext.startOctagonStateMachine()
+        XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
         _ = try self.cuttlefishContext.accountAvailable("13453464")
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
         _ = try self.cuttlefishContext.accountAvailable("13453464")
@@ -322,13 +389,14 @@ class OctagonResetTests: OctagonTestsBase {
     }
 
     func testResetReasonUserInitiatedReset() throws {
     }
 
     func testResetReasonUserInitiatedReset() throws {
-          // Make sure if establish fail we end up in untrusted instead of error
-          self.startCKAccountStatusMock()
+        // Make sure if establish fail we end up in untrusted instead of error
+        self.startCKAccountStatusMock()
 
 
-          self.cuttlefishContext.startOctagonStateMachine()
-          self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
+        self.cuttlefishContext.startOctagonStateMachine()
+        XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
+        self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
 
-          _ = try self.cuttlefishContext.accountAvailable("13453464")
+        _ = try self.cuttlefishContext.accountAvailable("13453464")
 
         let resetExpectation = self.expectation(description: "resetExpectation")
 
 
         let resetExpectation = self.expectation(description: "resetExpectation")
 
@@ -366,6 +434,7 @@ class OctagonResetTests: OctagonTestsBase {
         self.startCKAccountStatusMock()
 
         self.cuttlefishContext.startOctagonStateMachine()
         self.startCKAccountStatusMock()
 
         self.cuttlefishContext.startOctagonStateMachine()
+        XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
         _ = try self.cuttlefishContext.accountAvailable("13453464")
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
         _ = try self.cuttlefishContext.accountAvailable("13453464")
@@ -404,6 +473,7 @@ class OctagonResetTests: OctagonTestsBase {
         self.startCKAccountStatusMock()
 
         self.cuttlefishContext.startOctagonStateMachine()
         self.startCKAccountStatusMock()
 
         self.cuttlefishContext.startOctagonStateMachine()
+        XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
         let initiatorContext = self.manager.context(forContainerName: OTCKContainerName,
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
         let initiatorContext = self.manager.context(forContainerName: OTCKContainerName,
@@ -448,6 +518,7 @@ class OctagonResetTests: OctagonTestsBase {
         let contextName = OTDefaultContext
 
         self.cuttlefishContext.startOctagonStateMachine()
         let contextName = OTDefaultContext
 
         self.cuttlefishContext.startOctagonStateMachine()
+        XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
         self.startCKAccountStatusMock()
 
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
         self.startCKAccountStatusMock()
 
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
@@ -497,10 +568,9 @@ class OctagonResetTests: OctagonTestsBase {
         self.verifyDatabaseMocks()
 
         let dumpCallback = self.expectation(description: "dumpCallback callback occurs")
         self.verifyDatabaseMocks()
 
         let dumpCallback = self.expectation(description: "dumpCallback callback occurs")
-        self.tphClient.dump(withContainer: containerName, context: contextName) {
-            dump, _ in
+        self.tphClient.dump(withContainer: containerName, context: contextName) { dump, _ in
             XCTAssertNotNil(dump, "dump should not be nil")
             XCTAssertNotNil(dump, "dump should not be nil")
-            let egoSelf = dump!["self"] as? Dictionary<String, AnyObject>
+            let egoSelf = dump!["self"] as? [String: AnyObject]
             XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
             dumpCallback.fulfill()
         }
             XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
             dumpCallback.fulfill()
         }
@@ -513,6 +583,7 @@ class OctagonResetTests: OctagonTestsBase {
     func testLegacyJoinCircleDoesNotReset() throws {
         self.cuttlefishContext.startOctagonStateMachine()
         self.startCKAccountStatusMock()
     func testLegacyJoinCircleDoesNotReset() throws {
         self.cuttlefishContext.startOctagonStateMachine()
         self.startCKAccountStatusMock()
+        XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
 
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
 
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
@@ -556,6 +627,7 @@ class OctagonResetTests: OctagonTestsBase {
         self.startCKAccountStatusMock()
 
         self.cuttlefishContext.startOctagonStateMachine()
         self.startCKAccountStatusMock()
 
         self.cuttlefishContext.startOctagonStateMachine()
+        XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
         _ = try self.cuttlefishContext.accountAvailable("13453464")
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
         _ = try self.cuttlefishContext.accountAvailable("13453464")
@@ -579,6 +651,62 @@ class OctagonResetTests: OctagonTestsBase {
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateReady, within: 10 * NSEC_PER_SEC)
         self.verifyDatabaseMocks()
     }
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateReady, within: 10 * NSEC_PER_SEC)
         self.verifyDatabaseMocks()
     }
+
+    func testCliqueResetAllSPI() throws {
+        self.cuttlefishContext.startOctagonStateMachine()
+        self.startCKAccountStatusMock()
+        OctagonSetSOSFeatureEnabled(false)
+
+        XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
+
+        self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
+
+        let establishAndResetExpectation = self.expectation(description: "resetExpectation")
+        let clique: OTClique
+        let otcliqueContext = OTConfigurationContext()
+        var firstCliqueIdentifier: String?
+
+        otcliqueContext.context = OTDefaultContext
+        otcliqueContext.dsid = "13453464"
+        otcliqueContext.altDSID = self.mockAuthKit.altDSID!
+        otcliqueContext.authenticationAppleID = "appleID"
+        otcliqueContext.passwordEquivalentToken = "petpetpetpetpet"
+        otcliqueContext.otControl = self.otControl
+        otcliqueContext.ckksControl = self.ckksControl
+        otcliqueContext.sbd = OTMockSecureBackup(bottleID: nil, entropy: nil)
+
+        do {
+            clique = try OTClique.newFriends(withContextData: otcliqueContext, resetReason: .testGenerated)
+            XCTAssertNotNil(clique, "Clique should not be nil")
+            XCTAssertNotNil(clique.cliqueMemberIdentifier, "Should have a member identifier after a clique newFriends call")
+            firstCliqueIdentifier = clique.cliqueMemberIdentifier
+            establishAndResetExpectation.fulfill()
+        } catch {
+            XCTFail("Shouldn't have errored making new friends everything: \(error)")
+            throw error
+        }
+        self.wait(for: [establishAndResetExpectation], timeout: 10)
+
+        self.assertEnters(context: self.cuttlefishContext, state: OctagonStateReady, within: 10 * NSEC_PER_SEC)
+        assertAllCKKSViews(enter: SecCKKSZoneKeyStateReady, within: 10 * NSEC_PER_SEC)
+        self.verifyDatabaseMocks()
+
+        self.silentZoneDeletesAllowed = true
+
+        let newClique: OTClique
+        do {
+            newClique = try OTClique.resetProtectedData(otcliqueContext)
+            XCTAssertNotEqual(newClique.cliqueMemberIdentifier, firstCliqueIdentifier, "clique identifiers should be different")
+        } catch {
+            XCTFail("Shouldn't have errored resetting everything: \(error)")
+            throw error
+        }
+        XCTAssertNotNil(newClique, "newClique should not be nil")
+
+        self.assertEnters(context: self.cuttlefishContext, state: OctagonStateReady, within: 10 * NSEC_PER_SEC)
+        assertAllCKKSViews(enter: SecCKKSZoneKeyStateReady, within: 10 * NSEC_PER_SEC)
+        self.verifyDatabaseMocks()
+    }
 }
 
 #endif // OCTAGON
 }
 
 #endif // OCTAGON
index a239ad9b6ffbc74879096c40d5121ee7d49a3a27..11961ed33c1ae90a75f38cc7c8a169b2a85920aa 100644 (file)
@@ -3,9 +3,9 @@
 class OctagonSOSTests: OctagonTestsBase {
 
     func testSOSOctagonKeyConsistency() throws {
 class OctagonSOSTests: OctagonTestsBase {
 
     func testSOSOctagonKeyConsistency() throws {
-        self.putFakeKeyHierarchy(inCloudKit: self.manateeZoneID)
-        self.putSelfTLKShares(inCloudKit: self.manateeZoneID)
-        self.saveTLKMaterial(toKeychain: self.manateeZoneID)
+        self.putFakeKeyHierarchiesInCloudKit()
+        self.putSelfTLKSharesInCloudKit()
+        self.saveTLKMaterialToKeychain()
 
         self.startCKAccountStatusMock()
 
 
         self.startCKAccountStatusMock()
 
@@ -53,9 +53,9 @@ class OctagonSOSTests: OctagonTestsBase {
     }
 
     func testSOSOctagonKeyConsistencyLocked() throws {
     }
 
     func testSOSOctagonKeyConsistencyLocked() throws {
-        self.putFakeKeyHierarchy(inCloudKit: self.manateeZoneID)
-        self.putSelfTLKShares(inCloudKit: self.manateeZoneID)
-        self.saveTLKMaterial(toKeychain: self.manateeZoneID)
+        self.putFakeKeyHierarchiesInCloudKit()
+        self.putSelfTLKSharesInCloudKit()
+        self.saveTLKMaterialToKeychain()
 
         self.startCKAccountStatusMock()
 
 
         self.startCKAccountStatusMock()
 
@@ -115,9 +115,9 @@ class OctagonSOSTests: OctagonTestsBase {
     }
 
     func testSOSOctagonKeyConsistencySucceedsAfterUpdatingSOS() throws {
     }
 
     func testSOSOctagonKeyConsistencySucceedsAfterUpdatingSOS() throws {
-        self.putFakeKeyHierarchy(inCloudKit: self.manateeZoneID!)
-        self.putSelfTLKShares(inCloudKit: self.manateeZoneID!)
-        self.saveTLKMaterial(toKeychain: self.manateeZoneID!)
+        self.putFakeKeyHierarchiesInCloudKit()
+        self.putSelfTLKSharesInCloudKit()
+        self.saveTLKMaterialToKeychain()
 
         self.startCKAccountStatusMock()
 
 
         self.startCKAccountStatusMock()
 
@@ -292,6 +292,221 @@ class OctagonSOSTests: OctagonTestsBase {
             XCTAssertNotNil(error, "error should not be nil")
         }
     }
             XCTAssertNotNil(error, "error should not be nil")
         }
     }
+
+    func testPreapproveSOSPeersWhenInCircle() throws {
+        self.startCKAccountStatusMock()
+
+        self.mockSOSAdapter.circleStatus = SOSCCStatus(kSOSCCInCircle)
+        let peer1Preapproval = TPHashBuilder.hash(with: .SHA256, of: self.mockSOSAdapter.selfPeer.publicSigningKey.encodeSubjectPublicKeyInfo())
+
+        let peer2SOSMockPeer = self.createSOSPeer(peerID: "peer2ID")
+        self.mockSOSAdapter.trustedPeers.add(peer2SOSMockPeer)
+        let peer2Preapproval = TPHashBuilder.hash(with: .SHA256, of: peer2SOSMockPeer.publicSigningKey.encodeSubjectPublicKeyInfo())
+
+        let peer3SOSMockPeer = self.createSOSPeer(peerID: "peer3ID")
+        self.mockSOSAdapter.trustedPeers.add(peer3SOSMockPeer)
+        let peer3Preapproval = TPHashBuilder.hash(with: .SHA256, of: peer3SOSMockPeer.publicSigningKey.encodeSubjectPublicKeyInfo())
+
+        let establishTwiceExpectation = self.expectation(description: "establish should be called twice")
+        establishTwiceExpectation.expectedFulfillmentCount = 2
+
+        self.fakeCuttlefishServer.establishListener = { request in
+            XCTAssertTrue(request.hasPeer, "establish request should have a peer")
+
+            let newDynamicInfo = TPPeerDynamicInfo(data: request.peer.dynamicInfoAndSig.peerDynamicInfo, sig: request.peer.dynamicInfoAndSig.sig)
+            XCTAssertNotNil(newDynamicInfo, "should be able to make a dynamicInfo from protobuf")
+
+            XCTAssertTrue(newDynamicInfo?.preapprovals.contains(peer2Preapproval) ?? false, "Fake peer 2 should be preapproved")
+            XCTAssertTrue(newDynamicInfo?.preapprovals.contains(peer3Preapproval) ?? false, "Fake peer 3 should be preapproved")
+
+            establishTwiceExpectation.fulfill()
+            return nil
+        }
+
+        self.assertAllCKKSViewsUpload(tlkShares: 3)
+
+        // Just starting the state machine is sufficient; it should perform an SOS upgrade
+        self.cuttlefishContext.startOctagonStateMachine()
+        self.assertEnters(context: self.cuttlefishContext, state: OctagonStateReady, within: 10 * NSEC_PER_SEC)
+
+        self.assertAllCKKSViews(enter: SecCKKSZoneKeyStateReady, within: 10 * NSEC_PER_SEC)
+        self.verifyDatabaseMocks()
+
+        // And a reset does the right thing with preapprovals as well
+        do {
+            let arguments = OTConfigurationContext()
+            arguments.altDSID = try self.cuttlefishContext.authKitAdapter.primaryiCloudAccountAltDSID()
+            arguments.context = self.cuttlefishContext.contextID
+            arguments.otControl = self.otControl
+
+            let clique = try OTClique.newFriends(withContextData: arguments, resetReason: .testGenerated)
+            XCTAssertNotNil(clique, "Clique should not be nil")
+        } catch {
+            XCTFail("Shouldn't have errored making new friends: \(error)")
+        }
+
+        self.assertEnters(context: self.cuttlefishContext, state: OctagonStateReady, within: 10 * NSEC_PER_SEC)
+        self.assertAllCKKSViews(enter: SecCKKSZoneKeyStateReady, within: 10 * NSEC_PER_SEC)
+        self.verifyDatabaseMocks()
+
+        self.wait(for: [establishTwiceExpectation], timeout: 1)
+
+        // And do we do the right thing when joining via SOS preapproval?
+        let peer2JoinExpectation = self.expectation(description: "join called")
+        self.fakeCuttlefishServer.joinListener = { request in
+            XCTAssertTrue(request.hasPeer, "establish request should have a peer")
+
+            let newDynamicInfo = TPPeerDynamicInfo(data: request.peer.dynamicInfoAndSig.peerDynamicInfo, sig: request.peer.dynamicInfoAndSig.sig)
+            XCTAssertNotNil(newDynamicInfo, "should be able to make a dynamicInfo from protobuf")
+
+            XCTAssertFalse(newDynamicInfo?.preapprovals.contains(peer1Preapproval) ?? false, "Fake peer 1 should NOT be preapproved by peer2 (as it's already in Octagon)")
+            XCTAssertTrue(newDynamicInfo?.preapprovals.contains(peer3Preapproval) ?? false, "Fake peer 3 should be preapproved by peer2")
+
+            peer2JoinExpectation.fulfill()
+
+            return nil
+        }
+
+        let peer2mockSOS = CKKSMockSOSPresentAdapter(selfPeer: peer2SOSMockPeer, trustedPeers: self.mockSOSAdapter.allPeers(), essential: false)
+        let peer2 = self.makeInitiatorContext(contextID: "peer2", authKitAdapter: self.mockAuthKit2, sosAdapter: peer2mockSOS)
+
+        peer2.startOctagonStateMachine()
+        self.assertEnters(context: peer2, state: OctagonStateReady, within: 10 * NSEC_PER_SEC)
+        self.assertConsidersSelfTrusted(context: peer2)
+
+        self.wait(for: [peer2JoinExpectation], timeout: 1)
+    }
+
+    func testDoNotPreapproveSOSPeerWhenOutOfCircle() throws {
+        self.startCKAccountStatusMock()
+
+        // SOS returns 'trusted' peers without actually being in-circle
+        // We don't want to preapprove those peers
+
+        self.mockSOSAdapter.circleStatus = SOSCCStatus(kSOSCCNotInCircle)
+        let peer1Preapproval = TPHashBuilder.hash(with: .SHA256, of: self.mockSOSAdapter.selfPeer.publicSigningKey.encodeSubjectPublicKeyInfo())
+
+        let peer2SOSMockPeer = self.createSOSPeer(peerID: "peer2ID")
+        self.mockSOSAdapter.trustedPeers.add(peer2SOSMockPeer)
+        let peer2Preapproval = TPHashBuilder.hash(with: .SHA256, of: peer2SOSMockPeer.publicSigningKey.encodeSubjectPublicKeyInfo())
+
+        let peer3SOSMockPeer = self.createSOSPeer(peerID: "peer3ID")
+        self.mockSOSAdapter.trustedPeers.add(peer3SOSMockPeer)
+        let peer3Preapproval = TPHashBuilder.hash(with: .SHA256, of: peer3SOSMockPeer.publicSigningKey.encodeSubjectPublicKeyInfo())
+
+        self.fakeCuttlefishServer.establishListener = { request in
+            XCTAssertTrue(request.hasPeer, "establish request should have a peer")
+
+            let newDynamicInfo = TPPeerDynamicInfo(data: request.peer.dynamicInfoAndSig.peerDynamicInfo, sig: request.peer.dynamicInfoAndSig.sig)
+            XCTAssertNotNil(newDynamicInfo, "should be able to make a dynamicInfo from protobuf")
+
+            XCTAssertFalse(newDynamicInfo?.preapprovals.contains(peer2Preapproval) ?? false, "Fake peer 2 should not be preapproved")
+            XCTAssertFalse(newDynamicInfo?.preapprovals.contains(peer3Preapproval) ?? false, "Fake peer 3 should not be preapproved")
+
+            return nil
+        }
+
+        self.assertResetAndBecomeTrustedInDefaultContext()
+
+        self.assertEnters(context: self.cuttlefishContext, state: OctagonStateReady, within: 10 * NSEC_PER_SEC)
+        self.assertConsidersSelfTrusted(context: self.cuttlefishContext)
+
+        self.assertAllCKKSViews(enter: SecCKKSZoneKeyStateReady, within: 10 * NSEC_PER_SEC)
+
+        self.verifyDatabaseMocks()
+
+        // And do we do the right thing when joining via bottle?
+        let peer2JoinExpectation = self.expectation(description: "join called")
+        self.fakeCuttlefishServer.joinListener = { request in
+            XCTAssertTrue(request.hasPeer, "establish request should have a peer")
+
+            let newDynamicInfo = TPPeerDynamicInfo(data: request.peer.dynamicInfoAndSig.peerDynamicInfo, sig: request.peer.dynamicInfoAndSig.sig)
+            XCTAssertNotNil(newDynamicInfo, "should be able to make a dynamicInfo from protobuf")
+
+            XCTAssertFalse(newDynamicInfo?.preapprovals.contains(peer1Preapproval) ?? false, "Fake peer 1 should NOT be preapproved by peer2 (as it's not in SOS)")
+            XCTAssertFalse(newDynamicInfo?.preapprovals.contains(peer3Preapproval) ?? false, "Fake peer 3 should not be preapproved by peer2 (as it's not in SOS)")
+
+            peer2JoinExpectation.fulfill()
+
+            return nil
+        }
+
+        let peer2mockSOS = CKKSMockSOSPresentAdapter(selfPeer: peer2SOSMockPeer, trustedPeers: self.mockSOSAdapter.allPeers(), essential: false)
+        peer2mockSOS.circleStatus = SOSCCStatus(kSOSCCNotInCircle)
+        let peer2 = self.makeInitiatorContext(contextID: "peer2", authKitAdapter: self.mockAuthKit2, sosAdapter: peer2mockSOS)
+
+        peer2.startOctagonStateMachine()
+
+        _ = self.assertJoinViaEscrowRecovery(joiningContext: peer2, sponsor: self.cuttlefishContext)
+        self.assertEnters(context: peer2, state: OctagonStateReady, within: 10 * NSEC_PER_SEC)
+        self.assertConsidersSelfTrusted(context: peer2)
+
+        self.wait(for: [peer2JoinExpectation], timeout: 1)
+    }
+
+    func testRespondToNewOctagonPeerWhenUpdatingPreapprovedKeys() throws {
+        self.startCKAccountStatusMock()
+
+        self.mockSOSAdapter.circleStatus = SOSCCStatus(kSOSCCInCircle)
+        let peer1Preapproval = TPHashBuilder.hash(with: .SHA256, of: self.mockSOSAdapter.selfPeer.publicSigningKey.encodeSubjectPublicKeyInfo())
+
+        let peer2SOSMockPeer = self.createSOSPeer(peerID: "peer2ID")
+        self.mockSOSAdapter.trustedPeers.add(peer2SOSMockPeer)
+        let peer2Preapproval = TPHashBuilder.hash(with: .SHA256, of: peer2SOSMockPeer.publicSigningKey.encodeSubjectPublicKeyInfo())
+
+        self.assertAllCKKSViewsUpload(tlkShares: 2)
+
+        // Just starting the state machine is sufficient; it should perform an SOS upgrade
+        self.cuttlefishContext.startOctagonStateMachine()
+        self.assertEnters(context: self.cuttlefishContext, state: OctagonStateReady, within: 10 * NSEC_PER_SEC)
+        let peer1ID = try self.cuttlefishContext.accountMetadataStore.getEgoPeerID()
+
+        self.assertAllCKKSViews(enter: SecCKKSZoneKeyStateReady, within: 10 * NSEC_PER_SEC)
+        self.verifyDatabaseMocks()
+
+        // Another peer arrives, but we miss the Octagon push
+        let peer2mockSOS = CKKSMockSOSPresentAdapter(selfPeer: peer2SOSMockPeer, trustedPeers: self.mockSOSAdapter.allPeers(), essential: false)
+        let joiningContext = self.makeInitiatorContext(contextID: "joiner", authKitAdapter: self.mockAuthKit2, sosAdapter: peer2mockSOS)
+        let peer2ID = self.assertJoinViaEscrowRecovery(joiningContext: joiningContext, sponsor: self.cuttlefishContext)
+
+        // Now, SOS updates its key list: we should update our preapproved keys (and then also trust the newly-joined peer)
+        let peer3SOSMockPeer = self.createSOSPeer(peerID: "peer3ID")
+        self.mockSOSAdapter.trustedPeers.add(peer3SOSMockPeer)
+        let peer3Preapproval = TPHashBuilder.hash(with: .SHA256, of: peer3SOSMockPeer.publicSigningKey.encodeSubjectPublicKeyInfo())
+
+        let updateKeysExpectation = self.expectation(description: "UpdateTrust should fire (once)")
+        self.fakeCuttlefishServer.updateListener = { [unowned self] request in
+            XCTAssertEqual(request.peerID, peer1ID, "UpdateTrust should be for peer1")
+
+            let newDynamicInfo = request.dynamicInfoAndSig.dynamicInfo()
+
+            XCTAssertFalse(newDynamicInfo.preapprovals.contains(peer1Preapproval), "Fake peer 1 should NOT be preapproved by peer1 (as it's its own keys)")
+            XCTAssertTrue(newDynamicInfo.preapprovals.contains(peer2Preapproval), "Fake peer 2 should be preapproved by original peer")
+            XCTAssertTrue(newDynamicInfo.preapprovals.contains(peer3Preapproval), "Fake peer 3 should be preapproved by original peer")
+
+            self.fakeCuttlefishServer.updateListener = nil
+            updateKeysExpectation.fulfill()
+
+            return nil
+        }
+
+        // And we'll send TLKShares to the new SOS peer and the new Octagon peer
+        self.assertAllCKKSViewsUpload(tlkShares: 2)
+
+        // to avoid CKKS race conditions (wherein it uploads each TLKShare in its own operation), send the SOS notification only to the Octagon context
+        //self.mockSOSAdapter.sendTrustedPeerSetChangedUpdate()
+        self.cuttlefishContext.trustedPeerSetChanged(self.mockSOSAdapter)
+        self.wait(for: [updateKeysExpectation], timeout: 10)
+
+        self.assertEnters(context: self.cuttlefishContext, state: OctagonStateReady, within: 10 * NSEC_PER_SEC)
+        self.assertAllCKKSViews(enter: SecCKKSZoneKeyStateReady, within: 10 * NSEC_PER_SEC)
+        self.verifyDatabaseMocks()
+
+        XCTAssertTrue(self.fakeCuttlefishServer.assertCuttlefishState(FakeCuttlefishAssertion(peer: peer1ID, opinion: .trusts, target: peer1ID)),
+                      "peer 1 should trust peer 1")
+        XCTAssertTrue(self.fakeCuttlefishServer.assertCuttlefishState(FakeCuttlefishAssertion(peer: peer1ID, opinion: .trusts, target: peer2ID)),
+                      "peer 1 should trust peer 2")
+    }
 }
 
 #endif
 }
 
 #endif
index f1a10e25d0fcc9f17bf79757fb330803c4b89258..86e5caf39f3b916dda8660eb7b0f9ae42b5055a8 100644 (file)
@@ -2,9 +2,9 @@
 
 class OctagonSOSUpgradeTests: OctagonTestsBase {
     func testSOSUpgrade() throws {
 
 class OctagonSOSUpgradeTests: OctagonTestsBase {
     func testSOSUpgrade() throws {
-        self.putFakeKeyHierarchy(inCloudKit: self.manateeZoneID)
-        self.putSelfTLKShares(inCloudKit: self.manateeZoneID)
-        self.saveTLKMaterial(toKeychain: self.manateeZoneID)
+        self.putFakeKeyHierarchiesInCloudKit()
+        self.putSelfTLKSharesInCloudKit()
+        self.saveTLKMaterialToKeychain()
 
         XCTAssertTrue(OctagonPerformSOSUpgrade(), "SOS upgrade should be on")
 
 
         XCTAssertTrue(OctagonPerformSOSUpgrade(), "SOS upgrade should be on")
 
@@ -33,14 +33,18 @@ class OctagonSOSUpgradeTests: OctagonTestsBase {
         self.verifyDatabaseMocks()
 
         self.assertSelfTLKSharesInCloudKit(context: self.cuttlefishContext)
         self.verifyDatabaseMocks()
 
         self.assertSelfTLKSharesInCloudKit(context: self.cuttlefishContext)
-        assertAllCKKSViews(enter: SecCKKSZoneKeyStateReady, within: 10 * NSEC_PER_SEC)
+        self.assertAllCKKSViews(enter: SecCKKSZoneKeyStateReady, within: 10 * NSEC_PER_SEC)
+
+        // Also, CKKS should be configured with the prevailing policy version
+        XCTAssertNotNil(self.injectedManager?.policy, "Should have given CKKS a TPPolicy during SOS upgrade")
+        XCTAssertEqual(self.injectedManager?.policy?.version, prevailingPolicyVersion, "Policy given to CKKS should be prevailing policy")
     }
 
     // Verify that an SOS upgrade only does one establish (and no update trust).
     func testSOSUpgradeUpdateNoUpdateTrust() throws {
     }
 
     // Verify that an SOS upgrade only does one establish (and no update trust).
     func testSOSUpgradeUpdateNoUpdateTrust() throws {
-        self.putFakeKeyHierarchy(inCloudKit: self.manateeZoneID)
-        self.putSelfTLKShares(inCloudKit: self.manateeZoneID)
-        self.saveTLKMaterial(toKeychain: self.manateeZoneID)
+        self.putFakeKeyHierarchiesInCloudKit()
+        self.putSelfTLKSharesInCloudKit()
+        self.saveTLKMaterialToKeychain()
 
         XCTAssertTrue(OctagonPerformSOSUpgrade(), "SOS upgrade should be on")
 
 
         XCTAssertTrue(OctagonPerformSOSUpgrade(), "SOS upgrade should be on")
 
@@ -74,9 +78,9 @@ class OctagonSOSUpgradeTests: OctagonTestsBase {
     }
 
     func testSOSUpgradeAuthkitError() throws {
     }
 
     func testSOSUpgradeAuthkitError() throws {
-        self.putFakeKeyHierarchy(inCloudKit: self.manateeZoneID!)
-        self.putSelfTLKShares(inCloudKit: self.manateeZoneID!)
-        self.saveTLKMaterial(toKeychain: self.manateeZoneID!)
+        self.putFakeKeyHierarchiesInCloudKit()
+        self.putSelfTLKSharesInCloudKit()
+        self.saveTLKMaterialToKeychain()
 
         XCTAssertTrue(OctagonPerformSOSUpgrade(), "SOS upgrade should be on")
 
 
         XCTAssertTrue(OctagonPerformSOSUpgrade(), "SOS upgrade should be on")
 
@@ -116,9 +120,9 @@ class OctagonSOSUpgradeTests: OctagonTestsBase {
         // Test that we tries to perform SOS upgrade once we unlock device again
         //
 
         // Test that we tries to perform SOS upgrade once we unlock device again
         //
 
-        self.putFakeKeyHierarchy(inCloudKit: self.manateeZoneID)
-        self.putSelfTLKShares(inCloudKit: self.manateeZoneID)
-        self.saveTLKMaterial(toKeychain: self.manateeZoneID)
+        self.putFakeKeyHierarchiesInCloudKit()
+        self.putSelfTLKSharesInCloudKit()
+        self.saveTLKMaterialToKeychain()
 
         XCTAssertTrue(OctagonPerformSOSUpgrade(), "SOS upgrade should be on")
 
 
         XCTAssertTrue(OctagonPerformSOSUpgrade(), "SOS upgrade should be on")
 
@@ -148,9 +152,9 @@ class OctagonSOSUpgradeTests: OctagonTestsBase {
 
     func testSOSUpgradeDuringNetworkOutage() throws {
         // Test that we tries to perform SOS upgrade after a bit after a failure
 
     func testSOSUpgradeDuringNetworkOutage() throws {
         // Test that we tries to perform SOS upgrade after a bit after a failure
-        self.putFakeKeyHierarchy(inCloudKit: self.manateeZoneID)
-        self.putSelfTLKShares(inCloudKit: self.manateeZoneID)
-        self.saveTLKMaterial(toKeychain: self.manateeZoneID)
+        self.putFakeKeyHierarchiesInCloudKit()
+        self.putSelfTLKSharesInCloudKit()
+        self.saveTLKMaterialToKeychain()
 
         XCTAssertTrue(OctagonPerformSOSUpgrade(), "SOS upgrade should be on")
 
 
         XCTAssertTrue(OctagonPerformSOSUpgrade(), "SOS upgrade should be on")
 
@@ -182,9 +186,9 @@ class OctagonSOSUpgradeTests: OctagonTestsBase {
 
     func testSOSUpgradeStopsIfSplitGraph() throws {
         // Test that we tries to perform SOS upgrade after a bit after a failure
 
     func testSOSUpgradeStopsIfSplitGraph() throws {
         // Test that we tries to perform SOS upgrade after a bit after a failure
-        self.putFakeKeyHierarchy(inCloudKit: self.manateeZoneID)
-        self.putSelfTLKShares(inCloudKit: self.manateeZoneID)
-        self.saveTLKMaterial(toKeychain: self.manateeZoneID)
+        self.putFakeKeyHierarchiesInCloudKit()
+        self.putSelfTLKSharesInCloudKit()
+        self.saveTLKMaterialToKeychain()
 
         XCTAssertTrue(OctagonPerformSOSUpgrade(), "SOS upgrade should be on")
 
 
         XCTAssertTrue(OctagonPerformSOSUpgrade(), "SOS upgrade should be on")
 
@@ -242,8 +246,8 @@ class OctagonSOSUpgradeTests: OctagonTestsBase {
     }
 
     func testSOSUpgradeWithNoTLKs() throws {
     }
 
     func testSOSUpgradeWithNoTLKs() throws {
-        self.putFakeKeyHierarchy(inCloudKit: self.manateeZoneID)
-        self.putFakeDeviceStatus(inCloudKit: self.manateeZoneID)
+        self.putFakeKeyHierarchiesInCloudKit()
+        self.putFakeDeviceStatusesInCloudKit()
 
         self.startCKAccountStatusMock()
 
 
         self.startCKAccountStatusMock()
 
@@ -262,11 +266,11 @@ class OctagonSOSUpgradeTests: OctagonTestsBase {
     func testsSOSUpgradeWithCKKSConflict() throws {
         // Right after CKKS fetches for the first time, insert a new key hierarchy into CloudKit
         self.silentFetchesAllowed = false
     func testsSOSUpgradeWithCKKSConflict() throws {
         // Right after CKKS fetches for the first time, insert a new key hierarchy into CloudKit
         self.silentFetchesAllowed = false
-        self.expectCKFetchAndRun(beforeFinished: {
-            self.putFakeKeyHierarchy(inCloudKit: self.manateeZoneID)
-            self.putFakeDeviceStatus(inCloudKit: self.manateeZoneID)
+        self.expectCKFetchAndRun {
+            self.putFakeKeyHierarchiesInCloudKit()
+            self.putFakeDeviceStatusesInCloudKit()
             self.silentFetchesAllowed = true
             self.silentFetchesAllowed = true
-        })
+        }
 
         self.startCKAccountStatusMock()
 
 
         self.startCKAccountStatusMock()
 
@@ -283,7 +287,7 @@ class OctagonSOSUpgradeTests: OctagonTestsBase {
     }
 
     func testSOSJoin() throws {
     }
 
     func testSOSJoin() throws {
-        if(!OctagonPerformSOSUpgrade()) {
+        if !OctagonPerformSOSUpgrade() {
             return
         }
         self.startCKAccountStatusMock()
             return
         }
         self.startCKAccountStatusMock()
@@ -360,9 +364,9 @@ class OctagonSOSUpgradeTests: OctagonTestsBase {
 
     func testSOSJoinUponNotificationOfPreapproval() throws {
         // Peer 1 becomes SOS+Octagon
 
     func testSOSJoinUponNotificationOfPreapproval() throws {
         // Peer 1 becomes SOS+Octagon
-        self.putFakeKeyHierarchy(inCloudKit: self.manateeZoneID)
-        self.putSelfTLKShares(inCloudKit: self.manateeZoneID)
-        self.saveTLKMaterial(toKeychain: self.manateeZoneID)
+        self.putFakeKeyHierarchiesInCloudKit()
+        self.putSelfTLKSharesInCloudKit()
+        self.saveTLKMaterialToKeychain()
 
         XCTAssertTrue(OctagonPerformSOSUpgrade(), "SOS upgrade should be on")
 
 
         XCTAssertTrue(OctagonPerformSOSUpgrade(), "SOS upgrade should be on")
 
@@ -379,7 +383,7 @@ class OctagonSOSUpgradeTests: OctagonTestsBase {
 
         assertAllCKKSViews(enter: SecCKKSZoneKeyStateReady, within: 10 * NSEC_PER_SEC)
 
 
         assertAllCKKSViews(enter: SecCKKSZoneKeyStateReady, within: 10 * NSEC_PER_SEC)
 
-        // Peer 2 attempts to join via preapprovalh
+        // Peer 2 attempts to join via preapproval
         let peer2SOSMockPeer = self.createSOSPeer(peerID: "peer2ID")
         let peer2contextID = "peer2"
         let peer2mockSOS = CKKSMockSOSPresentAdapter(selfPeer: peer2SOSMockPeer, trustedPeers: self.mockSOSAdapter.allPeers(), essential: false)
         let peer2SOSMockPeer = self.createSOSPeer(peerID: "peer2ID")
         let peer2contextID = "peer2"
         let peer2mockSOS = CKKSMockSOSPresentAdapter(selfPeer: peer2SOSMockPeer, trustedPeers: self.mockSOSAdapter.allPeers(), essential: false)
@@ -433,9 +437,9 @@ class OctagonSOSUpgradeTests: OctagonTestsBase {
 
     func testSOSJoinUponNotificationOfPreapprovalRetry() throws {
         // Peer 1 becomes SOS+Octagon
 
     func testSOSJoinUponNotificationOfPreapprovalRetry() throws {
         // Peer 1 becomes SOS+Octagon
-        self.putFakeKeyHierarchy(inCloudKit: self.manateeZoneID!)
-        self.putSelfTLKShares(inCloudKit: self.manateeZoneID!)
-        self.saveTLKMaterial(toKeychain: self.manateeZoneID!)
+        self.putFakeKeyHierarchiesInCloudKit()
+        self.putSelfTLKSharesInCloudKit()
+        self.saveTLKMaterialToKeychain()
 
         XCTAssertTrue(OctagonPerformSOSUpgrade(), "SOS upgrade should be on")
 
 
         XCTAssertTrue(OctagonPerformSOSUpgrade(), "SOS upgrade should be on")
 
@@ -452,7 +456,7 @@ class OctagonSOSUpgradeTests: OctagonTestsBase {
 
         assertAllCKKSViews(enter: SecCKKSZoneKeyStateReady, within: 10 * NSEC_PER_SEC)
 
 
         assertAllCKKSViews(enter: SecCKKSZoneKeyStateReady, within: 10 * NSEC_PER_SEC)
 
-        // Peer 2 attempts to join via preapprovalh
+        // Peer 2 attempts to join via preapproval
         let peer2SOSMockPeer = self.createSOSPeer(peerID: "peer2ID")
         let peer2contextID = "peer2"
         let peer2mockSOS = CKKSMockSOSPresentAdapter(selfPeer: peer2SOSMockPeer, trustedPeers: self.mockSOSAdapter.allPeers(), essential: false)
         let peer2SOSMockPeer = self.createSOSPeer(peerID: "peer2ID")
         let peer2contextID = "peer2"
         let peer2mockSOS = CKKSMockSOSPresentAdapter(selfPeer: peer2SOSMockPeer, trustedPeers: self.mockSOSAdapter.allPeers(), essential: false)
@@ -517,9 +521,9 @@ class OctagonSOSUpgradeTests: OctagonTestsBase {
 
     func testSOSJoinUponNotificationOfPreapprovalRetryFail() throws {
         // Peer 1 becomes SOS+Octagon
 
     func testSOSJoinUponNotificationOfPreapprovalRetryFail() throws {
         // Peer 1 becomes SOS+Octagon
-        self.putFakeKeyHierarchy(inCloudKit: self.manateeZoneID!)
-        self.putSelfTLKShares(inCloudKit: self.manateeZoneID!)
-        self.saveTLKMaterial(toKeychain: self.manateeZoneID!)
+        self.putFakeKeyHierarchiesInCloudKit()
+        self.putSelfTLKSharesInCloudKit()
+        self.saveTLKMaterialToKeychain()
 
         XCTAssertTrue(OctagonPerformSOSUpgrade(), "SOS upgrade should be on")
 
 
         XCTAssertTrue(OctagonPerformSOSUpgrade(), "SOS upgrade should be on")
 
@@ -536,7 +540,7 @@ class OctagonSOSUpgradeTests: OctagonTestsBase {
 
         assertAllCKKSViews(enter: SecCKKSZoneKeyStateReady, within: 10 * NSEC_PER_SEC)
 
 
         assertAllCKKSViews(enter: SecCKKSZoneKeyStateReady, within: 10 * NSEC_PER_SEC)
 
-        // Peer 2 attempts to join via preapprovalh
+        // Peer 2 attempts to join via preapproval
         let peer2SOSMockPeer = self.createSOSPeer(peerID: "peer2ID")
         let peer2contextID = "peer2"
         let peer2mockSOS = CKKSMockSOSPresentAdapter(selfPeer: peer2SOSMockPeer, trustedPeers: self.mockSOSAdapter.allPeers(), essential: false)
         let peer2SOSMockPeer = self.createSOSPeer(peerID: "peer2ID")
         let peer2contextID = "peer2"
         let peer2mockSOS = CKKSMockSOSPresentAdapter(selfPeer: peer2SOSMockPeer, trustedPeers: self.mockSOSAdapter.allPeers(), essential: false)
@@ -658,6 +662,7 @@ class OctagonSOSUpgradeTests: OctagonTestsBase {
         self.cuttlefishContext.incompleteNotificationOfMachineIDListChange()
         self.wait(for: [updateTrustExpectation], timeout: 10)
 
         self.cuttlefishContext.incompleteNotificationOfMachineIDListChange()
         self.wait(for: [updateTrustExpectation], timeout: 10)
 
+        self.verifyDatabaseMocks()
         assertAllCKKSViews(enter: SecCKKSZoneKeyStateReady, within: 10 * NSEC_PER_SEC)
     }
 
         assertAllCKKSViews(enter: SecCKKSZoneKeyStateReady, within: 10 * NSEC_PER_SEC)
     }
 
@@ -694,8 +699,12 @@ class OctagonSOSUpgradeTests: OctagonTestsBase {
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateReady, within: 40 * NSEC_PER_SEC)
         self.assertConsidersSelfTrusted(context: self.cuttlefishContext)
 
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateReady, within: 40 * NSEC_PER_SEC)
         self.assertConsidersSelfTrusted(context: self.cuttlefishContext)
 
-        assertAllCKKSViews(enter: SecCKKSZoneKeyStateReady, within: 10 * NSEC_PER_SEC)
+        self.assertAllCKKSViews(enter: SecCKKSZoneKeyStateReady, within: 10 * NSEC_PER_SEC)
         self.verifyDatabaseMocks()
         self.verifyDatabaseMocks()
+
+        // Also, CKKS should be configured with the prevailing policy version
+        XCTAssertNotNil(self.injectedManager?.policy, "Should have given CKKS a TPPolicy during SOS upgrade")
+        XCTAssertEqual(self.injectedManager?.policy?.version, prevailingPolicyVersion, "Policy given to CKKS should be prevailing policy")
     }
 
     func testSOSDoNotAttemptUpgradeWhenPlatformDoesntSupport() throws {
     }
 
     func testSOSDoNotAttemptUpgradeWhenPlatformDoesntSupport() throws {
@@ -707,6 +716,9 @@ class OctagonSOSUpgradeTests: OctagonTestsBase {
         let everEnteredSOSUpgrade: CKKSCondition = self.cuttlefishContext.stateMachine.stateConditions[OctagonStateAttemptSOSUpgrade] as! CKKSCondition
 
         self.cuttlefishContext.startOctagonStateMachine()
         let everEnteredSOSUpgrade: CKKSCondition = self.cuttlefishContext.stateMachine.stateConditions[OctagonStateAttemptSOSUpgrade] as! CKKSCondition
 
         self.cuttlefishContext.startOctagonStateMachine()
+
+        // Cheat and even turn on CDP for the account
+        XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
         self.assertConsidersSelfUntrusted(context: self.cuttlefishContext)
 
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
         self.assertConsidersSelfUntrusted(context: self.cuttlefishContext)
 
@@ -727,9 +739,9 @@ class OctagonSOSUpgradeTests: OctagonTestsBase {
     }
 
     func testSosUpgradeAndReady() throws {
     }
 
     func testSosUpgradeAndReady() throws {
-        self.putFakeKeyHierarchy(inCloudKit: self.manateeZoneID)
-        self.putSelfTLKShares(inCloudKit: self.manateeZoneID)
-        self.saveTLKMaterial(toKeychain: self.manateeZoneID)
+        self.putFakeKeyHierarchiesInCloudKit()
+        self.putSelfTLKSharesInCloudKit()
+        self.saveTLKMaterialToKeychain()
 
         self.startCKAccountStatusMock()
 
 
         self.startCKAccountStatusMock()
 
@@ -743,7 +755,7 @@ class OctagonSOSUpgradeTests: OctagonTestsBase {
         self.mockSOSAdapter.circleStatus = SOSCCStatus(kSOSCCInCircle)
 
         let upgradeExpectation = self.expectation(description: "waitForOctagonUpgrade")
         self.mockSOSAdapter.circleStatus = SOSCCStatus(kSOSCCInCircle)
 
         let upgradeExpectation = self.expectation(description: "waitForOctagonUpgrade")
-        self.manager.wait(forOctagonUpgrade: OTCKContainerName, context: self.otcliqueContext.context ?? "defaultContext") { error in
+        self.manager.wait(forOctagonUpgrade: OTCKContainerName, context: self.otcliqueContext.context) { error in
             XCTAssertNil(error, "operation should not fail")
             upgradeExpectation.fulfill()
         }
             XCTAssertNil(error, "operation should not fail")
             upgradeExpectation.fulfill()
         }
@@ -803,7 +815,7 @@ class OctagonSOSUpgradeTests: OctagonTestsBase {
     }
 
     func testSOSJoinAndBottle() throws {
     }
 
     func testSOSJoinAndBottle() throws {
-        if(!OctagonPerformSOSUpgrade()) {
+        if !OctagonPerformSOSUpgrade() {
             return
         }
         self.startCKAccountStatusMock()
             return
         }
         self.startCKAccountStatusMock()
@@ -931,9 +943,9 @@ class OctagonSOSUpgradeTests: OctagonTestsBase {
     }
 
     func testSOSPeerUpdatePreapprovesNewPeer() throws {
     }
 
     func testSOSPeerUpdatePreapprovesNewPeer() throws {
-        self.putFakeKeyHierarchy(inCloudKit: self.manateeZoneID)
-        self.putSelfTLKShares(inCloudKit: self.manateeZoneID)
-        self.saveTLKMaterial(toKeychain: self.manateeZoneID)
+        self.putFakeKeyHierarchiesInCloudKit()
+        self.putSelfTLKSharesInCloudKit()
+        self.saveTLKMaterialToKeychain()
 
         self.startCKAccountStatusMock()
 
 
         self.startCKAccountStatusMock()
 
@@ -989,9 +1001,9 @@ class OctagonSOSUpgradeTests: OctagonTestsBase {
     }
 
     func testSOSPeerUpdateOnRestartAfterMissingNotification() throws {
     }
 
     func testSOSPeerUpdateOnRestartAfterMissingNotification() throws {
-        self.putFakeKeyHierarchy(inCloudKit: self.manateeZoneID)
-        self.putSelfTLKShares(inCloudKit: self.manateeZoneID)
-        self.saveTLKMaterial(toKeychain: self.manateeZoneID)
+        self.putFakeKeyHierarchiesInCloudKit()
+        self.putSelfTLKSharesInCloudKit()
+        self.saveTLKMaterialToKeychain()
 
         self.startCKAccountStatusMock()
 
 
         self.startCKAccountStatusMock()
 
@@ -1063,9 +1075,9 @@ class OctagonSOSUpgradeTests: OctagonTestsBase {
     }
 
     func testResetAndEstablishDoesNotReuploadSOSTLKShares() throws {
     }
 
     func testResetAndEstablishDoesNotReuploadSOSTLKShares() throws {
-        self.putFakeKeyHierarchy(inCloudKit: self.manateeZoneID)
-        self.putSelfTLKShares(inCloudKit: self.manateeZoneID)
-        self.saveTLKMaterial(toKeychain: self.manateeZoneID)
+        self.putFakeKeyHierarchiesInCloudKit()
+        self.putSelfTLKSharesInCloudKit()
+        self.saveTLKMaterialToKeychain()
 
         XCTAssertTrue(OctagonPerformSOSUpgrade(), "SOS upgrade should be on")
 
 
         XCTAssertTrue(OctagonPerformSOSUpgrade(), "SOS upgrade should be on")
 
@@ -1125,9 +1137,9 @@ class OctagonSOSUpgradeTests: OctagonTestsBase {
     }
 
     func testResetAndEstablishReusesSOSKeys() throws {
     }
 
     func testResetAndEstablishReusesSOSKeys() throws {
-        self.putFakeKeyHierarchy(inCloudKit: self.manateeZoneID)
-        self.putSelfTLKShares(inCloudKit: self.manateeZoneID)
-        self.saveTLKMaterial(toKeychain: self.manateeZoneID)
+        self.putFakeKeyHierarchiesInCloudKit()
+        self.putSelfTLKSharesInCloudKit()
+        self.saveTLKMaterialToKeychain()
 
         XCTAssertTrue(OctagonPerformSOSUpgrade(), "SOS upgrade should be on")
 
 
         XCTAssertTrue(OctagonPerformSOSUpgrade(), "SOS upgrade should be on")
 
@@ -1148,14 +1160,13 @@ class OctagonSOSUpgradeTests: OctagonTestsBase {
         let dumpExpectation = self.expectation(description: "dump callback occurs")
         var encryptionPubKey = Data()
         var signingPubKey = Data()
         let dumpExpectation = self.expectation(description: "dump callback occurs")
         var encryptionPubKey = Data()
         var signingPubKey = Data()
-        self.tphClient.dump(withContainer: self.cuttlefishContext.containerName, context: self.cuttlefishContext.contextID) {
-            dump, error in
+        self.tphClient.dump(withContainer: self.cuttlefishContext.containerName, context: self.cuttlefishContext.contextID) { dump, error in
             XCTAssertNil(error, "Should be no error dumping data")
             XCTAssertNotNil(dump, "dump should not be nil")
             XCTAssertNil(error, "Should be no error dumping data")
             XCTAssertNotNil(dump, "dump should not be nil")
-            let egoSelf = dump!["self"] as? Dictionary<String, AnyObject>
+            let egoSelf = dump!["self"] as? [String: AnyObject]
             XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
 
             XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
 
-            let permanentInfo = egoSelf!["permanentInfo"] as? Dictionary<String, AnyObject>
+            let permanentInfo = egoSelf!["permanentInfo"] as? [String: AnyObject]
             XCTAssertNotNil(permanentInfo, "should have a permanent info")
 
             let epk = permanentInfo!["encryption_pub_key"] as? Data
             XCTAssertNotNil(permanentInfo, "should have a permanent info")
 
             let epk = permanentInfo!["encryption_pub_key"] as? Data
@@ -1186,14 +1197,13 @@ class OctagonSOSUpgradeTests: OctagonTestsBase {
 
         // And check that the pub keys are equivalent
         let dumpResetExpectation = self.expectation(description: "dump callback occurs")
 
         // And check that the pub keys are equivalent
         let dumpResetExpectation = self.expectation(description: "dump callback occurs")
-        self.tphClient.dump(withContainer: self.cuttlefishContext.containerName, context: self.cuttlefishContext.contextID) {
-            dump, error in
+        self.tphClient.dump(withContainer: self.cuttlefishContext.containerName, context: self.cuttlefishContext.contextID) { dump, error in
             XCTAssertNil(error, "Should be no error dumping data")
             XCTAssertNotNil(dump, "dump should not be nil")
             XCTAssertNil(error, "Should be no error dumping data")
             XCTAssertNotNil(dump, "dump should not be nil")
-            let egoSelf = dump!["self"] as? Dictionary<String, AnyObject>
+            let egoSelf = dump!["self"] as? [String: AnyObject]
             XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
 
             XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
 
-            let permanentInfo = egoSelf!["permanentInfo"] as? Dictionary<String, AnyObject>
+            let permanentInfo = egoSelf!["permanentInfo"] as? [String: AnyObject]
             XCTAssertNotNil(permanentInfo, "should have a permanent info")
 
             let epk = permanentInfo!["encryption_pub_key"] as? Data
             XCTAssertNotNil(permanentInfo, "should have a permanent info")
 
             let epk = permanentInfo!["encryption_pub_key"] as? Data
@@ -1210,9 +1220,9 @@ class OctagonSOSUpgradeTests: OctagonTestsBase {
     }
 
     func testSOSUpgradeWithFailingAuthKit() throws {
     }
 
     func testSOSUpgradeWithFailingAuthKit() throws {
-        self.putFakeKeyHierarchy(inCloudKit: self.manateeZoneID)
-        self.putSelfTLKShares(inCloudKit: self.manateeZoneID)
-        self.saveTLKMaterial(toKeychain: self.manateeZoneID)
+        self.putFakeKeyHierarchiesInCloudKit()
+        self.putSelfTLKSharesInCloudKit()
+        self.saveTLKMaterialToKeychain()
 
         XCTAssertTrue(OctagonPerformSOSUpgrade(), "SOS upgrade should be on")
 
 
         XCTAssertTrue(OctagonPerformSOSUpgrade(), "SOS upgrade should be on")
 
@@ -1231,9 +1241,9 @@ class OctagonSOSUpgradeTests: OctagonTestsBase {
     }
 
     func testCliqueOctagonUpgrade () throws {
     }
 
     func testCliqueOctagonUpgrade () throws {
-        self.putFakeKeyHierarchy(inCloudKit: self.manateeZoneID)
-        self.putSelfTLKShares(inCloudKit: self.manateeZoneID)
-        self.saveTLKMaterial(toKeychain: self.manateeZoneID)
+        self.putFakeKeyHierarchiesInCloudKit()
+        self.putSelfTLKSharesInCloudKit()
+        self.saveTLKMaterialToKeychain()
         self.startCKAccountStatusMock()
         self.mockSOSAdapter.circleStatus = SOSCCStatus(kSOSCCInCircle)
 
         self.startCKAccountStatusMock()
         self.mockSOSAdapter.circleStatus = SOSCCStatus(kSOSCCInCircle)
 
@@ -1241,29 +1251,27 @@ class OctagonSOSUpgradeTests: OctagonTestsBase {
 
         OctagonSetPlatformSupportsSOS(true)
 
 
         OctagonSetPlatformSupportsSOS(true)
 
-        var clique: OTClique?
-        XCTAssertNoThrow(clique = try OTClique(contextData: self.otcliqueContext))
+        let clique = OTClique(contextData: self.otcliqueContext)
         XCTAssertNotNil(clique, "Clique should not be nil")
         XCTAssertNotNil(clique, "Clique should not be nil")
-        XCTAssertNoThrow(try clique!.waitForOctagonUpgrade(), "Upgrading should pass")
+        XCTAssertNoThrow(try clique.waitForOctagonUpgrade(), "Upgrading should pass")
 
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateReady, within: 10 * NSEC_PER_SEC)
         self.assertConsidersSelfTrusted(context: self.cuttlefishContext)
     }
 
     func testCliqueOctagonUpgradeFail () throws {
 
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateReady, within: 10 * NSEC_PER_SEC)
         self.assertConsidersSelfTrusted(context: self.cuttlefishContext)
     }
 
     func testCliqueOctagonUpgradeFail () throws {
-        self.putFakeKeyHierarchy(inCloudKit: self.manateeZoneID)
-        self.putSelfTLKShares(inCloudKit: self.manateeZoneID)
-        self.saveTLKMaterial(toKeychain: self.manateeZoneID)
+        self.putFakeKeyHierarchiesInCloudKit()
+        self.putSelfTLKSharesInCloudKit()
+        self.saveTLKMaterialToKeychain()
         self.startCKAccountStatusMock()
 
         XCTAssertTrue(OctagonPerformSOSUpgrade(), "SOS upgrade should be on")
 
         OctagonSetPlatformSupportsSOS(true)
 
         self.startCKAccountStatusMock()
 
         XCTAssertTrue(OctagonPerformSOSUpgrade(), "SOS upgrade should be on")
 
         OctagonSetPlatformSupportsSOS(true)
 
-        var clique: OTClique?
-        XCTAssertNoThrow(clique = try OTClique(contextData: self.otcliqueContext))
+        let clique = OTClique(contextData: self.otcliqueContext)
         XCTAssertNotNil(clique, "Clique should not be nil")
         XCTAssertNotNil(clique, "Clique should not be nil")
-        XCTAssertThrowsError(try clique!.waitForOctagonUpgrade(), "Upgrading should fail")
+        XCTAssertThrowsError(try clique.waitForOctagonUpgrade(), "Upgrading should fail")
 
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
         self.assertConsidersSelfUntrusted(context: self.cuttlefishContext)
 
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
         self.assertConsidersSelfUntrusted(context: self.cuttlefishContext)
@@ -1323,7 +1331,6 @@ class OctagonSOSUpgradeTests: OctagonTestsBase {
         XCTAssertTrue(self.fakeCuttlefishServer.assertCuttlefishState(FakeCuttlefishAssertion(peer: peer1ID, opinion: .trustsByPreapproval, target: peer2ID)),
                       "peer 1 should trust peer 3 by preapproval")
 
         XCTAssertTrue(self.fakeCuttlefishServer.assertCuttlefishState(FakeCuttlefishAssertion(peer: peer1ID, opinion: .trustsByPreapproval, target: peer2ID)),
                       "peer 1 should trust peer 3 by preapproval")
 
-
         XCTAssertTrue(self.fakeCuttlefishServer.assertCuttlefishState(FakeCuttlefishAssertion(peer: peer2ID, opinion: .trusts, target: peer1ID)),
                       "peer 2 should trust peer 1")
         XCTAssertTrue(self.fakeCuttlefishServer.assertCuttlefishState(FakeCuttlefishAssertion(peer: peer2ID, opinion: .trusts, target: peer3ID)),
         XCTAssertTrue(self.fakeCuttlefishServer.assertCuttlefishState(FakeCuttlefishAssertion(peer: peer2ID, opinion: .trusts, target: peer1ID)),
                       "peer 2 should trust peer 1")
         XCTAssertTrue(self.fakeCuttlefishServer.assertCuttlefishState(FakeCuttlefishAssertion(peer: peer2ID, opinion: .trusts, target: peer3ID)),
@@ -1359,7 +1366,7 @@ class OctagonSOSUpgradeTests: OctagonTestsBase {
 
         // And if peer3 decides to reupgrade, but it shouldn't: there's no potentially-trusted peer that preapproves it
         let upgradeExpectation = self.expectation(description: "sosUpgrade call returns")
 
         // And if peer3 decides to reupgrade, but it shouldn't: there's no potentially-trusted peer that preapproves it
         let upgradeExpectation = self.expectation(description: "sosUpgrade call returns")
-        peer3.attemptSOSUpgrade() { error in
+        peer3.attemptSOSUpgrade { error in
             XCTAssertNotNil(error, "should be an error performing an SOS upgrade (the second time)")
             upgradeExpectation.fulfill()
         }
             XCTAssertNotNil(error, "should be an error performing an SOS upgrade (the second time)")
             upgradeExpectation.fulfill()
         }
@@ -1371,7 +1378,7 @@ class OctagonSOSUpgradeTests: OctagonTestsBase {
 
         // And "wait for upgrade" does something reasonable too
         let upgradeWaitExpectation = self.expectation(description: "sosWaitForUpgrade call returns")
 
         // And "wait for upgrade" does something reasonable too
         let upgradeWaitExpectation = self.expectation(description: "sosWaitForUpgrade call returns")
-        peer3.waitForOctagonUpgrade() { error in
+        peer3.waitForOctagonUpgrade { error in
             XCTAssertNotNil(error, "should be an error waiting for an SOS upgrade (the second time)")
             upgradeWaitExpectation.fulfill()
         }
             XCTAssertNotNil(error, "should be an error waiting for an SOS upgrade (the second time)")
             upgradeWaitExpectation.fulfill()
         }
index 850a15b1291ee53fcebd410eaa65b594b5a94492..64bca2d79edfe29885692672f940a78b0bf17679 100644 (file)
@@ -12,6 +12,7 @@
 #import "KeychainCircle/KCJoiningRequestSession+Internal.h"
 #import "KeychainCircle/KCJoiningAcceptSession+Internal.h"
 #import <KeychainCircle/KCJoiningMessages.h>
 #import "KeychainCircle/KCJoiningRequestSession+Internal.h"
 #import "KeychainCircle/KCJoiningAcceptSession+Internal.h"
 #import <KeychainCircle/KCJoiningMessages.h>
+#import <KeychainCircle/PairingChannel.h>
 
 #import <TrustedPeers/TrustedPeers.h>
 #import <TrustedPeers/TPHash.h>
 
 #import <TrustedPeers/TrustedPeers.h>
 #import <TrustedPeers/TPHash.h>
@@ -59,6 +60,7 @@
 #import "keychain/ckks/CKKSTLKShare.h"
 #import "keychain/ckks/CKKSAnalytics.h"
 #import "keychain/ckks/CloudKitCategories.h"
 #import "keychain/ckks/CKKSTLKShare.h"
 #import "keychain/ckks/CKKSAnalytics.h"
 #import "keychain/ckks/CloudKitCategories.h"
+#import "keychain/ckks/CKKSCurrentKeyPointer.h"
 
 #import "keychain/ot/OctagonControlServer.h"
 
 
 #import "keychain/ot/OctagonControlServer.h"
 
 #import "keychain/ot/categories/OTAccountMetadataClassC+KeychainSupport.h"
 #import "keychain/ot/categories/OctagonEscrowRecoverer.h"
 
 #import "keychain/ot/categories/OTAccountMetadataClassC+KeychainSupport.h"
 #import "keychain/ot/categories/OctagonEscrowRecoverer.h"
 
+#import "KeychainCircle/generated_source/KCInitialMessageData.h"
+#import "keychain/ot/proto/generated_source/OTPairingMessage.h"
+#import "keychain/ot/proto/generated_source/OTSponsorToApplicantRound1M2.h"
+#import "keychain/ot/proto/generated_source/OTApplicantToSponsorRound2M1.h"
+#import "keychain/ot/proto/generated_source/OTSponsorToApplicantRound2M2.h"
+
 #import "keychain/otctl/OTControlCLI.h"
 
 // Also, we're going to need whatever TPH needs.
 #import "keychain/otctl/OTControlCLI.h"
 
 // Also, we're going to need whatever TPH needs.
index de42b91099047aecba03756fc516cb1927254cd5..5e17bae512d9c7b13dca050c78ec377017a671ba 100644 (file)
@@ -51,10 +51,12 @@ class OTMockDeviceInfoAdapter: OTDeviceInformationAdapter {
 }
 
 class OTMockAuthKitAdapter: OTAuthKitAdapter {
 }
 
 class OTMockAuthKitAdapter: OTAuthKitAdapter {
+
     // A nil altDSID means 'no authkit account'
     var altDSID: String?
 
     var hsa2: Bool
     // A nil altDSID means 'no authkit account'
     var altDSID: String?
 
     var hsa2: Bool
+    var isDemoAccount: Bool
 
     let currentMachineID: String
     var otherDevices: Set<String>
 
     let currentMachineID: String
     var otherDevices: Set<String>
@@ -74,6 +76,7 @@ class OTMockAuthKitAdapter: OTAuthKitAdapter {
         self.otherDevices = otherDevices
         self.excludeDevices = Set()
         self.hsa2 = true
         self.otherDevices = otherDevices
         self.excludeDevices = Set()
         self.hsa2 = true
+        self.isDemoAccount = false
         self.listeners = CKKSListenerCollection<OTAuthKitAdapterNotifier>(name: "test-authkit")
     }
 
         self.listeners = CKKSListenerCollection<OTAuthKitAdapterNotifier>(name: "test-authkit")
     }
 
@@ -90,6 +93,9 @@ class OTMockAuthKitAdapter: OTAuthKitAdapter {
         // TODO: do we need to examine altDSID here?
         return self.hsa2
     }
         // TODO: do we need to examine altDSID here?
         return self.hsa2
     }
+    func accountIsDemoAccount(_ error: NSErrorPointer) -> Bool {
+        return self.isDemoAccount
+    }
 
     func machineID() throws -> String {
         // TODO: throw if !accountPresent
 
     func machineID() throws -> String {
         // TODO: throw if !accountPresent
@@ -213,7 +219,7 @@ class OTMockSecEscrowRequest: NSObject, SecEscrowRequestable {
     }
 }
 
     }
 }
 
-class OctagonTestsBase: CloudKitKeychainSyncingTestsBase {
+class OctagonTestsBase: CloudKitKeychainSyncingMockXCTest {
 
     var tmpPath: String!
     var tmpURL: URL!
 
     var tmpPath: String!
     var tmpURL: URL!
@@ -223,7 +229,10 @@ class OctagonTestsBase: CloudKitKeychainSyncingTestsBase {
 
     var otcliqueContext: OTConfigurationContext!
 
 
     var otcliqueContext: OTConfigurationContext!
 
+    var intendedCKKSZones: Set<CKRecordZone.ID>!
     var manateeZoneID: CKRecordZone.ID!
     var manateeZoneID: CKRecordZone.ID!
+    var limitedPeersAllowedZoneID: CKRecordZone.ID!
+
     var fakeCuttlefishServer: FakeCuttlefishServer!
     var fakeCuttlefishCreator: FakeCuttlefishInvocableCreator!
     var tphClient: Client!
     var fakeCuttlefishServer: FakeCuttlefishServer!
     var fakeCuttlefishCreator: FakeCuttlefishInvocableCreator!
     var tphClient: Client!
@@ -240,6 +249,7 @@ class OctagonTestsBase: CloudKitKeychainSyncingTestsBase {
 
     var otControl: OTControl!
     var otXPCProxy: ProxyXPCConnection!
 
     var otControl: OTControl!
     var otXPCProxy: ProxyXPCConnection!
+
     var otControlEntitlementBearer: FakeOTControlEntitlementBearer!
     var otControlEntitlementChecker: OTControlProtocol!
     var otControlCLI: OTControlCLI!
     var otControlEntitlementBearer: FakeOTControlEntitlementBearer!
     var otControlEntitlementChecker: OTControlProtocol!
     var otControlCLI: OTControlCLI!
@@ -248,8 +258,6 @@ class OctagonTestsBase: CloudKitKeychainSyncingTestsBase {
 
     override static func setUp() {
         UserDefaults.standard.register(defaults: ["com.apple.CoreData.ConcurrencyDebug": 1])
 
     override static func setUp() {
         UserDefaults.standard.register(defaults: ["com.apple.CoreData.ConcurrencyDebug": 1])
-        OctagonSetShouldPerformInitialization(true)
-        SecCKKSEnable()
 
         super.setUp()
 
 
         super.setUp()
 
@@ -261,38 +269,46 @@ class OctagonTestsBase: CloudKitKeychainSyncingTestsBase {
         // Set the global bool to TRUE
         OctagonSetIsEnabled(true)
 
         // Set the global bool to TRUE
         OctagonSetIsEnabled(true)
 
+        // Set the global CKKS bool to TRUE
+        SecCKKSEnable()
+
         // Until we can reasonably run SOS in xctest, this must be off. Note that this makes our tests
         // not accurately reproduce what a real device would do.
         OctagonSetPlatformSupportsSOS(false)
 
         // Until we can reasonably run SOS in xctest, this must be off. Note that this makes our tests
         // not accurately reproduce what a real device would do.
         OctagonSetPlatformSupportsSOS(false)
 
-        // Tell SecDb not to initialize the manager (as we haven't made our fake one yet).
-        // Each test is responsible for initialization, to allow for pre-test setup
-        OctagonSetShouldPerformInitialization(false)
-
-        let actualDeviceAdapter = OTDeviceInformationActualAdapter()
-        self.mockDeviceInfo = OTMockDeviceInfoAdapter(modelID: actualDeviceAdapter.modelID(),
-                                                      deviceName: actualDeviceAdapter.deviceName(),
-                                                      serialNumber: NSUUID().uuidString,
-                                                      osVersion: actualDeviceAdapter.osVersion())
-
-        super.setUp()
-
-        // Octagon must initialize the views
-        self.automaticallyBeginCKKSViewCloudKitOperation = false
+        if self.mockDeviceInfo == nil {
+            let actualDeviceAdapter = OTDeviceInformationActualAdapter()
+            self.mockDeviceInfo = OTMockDeviceInfoAdapter(modelID: actualDeviceAdapter.modelID(),
+                                                          deviceName: actualDeviceAdapter.deviceName(),
+                                                          serialNumber: NSUUID().uuidString,
+                                                          osVersion: actualDeviceAdapter.osVersion())
+        }
 
 
-        // The CKKStests use the "keychain" view heavily, but that causes issues in Octagon as it isn't in the Octagon policy.
-        // Replace it with the Manatee view, unless you're on an appleTV: in that case, make it the LimitedPeersAllowed view
-        self.injectedManager!.clearAllViews()
-        #if !os(tvOS)
-        self.ckksViews = NSMutableSet(array: [self.injectedManager!.findOrCreateView("Manatee")])
         self.manateeZoneID = CKRecordZone.ID(zoneName: "Manatee")
         self.manateeZoneID = CKRecordZone.ID(zoneName: "Manatee")
-        #else
-        self.ckksViews = NSMutableSet(array: [self.injectedManager!.findOrCreateView("LimitedPeersAllowed")])
-        self.manateeZoneID = CKRecordZone.ID(zoneName: "LimitedPeersAllowed")
-        #endif
+        self.limitedPeersAllowedZoneID = CKRecordZone.ID(zoneName: "LimitedPeersAllowed")
+
+        // We'll use this set to limit the views that CKKS brings up in the tests (mostly for performance reasons)
+        if self.intendedCKKSZones == nil {
+            if self.mockDeviceInfo.mockModelID.contains("AppleTV") {
+                self.intendedCKKSZones = Set([
+                    self.limitedPeersAllowedZoneID!,
+                ])
+            } else {
+                self.intendedCKKSZones = Set([
+                    self.limitedPeersAllowedZoneID!,
+                    self.manateeZoneID!,
+                ])
+            }
+        }
+        self.ckksZones = NSMutableSet(array: Array(self.intendedCKKSZones))
 
 
-        self.zones!.removeAllObjects()
-        self.zones![self.manateeZoneID!] = FakeCKZone(zone: self.manateeZoneID!)
+        // Create the zones, so we can inject them into our fake cuttlefish server
+        self.zones = [:]
+        self.keys = [:]
+        self.ckksZones.forEach { obj in
+            let zoneID = obj as! CKRecordZone.ID
+            self.zones![zoneID] = FakeCKZone(zone: zoneID)
+        }
 
         // Asserting a type on self.zones seems to duplicate the dictionary, but not deep-copy the contents
         // We'll use them as NSMutableDictionaries, I guess
 
         // Asserting a type on self.zones seems to duplicate the dictionary, but not deep-copy the contents
         // We'll use them as NSMutableDictionaries, I guess
@@ -303,34 +319,46 @@ class OctagonTestsBase: CloudKitKeychainSyncingTestsBase {
 
         self.otFollowUpController = OTMockFollowUpController()
 
 
         self.otFollowUpController = OTMockFollowUpController()
 
-        // Octagon requires the self peer keys to be persisted in the keychain
-        saveToKeychain(keyPair: self.mockSOSAdapter.selfPeer.signingKey, label: "com.apple.securityd.sossigningkey")
-        saveToKeychain(keyPair: self.mockSOSAdapter.selfPeer.encryptionKey, label: "com.apple.securityd.sosencryptionkey")
-
         self.mockAuthKit = OTMockAuthKitAdapter(altDSID: UUID().uuidString, machineID: "MACHINE1", otherDevices: ["MACHINE2", "MACHINE3"])
         self.mockAuthKit2 = OTMockAuthKitAdapter(altDSID: self.mockAuthKit.altDSID, machineID: "MACHINE2", otherDevices: ["MACHINE1", "MACHINE3"])
         self.mockAuthKit3 = OTMockAuthKitAdapter(altDSID: self.mockAuthKit.altDSID, machineID: "MACHINE3", otherDevices: ["MACHINE1", "MACHINE2"])
 
         self.mockAuthKit = OTMockAuthKitAdapter(altDSID: UUID().uuidString, machineID: "MACHINE1", otherDevices: ["MACHINE2", "MACHINE3"])
         self.mockAuthKit2 = OTMockAuthKitAdapter(altDSID: self.mockAuthKit.altDSID, machineID: "MACHINE2", otherDevices: ["MACHINE1", "MACHINE3"])
         self.mockAuthKit3 = OTMockAuthKitAdapter(altDSID: self.mockAuthKit.altDSID, machineID: "MACHINE3", otherDevices: ["MACHINE1", "MACHINE2"])
 
-        // By default, not in SOS when test starts
-        // And under octagon, SOS trust is not essential
-        self.mockSOSAdapter.circleStatus = SOSCCStatus(kSOSCCNotInCircle)
-        self.mockSOSAdapter.essential = false
-
         let tphInterface = TrustedPeersHelperSetupProtocol(NSXPCInterface(with: TrustedPeersHelperProtocol.self))
         self.tphXPCProxy = ProxyXPCConnection(self.tphClient!, interface: tphInterface)
 
         let tphInterface = TrustedPeersHelperSetupProtocol(NSXPCInterface(with: TrustedPeersHelperProtocol.self))
         self.tphXPCProxy = ProxyXPCConnection(self.tphClient!, interface: tphInterface)
 
-        self.manager = OTManager(sosAdapter: self.mockSOSAdapter,
-                                 authKitAdapter: self.mockAuthKit,
-                                 deviceInformationAdapter: self.mockDeviceInfo,
-                                 apsConnectionClass: FakeAPSConnection.self,
-                                 escrowRequestClass: OTMockSecEscrowRequest.self,
-                                 loggerClass: OTMockLogger.self,
-                                 lockStateTracker: self.lockStateTracker,
-                                 accountStateTracker: self.accountStateTracker,
-                                 cuttlefishXPCConnection: tphXPCProxy.connection(),
-                                 cdpd: self.otFollowUpController)
+        self.disableConfigureCKKSViewManagerWithViews = true
+
+        // Now, perform further test initialization (including temporary keychain creation)
+        super.setUp()
+
+        self.injectedManager!.setSyncingViewsAllowList(Set(self.intendedCKKSZones!.map { $0.zoneName }))
+
+        // Ensure we've made the CKKSKeychainView objects we're interested in
+        self.ckksZones.forEach { obj in
+            let zoneID = obj as! CKRecordZone.ID
+            self.ckksViews.add(self.injectedManager!.findOrCreateView(zoneID.zoneName))
+        }
+
+        // Double-check that the world of zones and views looks like what we expect
+        XCTAssertEqual(self.ckksZones as? Set<CKRecordZone.ID>, self.intendedCKKSZones, "should still operate on our expected zones only")
+        XCTAssertEqual(self.ckksZones.count, self.ckksViews.count, "Should have the same number of views as expected zones")
+        XCTAssertEqual(self.ckksZones.count, self.zones!.count, "Should have the same number of fake zones as expected zones")
+
+        XCTAssertEqual(Set(self.ckksViews.map { ($0 as! CKKSKeychainView).zoneName }),
+                       Set(self.ckksZones.map { ($0 as! CKRecordZone.ID).zoneName }),
+                       "ckksViews should match ckksZones")
+
+        // Octagon must initialize the views
+        self.automaticallyBeginCKKSViewCloudKitOperation = false
 
 
-        OTManager.resetManager(true, to: self.manager)
+        // Octagon requires the self peer keys to be persisted in the keychain
+        saveToKeychain(keyPair: self.mockSOSAdapter.selfPeer.signingKey, label: "com.apple.securityd.sossigningkey")
+        saveToKeychain(keyPair: self.mockSOSAdapter.selfPeer.encryptionKey, label: "com.apple.securityd.sosencryptionkey")
+
+        // By default, not in SOS when test starts
+        // And under octagon, SOS trust is not essential
+        self.mockSOSAdapter.circleStatus = SOSCCStatus(kSOSCCNotInCircle)
+        self.mockSOSAdapter.essential = false
 
         self.cuttlefishContext = self.manager.context(forContainerName: OTCKContainerName, contextID: OTDefaultContext)
 
 
         self.cuttlefishContext = self.manager.context(forContainerName: OTCKContainerName, contextID: OTDefaultContext)
 
@@ -339,7 +367,6 @@ class OctagonTestsBase: CloudKitKeychainSyncingTestsBase {
 
         self.otXPCProxy = ProxyXPCConnection(self.otControlEntitlementChecker!, interface: OTSetupControlProtocol(NSXPCInterface(with: OTControlProtocol.self)))
         self.otControl = OTControl(connection: self.otXPCProxy.connection(), sync: true)
 
         self.otXPCProxy = ProxyXPCConnection(self.otControlEntitlementChecker!, interface: OTSetupControlProtocol(NSXPCInterface(with: OTControlProtocol.self)))
         self.otControl = OTControl(connection: self.otXPCProxy.connection(), sync: true)
-
         self.otControlCLI = OTControlCLI(otControl: self.otControl)
 
         self.otcliqueContext = OTConfigurationContext()
         self.otControlCLI = OTControlCLI(otControl: self.otControl)
 
         self.otcliqueContext = OTConfigurationContext()
@@ -349,7 +376,24 @@ class OctagonTestsBase: CloudKitKeychainSyncingTestsBase {
         self.otcliqueContext.otControl = self.otControl
     }
 
         self.otcliqueContext.otControl = self.otControl
     }
 
+    override func setUpOTManager(_ cloudKitClassDependencies: CKKSCloudKitClassDependencies) -> OTManager {
+        self.manager = OTManager(sosAdapter: self.mockSOSAdapter,
+                                 authKitAdapter: self.mockAuthKit,
+                                 deviceInformationAdapter: self.mockDeviceInfo,
+                                 apsConnectionClass: FakeAPSConnection.self,
+                                 escrowRequestClass: OTMockSecEscrowRequest.self,
+                                 loggerClass: OTMockLogger.self,
+                                 lockStateTracker: CKKSLockStateTracker(),
+                                 cloudKitClassDependencies: cloudKitClassDependencies,
+                                 cuttlefishXPCConnection: tphXPCProxy.connection(),
+                                 cdpd: self.otFollowUpController)
+        return self.manager
+    }
+
     override func tearDown() {
     override func tearDown() {
+        // Just to be sure
+        self.verifyDatabaseMocks()
+
         let statusExpectation = self.expectation(description: "status callback occurs")
         self.cuttlefishContext.rpcStatus { _, _ in
             statusExpectation.fulfill()
         let statusExpectation = self.expectation(description: "status callback occurs")
         self.cuttlefishContext.rpcStatus { _, _ in
             statusExpectation.fulfill()
@@ -368,15 +412,54 @@ class OctagonTestsBase: CloudKitKeychainSyncingTestsBase {
 
         XCTAssertTrue(self.manager.allContextsPause(10 * NSEC_PER_SEC), "All cuttlefish contexts should pause")
 
 
         XCTAssertTrue(self.manager.allContextsPause(10 * NSEC_PER_SEC), "All cuttlefish contexts should pause")
 
+        self.tphClient.containerMap.removeAllContainers()
+
         super.tearDown()
         super.tearDown()
+
+        self.cuttlefishContext = nil
+        self.manager = nil
+
+        self.otcliqueContext = nil
+
+        self.manateeZoneID = nil
+        self.limitedPeersAllowedZoneID = nil
+
+        self.fakeCuttlefishServer = nil
+        self.fakeCuttlefishCreator = nil
+        self.tphClient = nil
+        self.tphXPCProxy = nil
+
+        self.accountAltDSID = nil
+
+        self.mockAuthKit = nil
+        self.mockAuthKit2 = nil
+        self.mockAuthKit3 = nil
+
+        self.mockDeviceInfo = nil
+
+        self.otControl = nil
+        self.otXPCProxy = nil
+
+        self.otControlEntitlementBearer = nil
+        self.otControlEntitlementChecker = nil
+        self.otControlCLI = nil
+
+        self.otFollowUpController = nil
     }
 
     override func managedViewList() -> Set<String> {
     }
 
     override func managedViewList() -> Set<String> {
-        #if !os(tvOS)
-        return Set(["Manatee"])
-        #else
-        return Set(["LimitedPeersAllowed"])
-        #endif
+        if(self.overrideUseCKKSViewsFromPolicy) {
+            let viewNames = self.ckksZones.map { ($0 as! CKRecordZone.ID).zoneName }
+            return Set(viewNames)
+        } else {
+            // We only want to return the 'base' set of views here; not the full set.
+            // This should go away when CKKS4A is enabled...
+            #if !os(tvOS)
+            return Set(["LimitedPeersAllowed", "Manatee"])
+            #else
+            return Set(["LimitedPeersAllowed"])
+            #endif
+        }
     }
 
     func fetchEgoPeerID() -> String {
     }
 
     func fetchEgoPeerID() -> String {
@@ -392,11 +475,12 @@ class OctagonTestsBase: CloudKitKeychainSyncingTestsBase {
         return ret
     }
 
         return ret
     }
 
-    func setAllowListToCurrentAuthKit(container: String, context: String) {
+    func setAllowListToCurrentAuthKit(container: String, context: String, accountIsDemo: Bool) {
         let allowListExpectation = self.expectation(description: "set allow list callback occurs")
         let allowListExpectation = self.expectation(description: "set allow list callback occurs")
+        let honorIDMSListChanges = accountIsDemo ? false : true
         self.tphClient.setAllowedMachineIDsWithContainer(container,
                                                          context: context,
         self.tphClient.setAllowedMachineIDsWithContainer(container,
                                                          context: context,
-                                                         allowedMachineIDs: self.mockAuthKit.currentDeviceList()) { _, error in
+                                                         allowedMachineIDs: self.mockAuthKit.currentDeviceList(), honorIDMSListChanges: honorIDMSListChanges) { _, error in
             XCTAssertNil(error, "Should be no error setting allow list")
             allowListExpectation.fulfill()
         }
             XCTAssertNil(error, "Should be no error setting allow list")
             allowListExpectation.fulfill()
         }
@@ -418,7 +502,7 @@ class OctagonTestsBase: CloudKitKeychainSyncingTestsBase {
 
     func assertEnters(context: OTCuttlefishContext, state: String, within: UInt64) {
         XCTAssertEqual(0, (context.stateMachine.stateConditions[state] as! CKKSCondition).wait(within), "State machine should enter '\(state)'")
 
     func assertEnters(context: OTCuttlefishContext, state: String, within: UInt64) {
         XCTAssertEqual(0, (context.stateMachine.stateConditions[state] as! CKKSCondition).wait(within), "State machine should enter '\(state)'")
-        if(state == OctagonStateReady || state == OctagonStateUntrusted) {
+        if state == OctagonStateReady || state == OctagonStateUntrusted {
             XCTAssertEqual(0, context.stateMachine.paused.wait(10 * NSEC_PER_SEC), "State machine should pause soon")
         }
     }
             XCTAssertEqual(0, context.stateMachine.paused.wait(10 * NSEC_PER_SEC), "State machine should pause soon")
         }
     }
@@ -430,6 +514,9 @@ class OctagonTestsBase: CloudKitKeychainSyncingTestsBase {
     func assertConsidersSelfTrusted(context: OTCuttlefishContext, isLocked: Bool = false) {
         XCTAssertEqual(context.currentMemoizedTrustState(), .TRUSTED, "Trust state (for \(context)) should be trusted")
 
     func assertConsidersSelfTrusted(context: OTCuttlefishContext, isLocked: Bool = false) {
         XCTAssertEqual(context.currentMemoizedTrustState(), .TRUSTED, "Trust state (for \(context)) should be trusted")
 
+        let accountMetadata = try! context.accountMetadataStore.loadOrCreateAccountMetadata()
+        XCTAssertEqual(accountMetadata.attemptedJoin, .ATTEMPTED, "Should have 'attempted a join'")
+
         let statusexpectation = self.expectation(description: "trust status returns")
         let configuration = OTOperationConfiguration()
         configuration.timeoutWaitForCKAccount = 500 * NSEC_PER_MSEC
         let statusexpectation = self.expectation(description: "trust status returns")
         let configuration = OTOperationConfiguration()
         configuration.timeoutWaitForCKAccount = 500 * NSEC_PER_MSEC
@@ -462,7 +549,7 @@ class OctagonTestsBase: CloudKitKeychainSyncingTestsBase {
         cliqueConfiguration.dsid = "1234"
         cliqueConfiguration.altDSID = self.mockAuthKit.altDSID!
         cliqueConfiguration.otControl = self.otControl
         cliqueConfiguration.dsid = "1234"
         cliqueConfiguration.altDSID = self.mockAuthKit.altDSID!
         cliqueConfiguration.otControl = self.otControl
-        let otclique = try! OTClique(contextData: cliqueConfiguration)
+        let otclique = OTClique(contextData: cliqueConfiguration)
 
         let status = otclique.fetchStatus(nil)
         XCTAssertEqual(status, .in, "OTClique API should return (trusted)")
 
         let status = otclique.fetchStatus(nil)
         XCTAssertEqual(status, .in, "OTClique API should return (trusted)")
@@ -490,6 +577,21 @@ class OctagonTestsBase: CloudKitKeychainSyncingTestsBase {
         self.wait(for: [statusexpectation], timeout: 10)
     }
 
         self.wait(for: [statusexpectation], timeout: 10)
     }
 
+    func assertConsidersSelfWaitingForCDP(context: OTCuttlefishContext) {
+        XCTAssertEqual(context.currentMemoizedTrustState(), .UNKNOWN, "Trust state (for \(context)) should be unknown")
+        let statusexpectation = self.expectation(description: "trust status returns")
+        let configuration = OTOperationConfiguration()
+        configuration.timeoutWaitForCKAccount = 500 * NSEC_PER_MSEC
+        context.rpcTrustStatus(configuration) { egoStatus, _, _, _, _ in
+            // TODO: separate 'untrusted' and 'no trusted peers for account yet'
+            XCTAssertTrue([.notIn, .absent].contains(egoStatus), "Self peer (for \(context)) should be distrusted or absent")
+            statusexpectation.fulfill()
+        }
+        self.wait(for: [statusexpectation], timeout: 10)
+
+        XCTAssertEqual(self.fetchCDPStatus(context: context), .disabled, "CDP status should be 'disabled'")
+    }
+
     func assertAccountAvailable(context: OTCuttlefishContext) {
         XCTAssertEqual(context.currentMemoizedAccountState(), .ACCOUNT_AVAILABLE, "Account state (for \(context)) should be 'available''")
     }
     func assertAccountAvailable(context: OTCuttlefishContext) {
         XCTAssertEqual(context.currentMemoizedAccountState(), .ACCOUNT_AVAILABLE, "Account state (for \(context)) should be 'available''")
     }
@@ -498,6 +600,18 @@ class OctagonTestsBase: CloudKitKeychainSyncingTestsBase {
         XCTAssertEqual(context.currentMemoizedAccountState(), .NO_ACCOUNT, "Account state (for \(context)) should be no account")
     }
 
         XCTAssertEqual(context.currentMemoizedAccountState(), .NO_ACCOUNT, "Account state (for \(context)) should be no account")
     }
 
+    func fetchCDPStatus(context: OTCuttlefishContext) -> OTCDPStatus {
+        let config = OTConfigurationContext()
+        config.context = context.contextID
+        config.otControl = self.otControl
+
+        var error: NSError?
+        let cdpstatus = OTClique.getCDPStatus(config, error: &error)
+        XCTAssertNil(error, "Should have no error fetching CDP status")
+
+        return cdpstatus
+    }
+
     func assertTrusts(context: OTCuttlefishContext, includedPeerIDCount: Int, excludedPeerIDCount: Int) {
         let dumpCallback = self.expectation(description: "dump callback occurs")
         self.tphClient.dumpEgoPeer(withContainer: context.containerName, context: context.contextID) { _, _, _, dynamicInfo, error in
     func assertTrusts(context: OTCuttlefishContext, includedPeerIDCount: Int, excludedPeerIDCount: Int) {
         let dumpCallback = self.expectation(description: "dump callback occurs")
         self.tphClient.dumpEgoPeer(withContainer: context.containerName, context: context.contextID) { _, _, _, dynamicInfo, error in
@@ -514,6 +628,9 @@ class OctagonTestsBase: CloudKitKeychainSyncingTestsBase {
     func restartCKKSViews() {
         let viewNames = self.ckksViews.map { ($0 as! CKKSKeychainView).zoneName }
         self.ckksViews.removeAllObjects()
     func restartCKKSViews() {
         let viewNames = self.ckksViews.map { ($0 as! CKKSKeychainView).zoneName }
         self.ckksViews.removeAllObjects()
+
+        self.injectedManager!.resetSyncingPolicy()
+
         for view in viewNames {
             self.ckksViews.add(self.injectedManager!.restartZone(view))
         }
         for view in viewNames {
             self.ckksViews.add(self.injectedManager!.restartZone(view))
         }
@@ -525,25 +642,61 @@ class OctagonTestsBase: CloudKitKeychainSyncingTestsBase {
         }
     }
 
         }
     }
 
+    func sendAllCKKSViewsZoneChanged() {
+        for expectedView in self.ckksZones {
+            let view = self.injectedManager?.findView((expectedView as! CKRecordZone.ID).zoneName)
+            XCTAssertNotNil(view, "Should have a view '\(expectedView)'")
+            view!.notifyZoneChange(nil)
+        }
+    }
+
     func assert(ckks: CKKSKeychainView, enters: String, within: UInt64) {
         XCTAssertEqual(0, (ckks.keyHierarchyConditions[enters] as! CKKSCondition).wait(within), "CKKS state machine should enter '\(enters)' (currently '\(ckks.keyHierarchyState)')")
     }
 
     func assertAllCKKSViews(enter: String, within: UInt64) {
     func assert(ckks: CKKSKeychainView, enters: String, within: UInt64) {
         XCTAssertEqual(0, (ckks.keyHierarchyConditions[enters] as! CKKSCondition).wait(within), "CKKS state machine should enter '\(enters)' (currently '\(ckks.keyHierarchyState)')")
     }
 
     func assertAllCKKSViews(enter: String, within: UInt64) {
-        for view in self.ckksViews {
-            self.assert(ckks: view as! CKKSKeychainView, enters: enter, within: within)
+        for expectedView in self.ckksZones {
+            let view = self.injectedManager?.findView((expectedView as! CKRecordZone.ID).zoneName)
+            XCTAssertNotNil(view, "Should have a view '\(expectedView)'")
+            self.assert(ckks: view!, enters: enter, within: within)
         }
     }
 
     func assertAllCKKSViewsUploadKeyHierarchy(tlkShares: UInt) {
         }
     }
 
     func assertAllCKKSViewsUploadKeyHierarchy(tlkShares: UInt) {
-        self.ckksViews.forEach { view in
-            self.expectCKModifyKeyRecords(3, currentKeyPointerRecords: 3, tlkShareRecords: tlkShares, zoneID: (view as! CKKSKeychainView).zoneID)
+        for expectedView in self.ckksZones {
+            let view = self.injectedManager?.findView((expectedView as! CKRecordZone.ID).zoneName)
+            XCTAssertNotNil(view, "Should have a view '\(expectedView)'")
+            self.expectCKModifyKeyRecords(3, currentKeyPointerRecords: 3, tlkShareRecords: tlkShares, zoneID: view!.zoneID)
         }
     }
 
     func assertAllCKKSViewsUpload(tlkShares: UInt) {
         }
     }
 
     func assertAllCKKSViewsUpload(tlkShares: UInt) {
-        self.ckksViews.forEach { view in
-            self.expectCKModifyKeyRecords(0, currentKeyPointerRecords: 0, tlkShareRecords: tlkShares, zoneID: (view as! CKKSKeychainView).zoneID)
+        for expectedView in self.ckksZones {
+            self.expectCKModifyKeyRecords(0, currentKeyPointerRecords: 0, tlkShareRecords: tlkShares, zoneID: expectedView as! CKRecordZone.ID)
+        }
+    }
+
+    func putFakeKeyHierarchiesInCloudKit() {
+        self.ckksZones.forEach { zone in
+            self.putFakeKeyHierarchy(inCloudKit: zone as! CKRecordZone.ID)
+        }
+    }
+
+    func putSelfTLKSharesInCloudKit() {
+        self.ckksZones.forEach { zone in
+            self.putSelfTLKShares(inCloudKit: zone as! CKRecordZone.ID)
+        }
+    }
+
+    func putFakeDeviceStatusesInCloudKit() {
+        self.ckksZones.forEach { zone in
+            self.putFakeDeviceStatus(inCloudKit: zone as! CKRecordZone.ID)
+        }
+    }
+
+    func saveTLKMaterialToKeychain() {
+        self.ckksZones.forEach { zone in
+            self.saveTLKMaterial(toKeychain: zone as! CKRecordZone.ID)
         }
     }
 
         }
     }
 
@@ -557,11 +710,40 @@ class OctagonTestsBase: CloudKitKeychainSyncingTestsBase {
         self.wait(for: [resetExpectation], timeout: 30)
     }
 
         self.wait(for: [resetExpectation], timeout: 30)
     }
 
-    func putSelfTLKShareInCloudKit(context: OTCuttlefishContext, zoneID: CKRecordZone.ID) throws {
-        let accountMetadata = try context.accountMetadataStore.loadOrCreateAccountMetadata()
-        let peerKeys: OctagonSelfPeerKeys = try loadEgoKeysSync(peerID: accountMetadata.peerID)
+    func putSelfTLKSharesInCloudKit(context: OTCuttlefishContext) throws {
+        try self.ckksZones.forEach { zone in
+            try self.putTLKShareInCloudKit(to: context, from: context, zoneID: zone as! CKRecordZone.ID)
+        }
+    }
+
+    func putAllTLKSharesInCloudKit(to: OTCuttlefishContext, from: OTCuttlefishContext) throws {
+        try self.ckksZones.forEach { zone in
+            try self.putTLKShareInCloudKit(to: to, from: from, zoneID: zone as! CKRecordZone.ID)
+        }
+    }
+
+    func putTLKShareInCloudKit(to: OTCuttlefishContext, from: OTCuttlefishContext, zoneID: CKRecordZone.ID) throws {
+        let fromAccountMetadata = try from.accountMetadataStore.loadOrCreateAccountMetadata()
+        let fromPeerKeys: OctagonSelfPeerKeys = try loadEgoKeysSync(peerID: fromAccountMetadata.peerID)
+
+        let toAccountMetadata = try to.accountMetadataStore.loadOrCreateAccountMetadata()
+        let toPeerKeys: OctagonSelfPeerKeys = try loadEgoKeysSync(peerID: toAccountMetadata.peerID)
+
+        let zoneKeys = self.keys![zoneID] as! ZoneKeys
+        self.putTLKShare(inCloudKit: zoneKeys.tlk!, from: fromPeerKeys, to: toPeerKeys, zoneID: zoneID)
+    }
+
+    func putRecoveryKeyTLKSharesInCloudKit(recoveryKey: String, salt: String) throws {
+        try self.ckksZones.forEach { zone in
+            try self.putRecoveryKeyTLKShareInCloudKit(recoveryKey: recoveryKey, salt: salt, zoneID: zone as! CKRecordZone.ID)
+        }
+    }
+
+    func putRecoveryKeyTLKShareInCloudKit(recoveryKey: String, salt: String, zoneID: CKRecordZone.ID) throws {
+        let recoveryKeys = try RecoveryKey(recoveryKeyString: recoveryKey, recoverySalt: salt)
+
         let zoneKeys = self.keys![zoneID] as! ZoneKeys
         let zoneKeys = self.keys![zoneID] as! ZoneKeys
-        self.putTLKShare(inCloudKit: zoneKeys.tlk!, from: peerKeys, to: peerKeys, zoneID: zoneID)
+        self.putTLKShare(inCloudKit: zoneKeys.tlk!, from: recoveryKeys.peerKeys, to: recoveryKeys.peerKeys, zoneID: zoneID)
     }
 
     func assertSelfTLKSharesInCloudKit(context: OTCuttlefishContext) {
     }
 
     func assertSelfTLKSharesInCloudKit(context: OTCuttlefishContext) {
@@ -587,19 +769,25 @@ class OctagonTestsBase: CloudKitKeychainSyncingTestsBase {
         }
     }
 
         }
     }
 
-    func assertTLKShareInCloudKit(receiverPeerID: String, senderPeerID: String, zoneID: CKRecordZone.ID) throws {
-        let zone = self.zones![zoneID] as! FakeCKZone
+    func tlkShareInCloudKit(receiverPeerID: String, senderPeerID: String, zoneID: CKRecordZone.ID) throws -> Bool {
+        guard let zone = self.zones![zoneID] as? FakeCKZone else {
+            return false
+        }
 
         let tlkShares = zone.currentDatabase.allValues.filter { ($0 as? CKRecord)?.recordType == SecCKRecordTLKShareType }.map { CKKSTLKShareRecord(ckRecord: $0 as! CKRecord) }
 
         for share in tlkShares {
             if share.share.receiverPeerID == receiverPeerID && share.senderPeerID == senderPeerID {
                 // Found one!
 
         let tlkShares = zone.currentDatabase.allValues.filter { ($0 as? CKRecord)?.recordType == SecCKRecordTLKShareType }.map { CKKSTLKShareRecord(ckRecord: $0 as! CKRecord) }
 
         for share in tlkShares {
             if share.share.receiverPeerID == receiverPeerID && share.senderPeerID == senderPeerID {
                 // Found one!
-                return
+                return true
             }
         }
             }
         }
+        return false
+    }
 
 
-        XCTFail("Unable to find a TLKShare for peer ID \(String(describing: receiverPeerID)) sent by \(String(describing: senderPeerID))")
+    func assertTLKShareInCloudKit(receiverPeerID: String, senderPeerID: String, zoneID: CKRecordZone.ID) throws {
+        XCTAssertTrue(try self.tlkShareInCloudKit(receiverPeerID: receiverPeerID, senderPeerID: senderPeerID, zoneID: zoneID),
+                      "Should have found a TLKShare for peerID \(String(describing: receiverPeerID)) sent by \(String(describing: senderPeerID)) for \(zoneID)")
     }
 
     func assertMIDList(context: OTCuttlefishContext,
     }
 
     func assertMIDList(context: OTCuttlefishContext,
@@ -615,7 +803,7 @@ class OctagonTestsBase: CloudKitKeychainSyncingTestsBase {
         }
 
         for allowedMID in allowed {
         }
 
         for allowedMID in allowed {
-            var err : NSError?
+            var err: NSError?
             let onList = context.machineID(onMemoizedList: allowedMID, error: &err)
 
             XCTAssertNil(err, "Should not have failed determining memoized list state")
             let onList = context.machineID(onMemoizedList: allowedMID, error: &err)
 
             XCTAssertNil(err, "Should not have failed determining memoized list state")
@@ -630,7 +818,7 @@ class OctagonTestsBase: CloudKitKeychainSyncingTestsBase {
         }
 
         for disallowedMID in disallowed {
         }
 
         for disallowedMID in disallowed {
-            var err : NSError?
+            var err: NSError?
             let onList = context.machineID(onMemoizedList: disallowedMID, error: &err)
 
             XCTAssertNil(err, "Should not have failed determining memoized list state")
             let onList = context.machineID(onMemoizedList: disallowedMID, error: &err)
 
             XCTAssertNil(err, "Should not have failed determining memoized list state")
@@ -659,7 +847,7 @@ class OctagonTestsBase: CloudKitKeychainSyncingTestsBase {
         }
 
         if result != nil {
         }
 
         if result != nil {
-            if let dictionary = result as? Dictionary<CFString, Any> {
+            if let dictionary = result as? [CFString: Any] {
                 secret = dictionary[kSecValueData] as? Data
             } else {
                 throw ContainerError.failedToLoadSecretDueToType
                 secret = dictionary[kSecValueData] as? Data
             } else {
                 throw ContainerError.failedToLoadSecretDueToType
@@ -767,26 +955,43 @@ class OctagonTestsBase: CloudKitKeychainSyncingTestsBase {
                                     deviceInformationAdapter: self.makeInitiatorDeviceInfoAdapter())
     }
 
                                     deviceInformationAdapter: self.makeInitiatorDeviceInfoAdapter())
     }
 
-    @discardableResult func assertResetAndBecomeTrustedInDefaultContext() -> String {
-        self.cuttlefishContext.startOctagonStateMachine()
-        self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
+    @discardableResult
+    func assertResetAndBecomeTrustedInDefaultContext() -> String {
+        let ret = self.assertResetAndBecomeTrusted(context: self.cuttlefishContext)
+
+        // And, the default context runs CKKS:
+        self.assertAllCKKSViews(enter: SecCKKSZoneKeyStateReady, within: 10 * NSEC_PER_SEC)
+        self.verifyDatabaseMocks()
+        self.assertSelfTLKSharesInCloudKit(context: self.cuttlefishContext)
+
+        return ret
+    }
+
+    @discardableResult
+    func assertResetAndBecomeTrusted(context: OTCuttlefishContext) -> String {
+        context.startOctagonStateMachine()
+        XCTAssertNoThrow(try context.setCDPEnabled())
+        self.assertEnters(context: context, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
         do {
 
         do {
-            let clique = try OTClique.newFriends(withContextData: self.otcliqueContext, resetReason: .testGenerated)
+            let arguments = OTConfigurationContext()
+            arguments.altDSID = try context.authKitAdapter.primaryiCloudAccountAltDSID()
+            arguments.context = context.contextID
+            arguments.otControl = self.otControl
+
+            let clique = try OTClique.newFriends(withContextData: arguments, resetReason: .testGenerated)
             XCTAssertNotNil(clique, "Clique should not be nil")
         } catch {
             XCTFail("Shouldn't have errored making new friends: \(error)")
         }
 
             XCTAssertNotNil(clique, "Clique should not be nil")
         } catch {
             XCTFail("Shouldn't have errored making new friends: \(error)")
         }
 
-        // Now, we should be in 'ready'
-        self.assertEnters(context: self.cuttlefishContext, state: OctagonStateReady, within: 10 * NSEC_PER_SEC)
-        self.assertConsidersSelfTrusted(context: self.cuttlefishContext)
-        assertAllCKKSViews(enter: SecCKKSZoneKeyStateReady, within: 10 * NSEC_PER_SEC)
-        self.verifyDatabaseMocks()
-        self.assertSelfTLKSharesInCloudKit(context: self.cuttlefishContext)
-        return try! self.cuttlefishContext.accountMetadataStore.getEgoPeerID()
+        self.assertEnters(context: context, state: OctagonStateReady, within: 10 * NSEC_PER_SEC)
+        self.assertConsidersSelfTrusted(context: context)
+
+        return try! context.accountMetadataStore.getEgoPeerID()
     }
 
     }
 
+    @discardableResult
     func assertJoinViaEscrowRecovery(joiningContext: OTCuttlefishContext, sponsor: OTCuttlefishContext) -> String {
         do {
             joiningContext.startOctagonStateMachine()
     func assertJoinViaEscrowRecovery(joiningContext: OTCuttlefishContext, sponsor: OTCuttlefishContext) -> String {
         do {
             joiningContext.startOctagonStateMachine()
@@ -820,14 +1025,37 @@ class OctagonTestsBase: CloudKitKeychainSyncingTestsBase {
         }
     }
 
         }
     }
 
+    func assertJoinViaProximitySetup(joiningContext: OTCuttlefishContext, sponsor: OTCuttlefishContext) -> String {
+        do {
+            joiningContext.startOctagonStateMachine()
+            self.assertEnters(context: joiningContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
+
+            let (sponsorPairingChannel, initiatorPairingChannel) = self.setupPairingChannels(initiator: joiningContext, sponsor: sponsor)
+
+            let firstInitiatorPacket = self.sendPairingExpectingReply(channel: initiatorPairingChannel, packet: nil, reason: "session initialization")
+            let sponsorEpochPacket = self.sendPairingExpectingReply(channel: sponsorPairingChannel, packet: firstInitiatorPacket, reason: "sponsor epoch")
+            let initiatorIdentityPacket = self.sendPairingExpectingReply(channel: initiatorPairingChannel, packet: sponsorEpochPacket, reason: "initiator identity")
+            let sponsorVoucherPacket = self.sendPairingExpectingCompletionAndReply(channel: sponsorPairingChannel, packet: initiatorIdentityPacket, reason: "sponsor voucher")
+            self.sendPairingExpectingCompletion(channel: initiatorPairingChannel, packet: sponsorVoucherPacket, reason: "initiator completion")
+
+            self.assertEnters(context: joiningContext, state: OctagonStateReady, within: 10 * NSEC_PER_SEC)
+            self.assertConsidersSelfTrusted(context: joiningContext)
+
+            return try joiningContext.accountMetadataStore.getEgoPeerID()
+        } catch {
+            XCTFail("Expected no error: \(error)")
+            return "failed"
+        }
+    }
+
     func assertSelfOSVersion(_ osVersion: String) {
 
         let statusExpectation = self.expectation(description: "status callback occurs")
     func assertSelfOSVersion(_ osVersion: String) {
 
         let statusExpectation = self.expectation(description: "status callback occurs")
-        self.tphClient.dumpEgoPeer(withContainer: self.cuttlefishContext.containerName, context: self.cuttlefishContext.contextID, reply: { _, _, stableInfo, _, error in
+        self.tphClient.dumpEgoPeer(withContainer: self.cuttlefishContext.containerName, context: self.cuttlefishContext.contextID) { _, _, stableInfo, _, error in
             XCTAssertNil(error, "should be no error dumping ego peer")
             XCTAssertEqual(stableInfo?.osVersion, osVersion, "os version should be as required")
             statusExpectation.fulfill()
             XCTAssertNil(error, "should be no error dumping ego peer")
             XCTAssertEqual(stableInfo?.osVersion, osVersion, "os version should be as required")
             statusExpectation.fulfill()
-        })
+        }
 
         self.wait(for: [statusExpectation], timeout: 2)
     }
 
         self.wait(for: [statusExpectation], timeout: 2)
     }
@@ -845,14 +1073,14 @@ class OctagonTests: OctagonTestsBase {
                           machineID: "asdf",
                           bottleSalt: "123456789",
                           bottleID: UUID().uuidString,
                           machineID: "asdf",
                           bottleSalt: "123456789",
                           bottleID: UUID().uuidString,
-                          modelID: "asdf",
+                          modelID: "iPhone9,1",
                           deviceName: "asdf",
                           serialNumber: "1234",
                           osVersion: "asdf",
                           policyVersion: nil,
                           policySecrets: nil,
                           signingPrivKeyPersistentRef: nil,
                           deviceName: "asdf",
                           serialNumber: "1234",
                           osVersion: "asdf",
                           policyVersion: nil,
                           policySecrets: nil,
                           signingPrivKeyPersistentRef: nil,
-                          encPrivKeyPersistentRef: nil) { peerID, permanentInfo, permanentInfoSig, stableInfo, stableInfoSig, error in
+                          encPrivKeyPersistentRef: nil) { peerID, permanentInfo, permanentInfoSig, stableInfo, stableInfoSig, _, _, error in
                             XCTAssertNil(error, "Should be no error preparing identity")
                             XCTAssertNotNil(peerID, "Should be a peer ID")
                             XCTAssertNotNil(permanentInfo, "Should have a permenent info")
                             XCTAssertNil(error, "Should be no error preparing identity")
                             XCTAssertNotNil(peerID, "Should be a peer ID")
                             XCTAssertNotNil(permanentInfo, "Should have a permenent info")
@@ -879,14 +1107,14 @@ class OctagonTests: OctagonTestsBase {
                           machineID: "asdf",
                           bottleSalt: "123456789",
                           bottleID: UUID().uuidString,
                           machineID: "asdf",
                           bottleSalt: "123456789",
                           bottleID: UUID().uuidString,
-                          modelID: "asdf",
+                          modelID: "iPhone9,1",
                           deviceName: "asdf",
                           serialNumber: "1234",
                           osVersion: "asdf",
                           policyVersion: nil,
                           policySecrets: nil,
                           signingPrivKeyPersistentRef: nil,
                           deviceName: "asdf",
                           serialNumber: "1234",
                           osVersion: "asdf",
                           policyVersion: nil,
                           policySecrets: nil,
                           signingPrivKeyPersistentRef: nil,
-                          encPrivKeyPersistentRef: nil) { peerID, permanentInfo, permanentInfoSig, stableInfo, stableInfoSig, error in
+                          encPrivKeyPersistentRef: nil) { peerID, permanentInfo, permanentInfoSig, stableInfo, stableInfoSig, _, _, error in
                             XCTAssertNil(error, "Should be no error preparing identity")
                             XCTAssertNotNil(peerID, "Should be a peer ID")
                             XCTAssertNotNil(permanentInfo, "Should have a permenent info")
                             XCTAssertNil(error, "Should be no error preparing identity")
                             XCTAssertNotNil(peerID, "Should be a peer ID")
                             XCTAssertNotNil(permanentInfo, "Should have a permenent info")
@@ -907,14 +1135,14 @@ class OctagonTests: OctagonTestsBase {
                           machineID: "asdf",
                           bottleSalt: "123456789",
                           bottleID: UUID().uuidString,
                           machineID: "asdf",
                           bottleSalt: "123456789",
                           bottleID: UUID().uuidString,
-                          modelID: "asdf",
+                          modelID: "iPhone9,1",
                           deviceName: "asdf",
                           serialNumber: "1234",
                           osVersion: "asdf",
                           policyVersion: nil,
                           policySecrets: nil,
                           signingPrivKeyPersistentRef: nil,
                           deviceName: "asdf",
                           serialNumber: "1234",
                           osVersion: "asdf",
                           policyVersion: nil,
                           policySecrets: nil,
                           signingPrivKeyPersistentRef: nil,
-                          encPrivKeyPersistentRef: nil) { peerID, permanentInfo, permanentInfoSig, stableInfo, stableInfoSig, error in
+                          encPrivKeyPersistentRef: nil) { peerID, permanentInfo, permanentInfoSig, stableInfo, stableInfoSig, _, _, error in
                             XCTAssertNil(error, "Should be no error preparing identity")
                             XCTAssertNotNil(peerID, "Should be a peer ID")
                             XCTAssertNotNil(permanentInfo, "Should have a permenent info")
                             XCTAssertNil(error, "Should be no error preparing identity")
                             XCTAssertNotNil(peerID, "Should be a peer ID")
                             XCTAssertNotNil(permanentInfo, "Should have a permenent info")
@@ -930,88 +1158,30 @@ class OctagonTests: OctagonTestsBase {
         self.wait(for: [tphPrepareExpectation2], timeout: 10)
     }
 
         self.wait(for: [tphPrepareExpectation2], timeout: 10)
     }
 
-    func testAccountSave() throws {
-        let contextName = OTDefaultContext
-        let containerName = OTCKContainerName
-
-        self.startCKAccountStatusMock()
-
-        // Before resetAndEstablish, there shouldn't be any stored account state
-               XCTAssertThrowsError(try OTAccountMetadataClassC.loadFromKeychain(forContainer: containerName, contextID: contextName), "Before doing anything, loading a non-existent account state should fail")
-
-        let resetAndEstablishExpectation = self.expectation(description: "resetAndEstablish callback occurs")
-        self.manager.resetAndEstablish(containerName,
-                                       context: contextName,
-                                       altDSID: "new altDSID",
-                                       resetReason: .testGenerated) { resetError in
-                                        XCTAssertNil(resetError, "Should be no error calling resetAndEstablish")
-                                        resetAndEstablishExpectation.fulfill()
-        }
-
-        self.wait(for: [resetAndEstablishExpectation], timeout: 10)
-        self.assertEnters(context: self.cuttlefishContext, state: OctagonStateReady, within: 10 * NSEC_PER_SEC)
-
-        let selfPeerID = try self.cuttlefishContext.accountMetadataStore.loadOrCreateAccountMetadata().peerID
-
-        // After resetAndEstablish, you should be able to see the persisted account state
-        do {
-            let accountState = try OTAccountMetadataClassC.loadFromKeychain(forContainer: containerName, contextID: contextName)
-            XCTAssertEqual(selfPeerID, accountState.peerID, "Saved account state should have the same peer ID that prepare returned")
-        } catch {
-            XCTFail("error loading account state: \(error)")
-        }
-    }
-
-    func testLoadToNoAccount() throws {
-        // No CloudKit account, either
-        self.accountStatus = .noAccount
-        self.startCKAccountStatusMock()
-
-        // With no identity and AuthKit reporting no iCloud account, Octagon should go directly into 'no account'
-        self.mockAuthKit.altDSID = nil
-
-        let asyncExpectation = self.expectation(description: "dispatch works")
-        let quiescentExpectation = self.expectation(description: "quiescence has been determined")
-        DispatchQueue.global(qos: .userInitiated).async { [weak self] in
-            asyncExpectation.fulfill()
-
-            let c = self!.cuttlefishContext.stateMachine.paused
-            XCTAssertEqual(0, c.wait(10 * NSEC_PER_SEC), "State machine should become quiescent")
-            quiescentExpectation.fulfill()
-        }
-        // Wait for the block above to fire before continuing
-        self.wait(for: [asyncExpectation], timeout: 10)
-
-        // Run initialization, like the real secd will do
-        OctagonInitialize()
-
-        self.assertEnters(context: self.cuttlefishContext, state: OctagonStateNoAccount, within: 10 * NSEC_PER_SEC)
-        XCTAssertTrue(self.cuttlefishContext.stateMachine.isPaused(), "State machine should be stopped")
-        self.assertNoAccount(context: self.cuttlefishContext)
-
-        XCTAssertEqual(0, self.cuttlefishContext.stateMachine.paused.wait(10 * NSEC_PER_SEC), "State machine should be quiescent")
-
-        self.wait(for: [quiescentExpectation], timeout: 10)
-
-        // CKKS should also be logged out, since Octagon believes there's no account
-        assertAllCKKSViews(enter: SecCKKSZoneKeyStateLoggedOut, within: 10 * NSEC_PER_SEC)
-    }
-
     func testLoadToUntrusted() throws {
         self.startCKAccountStatusMock()
 
     func testLoadToUntrusted() throws {
         self.startCKAccountStatusMock()
 
+        // Tell SOS that it is absent, so we don't enable CDP on bringup
+        self.mockSOSAdapter.circleStatus = SOSCCStatus(kSOSCCCircleAbsent)
+
         // With no identity but AuthKit reporting an existing iCloud account, Octagon should go directly into 'untrusted'
         // With no identity but AuthKit reporting an existing iCloud account, Octagon should go directly into 'untrusted'
-        OctagonInitialize()
+        self.cuttlefishContext.startOctagonStateMachine()
+
+        self.assertEnters(context: self.cuttlefishContext, state: OctagonStateWaitForCDP, within: 10 * NSEC_PER_SEC)
+        XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
 
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
         self.assertConsidersSelfUntrusted(context: self.cuttlefishContext)
 
 
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
         self.assertConsidersSelfUntrusted(context: self.cuttlefishContext)
 
-        assertAllCKKSViews(enter: SecCKKSZoneKeyStateWaitForTLKCreation, within: 10 * NSEC_PER_SEC)
+        self.assertAllCKKSViews(enter: SecCKKSZoneKeyStateWaitForTLKCreation, within: 10 * NSEC_PER_SEC)
     }
 
     func testLoadToUntrustedIfTPHHasPreparedIdentityOnly() throws {
         self.startCKAccountStatusMock()
 
     }
 
     func testLoadToUntrustedIfTPHHasPreparedIdentityOnly() throws {
         self.startCKAccountStatusMock()
 
+        // Tell SOS that it is absent, so we don't enable CDP on bringup
+        self.mockSOSAdapter.circleStatus = SOSCCStatus(kSOSCCCircleAbsent)
+
         // Prepare an identity, then pretend like securityd thought it was in the right account
         let containerName = OTCKContainerName
         let contextName = OTDefaultContext
         // Prepare an identity, then pretend like securityd thought it was in the right account
         let containerName = OTCKContainerName
         let contextName = OTDefaultContext
@@ -1024,14 +1194,14 @@ class OctagonTests: OctagonTestsBase {
                           machineID: "asdf",
                           bottleSalt: "123456789",
                           bottleID: UUID().uuidString,
                           machineID: "asdf",
                           bottleSalt: "123456789",
                           bottleID: UUID().uuidString,
-                          modelID: "asdf",
+                          modelID: "iPhone9,1",
                           deviceName: "asdf",
                           serialNumber: "1234",
                           osVersion: "asdf",
                           policyVersion: nil,
                           policySecrets: nil,
                           signingPrivKeyPersistentRef: nil,
                           deviceName: "asdf",
                           serialNumber: "1234",
                           osVersion: "asdf",
                           policyVersion: nil,
                           policySecrets: nil,
                           signingPrivKeyPersistentRef: nil,
-                          encPrivKeyPersistentRef: nil) { peerID, permanentInfo, permanentInfoSig, stableInfo, stableInfoSig, error in
+                          encPrivKeyPersistentRef: nil) { peerID, permanentInfo, permanentInfoSig, stableInfo, stableInfoSig, _, _, error in
                             XCTAssertNil(error, "Should be no error preparing identity")
                             XCTAssertNotNil(peerID, "Should be a peer ID")
                             XCTAssertNotNil(permanentInfo, "Should have a permenent info")
                             XCTAssertNil(error, "Should be no error preparing identity")
                             XCTAssertNotNil(peerID, "Should be a peer ID")
                             XCTAssertNotNil(permanentInfo, "Should have a permenent info")
@@ -1051,88 +1221,26 @@ class OctagonTests: OctagonTestsBase {
 
         XCTAssertNoThrow(try account.saveToKeychain(forContainer: containerName, contextID: contextName), "Should be no error saving fake account metadata")
 
 
         XCTAssertNoThrow(try account.saveToKeychain(forContainer: containerName, contextID: contextName), "Should be no error saving fake account metadata")
 
-        OctagonInitialize()
-
-        self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
-        self.assertConsidersSelfUntrusted(context: self.cuttlefishContext)
-
-        // CKKS should be waiting for assistance
-        assertAllCKKSViews(enter: SecCKKSZoneKeyStateWaitForTLKCreation, within: 10 * NSEC_PER_SEC)
-    }
-
-    func testSignIn() throws {
-        self.startCKAccountStatusMock()
-
-        // Device is signed out
-        self.mockAuthKit.altDSID = nil
-        self.mockAuthKit.hsa2 = false
-
-        // With no account, Octagon should go directly into 'NoAccount'
         self.cuttlefishContext.startOctagonStateMachine()
         self.cuttlefishContext.startOctagonStateMachine()
-        self.assertEnters(context: self.cuttlefishContext, state: OctagonStateNoAccount, within: 10 * NSEC_PER_SEC)
 
 
-        // Sign in occurs
-        let newAltDSID = UUID().uuidString
-        self.mockAuthKit.altDSID = newAltDSID
-        self.mockAuthKit.hsa2 = true
-        XCTAssertNoThrow(try self.cuttlefishContext.accountAvailable(newAltDSID), "Sign-in shouldn't error")
-
-        // Octagon should go into 'untrusted'
-        self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 100 * NSEC_PER_SEC)
-        self.assertConsidersSelfUntrusted(context: self.cuttlefishContext)
-
-        assertAllCKKSViews(enter: SecCKKSZoneKeyStateWaitForTLKCreation, within: 10 * NSEC_PER_SEC)
-
-        // On sign-out, octagon should go back to 'no account'
-        self.mockAuthKit.altDSID = nil
-        XCTAssertNoThrow(try self.cuttlefishContext.accountNoLongerAvailable(), "sign-out shouldn't error")
-        self.assertEnters(context: self.cuttlefishContext, state: OctagonStateNoAccount, within: 10 * NSEC_PER_SEC)
-        self.assertNoAccount(context: self.cuttlefishContext)
-
-        assertAllCKKSViews(enter: SecCKKSZoneKeyStateWaitForTLKCreation, within: 10 * NSEC_PER_SEC)
-    }
-
-    func testSignInWithDelayedHSA2Status() throws {
-        self.startCKAccountStatusMock()
-
-        // Device is signed out
-        self.mockAuthKit.altDSID = nil
-        self.mockAuthKit.hsa2 = false
-
-        // With no account, Octagon should go directly into 'NoAccount'
-        self.cuttlefishContext.startOctagonStateMachine()
-        self.assertEnters(context: self.cuttlefishContext, state: OctagonStateNoAccount, within: 10 * NSEC_PER_SEC)
-
-        // Sign in occurs, but HSA2 status isn't here yet
-        let newAltDSID = UUID().uuidString
-        self.mockAuthKit.altDSID = newAltDSID
-        XCTAssertNoThrow(try self.cuttlefishContext.accountAvailable(newAltDSID), "Sign-in shouldn't error")
-
-        // Octagon should go into 'waitforhsa2'
-        self.assertEnters(context: self.cuttlefishContext, state: OctagonStateWaitForHSA2, within: 10 * NSEC_PER_SEC)
-
-        self.mockAuthKit.hsa2 = true
-        XCTAssertNoThrow(try self.cuttlefishContext.idmsTrustLevelChanged(), "Notification of IDMS trust level shouldn't error")
+        self.assertEnters(context: self.cuttlefishContext, state: OctagonStateWaitForCDP, within: 10 * NSEC_PER_SEC)
+        XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
 
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
         self.assertConsidersSelfUntrusted(context: self.cuttlefishContext)
 
 
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
         self.assertConsidersSelfUntrusted(context: self.cuttlefishContext)
 
-        assertAllCKKSViews(enter: SecCKKSZoneKeyStateWaitForTLKCreation, within: 10 * NSEC_PER_SEC)
-
-        // On sign-out, octagon should go back to 'no account'
-        self.mockAuthKit.altDSID = nil
-        XCTAssertNoThrow(try self.cuttlefishContext.accountNoLongerAvailable(), "sign-out shouldn't error")
-        self.assertEnters(context: self.cuttlefishContext, state: OctagonStateNoAccount, within: 10 * NSEC_PER_SEC)
-        self.assertNoAccount(context: self.cuttlefishContext)
-
+        // CKKS should be waiting for assistance
         assertAllCKKSViews(enter: SecCKKSZoneKeyStateWaitForTLKCreation, within: 10 * NSEC_PER_SEC)
     }
 
     func testNewFriendsForEmptyAccount() throws {
         self.startCKAccountStatusMock()
 
         assertAllCKKSViews(enter: SecCKKSZoneKeyStateWaitForTLKCreation, within: 10 * NSEC_PER_SEC)
     }
 
     func testNewFriendsForEmptyAccount() throws {
         self.startCKAccountStatusMock()
 
+        // Tell SOS that it is absent, so we don't enable CDP on bringup
+        self.mockSOSAdapter.circleStatus = SOSCCStatus(kSOSCCCircleAbsent)
+
         self.cuttlefishContext.startOctagonStateMachine()
         self.cuttlefishContext.startOctagonStateMachine()
-        self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
+        self.assertEnters(context: self.cuttlefishContext, state: OctagonStateWaitForCDP, within: 10 * NSEC_PER_SEC)
 
         do {
             let clique = try OTClique.newFriends(withContextData: self.otcliqueContext, resetReason: .testGenerated)
 
         do {
             let clique = try OTClique.newFriends(withContextData: self.otcliqueContext, resetReason: .testGenerated)
@@ -1146,12 +1254,19 @@ class OctagonTests: OctagonTestsBase {
         self.assertConsidersSelfTrusted(context: self.cuttlefishContext)
         self.assertConsidersSelfTrustedCachedAccountStatus(context: self.cuttlefishContext)
 
         self.assertConsidersSelfTrusted(context: self.cuttlefishContext)
         self.assertConsidersSelfTrustedCachedAccountStatus(context: self.cuttlefishContext)
 
+        // and the act of calling newFriends should set the CDP bit
+        XCTAssertEqual(self.fetchCDPStatus(context: self.cuttlefishContext), .enabled, "CDP status should be 'enabled'")
+
         // and all subCKKSes should enter ready...
         // and all subCKKSes should enter ready...
-        assertAllCKKSViews(enter: SecCKKSZoneKeyStateReady, within: 10 * NSEC_PER_SEC)
+        self.assertAllCKKSViews(enter: SecCKKSZoneKeyStateReady, within: 10 * NSEC_PER_SEC)
         self.verifyDatabaseMocks()
 
         self.assertSelfTLKSharesInCloudKit(context: self.cuttlefishContext)
 
         self.verifyDatabaseMocks()
 
         self.assertSelfTLKSharesInCloudKit(context: self.cuttlefishContext)
 
+        // Also, CKKS should be configured with the prevailing policy version
+        XCTAssertNotNil(self.injectedManager?.policy, "Should have given CKKS a TPPolicy during initialization")
+        XCTAssertEqual(self.injectedManager?.policy?.version, prevailingPolicyVersion, "Policy given to CKKS should be prevailing policy")
+
         // TODO: add a CKKS item
     }
 
         // TODO: add a CKKS item
     }
 
@@ -1196,7 +1311,7 @@ class OctagonTests: OctagonTestsBase {
         self.cuttlefishContext.startOctagonStateMachine()
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
         self.cuttlefishContext.startOctagonStateMachine()
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
-        let clique = try OTClique(contextData: self.otcliqueContext)
+        let clique = OTClique(contextData: self.otcliqueContext)
 
         // Now, call requestToJoin. It should cause an establish to happen
         try clique.requestToJoinCircle()
 
         // Now, call requestToJoin. It should cause an establish to happen
         try clique.requestToJoinCircle()
@@ -1227,6 +1342,7 @@ class OctagonTests: OctagonTestsBase {
         self.startCKAccountStatusMock()
 
         self.cuttlefishContext.startOctagonStateMachine()
         self.startCKAccountStatusMock()
 
         self.cuttlefishContext.startOctagonStateMachine()
+        XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
         do {
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
         do {
@@ -1255,6 +1371,7 @@ class OctagonTests: OctagonTestsBase {
     func testDeviceFetchRetry() throws {
         self.startCKAccountStatusMock()
         self.cuttlefishContext.startOctagonStateMachine()
     func testDeviceFetchRetry() throws {
         self.startCKAccountStatusMock()
         self.cuttlefishContext.startOctagonStateMachine()
+        XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
         let ckError = FakeCuttlefishServer.makeCloudKitCuttlefishError(code: .transactionalFailure)
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
         let ckError = FakeCuttlefishServer.makeCloudKitCuttlefishError(code: .transactionalFailure)
@@ -1281,36 +1398,22 @@ class OctagonTests: OctagonTestsBase {
 
     func testDeviceFetchRetryFail() throws {
         self.startCKAccountStatusMock()
 
     func testDeviceFetchRetryFail() throws {
         self.startCKAccountStatusMock()
-        self.cuttlefishContext.startOctagonStateMachine()
-        self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
+
+        self.assertResetAndBecomeTrustedInDefaultContext()
 
         let ckError = FakeCuttlefishServer.makeCloudKitCuttlefishError(code: .transactionalFailure)
         self.fakeCuttlefishServer.nextFetchErrors.append(ckError)
         self.fakeCuttlefishServer.nextFetchErrors.append(ckError)
 
         let ckError = FakeCuttlefishServer.makeCloudKitCuttlefishError(code: .transactionalFailure)
         self.fakeCuttlefishServer.nextFetchErrors.append(ckError)
         self.fakeCuttlefishServer.nextFetchErrors.append(ckError)
-        self.cuttlefishContext.notifyContainerChange(nil)
-
-        do {
-            let clique = try OTClique.newFriends(withContextData: self.otcliqueContext, resetReason: .testGenerated)
-            XCTAssertNotNil(clique, "Clique should not be nil")
-        } catch {
-            XCTFail("Shouldn't have errored making new friends: \(error)")
-        }
-
-        // Now, we should be in 'ready'
-        self.assertEnters(context: self.cuttlefishContext, state: OctagonStateReady, within: 20 * NSEC_PER_SEC)
-        self.assertConsidersSelfTrusted(context: self.cuttlefishContext)
-        assertAllCKKSViews(enter: SecCKKSZoneKeyStateReady, within: 10 * NSEC_PER_SEC)
-        self.verifyDatabaseMocks()
-        self.assertSelfTLKSharesInCloudKit(context: self.cuttlefishContext)
 
 
-        self.cuttlefishContext.notifyContainerChange(nil)
-        self.assertEnters(context: self.cuttlefishContext, state: OctagonStateReady, within: 20 * NSEC_PER_SEC)
+        self.sendContainerChangeWaitForFetchForStates(context: self.cuttlefishContext, states: [OctagonStateReadyUpdated])
+        self.assertEnters(context: self.cuttlefishContext, state: OctagonStateReady, within: 30 * NSEC_PER_SEC)
     }
 
     func testNewFriendsForEmptyAccountReturnsMoreChanges() throws {
         self.startCKAccountStatusMock()
 
         self.cuttlefishContext.startOctagonStateMachine()
     }
 
     func testNewFriendsForEmptyAccountReturnsMoreChanges() throws {
         self.startCKAccountStatusMock()
 
         self.cuttlefishContext.startOctagonStateMachine()
+        XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
         self.fakeCuttlefishServer.nextEstablishReturnsMoreChanges = true
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
         self.fakeCuttlefishServer.nextEstablishReturnsMoreChanges = true
@@ -1335,7 +1438,7 @@ class OctagonTests: OctagonTestsBase {
     }
 
     func testNewFriendsForEmptyAccountWithoutTLKsResetsZones() throws {
     }
 
     func testNewFriendsForEmptyAccountWithoutTLKsResetsZones() throws {
-        self.putFakeKeyHierarchy(inCloudKit: self.manateeZoneID!)
+        self.putFakeKeyHierarchiesInCloudKit()
         // But do NOT add them to the keychain
 
         // CKKS+Octagon should reset the zones and be ready
         // But do NOT add them to the keychain
 
         // CKKS+Octagon should reset the zones and be ready
@@ -1343,6 +1446,7 @@ class OctagonTests: OctagonTestsBase {
         self.startCKAccountStatusMock()
 
         self.cuttlefishContext.startOctagonStateMachine()
         self.startCKAccountStatusMock()
 
         self.cuttlefishContext.startOctagonStateMachine()
+        XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
         // CKKS should reset the zones after Octagon has entered
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
         // CKKS should reset the zones after Octagon has entered
@@ -1365,7 +1469,7 @@ class OctagonTests: OctagonTestsBase {
     }
 
     func testUploadTLKsRetry() throws {
     }
 
     func testUploadTLKsRetry() throws {
-        self.putFakeKeyHierarchy(inCloudKit: self.manateeZoneID!)
+        self.putFakeKeyHierarchiesInCloudKit()
         // But do NOT add them to the keychain
 
         // CKKS+Octagon should reset the zones and be ready
         // But do NOT add them to the keychain
 
         // CKKS+Octagon should reset the zones and be ready
@@ -1373,6 +1477,7 @@ class OctagonTests: OctagonTestsBase {
         self.startCKAccountStatusMock()
 
         self.cuttlefishContext.startOctagonStateMachine()
         self.startCKAccountStatusMock()
 
         self.cuttlefishContext.startOctagonStateMachine()
+        XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
         // CKKS should reset the zones after Octagon has entered
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
         // CKKS should reset the zones after Octagon has entered
@@ -1406,6 +1511,7 @@ class OctagonTests: OctagonTestsBase {
         self.startCKAccountStatusMock()
 
         self.cuttlefishContext.startOctagonStateMachine()
         self.startCKAccountStatusMock()
 
         self.cuttlefishContext.startOctagonStateMachine()
+        XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
         do {
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
         do {
@@ -1444,8 +1550,8 @@ class OctagonTests: OctagonTestsBase {
     }
 
     func testNewFriendsForEmptyAccountWithTLKs() throws {
     }
 
     func testNewFriendsForEmptyAccountWithTLKs() throws {
-        self.putFakeKeyHierarchy(inCloudKit: self.manateeZoneID!)
-        self.saveTLKMaterial(toKeychain: self.manateeZoneID!)
+        self.putFakeKeyHierarchiesInCloudKit()
+        self.saveTLKMaterialToKeychain()
 
         self.startCKAccountStatusMock()
 
 
         self.startCKAccountStatusMock()
 
@@ -1453,6 +1559,7 @@ class OctagonTests: OctagonTestsBase {
         assertAllCKKSViews(enter: SecCKKSZoneKeyStateLoggedOut, within: 10 * NSEC_PER_SEC)
 
         self.cuttlefishContext.startOctagonStateMachine()
         assertAllCKKSViews(enter: SecCKKSZoneKeyStateLoggedOut, within: 10 * NSEC_PER_SEC)
 
         self.cuttlefishContext.startOctagonStateMachine()
+        XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
         assertAllCKKSViews(enter: SecCKKSZoneKeyStateWaitForTrust, within: 10 * NSEC_PER_SEC)
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
         assertAllCKKSViews(enter: SecCKKSZoneKeyStateWaitForTrust, within: 10 * NSEC_PER_SEC)
@@ -1482,6 +1589,7 @@ class OctagonTests: OctagonTestsBase {
 
         self.startCKAccountStatusMock()
         self.cuttlefishContext.startOctagonStateMachine()
 
         self.startCKAccountStatusMock()
         self.cuttlefishContext.startOctagonStateMachine()
+        XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
         do {
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
         do {
@@ -1496,6 +1604,10 @@ class OctagonTests: OctagonTestsBase {
         assertAllCKKSViews(enter: SecCKKSZoneKeyStateReady, within: 10 * NSEC_PER_SEC)
         self.assertConsidersSelfTrusted(context: self.cuttlefishContext)
 
         assertAllCKKSViews(enter: SecCKKSZoneKeyStateReady, within: 10 * NSEC_PER_SEC)
         self.assertConsidersSelfTrusted(context: self.cuttlefishContext)
 
+        // Ensure CKKS has a policy after newFriends
+        XCTAssertNotNil(self.injectedManager?.policy, "Should have given CKKS a TPPolicy during initialization")
+        XCTAssertEqual(self.injectedManager?.policy?.version, prevailingPolicyVersion, "Policy given to CKKS should be prevailing policy")
+
         let peerID = try self.cuttlefishContext.accountMetadataStore.getEgoPeerID()
         XCTAssertNotNil(peerID, "Should have a peer ID after making new friends")
 
         let peerID = try self.cuttlefishContext.accountMetadataStore.getEgoPeerID()
         XCTAssertNotNil(peerID, "Should have a peer ID after making new friends")
 
@@ -1508,6 +1620,8 @@ class OctagonTests: OctagonTestsBase {
         self.restartCKKSViews()
         self.cuttlefishContext = self.manager.context(forContainerName: OTCKContainerName, contextID: OTDefaultContext)
 
         self.restartCKKSViews()
         self.cuttlefishContext = self.manager.context(forContainerName: OTCKContainerName, contextID: OTDefaultContext)
 
+        XCTAssertNil(self.injectedManager?.policy, "CKKS should not have a policy after 'restart'")
+
         let restartDate = Date()
         self.cuttlefishContext.startOctagonStateMachine()
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateReady, within: 10 * NSEC_PER_SEC)
         let restartDate = Date()
         self.cuttlefishContext.startOctagonStateMachine()
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateReady, within: 10 * NSEC_PER_SEC)
@@ -1520,14 +1634,40 @@ class OctagonTests: OctagonTestsBase {
         XCTAssertEqual(peerID, restartedPeerID, "Should have the same peer ID after restarting")
         assertAllCKKSViews(enter: SecCKKSZoneKeyStateReady, within: 10 * NSEC_PER_SEC)
 
         XCTAssertEqual(peerID, restartedPeerID, "Should have the same peer ID after restarting")
         assertAllCKKSViews(enter: SecCKKSZoneKeyStateReady, within: 10 * NSEC_PER_SEC)
 
+        XCTAssertNotNil(self.injectedManager?.policy, "Should have given CKKS a TPPolicy after restart")
+        XCTAssertEqual(self.injectedManager?.policy?.version, prevailingPolicyVersion, "Policy given to CKKS after restart should be prevailing policy")
+
         readyDate = CKKSAnalytics.logger().dateProperty(forKey: OctagonAnalyticsLastKeystateReady)
         XCTAssertNotNil(readyDate, "Should have a ready date")
         XCTAssert(readyDate! > restartDate, "ready date should be after re-startdate")
     }
 
         readyDate = CKKSAnalytics.logger().dateProperty(forKey: OctagonAnalyticsLastKeystateReady)
         XCTAssertNotNil(readyDate, "Should have a ready date")
         XCTAssert(readyDate! > restartDate, "ready date should be after re-startdate")
     }
 
+    func testFillInUnknownAttemptedJoinState() throws {
+        self.startCKAccountStatusMock()
+
+        _ = self.assertResetAndBecomeTrustedInDefaultContext()
+
+        try self.cuttlefishContext.accountMetadataStore.persistAccountChanges { metadata in
+            metadata.attemptedJoin = .UNKNOWN
+            return metadata
+        }
+
+        self.manager.removeContext(forContainerName: OTCKContainerName, contextID: OTDefaultContext)
+        self.restartCKKSViews()
+        self.cuttlefishContext = self.manager.context(forContainerName: OTCKContainerName, contextID: OTDefaultContext)
+
+        self.cuttlefishContext.startOctagonStateMachine()
+        self.assertEnters(context: self.cuttlefishContext, state: OctagonStateReady, within: 10 * NSEC_PER_SEC)
+
+        // And check that the metadata is fixed:
+        let metadata = try self.cuttlefishContext.accountMetadataStore.loadOrCreateAccountMetadata()
+        XCTAssertEqual(metadata.attemptedJoin, .ATTEMPTED, "Should have attempted a join")
+    }
+
     func testLoadToUntrustedOnRestartIfKeysGone() throws {
         self.startCKAccountStatusMock()
         self.cuttlefishContext.startOctagonStateMachine()
     func testLoadToUntrustedOnRestartIfKeysGone() throws {
         self.startCKAccountStatusMock()
         self.cuttlefishContext.startOctagonStateMachine()
+        XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
         do {
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
         do {
@@ -1567,6 +1707,7 @@ class OctagonTests: OctagonTestsBase {
     func testLoadToUntrustedOnRestartIfTPHLosesAllData() throws {
         self.startCKAccountStatusMock()
         self.cuttlefishContext.startOctagonStateMachine()
     func testLoadToUntrustedOnRestartIfTPHLosesAllData() throws {
         self.startCKAccountStatusMock()
         self.cuttlefishContext.startOctagonStateMachine()
+        XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
         do {
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
         do {
@@ -1606,6 +1747,7 @@ class OctagonTests: OctagonTestsBase {
     func testLoadToTrustedOnRestartIfMismatchedPeerIDs() throws {
         self.startCKAccountStatusMock()
         self.cuttlefishContext.startOctagonStateMachine()
     func testLoadToTrustedOnRestartIfMismatchedPeerIDs() throws {
         self.startCKAccountStatusMock()
         self.cuttlefishContext.startOctagonStateMachine()
+        XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
         do {
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
         do {
@@ -1641,70 +1783,6 @@ class OctagonTests: OctagonTestsBase {
         XCTAssertEqual(peerID, newPeerID, "Should now have TPH's peer ID")
     }
 
         XCTAssertEqual(peerID, newPeerID, "Should now have TPH's peer ID")
     }
 
-    func testStatusRPCsWithUnknownCloudKitAccount() throws {
-        // If CloudKit isn't returning our calls, we should still return something reasonable...
-        let statusexpectation = self.expectation(description: "trust status returns")
-        let configuration = OTOperationConfiguration()
-        configuration.timeoutWaitForCKAccount = 500 * NSEC_PER_MSEC
-        self.cuttlefishContext.rpcTrustStatus(configuration) { egoStatus, _, _, _, _ in
-            XCTAssertTrue([.absent].contains(egoStatus), "Self peer should be in the 'absent' state")
-            statusexpectation.fulfill()
-        }
-        self.wait(for: [statusexpectation], timeout: 10)
-
-        // Now sign in to 'untrusted'
-        self.startCKAccountStatusMock()
-
-        self.cuttlefishContext.startOctagonStateMachine()
-        self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
-        self.assertConsidersSelfUntrusted(context: self.cuttlefishContext)
-
-        // And restart, without any idea of the cloudkit state
-        self.ckaccountHoldOperation = BlockOperation()
-        self.injectedManager!.accountTracker = CKKSAccountStateTracker(self.injectedManager!.container,
-                                                                       nsnotificationCenterClass: FakeNSNotificationCenter.self as CKKSNSNotificationCenter.Type)
-        self.manager.accountStateTracker = self.injectedManager!.accountTracker
-
-        self.manager.removeContext(forContainerName: OTCKContainerName, contextID: OTDefaultContext)
-        self.restartCKKSViews()
-        self.cuttlefishContext = self.manager.context(forContainerName: OTCKContainerName, contextID: OTDefaultContext)
-
-        // Should know it's untrusted
-        self.assertConsidersSelfUntrusted(context: self.cuttlefishContext)
-        self.startCKAccountStatusMock()
-
-        // Now become ready
-        do {
-            let clique = try OTClique.newFriends(withContextData: self.otcliqueContext, resetReason: .testGenerated)
-            XCTAssertNotNil(clique, "Clique should not be nil")
-        } catch {
-            XCTFail("Shouldn't have errored making new friends: \(error)")
-        }
-
-        // Now, we should be in 'ready'
-        self.assertEnters(context: self.cuttlefishContext, state: OctagonStateReady, within: 10 * NSEC_PER_SEC)
-        self.assertConsidersSelfTrusted(context: self.cuttlefishContext)
-        self.assertConsidersSelfTrustedCachedAccountStatus(context: self.cuttlefishContext)
-
-        // and all subCKKSes should enter ready...
-        assertAllCKKSViews(enter: SecCKKSZoneKeyStateReady, within: 10 * NSEC_PER_SEC)
-        self.verifyDatabaseMocks()
-
-        // Restart one more time:
-
-        self.ckaccountHoldOperation = BlockOperation()
-        self.injectedManager!.accountTracker = CKKSAccountStateTracker(self.injectedManager!.container,
-                                                                       nsnotificationCenterClass: FakeNSNotificationCenter.self as CKKSNSNotificationCenter.Type)
-        self.manager.accountStateTracker = self.injectedManager!.accountTracker
-
-        self.manager.removeContext(forContainerName: OTCKContainerName, contextID: OTDefaultContext)
-        self.restartCKKSViews()
-        self.cuttlefishContext = self.manager.context(forContainerName: OTCKContainerName, contextID: OTDefaultContext)
-
-        self.assertConsidersSelfTrusted(context: self.cuttlefishContext)
-        self.assertConsidersSelfTrustedCachedAccountStatus(context: self.cuttlefishContext)
-    }
-
     func testRestoreToNewClique() throws {
         self.startCKAccountStatusMock()
 
     func testRestoreToNewClique() throws {
         self.startCKAccountStatusMock()
 
@@ -1737,11 +1815,10 @@ class OctagonTests: OctagonTestsBase {
         let memberIdentifier = clique!.cliqueMemberIdentifier
 
         let dumpExpectation = self.expectation(description: "dump callback occurs")
         let memberIdentifier = clique!.cliqueMemberIdentifier
 
         let dumpExpectation = self.expectation(description: "dump callback occurs")
-        self.tphClient.dump(withContainer: self.cuttlefishContext.containerName, context: self.cuttlefishContext.contextID) {
-            dump, error in
+        self.tphClient.dump(withContainer: self.cuttlefishContext.containerName, context: self.cuttlefishContext.contextID) { dump, error in
             XCTAssertNil(error, "Should be no error dumping data")
             XCTAssertNotNil(dump, "dump should not be nil")
             XCTAssertNil(error, "Should be no error dumping data")
             XCTAssertNotNil(dump, "dump should not be nil")
-            let egoSelf = dump!["self"] as? Dictionary<String, AnyObject>
+            let egoSelf = dump!["self"] as? [String: AnyObject]
             XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
             let peerID = egoSelf!["peerID"] as? String
             XCTAssertNotNil(peerID, "peerID should not be nil")
             XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
             let peerID = egoSelf!["peerID"] as? String
             XCTAssertNotNil(peerID, "peerID should not be nil")
@@ -1756,6 +1833,7 @@ class OctagonTests: OctagonTestsBase {
         self.startCKAccountStatusMock()
 
         self.cuttlefishContext.startOctagonStateMachine()
         self.startCKAccountStatusMock()
 
         self.cuttlefishContext.startOctagonStateMachine()
+        XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
         let clique: OTClique
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
         let clique: OTClique
@@ -1783,94 +1861,11 @@ class OctagonTests: OctagonTestsBase {
         // TODO: an item added here shouldn't sync
     }
 
         // TODO: an item added here shouldn't sync
     }
 
-    func testSignOut() throws {
-        self.startCKAccountStatusMock()
-
-        self.cuttlefishContext.startOctagonStateMachine()
-        self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
-
-        do {
-            let clique = try OTClique.newFriends(withContextData: self.otcliqueContext, resetReason: .testGenerated)
-            XCTAssertNotNil(clique, "Clique should not be nil")
-        } catch {
-            XCTFail("Shouldn't have errored making new friends: \(error)")
-        }
-
-        // Now, we should be in 'ready'
-        self.assertEnters(context: self.cuttlefishContext, state: OctagonStateReady, within: 10 * NSEC_PER_SEC)
-        self.assertConsidersSelfTrusted(context: self.cuttlefishContext)
-        self.verifyDatabaseMocks()
-        assertAllCKKSViews(enter: SecCKKSZoneKeyStateReady, within: 10 * NSEC_PER_SEC)
-
-        // And 'dump' should show some information
-        let dumpExpectation = self.expectation(description: "dump callback occurs")
-        self.tphClient.dump(withContainer: self.cuttlefishContext.containerName, context: self.cuttlefishContext.contextID) {
-            dump, error in
-            XCTAssertNil(error, "Should be no error dumping data")
-            XCTAssertNotNil(dump, "dump should not be nil")
-            let egoSelf = dump!["self"] as? Dictionary<String, AnyObject>
-            XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
-            let peerID = egoSelf!["peerID"] as? String
-            XCTAssertNotNil(peerID, "peerID should not be nil")
-
-            dumpExpectation.fulfill()
-        }
-        self.wait(for: [dumpExpectation], timeout: 10)
-
-        // Turn off the CK account too
-        self.accountStatus = .noAccount
-        self.accountStateTracker.notifyCKAccountStatusChangeAndWaitForSignal()
-
-        XCTAssertNoThrow(try self.cuttlefishContext.accountNoLongerAvailable(), "Should be no issue signing out")
-        self.assertEnters(context: self.cuttlefishContext, state: OctagonStateNoAccount, within: 10 * NSEC_PER_SEC)
-        self.assertNoAccount(context: self.cuttlefishContext)
-
-        // And 'dump' should show nothing
-        let signedOutDumpExpectation = self.expectation(description: "dump callback occurs")
-        self.tphClient.dump(withContainer: self.cuttlefishContext.containerName, context: self.cuttlefishContext.contextID) {
-            dump, error in
-            XCTAssertNil(error, "Should be no error dumping data")
-            XCTAssertNotNil(dump, "dump should not be nil")
-            let egoSelf = dump!["self"] as? Dictionary<String, AnyObject>
-            XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
-            XCTAssertEqual(egoSelf!.count, 0, "egoSelf should have zero elements")
-
-            signedOutDumpExpectation.fulfill()
-        }
-        self.wait(for: [signedOutDumpExpectation], timeout: 10)
-        assertAllCKKSViews(enter: SecCKKSZoneKeyStateLoggedOut, within: 10 * NSEC_PER_SEC)
-
-        //check trust status
-        let checkTrustExpectation = self.expectation(description: "checkTrustExpectation callback occurs")
-        let configuration = OTOperationConfiguration()
-        self.cuttlefishContext.rpcTrustStatus(configuration) { _, _, _, _, _ in
-            checkTrustExpectation.fulfill()
-        }
-        self.wait(for: [checkTrustExpectation], timeout: 10)
-
-        self.assertEnters(context: self.cuttlefishContext, state: OctagonStateNoAccount, within: 10 * NSEC_PER_SEC)
-
-        // And 'dump' should show nothing
-        let signedOutDumpExpectationAfterCheckTrustStatus = self.expectation(description: "dump callback occurs")
-        self.tphClient.dump(withContainer: self.cuttlefishContext.containerName, context: self.cuttlefishContext.contextID) {
-            dump, error in
-            XCTAssertNil(error, "Should be no error dumping data")
-            XCTAssertNotNil(dump, "dump should not be nil")
-            let egoSelf = dump!["self"] as? Dictionary<String, AnyObject>
-            XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
-            XCTAssertEqual(egoSelf!.count, 0, "egoSelf should have zero elements")
-
-            signedOutDumpExpectationAfterCheckTrustStatus.fulfill()
-        }
-        self.wait(for: [signedOutDumpExpectationAfterCheckTrustStatus], timeout: 10)
-        self.assertEnters(context: self.cuttlefishContext, state: OctagonStateNoAccount, within: 10 * NSEC_PER_SEC)
-        self.assertNoAccount(context: self.cuttlefishContext)
-    }
-
     func testCliqueFriendAPI() throws {
         self.startCKAccountStatusMock()
 
         self.cuttlefishContext.startOctagonStateMachine()
     func testCliqueFriendAPI() throws {
         self.startCKAccountStatusMock()
 
         self.cuttlefishContext.startOctagonStateMachine()
+        XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
         assertAllCKKSViews(enter: SecCKKSZoneKeyStateWaitForTLKCreation, within: 10 * NSEC_PER_SEC)
 
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
         assertAllCKKSViews(enter: SecCKKSZoneKeyStateWaitForTLKCreation, within: 10 * NSEC_PER_SEC)
 
@@ -1915,7 +1910,7 @@ class OctagonTests: OctagonTestsBase {
                                          accountStateTracker: self.accountStateTracker,
                                          deviceInformationAdapter: peer2DeviceAdapter)
 
                                          accountStateTracker: self.accountStateTracker,
                                          deviceInformationAdapter: peer2DeviceAdapter)
 
-        self.setAllowListToCurrentAuthKit(container: OTCKContainerName, context: peer2ContextID)
+        self.setAllowListToCurrentAuthKit(container: OTCKContainerName, context: peer2ContextID, accountIsDemo: false)
 
         var peer2ID: String!
         let joinExpectation = self.expectation(description: "join callback occurs")
 
         var peer2ID: String!
         let joinExpectation = self.expectation(description: "join callback occurs")
@@ -1932,7 +1927,7 @@ class OctagonTests: OctagonTestsBase {
                                policyVersion: nil,
                                policySecrets: nil,
                                signingPrivKeyPersistentRef: nil,
                                policyVersion: nil,
                                policySecrets: nil,
                                signingPrivKeyPersistentRef: nil,
-                               encPrivKeyPersistentRef: nil) { peerID, permanentInfo, permanentInfoSig, stableInfo, stableInfoSig, error in
+                               encPrivKeyPersistentRef: nil) { peerID, permanentInfo, permanentInfoSig, stableInfo, stableInfoSig, _, _, error in
                                 XCTAssertNil(error, "Should be no error preparing identity")
                                 XCTAssertNotNil(permanentInfo, "Should have a permanent identity")
                                 XCTAssertNotNil(permanentInfoSig, "Should have a permanent identity signature")
                                 XCTAssertNil(error, "Should be no error preparing identity")
                                 XCTAssertNotNil(permanentInfo, "Should have a permanent identity")
                                 XCTAssertNotNil(permanentInfoSig, "Should have a permanent identity signature")
@@ -1955,7 +1950,7 @@ class OctagonTests: OctagonTestsBase {
                                                                             voucherSig: voucherSig!,
                                                                             ckksKeys: [],
                                                                             tlkShares: [],
                                                                             voucherSig: voucherSig!,
                                                                             ckksKeys: [],
                                                                             tlkShares: [],
-                                                                            preapprovedKeys: []) { peerID, _, error in
+                                                                            preapprovedKeys: []) { peerID, _, _, _, error in
                                                                                 XCTAssertNil(error, "Should be no error joining")
                                                                                 XCTAssertNotNil(peerID, "Should have a peerID")
                                                                                 peer2ID = peerID
                                                                                 XCTAssertNil(error, "Should be no error joining")
                                                                                 XCTAssertNotNil(peerID, "Should have a peerID")
                                                                                 peer2ID = peerID
@@ -1970,6 +1965,7 @@ class OctagonTests: OctagonTestsBase {
         account.peerID = peer2ID
         account.icloudAccountState = .ACCOUNT_AVAILABLE
         account.trustState = .TRUSTED
         account.peerID = peer2ID
         account.icloudAccountState = .ACCOUNT_AVAILABLE
         account.trustState = .TRUSTED
+        account.attemptedJoin = . ATTEMPTED
         XCTAssertNoThrow(try account.saveToKeychain(forContainer: OTCKContainerName, contextID: peer2ContextID), "Should be no error saving fake account metadata")
 
         peer2.startOctagonStateMachine()
         XCTAssertNoThrow(try account.saveToKeychain(forContainer: OTCKContainerName, contextID: peer2ContextID), "Should be no error saving fake account metadata")
 
         peer2.startOctagonStateMachine()
@@ -2013,43 +2009,16 @@ class OctagonTests: OctagonTestsBase {
         }
     }
 
         }
     }
 
-    func testNoAccountLeadsToInitialize() throws {
-        self.startCKAccountStatusMock()
-
-        // With no identity and AuthKit reporting no iCloud account, Octagon should go directly into 'no account'
-        self.mockAuthKit.altDSID = nil
-
-        // Run initialization, like the real secd will do
-        OctagonInitialize()
-
-        self.assertEnters(context: self.cuttlefishContext, state: OctagonStateNoAccount, within: 10 * NSEC_PER_SEC)
-
-        self.mockAuthKit.altDSID = "1234"
-        let signinExpectation = self.expectation(description: "sign in returns")
-        self.otControl.sign(in: "1234", container: nil, context: OTDefaultContext) { error in
-            XCTAssertNil(error, "error should be nil")
-            signinExpectation.fulfill()
-        }
-        self.wait(for: [signinExpectation], timeout: 10)
-
-        self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
-
-    }
-
     func testOTCliqueOctagonAuthoritativeTrustResponse() throws {
         self.startCKAccountStatusMock()
         OctagonAuthoritativeTrustSetIsEnabled(true)
 
     func testOTCliqueOctagonAuthoritativeTrustResponse() throws {
         self.startCKAccountStatusMock()
         OctagonAuthoritativeTrustSetIsEnabled(true)
 
-        do {
-            let absentClique = try OTClique(contextData: self.otcliqueContext)
-            let absentStatus = absentClique.fetchStatus(nil)
-            XCTAssertEqual(absentStatus, CliqueStatus.absent, "clique should return Absent")
-        } catch {
-            XCTFail("Shouldn't have errored making new friends: \(error)")
-            throw error
-        }
+        let absentClique = OTClique(contextData: self.otcliqueContext)
+        let absentStatus = absentClique.fetchStatus(nil)
+        XCTAssertEqual(absentStatus, CliqueStatus.absent, "clique should return Absent")
 
         self.cuttlefishContext.startOctagonStateMachine()
 
         self.cuttlefishContext.startOctagonStateMachine()
+        XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
         let clique: OTClique
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
         let clique: OTClique
@@ -2094,7 +2063,7 @@ class OctagonTests: OctagonTestsBase {
             XCTFail("Shouldn't have errored making new friends: \(error)")
             throw error
         }
             XCTFail("Shouldn't have errored making new friends: \(error)")
             throw error
         }
-        let newContext = self.manager.context(forContainerName: OTCKContainerName, contextID: newOTCliqueContext.context!)
+        let newContext = self.manager.context(forContainerName: OTCKContainerName, contextID: newOTCliqueContext.context)
         self.assertEnters(context: newContext, state: OctagonStateReady, within: 10 * NSEC_PER_SEC)
     }
 
         self.assertEnters(context: newContext, state: OctagonStateReady, within: 10 * NSEC_PER_SEC)
     }
 
@@ -2119,39 +2088,17 @@ class OctagonTests: OctagonTestsBase {
                                  json: false)
     }
 
                                  json: false)
     }
 
-    func testNoAccountTimeoutTransitionWatcher() throws {
-        self.startCKAccountStatusMock()
-
-        // With no identity and AuthKit reporting no iCloud account, Octagon should go directly into 'no account'
-        self.mockAuthKit.altDSID = nil
-
-        // Run initialization, like the real secd will do
-        OctagonInitialize()
-        self.cuttlefishContext.stateMachine.setWatcherTimeout(2 * NSEC_PER_SEC)
-
-        self.assertEnters(context: self.cuttlefishContext, state: OctagonStateNoAccount, within: 10 * NSEC_PER_SEC)
-        XCTAssertTrue(self.cuttlefishContext.stateMachine.isPaused(), "State machine should be stopped")
-        self.assertNoAccount(context: self.cuttlefishContext)
-        XCTAssertEqual(0, self.cuttlefishContext.stateMachine.paused.wait(10 * NSEC_PER_SEC), "State machine should be quiescent")
-
-        let joinWithBottleExpectation = self.expectation(description: "joinWithBottle callback occurs")
-        self.cuttlefishContext.join(withBottle: "bottleID", entropy: Data(), bottleSalt: "peer2AltDSID") { error in
-            XCTAssertNotNil(error, "error should not be nil")
-            joinWithBottleExpectation.fulfill()
-        }
-        self.wait(for: [joinWithBottleExpectation], timeout: 3)
-    }
-
     func testFailingStateTransitionWatcher() throws {
         self.startCKAccountStatusMock()
 
         self.cuttlefishContext.startOctagonStateMachine()
     func testFailingStateTransitionWatcher() throws {
         self.startCKAccountStatusMock()
 
         self.cuttlefishContext.startOctagonStateMachine()
+        XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
         // Set up a watcher that we expect to fail...
         let path = OctagonStateTransitionPath(from: [
             OctagonStateResetAndEstablish: [
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
         // Set up a watcher that we expect to fail...
         let path = OctagonStateTransitionPath(from: [
             OctagonStateResetAndEstablish: [
-                OctagonStateInitiatorVouchWithBottle: [
+                OctagonStateBottleJoinVouchWithBottle: [
                         OctagonStateResetAndEstablish: OctagonStateTransitionPathStep.success(),
                     ],
                 ],
                         OctagonStateResetAndEstablish: OctagonStateTransitionPathStep.success(),
                     ],
                 ],
@@ -2163,10 +2110,10 @@ class OctagonTests: OctagonTestsBase {
         self.cuttlefishContext.stateMachine.register(watcher)
 
         let watcherCompleteOperationExpectation = self.expectation(description: "watcherCompleteOperationExpectation returns")
         self.cuttlefishContext.stateMachine.register(watcher)
 
         let watcherCompleteOperationExpectation = self.expectation(description: "watcherCompleteOperationExpectation returns")
-        let watcherFinishOp = CKKSResultOperation.named("should-fail-cleanup", with: {
+        let watcherFinishOp = CKKSResultOperation.named("should-fail-cleanup") {
             XCTAssertNotNil(watcher.result.error, "watcher should have errored")
             watcherCompleteOperationExpectation.fulfill()
             XCTAssertNotNil(watcher.result.error, "watcher should have errored")
             watcherCompleteOperationExpectation.fulfill()
-        })
+        }
 
         watcherFinishOp.addDependency(watcher.result)
         self.operationQueue.addOperation(watcherFinishOp)
 
         watcherFinishOp.addDependency(watcher.result)
         self.operationQueue.addOperation(watcherFinishOp)
@@ -2189,6 +2136,7 @@ class OctagonTests: OctagonTestsBase {
         self.startCKAccountStatusMock()
 
         self.cuttlefishContext.startOctagonStateMachine()
         self.startCKAccountStatusMock()
 
         self.cuttlefishContext.startOctagonStateMachine()
+        XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
         let stateTransitionOp = OctagonStateTransitionOperation(name: "will-never-run",
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
         let stateTransitionOp = OctagonStateTransitionOperation(name: "will-never-run",
@@ -2202,7 +2150,7 @@ class OctagonTests: OctagonTestsBase {
         // Set up a watcher that we expect to fail due to its initial transition op timing out...
         let path = OctagonStateTransitionPath(from: [
             OctagonStateResetAndEstablish: [
         // Set up a watcher that we expect to fail due to its initial transition op timing out...
         let path = OctagonStateTransitionPath(from: [
             OctagonStateResetAndEstablish: [
-                OctagonStateInitiatorVouchWithBottle: [
+                OctagonStateBottleJoinVouchWithBottle: [
                         OctagonStateResetAndEstablish: OctagonStateTransitionPathStep.success(),
                     ],
                 ],
                         OctagonStateResetAndEstablish: OctagonStateTransitionPathStep.success(),
                     ],
                 ],
@@ -2214,10 +2162,10 @@ class OctagonTests: OctagonTestsBase {
         self.cuttlefishContext.stateMachine.register(watcher)
 
         let watcherCompleteOperationExpectation = self.expectation(description: "watcherCompleteOperationExpectation returns")
         self.cuttlefishContext.stateMachine.register(watcher)
 
         let watcherCompleteOperationExpectation = self.expectation(description: "watcherCompleteOperationExpectation returns")
-        let watcherFinishOp = CKKSResultOperation.named("should-fail-cleanup", with: {
+        let watcherFinishOp = CKKSResultOperation.named("should-fail-cleanup") {
             XCTAssertNotNil(watcher.result.error, "watcher should have errored")
             watcherCompleteOperationExpectation.fulfill()
             XCTAssertNotNil(watcher.result.error, "watcher should have errored")
             watcherCompleteOperationExpectation.fulfill()
-        })
+        }
 
         watcherFinishOp.addDependency(watcher.result)
         self.operationQueue.addOperation(watcherFinishOp)
 
         watcherFinishOp.addDependency(watcher.result)
         self.operationQueue.addOperation(watcherFinishOp)
@@ -2233,6 +2181,7 @@ class OctagonTests: OctagonTestsBase {
 
         OctagonAuthoritativeTrustSetIsEnabled(true)
         self.cuttlefishContext.startOctagonStateMachine()
 
         OctagonAuthoritativeTrustSetIsEnabled(true)
         self.cuttlefishContext.startOctagonStateMachine()
+        XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
         let clique: OTClique
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
         let clique: OTClique
@@ -2260,7 +2209,7 @@ class OctagonTests: OctagonTestsBase {
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
         let cfuExpectation = self.expectation(description: "cfu callback occurs")
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
         let cfuExpectation = self.expectation(description: "cfu callback occurs")
-        self.cuttlefishContext.setPostedBool(false)
+        self.cuttlefishContext.followupHandler.clearAllPostedFlags()
         self.cuttlefishContext.checkTrustStatusAndPostRepairCFUIfNecessary { _, posted, _, error in
             #if !os(tvOS)
             XCTAssertTrue(posted, "posted should be true")
         self.cuttlefishContext.checkTrustStatusAndPostRepairCFUIfNecessary { _, posted, _, error in
             #if !os(tvOS)
             XCTAssertTrue(posted, "posted should be true")
@@ -2274,6 +2223,9 @@ class OctagonTests: OctagonTestsBase {
     }
 
     func testDeviceLockedDuringAccountRetrieval() throws {
     }
 
     func testDeviceLockedDuringAccountRetrieval() throws {
+        // Tell SOS that it is absent, so we don't enable CDP on bringup
+        self.mockSOSAdapter.circleStatus = SOSCCStatus(kSOSCCCircleAbsent)
+
         self.startCKAccountStatusMock()
 
         self.aksLockState = true
         self.startCKAccountStatusMock()
 
         self.aksLockState = true
@@ -2298,6 +2250,9 @@ class OctagonTests: OctagonTestsBase {
         self.aksLockState = false
         self.lockStateTracker.recheck()
 
         self.aksLockState = false
         self.lockStateTracker.recheck()
 
+        self.assertEnters(context: initiatorContext, state: OctagonStateWaitForCDP, within: 10 * NSEC_PER_SEC)
+
+        XCTAssertNoThrow(try initiatorContext.setCDPEnabled())
         self.assertEnters(context: initiatorContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
     }
 
         self.assertEnters(context: initiatorContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
     }
 
@@ -2305,6 +2260,7 @@ class OctagonTests: OctagonTestsBase {
         self.startCKAccountStatusMock()
 
         self.cuttlefishContext.startOctagonStateMachine()
         self.startCKAccountStatusMock()
 
         self.cuttlefishContext.startOctagonStateMachine()
+        XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
         do {
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
         do {
@@ -2333,6 +2289,7 @@ class OctagonTests: OctagonTestsBase {
     func testFetchViewList() throws {
         self.startCKAccountStatusMock()
         self.cuttlefishContext.startOctagonStateMachine()
     func testFetchViewList() throws {
         self.startCKAccountStatusMock()
         self.cuttlefishContext.startOctagonStateMachine()
+        XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
         do {
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
         do {
@@ -2358,16 +2315,18 @@ class OctagonTests: OctagonTestsBase {
                                  "WiFi",
                                  "Health",
                                  "Manatee",
                                  "WiFi",
                                  "Health",
                                  "Manatee",
-                                 "CreditCards",
-                                 "Passwords",
+                                 // <rdar://problem/57810109> Cuttlefish: remove Safari prefix from view names
+                                 "SafariCreditCards",
+                                 "SafariPasswords",
                                  "ApplePay", ])
         #else
         let expectedViews = Set(["LimitedPeersAllowed",
                                  "ApplePay", ])
         #else
         let expectedViews = Set(["LimitedPeersAllowed",
+                                 "Home",
                                  "WiFi", ])
         #endif
 
         let getViewsExpectation = self.expectation(description: "getViews callback happens")
                                  "WiFi", ])
         #endif
 
         let getViewsExpectation = self.expectation(description: "getViews callback happens")
-        self.tphClient.getViewsWithContainer(OTCKContainerName, context: OTDefaultContext, inViews: []) { outViews, error in
+        self.tphClient.fetchCurrentPolicy(withContainer: OTCKContainerName, context: OTDefaultContext) { outViews, _, error in
             XCTAssertNil(error, "should not have failed")
             XCTAssertEqual(expectedViews, Set(outViews!))
             getViewsExpectation.fulfill()
             XCTAssertNil(error, "should not have failed")
             XCTAssertEqual(expectedViews, Set(outViews!))
             getViewsExpectation.fulfill()
@@ -2375,73 +2334,6 @@ class OctagonTests: OctagonTestsBase {
         self.wait(for: [getViewsExpectation], timeout: 10)
     }
 
         self.wait(for: [getViewsExpectation], timeout: 10)
     }
 
-    func testMergedViewListOff() throws {
-        self.startCKAccountStatusMock()
-        self.cuttlefishContext.viewManager!.setOverrideCKKSViewsFromPolicy(false)
-        self.cuttlefishContext.startOctagonStateMachine()
-        self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
-
-        do {
-            let clique = try OTClique.newFriends(withContextData: self.otcliqueContext, resetReason: .testGenerated)
-            XCTAssertNotNil(clique, "Clique should not be nil")
-        } catch {
-            XCTFail("Shouldn't have errored making new friends: \(error)")
-        }
-
-        // Now, we should be in 'ready'
-        self.assertEnters(context: self.cuttlefishContext, state: OctagonStateReady, within: 10 * NSEC_PER_SEC)
-        self.assertConsidersSelfTrusted(context: self.cuttlefishContext)
-
-        let viewList = self.cuttlefishContext.viewManager!.viewList()
-        #if !os(tvOS)
-        let expected = Set<String>(["Manatee"])
-        #else
-        let expected = Set<String>(["LimitedPeersAllowed"])
-        #endif
-        XCTAssertEqual(expected, viewList)
-    }
-
-    func testMergedViewListOn() throws {
-        /*
-        self.startCKAccountStatusMock()
-        self.cuttlefishContext.viewManager!.setOverrideCKKSViewsFromPolicy(true)
-        self.cuttlefishContext.startOctagonStateMachine()
-        self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
-
-        do {
-            let clique = try OTClique.newFriends(withContextData: self.otcliqueContext, .testGenerated)
-            XCTAssertNotNil(clique, "Clique should not be nil")
-        } catch {
-            XCTFail("Shouldn't have errored making new friends: \(error)")
-        }
-
-        // Now, we should be in 'ready'
-        self.assertEnters(context: self.cuttlefishContext, state: OctagonStateReady, within: 10 * NSEC_PER_SEC)
-        self.assertConsidersSelfTrusted(context: self.cuttlefishContext)
-
-        let viewList = self.cuttlefishContext.viewManager!.viewList()
-        let expected = Set<String>([
-                                    "ApplePay",                         
-                                    "Applications",
-                                    "AutoUnlock",
-                                    "Backstop",
-                                    "DevicePairing",
-                                    "Engram",
-                                    "Health",
-                                    "Home",
-                                    "LimitedPeersAllowed",
-                                    "Manatee",
-                                    "ProtectedCloudStorage",
-                                    "SafariCreditCards",
-                                    "SafariPasswords",
-                                    "SecureObjectSync",
-                                    "WiFi",
-                                    "keychain",                
-                                    ])
-        XCTAssertEqual(expected, viewList)
- */
-    }
-
     let octagonNotificationName = "com.apple.security.octagon.trust-status-change"
 
     func testNotifications() throws {
     let octagonNotificationName = "com.apple.security.octagon.trust-status-change"
 
     func testNotifications() throws {
@@ -2452,8 +2344,9 @@ class OctagonTests: OctagonTestsBase {
         let untrustedNotification = XCTDarwinNotificationExpectation(notificationName: octagonNotificationName)
 
         self.startCKAccountStatusMock()
         let untrustedNotification = XCTDarwinNotificationExpectation(notificationName: octagonNotificationName)
 
         self.startCKAccountStatusMock()
-        self.cuttlefishContext.startOctagonStateMachine()
 
 
+        self.cuttlefishContext.startOctagonStateMachine()
+        XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
         self.wait(for: [untrustedNotification], timeout: 2)
 
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
         self.wait(for: [untrustedNotification], timeout: 2)
 
@@ -2505,11 +2398,11 @@ class OctagonTests: OctagonTestsBase {
         self.assertConsidersSelfTrusted(context: self.cuttlefishContext)
 
         let statusExpectation = self.expectation(description: "status callback occurs")
         self.assertConsidersSelfTrusted(context: self.cuttlefishContext)
 
         let statusExpectation = self.expectation(description: "status callback occurs")
-        self.tphClient.dumpEgoPeer(withContainer: self.cuttlefishContext.containerName, context: self.cuttlefishContext.contextID, reply: { _, _, stableInfo, _, error in
+        self.tphClient.dumpEgoPeer(withContainer: self.cuttlefishContext.containerName, context: self.cuttlefishContext.contextID) { _, _, stableInfo, _, error in
             XCTAssertNil(error, "should be no error dumping ego peer")
             XCTAssertEqual(stableInfo?.deviceName, newDeviceName, "device name should be updated")
             statusExpectation.fulfill()
             XCTAssertNil(error, "should be no error dumping ego peer")
             XCTAssertEqual(stableInfo?.deviceName, newDeviceName, "device name should be updated")
             statusExpectation.fulfill()
-        })
+        }
         self.wait(for: [statusExpectation], timeout: 2)
 
         // Receiving a push shouldn't cause another update to be sent
         self.wait(for: [statusExpectation], timeout: 2)
 
         // Receiving a push shouldn't cause another update to be sent
@@ -2616,11 +2509,28 @@ class OctagonTests: OctagonTestsBase {
         let untrustedNotification = XCTDarwinNotificationExpectation(notificationName: octagonNotificationName)
 
         self.startCKAccountStatusMock()
         let untrustedNotification = XCTDarwinNotificationExpectation(notificationName: octagonNotificationName)
 
         self.startCKAccountStatusMock()
+        XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
         self.cuttlefishContext.startOctagonStateMachine()
 
         self.cuttlefishContext.startOctagonStateMachine()
 
+        // Octagon will fetch once to determine its trust state
+        let trustStateFetchExpectation = self.expectation(description: "trust state fetch occurs")
+        self.fakeCuttlefishServer.fetchChangesListener = { [unowned self] _ in
+            self.fakeCuttlefishServer.fetchChangesListener = nil
+            trustStateFetchExpectation.fulfill()
+            return nil
+        }
+
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
         self.wait(for: [untrustedNotification], timeout: 2)
 
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
         self.wait(for: [untrustedNotification], timeout: 2)
 
+        self.wait(for: [trustStateFetchExpectation], timeout: 10)
+
+        let fetchExpectation = self.expectation(description: "fetch occurs")
+        self.fakeCuttlefishServer.fetchChangesListener = { _ in
+            fetchExpectation.fulfill()
+            return nil
+        }
+
         self.cuttlefishContext.notifyContainerChange(nil)
 
         self.cuttlefishContext.notifyContainerChange(nil)
         self.cuttlefishContext.notifyContainerChange(nil)
 
         self.cuttlefishContext.notifyContainerChange(nil)
@@ -2631,7 +2541,8 @@ class OctagonTests: OctagonTestsBase {
 
         self.cuttlefishContext.notifyContainerChange(nil)
 
 
         self.cuttlefishContext.notifyContainerChange(nil)
 
-        XCTAssertEqual(self.fakeCuttlefishServer.fetchChangesCalledCount, 1, "fetchChanges should have been called 1 times")
+        self.wait(for: [fetchExpectation], timeout: 10)
+        XCTAssertEqual(self.fakeCuttlefishServer.fetchChangesCalledCount, 2, "fetchChanges should have been called 1 times")
 
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
         self.assertAllCKKSViews(enter: SecCKKSZoneKeyStateWaitForTLKCreation, within: 10 * NSEC_PER_SEC)
 
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
         self.assertAllCKKSViews(enter: SecCKKSZoneKeyStateWaitForTLKCreation, within: 10 * NSEC_PER_SEC)
@@ -2641,20 +2552,38 @@ class OctagonTests: OctagonTestsBase {
         let untrustedNotification = XCTDarwinNotificationExpectation(notificationName: octagonNotificationName)
 
         self.startCKAccountStatusMock()
         let untrustedNotification = XCTDarwinNotificationExpectation(notificationName: octagonNotificationName)
 
         self.startCKAccountStatusMock()
+
+        // Set the CDP bit before the test begins, so we don't have to fetch to discover CDP status
+        XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
+
         self.cuttlefishContext.startOctagonStateMachine()
 
         self.cuttlefishContext.startOctagonStateMachine()
 
+        // Octagon will fetch once to determine its trust state
+        let trustStateFetchExpectation = self.expectation(description: "trust state fetch occurs")
+        self.fakeCuttlefishServer.fetchChangesListener = { [unowned self] _ in
+            self.fakeCuttlefishServer.fetchChangesListener = nil
+            trustStateFetchExpectation.fulfill()
+            return nil
+        }
+
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
         self.wait(for: [untrustedNotification], timeout: 2)
 
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
         self.wait(for: [untrustedNotification], timeout: 2)
 
-        self.cuttlefishContext.notifyContainerChange(nil)
+        self.wait(for: [trustStateFetchExpectation], timeout: 10)
 
 
-        self.cuttlefishContext.notifyContainerChange(nil)
+        let fetchExpectation = self.expectation(description: "fetch occurs")
+        self.fakeCuttlefishServer.fetchChangesListener = { _ in
+            fetchExpectation.fulfill()
+            return nil
+        }
 
         self.cuttlefishContext.notifyContainerChange(nil)
 
         self.cuttlefishContext.notifyContainerChange(nil)
-
+        self.cuttlefishContext.notifyContainerChange(nil)
+        self.cuttlefishContext.notifyContainerChange(nil)
         self.cuttlefishContext.notifyContainerChange(nil)
 
         self.cuttlefishContext.notifyContainerChange(nil)
 
-        XCTAssertEqual(self.fakeCuttlefishServer.fetchChangesCalledCount, 1, "fetchChanges should have been called 1 times")
+        self.wait(for: [fetchExpectation], timeout: 10)
+        XCTAssertEqual(self.fakeCuttlefishServer.fetchChangesCalledCount, 2, "fetchChanges should have been called 1 times")
 
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
         self.assertAllCKKSViews(enter: SecCKKSZoneKeyStateWaitForTLKCreation, within: 10 * NSEC_PER_SEC)
 
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
         self.assertAllCKKSViews(enter: SecCKKSZoneKeyStateWaitForTLKCreation, within: 10 * NSEC_PER_SEC)
@@ -2664,7 +2593,14 @@ class OctagonTests: OctagonTestsBase {
         self.startCKAccountStatusMock()
         OctagonSetPlatformSupportsSOS(true)
 
         self.startCKAccountStatusMock()
         OctagonSetPlatformSupportsSOS(true)
 
-        let initiatorPiggybackingConfig = OTJoiningConfiguration(protocolType: OTProtocolPiggybacking, uniqueDeviceID: "initiator", uniqueClientID: "acceptor", containerName: OTCKContainerName, contextID: OTDefaultContext, epoch: 1, isInitiator: true)
+        let initiatorPiggybackingConfig = OTJoiningConfiguration(protocolType: OTProtocolPiggybacking,
+                                                                 uniqueDeviceID: "initiator",
+                                                                 uniqueClientID: "acceptor",
+                                                                 pairingUUID: UUID().uuidString,
+                                                                 containerName: OTCKContainerName,
+                                                                 contextID: OTDefaultContext,
+                                                                 epoch: 1,
+                                                                 isInitiator: true)
 
         let resetAndEstablishExpectation = self.expectation(description: "resetAndEstablish callback occurs")
         self.manager.resetAndEstablish(OTCKContainerName,
 
         let resetAndEstablishExpectation = self.expectation(description: "resetAndEstablish callback occurs")
         self.manager.resetAndEstablish(OTCKContainerName,
@@ -2707,6 +2643,7 @@ class OctagonTests: OctagonTestsBase {
         self.startCKAccountStatusMock()
 
         self.cuttlefishContext.startOctagonStateMachine()
         self.startCKAccountStatusMock()
 
         self.cuttlefishContext.startOctagonStateMachine()
+        XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
         do {
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
         do {
@@ -2740,12 +2677,27 @@ class OctagonTestsOverrideModelBase: OctagonTestsBase {
     struct TestCase {
         let model: String
         let success: Bool
     struct TestCase {
         let model: String
         let success: Bool
-        let sendTLKs: Bool
+        let manateeTLKs: Bool
+        let limitedTLKs: Bool
+    }
+
+    func assertTLKs(expectation: TestCase, receiverPeerID: String, senderPeerID: String) throws {
+        let haveManateeTLK = try self.tlkShareInCloudKit(receiverPeerID: receiverPeerID,
+                                                         senderPeerID: senderPeerID,
+                                                         zoneID: self.manateeZoneID)
+        let haveLimitedPeersAllowedTLK = try self.tlkShareInCloudKit(receiverPeerID: receiverPeerID,
+                                                                     senderPeerID: senderPeerID,
+                                                                     zoneID: self.limitedPeersAllowedZoneID)
+
+        XCTAssertEqual(haveManateeTLK, expectation.manateeTLKs, "manatee should be what's expected: \(expectation)")
+        XCTAssertEqual(haveLimitedPeersAllowedTLK, expectation.limitedTLKs, "limited should be what's expected: \(expectation)")
     }
 
     func _testVouchers(expectations: [TestCase]) throws {
         self.startCKAccountStatusMock()
     }
 
     func _testVouchers(expectations: [TestCase]) throws {
         self.startCKAccountStatusMock()
+
         self.cuttlefishContext.startOctagonStateMachine()
         self.cuttlefishContext.startOctagonStateMachine()
+        XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
         let clique: OTClique
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
         let clique: OTClique
@@ -2760,7 +2712,15 @@ class OctagonTestsOverrideModelBase: OctagonTestsBase {
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateReady, within: 10 * NSEC_PER_SEC)
         self.assertConsidersSelfTrusted(context: self.cuttlefishContext)
 
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateReady, within: 10 * NSEC_PER_SEC)
         self.assertConsidersSelfTrusted(context: self.cuttlefishContext)
 
-        _ = fetchEgoPeerID()
+        self.assertAllCKKSViews(enter: SecCKKSZoneKeyStateReady, within: 10 * NSEC_PER_SEC)
+
+        let ckksKeys: [CKKSKeychainBackedKeySet] = self.ckksViews.compactMap { view in
+            let viewName = (view as! CKKSKeychainView).zoneName
+            let currentKeySet = CKKSCurrentKeySet.load(forZone: CKRecordZone.ID(zoneName: viewName))
+            return try! currentKeySet.asKeychainBackedSet()
+        }
+
+        let senderPeerID = fetchEgoPeerID()
 
         for testCase in expectations {
             let model = testCase.model
 
         for testCase in expectations {
             let model = testCase.model
@@ -2775,7 +2735,7 @@ class OctagonTestsOverrideModelBase: OctagonTestsBase {
                                      accountStateTracker: self.accountStateTracker,
                                      deviceInformationAdapter: OTMockDeviceInfoAdapter(modelID: "iPhone9,1", deviceName: "test-SOS-iphone", serialNumber: "456", osVersion: "iOS (fake version)"))
 
                                      accountStateTracker: self.accountStateTracker,
                                      deviceInformationAdapter: OTMockDeviceInfoAdapter(modelID: "iPhone9,1", deviceName: "test-SOS-iphone", serialNumber: "456", osVersion: "iOS (fake version)"))
 
-            self.setAllowListToCurrentAuthKit(container: OTCKContainerName, context: peer2ContextID)
+            self.setAllowListToCurrentAuthKit(container: OTCKContainerName, context: peer2ContextID, accountIsDemo: false)
 
             let peer2DeviceName = "peer2-asdf"
             let joinExpectation = self.expectation(description: "join callback occurs")
 
             let peer2DeviceName = "peer2-asdf"
             let joinExpectation = self.expectation(description: "join callback occurs")
@@ -2792,7 +2752,7 @@ class OctagonTestsOverrideModelBase: OctagonTestsBase {
                                    policyVersion: nil,
                                    policySecrets: nil,
                                    signingPrivKeyPersistentRef: nil,
                                    policyVersion: nil,
                                    policySecrets: nil,
                                    signingPrivKeyPersistentRef: nil,
-                                   encPrivKeyPersistentRef: nil) { peerID, permanentInfo, permanentInfoSig, stableInfo, stableInfoSig, error in
+                                   encPrivKeyPersistentRef: nil) { peerID, permanentInfo, permanentInfoSig, stableInfo, stableInfoSig, _, _, error in
                                     XCTAssertNil(error, "Should be no error preparing identity")
                                     XCTAssertNotNil(permanentInfo, "Should have a permanent identity")
                                     XCTAssertNotNil(permanentInfoSig, "Should have a permanent identity signature")
                                     XCTAssertNil(error, "Should be no error preparing identity")
                                     XCTAssertNotNil(permanentInfo, "Should have a permanent identity")
                                     XCTAssertNotNil(permanentInfoSig, "Should have a permanent identity signature")
@@ -2806,17 +2766,22 @@ class OctagonTestsOverrideModelBase: OctagonTestsBase {
                                                              permanentInfoSig: permanentInfoSig!,
                                                              stableInfo: stableInfo!,
                                                              stableInfoSig: stableInfoSig!,
                                                              permanentInfoSig: permanentInfoSig!,
                                                              stableInfo: stableInfo!,
                                                              stableInfoSig: stableInfoSig!,
-                                                             ckksKeys: []) { voucher, voucherSig, error in
+                                                             ckksKeys: ckksKeys) { voucher, voucherSig, error in
                                                                 XCTAssertNil(error, "Should be no error vouching")
                                                                 XCTAssertNotNil(voucher, "Should have a voucher")
                                                                 XCTAssertNotNil(voucherSig, "Should have a voucher signature")
                                                                 XCTAssertNil(error, "Should be no error vouching")
                                                                 XCTAssertNotNil(voucher, "Should have a voucher")
                                                                 XCTAssertNotNil(voucherSig, "Should have a voucher signature")
+
+                                                                try! self.assertTLKs(expectation: testCase,
+                                                                                     receiverPeerID: peerID!,
+                                                                                     senderPeerID: senderPeerID)
+
                                                                 self.tphClient.join(withContainer: OTCKContainerName,
                                                                                     context: peer2ContextID,
                                                                                     voucherData: voucher!,
                                                                                     voucherSig: voucherSig!,
                                                                                     ckksKeys: [],
                                                                                     tlkShares: [],
                                                                 self.tphClient.join(withContainer: OTCKContainerName,
                                                                                     context: peer2ContextID,
                                                                                     voucherData: voucher!,
                                                                                     voucherSig: voucherSig!,
                                                                                     ckksKeys: [],
                                                                                     tlkShares: [],
-                                                                                    preapprovedKeys: []) { peerID, _, error in
+                                                                                    preapprovedKeys: []) { peerID, _, _, _, error in
                                                                                         XCTAssertNil(error, "Should be no error joining")
                                                                                         XCTAssertNotNil(peerID, "Should have a peerID")
                                                                                         joinExpectation.fulfill()
                                                                                         XCTAssertNil(error, "Should be no error joining")
                                                                                         XCTAssertNotNil(peerID, "Should have a peerID")
                                                                                         joinExpectation.fulfill()
@@ -2834,9 +2799,15 @@ class OctagonTestsOverrideModelBase: OctagonTestsBase {
                                                                 XCTAssertNil(voucher, "voucher should be nil")
                                                                 XCTAssertNil(voucherSig, "voucherSig should be nil")
                                                                 XCTAssertNotNil(error, "error should be non nil")
                                                                 XCTAssertNil(voucher, "voucher should be nil")
                                                                 XCTAssertNil(voucherSig, "voucherSig should be nil")
                                                                 XCTAssertNotNil(error, "error should be non nil")
+
+                                                                try! self.assertTLKs(expectation: testCase,
+                                                                                     receiverPeerID: peerID!,
+                                                                                     senderPeerID: senderPeerID)
+
                                                                 joinExpectation.fulfill()
                                         }
                                     }
                                                                 joinExpectation.fulfill()
                                         }
                                     }
+
             }
             self.wait(for: [joinExpectation], timeout: 10)
         }
             }
             self.wait(for: [joinExpectation], timeout: 10)
         }
@@ -2845,6 +2816,7 @@ class OctagonTestsOverrideModelBase: OctagonTestsBase {
     func _testJoin(expectations: [TestCase]) throws {
         self.startCKAccountStatusMock()
         self.cuttlefishContext.startOctagonStateMachine()
     func _testJoin(expectations: [TestCase]) throws {
         self.startCKAccountStatusMock()
         self.cuttlefishContext.startOctagonStateMachine()
+        XCTAssertNoThrow(try self.cuttlefishContext.setCDPEnabled())
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
         let clique: OTClique
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
         let clique: OTClique
@@ -2872,7 +2844,7 @@ class OctagonTestsOverrideModelBase: OctagonTestsBase {
             let peer2DeviceName = "peer2-device-name"
             var peer2ID: String!
 
             let peer2DeviceName = "peer2-device-name"
             var peer2ID: String!
 
-            self.setAllowListToCurrentAuthKit(container: OTCKContainerName, context: peer2ContextID)
+            self.setAllowListToCurrentAuthKit(container: OTCKContainerName, context: peer2ContextID, accountIsDemo: false)
 
             let joinExpectation = self.expectation(description: "join callback occurs")
             self.tphClient.prepare(withContainer: OTCKContainerName,
 
             let joinExpectation = self.expectation(description: "join callback occurs")
             self.tphClient.prepare(withContainer: OTCKContainerName,
@@ -2888,7 +2860,7 @@ class OctagonTestsOverrideModelBase: OctagonTestsBase {
                                    policyVersion: nil,
                                    policySecrets: nil,
                                    signingPrivKeyPersistentRef: nil,
                                    policyVersion: nil,
                                    policySecrets: nil,
                                    signingPrivKeyPersistentRef: nil,
-                                   encPrivKeyPersistentRef: nil) { peerID, permanentInfo, permanentInfoSig, stableInfo, stableInfoSig, error in
+                                   encPrivKeyPersistentRef: nil) { peerID, permanentInfo, permanentInfoSig, stableInfo, stableInfoSig, _, _, error in
                                     XCTAssertNil(error, "Should be no error preparing identity")
                                     XCTAssertNotNil(permanentInfo, "Should have a permanent identity")
                                     XCTAssertNotNil(permanentInfoSig, "Should have a permanent identity signature")
                                     XCTAssertNil(error, "Should be no error preparing identity")
                                     XCTAssertNotNil(permanentInfo, "Should have a permanent identity")
                                     XCTAssertNotNil(permanentInfoSig, "Should have a permanent identity signature")
@@ -2911,7 +2883,7 @@ class OctagonTestsOverrideModelBase: OctagonTestsBase {
                                                         voucherSig: voucher!.sig,
                                                         ckksKeys: [],
                                                         tlkShares: [],
                                                         voucherSig: voucher!.sig,
                                                         ckksKeys: [],
                                                         tlkShares: [],
-                                                        preapprovedKeys: []) { peerID, _, error in
+                                                        preapprovedKeys: []) { peerID, _, _, _, error in
                                                             if expectedSuccess {
                                                                 XCTAssertNil(error, "expected success")
                                                                 XCTAssertNotNil(peerID, "peerID should be set")
                                                             if expectedSuccess {
                                                                 XCTAssertNil(error, "expected success")
                                                                 XCTAssertNotNil(peerID, "peerID should be set")
@@ -2939,9 +2911,11 @@ class OctagonTestsOverrideModelBase: OctagonTestsBase {
                     return nil
                 }
 
                     return nil
                 }
 
-                // Maybe send TLKs?
-                if testCase.sendTLKs {
-                    self.assertAllCKKSViewsUpload(tlkShares: 1)
+                if testCase.manateeTLKs {
+                    self.expectCKModifyKeyRecords(0, currentKeyPointerRecords: 0, tlkShareRecords: 1, zoneID: self.manateeZoneID)
+                }
+                if testCase.limitedTLKs {
+                    self.expectCKModifyKeyRecords(0, currentKeyPointerRecords: 0, tlkShareRecords: 1, zoneID: self.limitedPeersAllowedZoneID)
                 }
 
                 self.cuttlefishContext.notifyContainerChange(nil)
                 }
 
                 self.cuttlefishContext.notifyContainerChange(nil)
@@ -2950,69 +2924,59 @@ class OctagonTestsOverrideModelBase: OctagonTestsBase {
                 self.fakeCuttlefishServer.updateListener = nil
 
                 self.sendAllCKKSTrustedPeersChanged()
                 self.fakeCuttlefishServer.updateListener = nil
 
                 self.sendAllCKKSTrustedPeersChanged()
-                self.assertAllCKKSViews(enter: SecCKKSZoneKeyStateReady, within: 10 * NSEC_PER_SEC)
                 self.verifyDatabaseMocks()
                 self.verifyDatabaseMocks()
+                self.assertAllCKKSViews(enter: SecCKKSZoneKeyStateReady, within: 10 * NSEC_PER_SEC)
             }
             }
+
+            try self.assertTLKs(expectation: testCase,
+                                receiverPeerID: peer2ID,
+                                senderPeerID: peer1ID)
         }
     }
 }
 
         }
     }
 }
 
-class OctagonTestsOverrideModletV: OctagonTestsOverrideModelBase {
-    // If this test is running on a TV, we will send TLKs, since we're using the LimitedPeersAllowed view
-    #if !os(tvOS)
-    let sendTLKsToAllPeers = false
-    #else
-    let sendTLKsToAllPeers = true
-    #endif
-
+class OctagonTestsOverrideModelTV: OctagonTestsOverrideModelBase {
     override func setUp() {
     override func setUp() {
-        super.setUp()
+        self.mockDeviceInfo = OTMockDeviceInfoAdapter(modelID: "AppleTV5,3",
+                                                      deviceName: "intro-TV",
+                                                      serialNumber: "456",
+                                                      osVersion: "tvOS (whatever TV version)")
 
 
-        self.mockDeviceInfo.mockModelID = "AppleTV5,3"
-        self.mockDeviceInfo.mockDeviceName = "intro-TV"
-        self.mockDeviceInfo.mockSerialNumber = "456"
-        self.mockDeviceInfo.mockOsVersion = "iOS (whatever TV version)"
+        super.setUp()
     }
 
     func testVoucherFromTV() throws {
     }
 
     func testVoucherFromTV() throws {
-        try self._testVouchers(expectations: [TestCase(model: "AppleTV5,3", success: true, sendTLKs: sendTLKsToAllPeers),
-                                              TestCase(model: "MacFoo", success: false, sendTLKs: sendTLKsToAllPeers),
-                                              TestCase(model: "Watch17", success: false, sendTLKs: sendTLKsToAllPeers), ])
+        try self._testVouchers(expectations: [TestCase(model: "AppleTV5,3", success: true, manateeTLKs: false, limitedTLKs: true),
+                                              TestCase(model: "MacFoo", success: false, manateeTLKs: false, limitedTLKs: false),
+                                              TestCase(model: "Watch17", success: false, manateeTLKs: false, limitedTLKs: false), ])
     }
 
     func testJoinFromTV() throws {
     }
 
     func testJoinFromTV() throws {
-        try self._testJoin(expectations: [TestCase(model: "AppleTV5,3", success: true, sendTLKs: sendTLKsToAllPeers),
-                                          TestCase(model: "MacFoo", success: false, sendTLKs: sendTLKsToAllPeers),
-                                          TestCase(model: "Watch17", success: false, sendTLKs: sendTLKsToAllPeers), ])
+        try self._testJoin(expectations: [TestCase(model: "AppleTV5,3", success: true, manateeTLKs: false, limitedTLKs: true),
+                                          TestCase(model: "MacFoo", success: false, manateeTLKs: false, limitedTLKs: false),
+                                          TestCase(model: "Watch17", success: false, manateeTLKs: false, limitedTLKs: false), ])
     }
 }
 
 class OctagonTestsOverrideModelMac: OctagonTestsOverrideModelBase {
     }
 }
 
 class OctagonTestsOverrideModelMac: OctagonTestsOverrideModelBase {
-    #if !os(tvOS)
-    let sendTLKsToAllPeers = false
-    #else
-    let sendTLKsToAllPeers = true
-    #endif
-
     override func setUp() {
     override func setUp() {
+        self.mockDeviceInfo = OTMockDeviceInfoAdapter(modelID: "Mac17",
+                                                      deviceName: "macbook",
+                                                      serialNumber: "456",
+                                                      osVersion: "OSX 11")
         super.setUp()
         super.setUp()
-
-        self.mockDeviceInfo.mockModelID = "Mac17"
-        self.mockDeviceInfo.mockDeviceName = "macbook"
-        self.mockDeviceInfo.mockSerialNumber = "456"
-        self.mockDeviceInfo.mockOsVersion = "OSX 11"
     }
 
     func testVoucherFromMac() throws {
     }
 
     func testVoucherFromMac() throws {
-        try self._testVouchers(expectations: [TestCase(model: "AppleTV5,3", success: true, sendTLKs: sendTLKsToAllPeers),
-                                              TestCase(model: "MacFoo", success: true, sendTLKs: sendTLKsToAllPeers),
-                                              TestCase(model: "Watch17", success: true, sendTLKs: sendTLKsToAllPeers), ])
+        try self._testVouchers(expectations: [TestCase(model: "AppleTV5,3", success: true, manateeTLKs: false, limitedTLKs: true),
+                                              TestCase(model: "MacFoo", success: true, manateeTLKs: true, limitedTLKs: true),
+                                              TestCase(model: "Watch17", success: true, manateeTLKs: true, limitedTLKs: true), ])
     }
 
     func testJoinFromMac() throws {
     }
 
     func testJoinFromMac() throws {
-        try self._testJoin(expectations: [TestCase(model: "AppleTV5,3", success: true, sendTLKs: sendTLKsToAllPeers),
-                                          TestCase(model: "MacFoo", success: true, sendTLKs: true),
-                                          TestCase(model: "Watch17", success: true, sendTLKs: true), ])
+        try self._testJoin(expectations: [TestCase(model: "AppleTV5,3", success: true, manateeTLKs: false, limitedTLKs: true),
+                                          TestCase(model: "MacFoo", success: true, manateeTLKs: true, limitedTLKs: true),
+                                          TestCase(model: "Watch17", success: true, manateeTLKs: true, limitedTLKs: true), ])
     }
 }
 
     }
 }
 
index 17901fea6c705af240e512ae90352f2794210112..2c76a5ecaf11e963735ab61ca54974ce61e96600 100644 (file)
@@ -92,12 +92,10 @@ extension OctagonPairingTests {
         }
         self.wait(for: [rpcEpochCallbackOccurs], timeout: 10)
 
         }
         self.wait(for: [rpcEpochCallbackOccurs], timeout: 10)
 
-        let initiator1Context = self.manager.context(forContainerName: OTCKContainerName, contextID: OTDefaultContext)
-
         let clientStateMachine = self.manager.clientStateMachine(forContainerName: OTCKContainerName, contextID: self.contextForAcceptor, clientName: self.initiatorName)
         let clientStateMachine = self.manager.clientStateMachine(forContainerName: OTCKContainerName, contextID: self.contextForAcceptor, clientName: self.initiatorName)
-        initiator1Context.startOctagonStateMachine()
+        self.cuttlefishContext.startOctagonStateMachine()
 
 
-        self.assertEnters(context: initiator1Context, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
+        self.assertEnters(context: self.cuttlefishContext, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
         var peerID: String = ""
         var permanentInfo = Data(count: 0)
 
         var peerID: String = ""
         var permanentInfo = Data(count: 0)
@@ -140,15 +138,15 @@ extension OctagonPairingTests {
         self.wait(for: [firstMessageAcceptorCallback], timeout: 10)
 
         let rpcJoinCallback = self.expectation(description: "joining callback")
         self.wait(for: [firstMessageAcceptorCallback], timeout: 10)
 
         let rpcJoinCallback = self.expectation(description: "joining callback")
-        self.manager.rpcJoin(with: self.initiatorPiggybackingConfig, vouchData: voucher, vouchSig: voucherSig, preapprovedKeys: nil) { error in
+        self.manager.rpcJoin(with: self.initiatorPiggybackingConfig, vouchData: voucher, vouchSig: voucherSig) { error in
             XCTAssertNil(error, "error should be nil")
             rpcJoinCallback.fulfill()
         }
         self.wait(for: [rpcJoinCallback], timeout: 10)
 
             XCTAssertNil(error, "error should be nil")
             rpcJoinCallback.fulfill()
         }
         self.wait(for: [rpcJoinCallback], timeout: 10)
 
-        assertAllCKKSViews(enter: SecCKKSZoneKeyStateReady, within: 10 * NSEC_PER_SEC)
-
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateReady, within: 10 * NSEC_PER_SEC)
         self.assertEnters(context: self.cuttlefishContext, state: OctagonStateReady, within: 10 * NSEC_PER_SEC)
+        self.assertAllCKKSViews(enter: SecCKKSZoneKeyStateReady, within: 10 * NSEC_PER_SEC)
+
         self.assertEnters(context: self.cuttlefishContextForAcceptor, state: OctagonStateReady, within: 10 * NSEC_PER_SEC)
         self.assertConsidersSelfTrusted(context: self.cuttlefishContext)
         self.assertConsidersSelfTrusted(context: self.cuttlefishContextForAcceptor)
         self.assertEnters(context: self.cuttlefishContextForAcceptor, state: OctagonStateReady, within: 10 * NSEC_PER_SEC)
         self.assertConsidersSelfTrusted(context: self.cuttlefishContext)
         self.assertConsidersSelfTrusted(context: self.cuttlefishContextForAcceptor)
@@ -156,14 +154,13 @@ extension OctagonPairingTests {
         clientStateMachine.notifyContainerChange()
 
         let initiatorDumpCallback = self.expectation(description: "initiatorDumpCallback callback occurs")
         clientStateMachine.notifyContainerChange()
 
         let initiatorDumpCallback = self.expectation(description: "initiatorDumpCallback callback occurs")
-        self.tphClient.dump(withContainer: self.cuttlefishContext.containerName, context: self.cuttlefishContext.contextID) {
-            dump, _ in
+        self.tphClient.dump(withContainer: self.cuttlefishContext.containerName, context: self.cuttlefishContext.contextID) { dump, _ in
             XCTAssertNotNil(dump, "dump should not be nil")
             XCTAssertNotNil(dump, "dump should not be nil")
-            let egoSelf = dump!["self"] as? Dictionary<String, AnyObject>
+            let egoSelf = dump!["self"] as? [String: AnyObject]
             XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
             XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
-            let dynamicInfo = egoSelf!["dynamicInfo"] as? Dictionary<String, AnyObject>
+            let dynamicInfo = egoSelf!["dynamicInfo"] as? [String: AnyObject]
             XCTAssertNotNil(dynamicInfo, "dynamicInfo should not be nil")
             XCTAssertNotNil(dynamicInfo, "dynamicInfo should not be nil")
-            let included = dynamicInfo!["included"] as? Array<String>
+            let included = dynamicInfo!["included"] as? [String]
             XCTAssertNotNil(included, "included should not be nil")
             XCTAssertEqual(included!.count, 2, "should be 2 peer ids")
 
             XCTAssertNotNil(included, "included should not be nil")
             XCTAssertEqual(included!.count, 2, "should be 2 peer ids")
 
@@ -172,14 +169,13 @@ extension OctagonPairingTests {
         self.wait(for: [initiatorDumpCallback], timeout: 10)
 
         let acceptorDumpCallback = self.expectation(description: "acceptorDumpCallback callback occurs")
         self.wait(for: [initiatorDumpCallback], timeout: 10)
 
         let acceptorDumpCallback = self.expectation(description: "acceptorDumpCallback callback occurs")
-        self.tphClient.dump(withContainer: self.cuttlefishContext.containerName, context: self.contextForAcceptor) {
-            dump, _ in
+        self.tphClient.dump(withContainer: self.cuttlefishContext.containerName, context: self.contextForAcceptor) { dump, _ in
             XCTAssertNotNil(dump, "dump should not be nil")
             XCTAssertNotNil(dump, "dump should not be nil")
-            let egoSelf = dump!["self"] as? Dictionary<String, AnyObject>
+            let egoSelf = dump!["self"] as? [String: AnyObject]
             XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
             XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
-            let dynamicInfo = egoSelf!["dynamicInfo"] as? Dictionary<String, AnyObject>
+            let dynamicInfo = egoSelf!["dynamicInfo"] as? [String: AnyObject]
             XCTAssertNotNil(dynamicInfo, "dynamicInfo should not be nil")
             XCTAssertNotNil(dynamicInfo, "dynamicInfo should not be nil")
-            let included = dynamicInfo!["included"] as? Array<String>
+            let included = dynamicInfo!["included"] as? [String]
             XCTAssertNotNil(included, "included should not be nil")
             XCTAssertEqual(included!.count, 2, "should be 2 peer ids")
             acceptorDumpCallback.fulfill()
             XCTAssertNotNil(included, "included should not be nil")
             XCTAssertEqual(included!.count, 2, "should be 2 peer ids")
             acceptorDumpCallback.fulfill()
@@ -187,9 +183,11 @@ extension OctagonPairingTests {
         self.wait(for: [acceptorDumpCallback], timeout: 10)
 
         self.verifyDatabaseMocks()
         self.wait(for: [acceptorDumpCallback], timeout: 10)
 
         self.verifyDatabaseMocks()
+
+        self.assertAllCKKSViews(enter: SecCKKSZoneKeyStateReady, within: 10 * NSEC_PER_SEC)
     }
 
     }
 
-    func testVersion2ofPiggybacking() {
+    func testVersion2ofPiggybacking() throws {
         KCSetJoiningOctagonPiggybackingEnabled(true)
         OctagonSetIsEnabled(true)
         self.startCKAccountStatusMock()
         KCSetJoiningOctagonPiggybackingEnabled(true)
         OctagonSetIsEnabled(true)
         self.startCKAccountStatusMock()
@@ -264,7 +262,7 @@ extension OctagonPairingTests {
                                                                  error: nil)
         XCTAssertNotNil(requestCircleSession, "No request secret session")
 
                                                                  error: nil)
         XCTAssertNotNil(requestCircleSession, "No request secret session")
 
-        requestCircleSession.setJoiningConfigurationObject(self.initiatorPiggybackingConfig)
+        requestCircleSession.setContextIDOnJoiningConfiguration(self.initiatorPiggybackingConfig.contextID)
         requestCircleSession.setControlObject(self.otControl)
 
         var identityMessage: Data?
         requestCircleSession.setControlObject(self.otControl)
 
         var identityMessage: Data?
@@ -276,6 +274,10 @@ extension OctagonPairingTests {
             XCTAssertNil(error, "error retrieving identityMessage message")
         }
 
             XCTAssertNil(error, "error retrieving identityMessage message")
         }
 
+        // Double-check that there's an Octagon message in the packet
+        let initiatorIdentityMessage = try self.unpackPiggybackingInitialMessage(identityMessage: identityMessage!, session: acceptSession!.accessSession())
+        XCTAssertTrue(initiatorIdentityMessage.hasPrepare, "Pairing message should contain prepared information")
+
         var voucherMessage: Data?
         do {
             voucherMessage = try acceptSession!.processMessage(identityMessage!)
         var voucherMessage: Data?
         do {
             voucherMessage = try acceptSession!.processMessage(identityMessage!)
@@ -312,14 +314,13 @@ extension OctagonPairingTests {
         self.verifyDatabaseMocks()
 
         let initiatorDumpCallback = self.expectation(description: "initiatorDumpCallback callback occurs")
         self.verifyDatabaseMocks()
 
         let initiatorDumpCallback = self.expectation(description: "initiatorDumpCallback callback occurs")
-        self.tphClient.dump(withContainer: self.cuttlefishContext.containerName, context: self.cuttlefishContext.contextID) {
-            dump, _ in
+        self.tphClient.dump(withContainer: self.cuttlefishContext.containerName, context: self.cuttlefishContext.contextID) { dump, _ in
             XCTAssertNotNil(dump, "dump should not be nil")
             XCTAssertNotNil(dump, "dump should not be nil")
-            let egoSelf = dump!["self"] as? Dictionary<String, AnyObject>
+            let egoSelf = dump!["self"] as? [String: AnyObject]
             XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
             XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
-            let dynamicInfo = egoSelf!["dynamicInfo"] as? Dictionary<String, AnyObject>
+            let dynamicInfo = egoSelf!["dynamicInfo"] as? [String: AnyObject]
             XCTAssertNotNil(dynamicInfo, "dynamicInfo should not be nil")
             XCTAssertNotNil(dynamicInfo, "dynamicInfo should not be nil")
-            let included = dynamicInfo!["included"] as? Array<String>
+            let included = dynamicInfo!["included"] as? [String]
             XCTAssertNotNil(included, "included should not be nil")
             XCTAssertEqual(included!.count, 2, "should be 2 peer ids")
 
             XCTAssertNotNil(included, "included should not be nil")
             XCTAssertEqual(included!.count, 2, "should be 2 peer ids")
 
@@ -328,14 +329,13 @@ extension OctagonPairingTests {
         self.wait(for: [initiatorDumpCallback], timeout: 10)
 
         let acceptorDumpCallback = self.expectation(description: "acceptorDumpCallback callback occurs")
         self.wait(for: [initiatorDumpCallback], timeout: 10)
 
         let acceptorDumpCallback = self.expectation(description: "acceptorDumpCallback callback occurs")
-        self.tphClient.dump(withContainer: self.cuttlefishContext.containerName, context: self.contextForAcceptor) {
-            dump, _ in
+        self.tphClient.dump(withContainer: self.cuttlefishContext.containerName, context: self.contextForAcceptor) { dump, _ in
             XCTAssertNotNil(dump, "dump should not be nil")
             XCTAssertNotNil(dump, "dump should not be nil")
-            let egoSelf = dump!["self"] as? Dictionary<String, AnyObject>
+            let egoSelf = dump!["self"] as? [String: AnyObject]
             XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
             XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
-            let dynamicInfo = egoSelf!["dynamicInfo"] as? Dictionary<String, AnyObject>
+            let dynamicInfo = egoSelf!["dynamicInfo"] as? [String: AnyObject]
             XCTAssertNotNil(dynamicInfo, "dynamicInfo should not be nil")
             XCTAssertNotNil(dynamicInfo, "dynamicInfo should not be nil")
-            let included = dynamicInfo!["included"] as? Array<String>
+            let included = dynamicInfo!["included"] as? [String]
             XCTAssertNotNil(included, "included should not be nil")
             XCTAssertEqual(included!.count, 2, "should be 2 peer ids")
             acceptorDumpCallback.fulfill()
             XCTAssertNotNil(included, "included should not be nil")
             XCTAssertEqual(included!.count, 2, "should be 2 peer ids")
             acceptorDumpCallback.fulfill()
@@ -545,7 +545,7 @@ extension OctagonPairingTests {
         self.wait(for: [firstMessageWithNewJoiningConfigCallback], timeout: 10)
     }
 
         self.wait(for: [firstMessageWithNewJoiningConfigCallback], timeout: 10)
     }
 
-    func testVersion2ofPiggybackingWithSOS() {
+    func testVersion2ofPiggybackingWithSOS() throws {
         KCSetJoiningOctagonPiggybackingEnabled(true)
         OctagonSetPlatformSupportsSOS(true)
         self.startCKAccountStatusMock()
         KCSetJoiningOctagonPiggybackingEnabled(true)
         OctagonSetPlatformSupportsSOS(true)
         self.startCKAccountStatusMock()
@@ -626,7 +626,7 @@ extension OctagonPairingTests {
                                                                  error: nil)
         XCTAssertNotNil(requestCircleSession, "No request secret session")
 
                                                                  error: nil)
         XCTAssertNotNil(requestCircleSession, "No request secret session")
 
-        requestCircleSession.setJoiningConfigurationObject(self.initiatorPiggybackingConfig)
+        requestCircleSession.setContextIDOnJoiningConfiguration(self.initiatorPiggybackingConfig.contextID)
         requestCircleSession.setControlObject(self.otControl)
 
         var identityMessage: Data?
         requestCircleSession.setControlObject(self.otControl)
 
         var identityMessage: Data?
@@ -641,6 +641,10 @@ extension OctagonPairingTests {
             XCTAssertNil(error, "error retrieving identityMessage message")
         }
 
             XCTAssertNil(error, "error retrieving identityMessage message")
         }
 
+        // Double-check that there's an Octagon message in the packet
+        let initiatorIdentityMessage = try self.unpackPiggybackingInitialMessage(identityMessage: identityMessage!, session: acceptSession!.accessSession())
+        XCTAssertTrue(initiatorIdentityMessage.hasPrepare, "Pairing message should contain prepared information")
+
         var voucherMessage: Data?
         do {
             voucherMessage = try acceptSession!.processMessage(identityMessage!)
         var voucherMessage: Data?
         do {
             voucherMessage = try acceptSession!.processMessage(identityMessage!)
@@ -650,7 +654,6 @@ extension OctagonPairingTests {
             XCTAssertNotNil(voucherMessage, "No voucherMessage message")
         } catch {
             XCTAssertNil(error, "error retrieving voucherMessage message")
             XCTAssertNotNil(voucherMessage, "No voucherMessage message")
         } catch {
             XCTAssertNil(error, "error retrieving voucherMessage message")
-
         }
 
         var nothing: Data?
         }
 
         var nothing: Data?
@@ -680,14 +683,13 @@ extension OctagonPairingTests {
         self.assertTLKSharesInCloudKit(receiver: self.cuttlefishContextForAcceptor, sender: self.cuttlefishContext)
 
         let initiatorDumpCallback = self.expectation(description: "initiatorDumpCallback callback occurs")
         self.assertTLKSharesInCloudKit(receiver: self.cuttlefishContextForAcceptor, sender: self.cuttlefishContext)
 
         let initiatorDumpCallback = self.expectation(description: "initiatorDumpCallback callback occurs")
-        self.tphClient.dump(withContainer: self.cuttlefishContext.containerName, context: self.cuttlefishContext.contextID) {
-            dump, _ in
+        self.tphClient.dump(withContainer: self.cuttlefishContext.containerName, context: self.cuttlefishContext.contextID) { dump, _ in
             XCTAssertNotNil(dump, "dump should not be nil")
             XCTAssertNotNil(dump, "dump should not be nil")
-            let egoSelf = dump!["self"] as? Dictionary<String, AnyObject>
+            let egoSelf = dump!["self"] as? [String: AnyObject]
             XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
             XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
-            let dynamicInfo = egoSelf!["dynamicInfo"] as? Dictionary<String, AnyObject>
+            let dynamicInfo = egoSelf!["dynamicInfo"] as? [String: AnyObject]
             XCTAssertNotNil(dynamicInfo, "dynamicInfo should not be nil")
             XCTAssertNotNil(dynamicInfo, "dynamicInfo should not be nil")
-            let included = dynamicInfo!["included"] as? Array<String>
+            let included = dynamicInfo!["included"] as? [String]
             XCTAssertNotNil(included, "included should not be nil")
             XCTAssertEqual(included!.count, 2, "should be 2 peer ids")
 
             XCTAssertNotNil(included, "included should not be nil")
             XCTAssertEqual(included!.count, 2, "should be 2 peer ids")
 
@@ -696,14 +698,13 @@ extension OctagonPairingTests {
         self.wait(for: [initiatorDumpCallback], timeout: 10)
 
         let acceptorDumpCallback = self.expectation(description: "acceptorDumpCallback callback occurs")
         self.wait(for: [initiatorDumpCallback], timeout: 10)
 
         let acceptorDumpCallback = self.expectation(description: "acceptorDumpCallback callback occurs")
-        self.tphClient.dump(withContainer: self.cuttlefishContext.containerName, context: self.contextForAcceptor) {
-            dump, _ in
+        self.tphClient.dump(withContainer: self.cuttlefishContext.containerName, context: self.contextForAcceptor) { dump, _ in
             XCTAssertNotNil(dump, "dump should not be nil")
             XCTAssertNotNil(dump, "dump should not be nil")
-            let egoSelf = dump!["self"] as? Dictionary<String, AnyObject>
+            let egoSelf = dump!["self"] as? [String: AnyObject]
             XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
             XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
-            let dynamicInfo = egoSelf!["dynamicInfo"] as? Dictionary<String, AnyObject>
+            let dynamicInfo = egoSelf!["dynamicInfo"] as? [String: AnyObject]
             XCTAssertNotNil(dynamicInfo, "dynamicInfo should not be nil")
             XCTAssertNotNil(dynamicInfo, "dynamicInfo should not be nil")
-            let included = dynamicInfo!["included"] as? Array<String>
+            let included = dynamicInfo!["included"] as? [String]
             XCTAssertNotNil(included, "included should not be nil")
             XCTAssertEqual(included!.count, 2, "should be 2 peer ids")
             acceptorDumpCallback.fulfill()
             XCTAssertNotNil(included, "included should not be nil")
             XCTAssertEqual(included!.count, 2, "should be 2 peer ids")
             acceptorDumpCallback.fulfill()
@@ -791,7 +792,7 @@ extension OctagonPairingTests {
                                                                  error: nil)
         XCTAssertNotNil(requestCircleSession, "No request secret session")
 
                                                                  error: nil)
         XCTAssertNotNil(requestCircleSession, "No request secret session")
 
-        requestCircleSession.setJoiningConfigurationObject(self.initiatorPiggybackingConfig)
+        requestCircleSession.setContextIDOnJoiningConfiguration(self.initiatorPiggybackingConfig.contextID)
         requestCircleSession.setControlObject(self.otControl)
 
         var identityMessage: Data?
         requestCircleSession.setControlObject(self.otControl)
 
         var identityMessage: Data?
index e6b28859b0a3c2d44cd63bb910df2ea181d43391..142b3410018c85d54a31e527cf4184fa753ef6bc 100644 (file)
@@ -145,14 +145,13 @@ extension OctagonPairingTests {
         clientStateMachine.notifyContainerChange()
 
         let initiatorDumpCallback = self.expectation(description: "initiatorDumpCallback callback occurs")
         clientStateMachine.notifyContainerChange()
 
         let initiatorDumpCallback = self.expectation(description: "initiatorDumpCallback callback occurs")
-        self.tphClient.dump(withContainer: self.cuttlefishContext.containerName, context: self.cuttlefishContext.contextID) {
-            dump, _ in
+        self.tphClient.dump(withContainer: self.cuttlefishContext.containerName, context: self.cuttlefishContext.contextID) { dump, _ in
             XCTAssertNotNil(dump, "dump should not be nil")
             XCTAssertNotNil(dump, "dump should not be nil")
-            let egoSelf = dump!["self"] as? Dictionary<String, AnyObject>
+            let egoSelf = dump!["self"] as? [String: AnyObject]
             XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
             XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
-            let dynamicInfo = egoSelf!["dynamicInfo"] as? Dictionary<String, AnyObject>
+            let dynamicInfo = egoSelf!["dynamicInfo"] as? [String: AnyObject]
             XCTAssertNotNil(dynamicInfo, "dynamicInfo should not be nil")
             XCTAssertNotNil(dynamicInfo, "dynamicInfo should not be nil")
-            let included = dynamicInfo!["included"] as? Array<String>
+            let included = dynamicInfo!["included"] as? [String]
             XCTAssertNotNil(included, "included should not be nil")
             XCTAssertEqual(included!.count, 2, "should be 2 peer ids")
 
             XCTAssertNotNil(included, "included should not be nil")
             XCTAssertEqual(included!.count, 2, "should be 2 peer ids")
 
@@ -161,14 +160,13 @@ extension OctagonPairingTests {
         self.wait(for: [initiatorDumpCallback], timeout: 10)
 
         let acceptorDumpCallback = self.expectation(description: "acceptorDumpCallback callback occurs")
         self.wait(for: [initiatorDumpCallback], timeout: 10)
 
         let acceptorDumpCallback = self.expectation(description: "acceptorDumpCallback callback occurs")
-        self.tphClient.dump(withContainer: self.cuttlefishContext.containerName, context: self.contextForAcceptor) {
-            dump, _ in
+        self.tphClient.dump(withContainer: self.cuttlefishContext.containerName, context: self.contextForAcceptor) { dump, _ in
             XCTAssertNotNil(dump, "dump should not be nil")
             XCTAssertNotNil(dump, "dump should not be nil")
-            let egoSelf = dump!["self"] as? Dictionary<String, AnyObject>
+            let egoSelf = dump!["self"] as? [String: AnyObject]
             XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
             XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
-            let dynamicInfo = egoSelf!["dynamicInfo"] as? Dictionary<String, AnyObject>
+            let dynamicInfo = egoSelf!["dynamicInfo"] as? [String: AnyObject]
             XCTAssertNotNil(dynamicInfo, "dynamicInfo should not be nil")
             XCTAssertNotNil(dynamicInfo, "dynamicInfo should not be nil")
-            let included = dynamicInfo!["included"] as? Array<String>
+            let included = dynamicInfo!["included"] as? [String]
             XCTAssertNotNil(included, "included should not be nil")
             XCTAssertEqual(included!.count, 2, "should be 2 peer ids")
             acceptorDumpCallback.fulfill()
             XCTAssertNotNil(included, "included should not be nil")
             XCTAssertEqual(included!.count, 2, "should be 2 peer ids")
             acceptorDumpCallback.fulfill()
@@ -285,14 +283,13 @@ extension OctagonPairingTests {
         clientStateMachine2.notifyContainerChange()
 
         let pair2InitiatorDumpCallback = self.expectation(description: "initiatorDumpCallback callback occurs")
         clientStateMachine2.notifyContainerChange()
 
         let pair2InitiatorDumpCallback = self.expectation(description: "initiatorDumpCallback callback occurs")
-        self.tphClient.dump(withContainer: self.cuttlefishContext.containerName, context: self.cuttlefishContext.contextID) {
-            dump, _ in
+        self.tphClient.dump(withContainer: self.cuttlefishContext.containerName, context: self.cuttlefishContext.contextID) { dump, _ in
             XCTAssertNotNil(dump, "dump should not be nil")
             XCTAssertNotNil(dump, "dump should not be nil")
-            let egoSelf = dump!["self"] as? Dictionary<String, AnyObject>
+            let egoSelf = dump!["self"] as? [String: AnyObject]
             XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
             XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
-            let dynamicInfo = egoSelf!["dynamicInfo"] as? Dictionary<String, AnyObject>
+            let dynamicInfo = egoSelf!["dynamicInfo"] as? [String: AnyObject]
             XCTAssertNotNil(dynamicInfo, "dynamicInfo should not be nil")
             XCTAssertNotNil(dynamicInfo, "dynamicInfo should not be nil")
-            let included = dynamicInfo!["included"] as? Array<String>
+            let included = dynamicInfo!["included"] as? [String]
             XCTAssertNotNil(included, "included should not be nil")
             XCTAssertEqual(included!.count, 2, "should be 2 peer ids")
 
             XCTAssertNotNil(included, "included should not be nil")
             XCTAssertEqual(included!.count, 2, "should be 2 peer ids")
 
@@ -301,14 +298,13 @@ extension OctagonPairingTests {
         self.wait(for: [pair2InitiatorDumpCallback], timeout: 10)
 
         let pair2AcceptorDumpCallback = self.expectation(description: "acceptorDumpCallback callback occurs")
         self.wait(for: [pair2InitiatorDumpCallback], timeout: 10)
 
         let pair2AcceptorDumpCallback = self.expectation(description: "acceptorDumpCallback callback occurs")
-        self.tphClient.dump(withContainer: self.cuttlefishContext.containerName, context: self.contextForAcceptor) {
-            dump, _ in
+        self.tphClient.dump(withContainer: self.cuttlefishContext.containerName, context: self.contextForAcceptor) { dump, _ in
             XCTAssertNotNil(dump, "dump should not be nil")
             XCTAssertNotNil(dump, "dump should not be nil")
-            let egoSelf = dump!["self"] as? Dictionary<String, AnyObject>
+            let egoSelf = dump!["self"] as? [String: AnyObject]
             XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
             XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
-            let dynamicInfo = egoSelf!["dynamicInfo"] as? Dictionary<String, AnyObject>
+            let dynamicInfo = egoSelf!["dynamicInfo"] as? [String: AnyObject]
             XCTAssertNotNil(dynamicInfo, "dynamicInfo should not be nil")
             XCTAssertNotNil(dynamicInfo, "dynamicInfo should not be nil")
-            let included = dynamicInfo!["included"] as? Array<String>
+            let included = dynamicInfo!["included"] as? [String]
             XCTAssertNotNil(included, "included should not be nil")
             XCTAssertEqual(included!.count, 3, "should be 3 peer ids")
             pair2AcceptorDumpCallback.fulfill()
             XCTAssertNotNil(included, "included should not be nil")
             XCTAssertEqual(included!.count, 3, "should be 3 peer ids")
             pair2AcceptorDumpCallback.fulfill()
@@ -437,14 +433,13 @@ extension OctagonPairingTests {
         clientStateMachine.notifyContainerChange()
 
         let initiatorDumpCallback = self.expectation(description: "initiatorDumpCallback callback occurs")
         clientStateMachine.notifyContainerChange()
 
         let initiatorDumpCallback = self.expectation(description: "initiatorDumpCallback callback occurs")
-        self.tphClient.dump(withContainer: self.cuttlefishContext.containerName, context: self.cuttlefishContext.contextID) {
-            dump, _ in
+        self.tphClient.dump(withContainer: self.cuttlefishContext.containerName, context: self.cuttlefishContext.contextID) { dump, _ in
             XCTAssertNotNil(dump, "dump should not be nil")
             XCTAssertNotNil(dump, "dump should not be nil")
-            let egoSelf = dump!["self"] as? Dictionary<String, AnyObject>
+            let egoSelf = dump!["self"] as? [String: AnyObject]
             XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
             XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
-            let dynamicInfo = egoSelf!["dynamicInfo"] as? Dictionary<String, AnyObject>
+            let dynamicInfo = egoSelf!["dynamicInfo"] as? [String: AnyObject]
             XCTAssertNotNil(dynamicInfo, "dynamicInfo should not be nil")
             XCTAssertNotNil(dynamicInfo, "dynamicInfo should not be nil")
-            let included = dynamicInfo!["included"] as? Array<String>
+            let included = dynamicInfo!["included"] as? [String]
             XCTAssertNotNil(included, "included should not be nil")
             XCTAssertEqual(included!.count, 2, "should be 2 peer ids")
 
             XCTAssertNotNil(included, "included should not be nil")
             XCTAssertEqual(included!.count, 2, "should be 2 peer ids")
 
@@ -453,14 +448,13 @@ extension OctagonPairingTests {
         self.wait(for: [initiatorDumpCallback], timeout: 10)
 
         let acceptorDumpCallback = self.expectation(description: "acceptorDumpCallback callback occurs")
         self.wait(for: [initiatorDumpCallback], timeout: 10)
 
         let acceptorDumpCallback = self.expectation(description: "acceptorDumpCallback callback occurs")
-        self.tphClient.dump(withContainer: self.cuttlefishContext.containerName, context: self.contextForAcceptor) {
-            dump, _ in
+        self.tphClient.dump(withContainer: self.cuttlefishContext.containerName, context: self.contextForAcceptor) { dump, _ in
             XCTAssertNotNil(dump, "dump should not be nil")
             XCTAssertNotNil(dump, "dump should not be nil")
-            let egoSelf = dump!["self"] as? Dictionary<String, AnyObject>
+            let egoSelf = dump!["self"] as? [String: AnyObject]
             XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
             XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
-            let dynamicInfo = egoSelf!["dynamicInfo"] as? Dictionary<String, AnyObject>
+            let dynamicInfo = egoSelf!["dynamicInfo"] as? [String: AnyObject]
             XCTAssertNotNil(dynamicInfo, "dynamicInfo should not be nil")
             XCTAssertNotNil(dynamicInfo, "dynamicInfo should not be nil")
-            let included = dynamicInfo!["included"] as? Array<String>
+            let included = dynamicInfo!["included"] as? [String]
             XCTAssertNotNil(included, "included should not be nil")
             XCTAssertEqual(included!.count, 2, "should be 2 peer ids")
             acceptorDumpCallback.fulfill()
             XCTAssertNotNil(included, "included should not be nil")
             XCTAssertEqual(included!.count, 2, "should be 2 peer ids")
             acceptorDumpCallback.fulfill()
@@ -549,14 +543,13 @@ extension OctagonPairingTests {
         clientStateMachine2.notifyContainerChange()
 
         let pair2InitiatorDumpCallback = self.expectation(description: "initiatorDumpCallback callback occurs")
         clientStateMachine2.notifyContainerChange()
 
         let pair2InitiatorDumpCallback = self.expectation(description: "initiatorDumpCallback callback occurs")
-        self.tphClient.dump(withContainer: self.cuttlefishContext.containerName, context: self.cuttlefishContext.contextID) {
-            dump, _ in
+        self.tphClient.dump(withContainer: self.cuttlefishContext.containerName, context: self.cuttlefishContext.contextID) { dump, _ in
             XCTAssertNotNil(dump, "dump should not be nil")
             XCTAssertNotNil(dump, "dump should not be nil")
-            let egoSelf = dump!["self"] as? Dictionary<String, AnyObject>
+            let egoSelf = dump!["self"] as? [String: AnyObject]
             XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
             XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
-            let dynamicInfo = egoSelf!["dynamicInfo"] as? Dictionary<String, AnyObject>
+            let dynamicInfo = egoSelf!["dynamicInfo"] as? [String: AnyObject]
             XCTAssertNotNil(dynamicInfo, "dynamicInfo should not be nil")
             XCTAssertNotNil(dynamicInfo, "dynamicInfo should not be nil")
-            let included = dynamicInfo!["included"] as? Array<String>
+            let included = dynamicInfo!["included"] as? [String]
             XCTAssertNotNil(included, "included should not be nil")
             XCTAssertEqual(included!.count, 2, "should be 2 peer ids")
 
             XCTAssertNotNil(included, "included should not be nil")
             XCTAssertEqual(included!.count, 2, "should be 2 peer ids")
 
@@ -565,14 +558,13 @@ extension OctagonPairingTests {
         self.wait(for: [pair2InitiatorDumpCallback], timeout: 10)
 
         let pair2AcceptorDumpCallback = self.expectation(description: "acceptorDumpCallback callback occurs")
         self.wait(for: [pair2InitiatorDumpCallback], timeout: 10)
 
         let pair2AcceptorDumpCallback = self.expectation(description: "acceptorDumpCallback callback occurs")
-        self.tphClient.dump(withContainer: self.cuttlefishContext.containerName, context: self.contextForAcceptor) {
-            dump, _ in
+        self.tphClient.dump(withContainer: self.cuttlefishContext.containerName, context: self.contextForAcceptor) { dump, _ in
             XCTAssertNotNil(dump, "dump should not be nil")
             XCTAssertNotNil(dump, "dump should not be nil")
-            let egoSelf = dump!["self"] as? Dictionary<String, AnyObject>
+            let egoSelf = dump!["self"] as? [String: AnyObject]
             XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
             XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
-            let dynamicInfo = egoSelf!["dynamicInfo"] as? Dictionary<String, AnyObject>
+            let dynamicInfo = egoSelf!["dynamicInfo"] as? [String: AnyObject]
             XCTAssertNotNil(dynamicInfo, "dynamicInfo should not be nil")
             XCTAssertNotNil(dynamicInfo, "dynamicInfo should not be nil")
-            let included = dynamicInfo!["included"] as? Array<String>
+            let included = dynamicInfo!["included"] as? [String]
             XCTAssertNotNil(included, "included should not be nil")
             XCTAssertEqual(included!.count, 3, "should be 3 peer ids")
             pair2AcceptorDumpCallback.fulfill()
             XCTAssertNotNil(included, "included should not be nil")
             XCTAssertEqual(included!.count, 3, "should be 3 peer ids")
             pair2AcceptorDumpCallback.fulfill()
@@ -973,14 +965,13 @@ extension OctagonPairingTests {
         clientStateMachine.notifyContainerChange()
 
         let initiatorDumpCallback = self.expectation(description: "initiatorDumpCallback callback occurs")
         clientStateMachine.notifyContainerChange()
 
         let initiatorDumpCallback = self.expectation(description: "initiatorDumpCallback callback occurs")
-        self.tphClient.dump(withContainer: self.cuttlefishContext.containerName, context: self.cuttlefishContext.contextID) {
-            dump, _ in
+        self.tphClient.dump(withContainer: self.cuttlefishContext.containerName, context: self.cuttlefishContext.contextID) { dump, _ in
             XCTAssertNotNil(dump, "dump should not be nil")
             XCTAssertNotNil(dump, "dump should not be nil")
-            let egoSelf = dump!["self"] as? Dictionary<String, AnyObject>
+            let egoSelf = dump!["self"] as? [String: AnyObject]
             XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
             XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
-            let dynamicInfo = egoSelf!["dynamicInfo"] as? Dictionary<String, AnyObject>
+            let dynamicInfo = egoSelf!["dynamicInfo"] as? [String: AnyObject]
             XCTAssertNotNil(dynamicInfo, "dynamicInfo should not be nil")
             XCTAssertNotNil(dynamicInfo, "dynamicInfo should not be nil")
-            let included = dynamicInfo!["included"] as? Array<String>
+            let included = dynamicInfo!["included"] as? [String]
             XCTAssertNotNil(included, "included should not be nil")
             XCTAssertEqual(included!.count, 2, "should be 2 peer ids")
 
             XCTAssertNotNil(included, "included should not be nil")
             XCTAssertEqual(included!.count, 2, "should be 2 peer ids")
 
@@ -989,14 +980,13 @@ extension OctagonPairingTests {
         self.wait(for: [initiatorDumpCallback], timeout: 10)
 
         let acceptorDumpCallback = self.expectation(description: "acceptorDumpCallback callback occurs")
         self.wait(for: [initiatorDumpCallback], timeout: 10)
 
         let acceptorDumpCallback = self.expectation(description: "acceptorDumpCallback callback occurs")
-        self.tphClient.dump(withContainer: self.cuttlefishContext.containerName, context: self.contextForAcceptor) {
-            dump, _ in
+        self.tphClient.dump(withContainer: self.cuttlefishContext.containerName, context: self.contextForAcceptor) { dump, _ in
             XCTAssertNotNil(dump, "dump should not be nil")
             XCTAssertNotNil(dump, "dump should not be nil")
-            let egoSelf = dump!["self"] as? Dictionary<String, AnyObject>
+            let egoSelf = dump!["self"] as? [String: AnyObject]
             XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
             XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
-            let dynamicInfo = egoSelf!["dynamicInfo"] as? Dictionary<String, AnyObject>
+            let dynamicInfo = egoSelf!["dynamicInfo"] as? [String: AnyObject]
             XCTAssertNotNil(dynamicInfo, "dynamicInfo should not be nil")
             XCTAssertNotNil(dynamicInfo, "dynamicInfo should not be nil")
-            let included = dynamicInfo!["included"] as? Array<String>
+            let included = dynamicInfo!["included"] as? [String]
             XCTAssertNotNil(included, "included should not be nil")
             XCTAssertEqual(included!.count, 2, "should be 2 peer ids")
             acceptorDumpCallback.fulfill()
             XCTAssertNotNil(included, "included should not be nil")
             XCTAssertEqual(included!.count, 2, "should be 2 peer ids")
             acceptorDumpCallback.fulfill()
@@ -1036,14 +1026,13 @@ extension OctagonPairingTests {
         clientStateMachine.notifyContainerChange()
 
         let pair2InitiatorDumpCallback = self.expectation(description: "initiatorDumpCallback callback occurs")
         clientStateMachine.notifyContainerChange()
 
         let pair2InitiatorDumpCallback = self.expectation(description: "initiatorDumpCallback callback occurs")
-        self.tphClient.dump(withContainer: self.cuttlefishContext.containerName, context: self.cuttlefishContext.contextID) {
-            dump, _ in
+        self.tphClient.dump(withContainer: self.cuttlefishContext.containerName, context: self.cuttlefishContext.contextID) { dump, _ in
             XCTAssertNotNil(dump, "dump should not be nil")
             XCTAssertNotNil(dump, "dump should not be nil")
-            let egoSelf = dump!["self"] as? Dictionary<String, AnyObject>
+            let egoSelf = dump!["self"] as? [String: AnyObject]
             XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
             XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
-            let dynamicInfo = egoSelf!["dynamicInfo"] as? Dictionary<String, AnyObject>
+            let dynamicInfo = egoSelf!["dynamicInfo"] as? [String: AnyObject]
             XCTAssertNotNil(dynamicInfo, "dynamicInfo should not be nil")
             XCTAssertNotNil(dynamicInfo, "dynamicInfo should not be nil")
-            let included = dynamicInfo!["included"] as? Array<String>
+            let included = dynamicInfo!["included"] as? [String]
             XCTAssertNotNil(included, "included should not be nil")
             XCTAssertEqual(included!.count, 2, "should be 2 peer ids")
 
             XCTAssertNotNil(included, "included should not be nil")
             XCTAssertEqual(included!.count, 2, "should be 2 peer ids")
 
@@ -1052,14 +1041,13 @@ extension OctagonPairingTests {
         self.wait(for: [pair2InitiatorDumpCallback], timeout: 10)
 
         let pair2AcceptorDumpCallback = self.expectation(description: "acceptorDumpCallback callback occurs")
         self.wait(for: [pair2InitiatorDumpCallback], timeout: 10)
 
         let pair2AcceptorDumpCallback = self.expectation(description: "acceptorDumpCallback callback occurs")
-        self.tphClient.dump(withContainer: self.cuttlefishContext.containerName, context: self.contextForAcceptor) {
-            dump, _ in
+        self.tphClient.dump(withContainer: self.cuttlefishContext.containerName, context: self.contextForAcceptor) { dump, _ in
             XCTAssertNotNil(dump, "dump should not be nil")
             XCTAssertNotNil(dump, "dump should not be nil")
-            let egoSelf = dump!["self"] as? Dictionary<String, AnyObject>
+            let egoSelf = dump!["self"] as? [String: AnyObject]
             XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
             XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
-            let dynamicInfo = egoSelf!["dynamicInfo"] as? Dictionary<String, AnyObject>
+            let dynamicInfo = egoSelf!["dynamicInfo"] as? [String: AnyObject]
             XCTAssertNotNil(dynamicInfo, "dynamicInfo should not be nil")
             XCTAssertNotNil(dynamicInfo, "dynamicInfo should not be nil")
-            let included = dynamicInfo!["included"] as? Array<String>
+            let included = dynamicInfo!["included"] as? [String]
             XCTAssertNotNil(included, "included should not be nil")
             XCTAssertEqual(included!.count, 3, "should be 3 peer ids")
             pair2AcceptorDumpCallback.fulfill()
             XCTAssertNotNil(included, "included should not be nil")
             XCTAssertEqual(included!.count, 3, "should be 3 peer ids")
             pair2AcceptorDumpCallback.fulfill()
@@ -1263,14 +1251,13 @@ extension OctagonPairingTests {
         clientStateMachine.notifyContainerChange()
 
         let initiatorDumpCallback = self.expectation(description: "initiatorDumpCallback callback occurs")
         clientStateMachine.notifyContainerChange()
 
         let initiatorDumpCallback = self.expectation(description: "initiatorDumpCallback callback occurs")
-        self.tphClient.dump(withContainer: self.cuttlefishContext.containerName, context: self.cuttlefishContext.contextID) {
-            dump, _ in
+        self.tphClient.dump(withContainer: self.cuttlefishContext.containerName, context: self.cuttlefishContext.contextID) { dump, _ in
             XCTAssertNotNil(dump, "dump should not be nil")
             XCTAssertNotNil(dump, "dump should not be nil")
-            let egoSelf = dump!["self"] as? Dictionary<String, AnyObject>
+            let egoSelf = dump!["self"] as? [String: AnyObject]
             XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
             XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
-            let dynamicInfo = egoSelf!["dynamicInfo"] as? Dictionary<String, AnyObject>
+            let dynamicInfo = egoSelf!["dynamicInfo"] as? [String: AnyObject]
             XCTAssertNotNil(dynamicInfo, "dynamicInfo should not be nil")
             XCTAssertNotNil(dynamicInfo, "dynamicInfo should not be nil")
-            let included = dynamicInfo!["included"] as? Array<String>
+            let included = dynamicInfo!["included"] as? [String]
             XCTAssertNotNil(included, "included should not be nil")
             XCTAssertEqual(included!.count, 2, "should be 2 peer ids")
 
             XCTAssertNotNil(included, "included should not be nil")
             XCTAssertEqual(included!.count, 2, "should be 2 peer ids")
 
@@ -1279,14 +1266,13 @@ extension OctagonPairingTests {
         self.wait(for: [initiatorDumpCallback], timeout: 10)
 
         let acceptorDumpCallback = self.expectation(description: "acceptorDumpCallback callback occurs")
         self.wait(for: [initiatorDumpCallback], timeout: 10)
 
         let acceptorDumpCallback = self.expectation(description: "acceptorDumpCallback callback occurs")
-        self.tphClient.dump(withContainer: self.cuttlefishContext.containerName, context: self.contextForAcceptor) {
-            dump, _ in
+        self.tphClient.dump(withContainer: self.cuttlefishContext.containerName, context: self.contextForAcceptor) { dump, _ in
             XCTAssertNotNil(dump, "dump should not be nil")
             XCTAssertNotNil(dump, "dump should not be nil")
-            let egoSelf = dump!["self"] as? Dictionary<String, AnyObject>
+            let egoSelf = dump!["self"] as? [String: AnyObject]
             XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
             XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
-            let dynamicInfo = egoSelf!["dynamicInfo"] as? Dictionary<String, AnyObject>
+            let dynamicInfo = egoSelf!["dynamicInfo"] as? [String: AnyObject]
             XCTAssertNotNil(dynamicInfo, "dynamicInfo should not be nil")
             XCTAssertNotNil(dynamicInfo, "dynamicInfo should not be nil")
-            let included = dynamicInfo!["included"] as? Array<String>
+            let included = dynamicInfo!["included"] as? [String]
             XCTAssertNotNil(included, "included should not be nil")
             XCTAssertEqual(included!.count, 2, "should be 2 peer ids")
             acceptorDumpCallback.fulfill()
             XCTAssertNotNil(included, "included should not be nil")
             XCTAssertEqual(included!.count, 2, "should be 2 peer ids")
             acceptorDumpCallback.fulfill()
@@ -1327,14 +1313,13 @@ extension OctagonPairingTests {
         clientStateMachine.notifyContainerChange()
 
         let pair2InitiatorDumpCallback = self.expectation(description: "initiatorDumpCallback callback occurs")
         clientStateMachine.notifyContainerChange()
 
         let pair2InitiatorDumpCallback = self.expectation(description: "initiatorDumpCallback callback occurs")
-        self.tphClient.dump(withContainer: self.cuttlefishContext.containerName, context: self.cuttlefishContext.contextID) {
-            dump, _ in
+        self.tphClient.dump(withContainer: self.cuttlefishContext.containerName, context: self.cuttlefishContext.contextID) { dump, _ in
             XCTAssertNotNil(dump, "dump should not be nil")
             XCTAssertNotNil(dump, "dump should not be nil")
-            let egoSelf = dump!["self"] as? Dictionary<String, AnyObject>
+            let egoSelf = dump!["self"] as? [String: AnyObject]
             XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
             XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
-            let dynamicInfo = egoSelf!["dynamicInfo"] as? Dictionary<String, AnyObject>
+            let dynamicInfo = egoSelf!["dynamicInfo"] as? [String: AnyObject]
             XCTAssertNotNil(dynamicInfo, "dynamicInfo should not be nil")
             XCTAssertNotNil(dynamicInfo, "dynamicInfo should not be nil")
-            let included = dynamicInfo!["included"] as? Array<String>
+            let included = dynamicInfo!["included"] as? [String]
             XCTAssertNotNil(included, "included should not be nil")
             XCTAssertEqual(included!.count, 2, "should be 2 peer ids")
 
             XCTAssertNotNil(included, "included should not be nil")
             XCTAssertEqual(included!.count, 2, "should be 2 peer ids")
 
@@ -1343,14 +1328,13 @@ extension OctagonPairingTests {
         self.wait(for: [pair2InitiatorDumpCallback], timeout: 10)
 
         let pair2AcceptorDumpCallback = self.expectation(description: "acceptorDumpCallback callback occurs")
         self.wait(for: [pair2InitiatorDumpCallback], timeout: 10)
 
         let pair2AcceptorDumpCallback = self.expectation(description: "acceptorDumpCallback callback occurs")
-        self.tphClient.dump(withContainer: self.cuttlefishContext.containerName, context: self.contextForAcceptor) {
-            dump, _ in
+        self.tphClient.dump(withContainer: self.cuttlefishContext.containerName, context: self.contextForAcceptor) { dump, _ in
             XCTAssertNotNil(dump, "dump should not be nil")
             XCTAssertNotNil(dump, "dump should not be nil")
-            let egoSelf = dump!["self"] as? Dictionary<String, AnyObject>
+            let egoSelf = dump!["self"] as? [String: AnyObject]
             XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
             XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
-            let dynamicInfo = egoSelf!["dynamicInfo"] as? Dictionary<String, AnyObject>
+            let dynamicInfo = egoSelf!["dynamicInfo"] as? [String: AnyObject]
             XCTAssertNotNil(dynamicInfo, "dynamicInfo should not be nil")
             XCTAssertNotNil(dynamicInfo, "dynamicInfo should not be nil")
-            let included = dynamicInfo!["included"] as? Array<String>
+            let included = dynamicInfo!["included"] as? [String]
             XCTAssertNotNil(included, "included should not be nil")
             XCTAssertEqual(included!.count, 3, "should be 3 peer ids")
             pair2AcceptorDumpCallback.fulfill()
             XCTAssertNotNil(included, "included should not be nil")
             XCTAssertEqual(included!.count, 3, "should be 3 peer ids")
             pair2AcceptorDumpCallback.fulfill()
index 1103b6a9af242262bdafd03480526dea28a6c21b..47786d254236188189324ab9c37ad67ccec88550 100644 (file)
@@ -11,6 +11,28 @@ extension OctagonPairingTests {
 //        XCTAssert(SOSCircleHasPeer(self.circle, self.fcAcceptor.peerInfo(), nil), "HasPeer 2") <rdar://problem/54040068>
     }
 
 //        XCTAssert(SOSCircleHasPeer(self.circle, self.fcAcceptor.peerInfo(), nil), "HasPeer 2") <rdar://problem/54040068>
     }
 
+    func tlkInPairingChannel(packet: Data) throws -> Bool {
+        let plist = try self.pairingPacketToPlist(packet: packet)
+
+        guard let arrayOfItems = (plist["d"] as? [[String: Any]]) else {
+            return false
+        }
+
+        var foundTLK = false
+        arrayOfItems.forEach { item in
+            guard let agrp = (item["agrp"] as? String) else {
+                return
+            }
+            guard let cls = (item["class"] as? String) else {
+                return
+            }
+            if cls == "inet" && agrp == "com.apple.security.ckks" {
+                foundTLK = true
+            }
+        }
+        return foundTLK
+    }
+
     func testJoin() {
         self.startCKAccountStatusMock()
 
     func testJoin() {
         self.startCKAccountStatusMock()
 
@@ -99,7 +121,7 @@ extension OctagonPairingTests {
         /* calling Join */
         let rpcJoinCallbackOccurs = self.expectation(description: "rpcJoin callback occurs")
 
         /* calling Join */
         let rpcJoinCallbackOccurs = self.expectation(description: "rpcJoin callback occurs")
 
-        self.cuttlefishContext.rpcJoin(v, vouchSig: vS, preapprovedKeys: nil) { error in
+        self.cuttlefishContext.rpcJoin(v, vouchSig: vS) { error in
             XCTAssertNil(error, "error should be nil")
             rpcJoinCallbackOccurs.fulfill()
         }
             XCTAssertNil(error, "error should be nil")
             rpcJoinCallbackOccurs.fulfill()
         }
@@ -214,7 +236,7 @@ extension OctagonPairingTests {
 
         let rpcJoinCallbackOccurs = self.expectation(description: "rpcJoin callback occurs")
 
 
         let rpcJoinCallbackOccurs = self.expectation(description: "rpcJoin callback occurs")
 
-        self.cuttlefishContext.rpcJoin(v, vouchSig: vS, preapprovedKeys: nil) { error in
+        self.cuttlefishContext.rpcJoin(v, vouchSig: vS) { error in
             XCTAssertNil(error, "error should be nil")
             rpcJoinCallbackOccurs.fulfill()
         }
             XCTAssertNil(error, "error should be nil")
             rpcJoinCallbackOccurs.fulfill()
         }
@@ -333,7 +355,7 @@ extension OctagonPairingTests {
 
         let rpcJoinCallbackOccurs = self.expectation(description: "rpcJoin callback occurs")
 
 
         let rpcJoinCallbackOccurs = self.expectation(description: "rpcJoin callback occurs")
 
-        self.cuttlefishContext.rpcJoin(v, vouchSig: vS, preapprovedKeys: nil) { error in
+        self.cuttlefishContext.rpcJoin(v, vouchSig: vS) { error in
             XCTAssertNotNil(error, "error should be set")
             rpcJoinCallbackOccurs.fulfill()
         }
             XCTAssertNotNil(error, "error should be set")
             rpcJoinCallbackOccurs.fulfill()
         }
@@ -350,11 +372,11 @@ extension OctagonPairingTests {
         let clientStateMachine = self.manager.clientStateMachine(forContainerName: OTCKContainerName, contextID: self.contextForAcceptor, clientName: self.initiatorName)
 
         self.silentFetchesAllowed = false
         let clientStateMachine = self.manager.clientStateMachine(forContainerName: OTCKContainerName, contextID: self.contextForAcceptor, clientName: self.initiatorName)
 
         self.silentFetchesAllowed = false
-        self.expectCKFetchAndRun(beforeFinished: {
-            self.putFakeKeyHierarchy(inCloudKit: self.manateeZoneID)
-            self.putFakeDeviceStatus(inCloudKit: self.manateeZoneID)
+        self.expectCKFetchAndRun {
+            self.putFakeKeyHierarchiesInCloudKit()
+            self.putFakeDeviceStatusesInCloudKit()
             self.silentFetchesAllowed = true
             self.silentFetchesAllowed = true
-        })
+        }
 
         clientStateMachine.startOctagonStateMachine()
         self.cuttlefishContext.startOctagonStateMachine()
 
         clientStateMachine.startOctagonStateMachine()
         self.cuttlefishContext.startOctagonStateMachine()
@@ -433,7 +455,7 @@ extension OctagonPairingTests {
         /* calling Join */
         let rpcJoinCallbackOccurs = self.expectation(description: "rpcJoin callback occurs")
 
         /* calling Join */
         let rpcJoinCallbackOccurs = self.expectation(description: "rpcJoin callback occurs")
 
-        self.cuttlefishContext.rpcJoin(v, vouchSig: vS, preapprovedKeys: nil) { error in
+        self.cuttlefishContext.rpcJoin(v, vouchSig: vS) { error in
             XCTAssertNil(error, "error should be nil")
             rpcJoinCallbackOccurs.fulfill()
         }
             XCTAssertNil(error, "error should be nil")
             rpcJoinCallbackOccurs.fulfill()
         }
@@ -519,7 +541,7 @@ extension OctagonPairingTests {
         self.wait(for: [voucherCallback], timeout: 10)
 
         let rpcJoinCallback = self.expectation(description: "joining octagon callback")
         self.wait(for: [voucherCallback], timeout: 10)
 
         let rpcJoinCallback = self.expectation(description: "joining octagon callback")
-        self.manager.rpcJoin(with: self.initiatorPairingConfig, vouchData: voucher, vouchSig: voucherSig, preapprovedKeys: nil) { error in
+        self.manager.rpcJoin(with: self.initiatorPairingConfig, vouchData: voucher, vouchSig: voucherSig) { error in
             XCTAssertNil(error, "error should be nil")
             rpcJoinCallback.fulfill()
         }
             XCTAssertNil(error, "error should be nil")
             rpcJoinCallback.fulfill()
         }
@@ -689,73 +711,28 @@ extension OctagonPairingTests {
         self.wait(for: [signInCallback], timeout: 10)
 
         /* INITIATOR FIRST RTT JOINING MESSAGE*/
         self.wait(for: [signInCallback], timeout: 10)
 
         /* INITIATOR FIRST RTT JOINING MESSAGE*/
-        var initiatorFirstPacket = Data()
-        let firstInitiatorCallback = self.expectation(description: "firstInitiatorCallback callback occurs")
-
-        initiator.exchangePacket(nil) { complete, packet, error in
-            XCTAssertFalse(complete, "should be false")
-            XCTAssertNotNil(packet, "packet should not be nil")
-            XCTAssertNil(error, "error should be nil")
-            initiatorFirstPacket = packet!
-            firstInitiatorCallback.fulfill()
-        }
-
-        self.wait(for: [firstInitiatorCallback], timeout: 10)
+        let initiatorFirstPacket = self.sendPairingExpectingReply(channel: initiator, packet: nil, reason: "session initiation")
 
         /* ACCEPTOR FIRST RTT EPOCH*/
 
         /* ACCEPTOR FIRST RTT EPOCH*/
-        var acceptorFirstPacket = Data()
-        let firstAcceptorCallback = self.expectation(description: "firstAcceptorCallback callback occurs")
-
-        acceptor.exchangePacket(initiatorFirstPacket) { complete, packet, error in
-            XCTAssertFalse(complete, "should be false")
-            XCTAssertNotNil(packet, "packet should not be nil")
-            XCTAssertNil(error, "error should be nil")
-            acceptorFirstPacket = packet!
-            firstAcceptorCallback.fulfill()
-        }
-        self.wait(for: [firstAcceptorCallback], timeout: 10)
+        let acceptorEpochPacket = self.sendPairingExpectingReply(channel: acceptor, packet: initiatorFirstPacket, reason: "epoch return")
 
         /* INITIATOR SECOND RTT PREPARE*/
 
         /* INITIATOR SECOND RTT PREPARE*/
-        var initiatorSecondPacket = Data()
-        let secondInitiatorCallback = self.expectation(description: "secondInitiatorCallback callback occurs")
-
-        initiator.exchangePacket(acceptorFirstPacket) { complete, packet, error in
-            XCTAssertFalse(complete, "should be false")
-            XCTAssertNotNil(packet, "packet should not be nil")
-            XCTAssertNil(error, "error should be nil")
-            initiatorSecondPacket = packet!
-            secondInitiatorCallback.fulfill()
-        }
-
-        self.wait(for: [secondInitiatorCallback], timeout: 10)
+        let initiatorPreparedIdentityPacket = self.sendPairingExpectingReply(channel: initiator, packet: acceptorEpochPacket, reason: "prepared identity")
 
         /* ACCEPTOR SECOND RTT */
 
         /* ACCEPTOR SECOND RTT */
-        var acceptorSecondPacket = Data()
-        let SecondAcceptorCallback = self.expectation(description: "SecondAcceptorCallback callback occurs")
-
-        acceptor.exchangePacket(initiatorSecondPacket) { complete, packet, error in
-            XCTAssertFalse(complete, "should be false")
-            XCTAssertNotNil(packet, "packet should not be nil")
-            XCTAssertNil(error, "error should be nil")
-            acceptorSecondPacket = packet!
-            SecondAcceptorCallback.fulfill()
-        }
-        self.wait(for: [SecondAcceptorCallback], timeout: 10)
-        XCTAssertNotNil(acceptorSecondPacket, "acceptor second packet should not be nil")
+        let acceptorVoucherPacket = self.sendPairingExpectingReply(channel: acceptor, packet: initiatorPreparedIdentityPacket, reason: "epoch return")
 
         /* INITIATOR THIRD STEP*/
 
         /* INITIATOR THIRD STEP*/
-        var initiatorThirdPacket: Data?
-        let thirdInitiatorCallback = self.expectation(description: "thirdInitiatorCallback callback occurs")
+        let initiatorThirdPacket = self.sendPairingExpectingReply(channel: initiator, packet: acceptorVoucherPacket, reason: "intitiator third packet")
 
 
-        initiator.exchangePacket(acceptorSecondPacket) { complete, packet, error in
-            XCTAssertFalse(complete, "should be false")
-            XCTAssertNotNil(packet, "packet should not be nil")
-            XCTAssertNil(error, "error should be nil")
-            initiatorThirdPacket = packet!
-            thirdInitiatorCallback.fulfill()
-        }
-        self.wait(for: [thirdInitiatorCallback], timeout: 10)
-        XCTAssertNotNil(initiatorThirdPacket, "acceptor second packet should not be nil")
+        /* ACCEPTOR THIRD STEP */
+        let acceptorThirdPacket = self.sendPairingExpectingCompletionAndReply(channel: acceptor, packet: initiatorThirdPacket, reason: "acceptor third packet")
+        XCTAssertFalse(try self.tlkInPairingChannel(packet: acceptorThirdPacket), "pairing channel should NOT transport TLKs for SOS+Octagon")
+
+        /* INITIATOR FOURTH STEP*/
+        self.sendPairingExpectingCompletion(channel: initiator, packet: acceptorThirdPacket, reason: "final packet receipt")
+
+        // pairing completes here
 
         assertAllCKKSViews(enter: SecCKKSZoneKeyStateReady, within: 10 * NSEC_PER_SEC)
 
 
         assertAllCKKSViews(enter: SecCKKSZoneKeyStateReady, within: 10 * NSEC_PER_SEC)
 
@@ -767,14 +744,13 @@ extension OctagonPairingTests {
         self.verifyDatabaseMocks()
 
         let initiatorDumpCallback = self.expectation(description: "initiatorDumpCallback callback occurs")
         self.verifyDatabaseMocks()
 
         let initiatorDumpCallback = self.expectation(description: "initiatorDumpCallback callback occurs")
-        self.tphClient.dump(withContainer: self.cuttlefishContext.containerName, context: self.cuttlefishContext.contextID) {
-            dump, _ in
+        self.tphClient.dump(withContainer: self.cuttlefishContext.containerName, context: self.cuttlefishContext.contextID) { dump, _ in
             XCTAssertNotNil(dump, "dump should not be nil")
             XCTAssertNotNil(dump, "dump should not be nil")
-            let egoSelf = dump!["self"] as? Dictionary<String, AnyObject>
+            let egoSelf = dump!["self"] as? [String: AnyObject]
             XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
             XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
-            let dynamicInfo = egoSelf!["dynamicInfo"] as? Dictionary<String, AnyObject>
+            let dynamicInfo = egoSelf!["dynamicInfo"] as? [String: AnyObject]
             XCTAssertNotNil(dynamicInfo, "dynamicInfo should not be nil")
             XCTAssertNotNil(dynamicInfo, "dynamicInfo should not be nil")
-            let included = dynamicInfo!["included"] as? Array<String>
+            let included = dynamicInfo!["included"] as? [String]
             XCTAssertNotNil(included, "included should not be nil")
             XCTAssertEqual(included!.count, 2, "should be 2 peer ids")
 
             XCTAssertNotNil(included, "included should not be nil")
             XCTAssertEqual(included!.count, 2, "should be 2 peer ids")
 
@@ -783,14 +759,13 @@ extension OctagonPairingTests {
         self.wait(for: [initiatorDumpCallback], timeout: 10)
 
         let acceptorDumpCallback = self.expectation(description: "acceptorDumpCallback callback occurs")
         self.wait(for: [initiatorDumpCallback], timeout: 10)
 
         let acceptorDumpCallback = self.expectation(description: "acceptorDumpCallback callback occurs")
-        self.tphClient.dump(withContainer: self.cuttlefishContext.containerName, context: self.contextForAcceptor) {
-            dump, _ in
+        self.tphClient.dump(withContainer: self.cuttlefishContext.containerName, context: self.contextForAcceptor) { dump, _ in
             XCTAssertNotNil(dump, "dump should not be nil")
             XCTAssertNotNil(dump, "dump should not be nil")
-            let egoSelf = dump!["self"] as? Dictionary<String, AnyObject>
+            let egoSelf = dump!["self"] as? [String: AnyObject]
             XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
             XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
-            let dynamicInfo = egoSelf!["dynamicInfo"] as? Dictionary<String, AnyObject>
+            let dynamicInfo = egoSelf!["dynamicInfo"] as? [String: AnyObject]
             XCTAssertNotNil(dynamicInfo, "dynamicInfo should not be nil")
             XCTAssertNotNil(dynamicInfo, "dynamicInfo should not be nil")
-            let included = dynamicInfo!["included"] as? Array<String>
+            let included = dynamicInfo!["included"] as? [String]
             XCTAssertNotNil(included, "included should not be nil")
             XCTAssertEqual(included!.count, 2, "should be 2 peer ids")
             acceptorDumpCallback.fulfill()
             XCTAssertNotNil(included, "included should not be nil")
             XCTAssertEqual(included!.count, 2, "should be 2 peer ids")
             acceptorDumpCallback.fulfill()
@@ -802,7 +777,7 @@ extension OctagonPairingTests {
         self.assertSOSSuccess()
     }
 
         self.assertSOSSuccess()
     }
 
-    func testProximitySetupUsingCliqueOctagonOnly() {
+    func testProximitySetupUsingCliqueOctagonOnly() throws {
         OctagonSetPlatformSupportsSOS(false)
         OctagonSetIsEnabled(true)
         self.startCKAccountStatusMock()
         OctagonSetPlatformSupportsSOS(false)
         OctagonSetIsEnabled(true)
         self.startCKAccountStatusMock()
@@ -831,73 +806,22 @@ extension OctagonPairingTests {
         self.wait(for: [signInCallback], timeout: 10)
 
         /* INITIATOR FIRST RTT JOINING MESSAGE*/
         self.wait(for: [signInCallback], timeout: 10)
 
         /* INITIATOR FIRST RTT JOINING MESSAGE*/
-        var initiatorFirstPacket = Data()
-        let firstInitiatorCallback = self.expectation(description: "firstInitiatorCallback callback occurs")
-
-        initiator.exchangePacket(nil) { complete, packet, error in
-            XCTAssertFalse(complete, "should be false")
-            XCTAssertNotNil(packet, "packet should not be nil")
-            XCTAssertNil(error, "error should be nil")
-            initiatorFirstPacket = packet!
-            firstInitiatorCallback.fulfill()
-        }
-
-        self.wait(for: [firstInitiatorCallback], timeout: 10)
+        let initiatorFirstPacket = self.sendPairingExpectingReply(channel: initiator, packet: nil, reason: "session initiation")
 
         /* ACCEPTOR FIRST RTT EPOCH*/
 
         /* ACCEPTOR FIRST RTT EPOCH*/
-        var acceptorFirstPacket = Data()
-        let firstAcceptorCallback = self.expectation(description: "firstAcceptorCallback callback occurs")
-
-        acceptor.exchangePacket(initiatorFirstPacket) { complete, packet, error in
-            XCTAssertFalse(complete, "should be false")
-            XCTAssertNotNil(packet, "packet should not be nil")
-            XCTAssertNil(error, "error should be nil")
-            acceptorFirstPacket = packet!
-            firstAcceptorCallback.fulfill()
-        }
-        self.wait(for: [firstAcceptorCallback], timeout: 10)
+        let acceptorEpochPacket = self.sendPairingExpectingReply(channel: acceptor, packet: initiatorFirstPacket, reason: "epoch return")
 
         /* INITIATOR SECOND RTT PREPARE*/
 
         /* INITIATOR SECOND RTT PREPARE*/
-        var initiatorSecondPacket = Data()
-        let secondInitiatorCallback = self.expectation(description: "secondInitiatorCallback callback occurs")
-
-        initiator.exchangePacket(acceptorFirstPacket) { complete, packet, error in
-            XCTAssertFalse(complete, "should be false")
-            XCTAssertNotNil(packet, "packet should not be nil")
-            XCTAssertNil(error, "error should be nil")
-            initiatorSecondPacket = packet!
-            secondInitiatorCallback.fulfill()
-        }
-
-        self.wait(for: [secondInitiatorCallback], timeout: 10)
+        let initiatorPreparedIdentityPacket = self.sendPairingExpectingReply(channel: initiator, packet: acceptorEpochPacket, reason: "prepared identity")
 
         /* ACCEPTOR SECOND RTT */
 
         /* ACCEPTOR SECOND RTT */
-        var acceptorSecondPacket = Data()
-        let SecondAcceptorCallback = self.expectation(description: "SecondAcceptorCallback callback occurs")
+        let acceptorVoucherPacket = self.sendPairingExpectingCompletionAndReply(channel: acceptor, packet: initiatorPreparedIdentityPacket, reason: "acceptor third packet")
 
 
-        acceptor.exchangePacket(initiatorSecondPacket) { complete, packet, error in
-            XCTAssertTrue(complete, "should be true")
-            XCTAssertNotNil(packet, "packet should not be nil")
-            XCTAssertNil(error, "error should be nil")
-            acceptorSecondPacket = packet!
-            SecondAcceptorCallback.fulfill()
-        }
-        self.wait(for: [SecondAcceptorCallback], timeout: 10)
-        XCTAssertNotNil(acceptorSecondPacket, "acceptor second packet should not be nil")
+        // the tlks are in the 3rd roundtrip, but lets check here too
+        XCTAssertFalse(try self.tlkInPairingChannel(packet: acceptorVoucherPacket), "pairing channel should not transport TLKs for octagon")
 
         /* INITIATOR THIRD STEP*/
 
         /* INITIATOR THIRD STEP*/
-        var initiatorThirdPacket: Data?
-        let thirdInitiatorCallback = self.expectation(description: "thirdInitiatorCallback callback occurs")
-
-        initiator.exchangePacket(acceptorSecondPacket) { complete, packet, error in
-            XCTAssertTrue(complete, "should be true")
-            XCTAssertNil(packet, "packet should be nil")
-            XCTAssertNil(error, "error should be nil")
-            initiatorThirdPacket = (packet)
-            thirdInitiatorCallback.fulfill()
-        }
-        self.wait(for: [thirdInitiatorCallback], timeout: 10)
-        XCTAssertNil(initiatorThirdPacket, "acceptor second packet should be nil")
+        self.sendPairingExpectingCompletion(channel: initiator, packet: acceptorVoucherPacket, reason: "final packet receipt")
 
         assertAllCKKSViews(enter: SecCKKSZoneKeyStateReady, within: 10 * NSEC_PER_SEC)
 
 
         assertAllCKKSViews(enter: SecCKKSZoneKeyStateReady, within: 10 * NSEC_PER_SEC)
 
@@ -909,14 +833,13 @@ extension OctagonPairingTests {
         self.verifyDatabaseMocks()
 
         let initiatorDumpCallback = self.expectation(description: "initiatorDumpCallback callback occurs")
         self.verifyDatabaseMocks()
 
         let initiatorDumpCallback = self.expectation(description: "initiatorDumpCallback callback occurs")
-        self.tphClient.dump(withContainer: self.cuttlefishContext.containerName, context: self.cuttlefishContext.contextID) {
-            dump, _ in
+        self.tphClient.dump(withContainer: self.cuttlefishContext.containerName, context: self.cuttlefishContext.contextID) { dump, _ in
             XCTAssertNotNil(dump, "dump should not be nil")
             XCTAssertNotNil(dump, "dump should not be nil")
-            let egoSelf = dump!["self"] as? Dictionary<String, AnyObject>
+            let egoSelf = dump!["self"] as? [String: AnyObject]
             XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
             XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
-            let dynamicInfo = egoSelf!["dynamicInfo"] as? Dictionary<String, AnyObject>
+            let dynamicInfo = egoSelf!["dynamicInfo"] as? [String: AnyObject]
             XCTAssertNotNil(dynamicInfo, "dynamicInfo should not be nil")
             XCTAssertNotNil(dynamicInfo, "dynamicInfo should not be nil")
-            let included = dynamicInfo!["included"] as? Array<String>
+            let included = dynamicInfo!["included"] as? [String]
             XCTAssertNotNil(included, "included should not be nil")
             XCTAssertEqual(included!.count, 2, "should be 2 peer ids")
 
             XCTAssertNotNil(included, "included should not be nil")
             XCTAssertEqual(included!.count, 2, "should be 2 peer ids")
 
@@ -925,14 +848,13 @@ extension OctagonPairingTests {
         self.wait(for: [initiatorDumpCallback], timeout: 10)
 
         let acceptorDumpCallback = self.expectation(description: "acceptorDumpCallback callback occurs")
         self.wait(for: [initiatorDumpCallback], timeout: 10)
 
         let acceptorDumpCallback = self.expectation(description: "acceptorDumpCallback callback occurs")
-        self.tphClient.dump(withContainer: self.cuttlefishContext.containerName, context: self.contextForAcceptor) {
-            dump, _ in
+        self.tphClient.dump(withContainer: self.cuttlefishContext.containerName, context: self.contextForAcceptor) { dump, _ in
             XCTAssertNotNil(dump, "dump should not be nil")
             XCTAssertNotNil(dump, "dump should not be nil")
-            let egoSelf = dump!["self"] as? Dictionary<String, AnyObject>
+            let egoSelf = dump!["self"] as? [String: AnyObject]
             XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
             XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
-            let dynamicInfo = egoSelf!["dynamicInfo"] as? Dictionary<String, AnyObject>
+            let dynamicInfo = egoSelf!["dynamicInfo"] as? [String: AnyObject]
             XCTAssertNotNil(dynamicInfo, "dynamicInfo should not be nil")
             XCTAssertNotNil(dynamicInfo, "dynamicInfo should not be nil")
-            let included = dynamicInfo!["included"] as? Array<String>
+            let included = dynamicInfo!["included"] as? [String]
             XCTAssertNotNil(included, "included should not be nil")
             XCTAssertEqual(included!.count, 2, "should be 2 peer ids")
             acceptorDumpCallback.fulfill()
             XCTAssertNotNil(included, "included should not be nil")
             XCTAssertEqual(included!.count, 2, "should be 2 peer ids")
             acceptorDumpCallback.fulfill()
@@ -958,7 +880,11 @@ extension OctagonPairingTests {
 
         self.assertEnters(context: initiator1Context, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
 
         self.assertEnters(context: initiator1Context, state: OctagonStateUntrusted, within: 10 * NSEC_PER_SEC)
 
-        let (acceptor, initiator) = self.setupPairingEndpoints(withPairNumber: "1", initiatorContextID: OTDefaultContext, acceptorContextID: self.contextForAcceptor, initiatorUniqueID: self.initiatorName, acceptorUniqueID: "acceptor-2")
+        let (acceptor, initiator) = self.setupPairingEndpoints(withPairNumber: "1",
+                                                               initiatorContextID: OTDefaultContext,
+                                                               acceptorContextID: self.contextForAcceptor,
+                                                               initiatorUniqueID: self.initiatorName,
+                                                               acceptorUniqueID: "acceptor-2")
 
         XCTAssertNotNil(acceptor, "acceptor should not be nil")
         XCTAssertNotNil(initiator, "initiator should not be nil")
 
         XCTAssertNotNil(acceptor, "acceptor should not be nil")
         XCTAssertNotNil(initiator, "initiator should not be nil")
@@ -971,73 +897,30 @@ extension OctagonPairingTests {
         self.wait(for: [signInCallback], timeout: 10)
 
         /* INITIATOR FIRST RTT JOINING MESSAGE*/
         self.wait(for: [signInCallback], timeout: 10)
 
         /* INITIATOR FIRST RTT JOINING MESSAGE*/
-        var initiatorFirstPacket = Data()
-        let firstInitiatorCallback = self.expectation(description: "firstInitiatorCallback callback occurs")
-
-        initiator.exchangePacket(nil) { complete, packet, error in
-            XCTAssertFalse(complete, "should be false")
-            XCTAssertNotNil(packet, "packet should not be nil")
-            XCTAssertNil(error, "error should be nil")
-            initiatorFirstPacket = packet!
-            firstInitiatorCallback.fulfill()
-        }
-
-        self.wait(for: [firstInitiatorCallback], timeout: 10)
+        let initiatorFirstPacket = self.sendPairingExpectingReply(channel: initiator, packet: nil, reason: "session initiation")
 
         /* ACCEPTOR FIRST RTT EPOCH*/
 
         /* ACCEPTOR FIRST RTT EPOCH*/
-        var acceptorFirstPacket = Data()
-        let firstAcceptorCallback = self.expectation(description: "firstAcceptorCallback callback occurs")
-
-        acceptor.exchangePacket(initiatorFirstPacket) { complete, packet, error in
-            XCTAssertFalse(complete, "should be false")
-            XCTAssertNotNil(packet, "packet should not be nil")
-            XCTAssertNil(error, "error should be nil")
-            acceptorFirstPacket = packet!
-            firstAcceptorCallback.fulfill()
-        }
-        self.wait(for: [firstAcceptorCallback], timeout: 10)
+        let acceptorEpochPacket = self.sendPairingExpectingReply(channel: acceptor, packet: initiatorFirstPacket, reason: "epoch return")
 
         /* INITIATOR SECOND RTT PREPARE*/
 
         /* INITIATOR SECOND RTT PREPARE*/
-        var initiatorSecondPacket = Data()
-        let secondInitiatorCallback = self.expectation(description: "secondInitiatorCallback callback occurs")
-
-        initiator.exchangePacket(acceptorFirstPacket) { complete, packet, error in
-            XCTAssertFalse(complete, "should be false")
-            XCTAssertNotNil(packet, "packet should not be nil")
-            XCTAssertNil(error, "error should be nil")
-            initiatorSecondPacket = packet!
-            secondInitiatorCallback.fulfill()
-        }
-
-        self.wait(for: [secondInitiatorCallback], timeout: 10)
+        let initiatorPreparedIdentityPacket = self.sendPairingExpectingReply(channel: initiator, packet: acceptorEpochPacket, reason: "prepared identity")
 
         /* ACCEPTOR SECOND RTT */
 
         /* ACCEPTOR SECOND RTT */
-        var acceptorSecondPacket = Data()
-        let SecondAcceptorCallback = self.expectation(description: "SecondAcceptorCallback callback occurs")
+        let acceptorVoucherPacket = self.sendPairingExpectingReply(channel: acceptor, packet: initiatorPreparedIdentityPacket, reason: "epoch return")
 
 
-        acceptor.exchangePacket(initiatorSecondPacket) { complete, packet, error in
-            XCTAssertFalse(complete, "should be false")
-            XCTAssertNotNil(packet, "packet should not be nil")
-            XCTAssertNil(error, "error should be nil")
-            acceptorSecondPacket = packet!
-            SecondAcceptorCallback.fulfill()
-        }
-        self.wait(for: [SecondAcceptorCallback], timeout: 10)
-        XCTAssertNotNil(acceptorSecondPacket, "acceptor second packet should not be nil")
+        // the tlks are in the 3rd roundtrip, but lets check here too
+        XCTAssertFalse(try self.tlkInPairingChannel(packet: acceptorVoucherPacket), "pairing channel should transport TLKs for SOS not 2nd step though")
 
         /* INITIATOR THIRD STEP*/
 
         /* INITIATOR THIRD STEP*/
-        var initiatorThirdPacket: Data?
-        let thirdInitiatorCallback = self.expectation(description: "thirdInitiatorCallback callback occurs")
+        let initiatorThirdPacket = self.sendPairingExpectingReply(channel: initiator, packet: acceptorVoucherPacket, reason: "intitiator third packet")
 
 
-        initiator.exchangePacket(acceptorSecondPacket) { complete, packet, error in
-            XCTAssertFalse(complete, "should be false")
-            XCTAssertNotNil(packet, "packet should not be nil")
-            XCTAssertNil(error, "error should be nil")
-            initiatorThirdPacket = packet!
-            thirdInitiatorCallback.fulfill()
-        }
-        self.wait(for: [thirdInitiatorCallback], timeout: 10)
-        XCTAssertNotNil(initiatorThirdPacket, "acceptor second packet should not be nil")
+        /* ACCEPTOR THIRD STEP */
+        let acceptorThirdPacket = self.sendPairingExpectingCompletionAndReply(channel: acceptor, packet: initiatorThirdPacket, reason: "acceptor third packet")
+
+        XCTAssertTrue(try self.tlkInPairingChannel(packet: acceptorThirdPacket), "pairing channel should transport TLKs for SOS")
+
+        /* INITIATOR FORTH STEP*/
+        self.sendPairingExpectingCompletion(channel: initiator, packet: acceptorThirdPacket, reason: "final packet receipt")
 
         self.assertSOSSuccess()
     }
 
         self.assertSOSSuccess()
     }
@@ -1075,73 +958,19 @@ extension OctagonPairingTests {
         self.wait(for: [signInCallback], timeout: 10)
 
         /* INITIATOR FIRST RTT JOINING MESSAGE*/
         self.wait(for: [signInCallback], timeout: 10)
 
         /* INITIATOR FIRST RTT JOINING MESSAGE*/
-        var initiatorFirstPacket = Data()
-        let firstInitiatorCallback = self.expectation(description: "firstInitiatorCallback callback occurs")
-
-        initiator.exchangePacket(nil) { complete, packet, error in
-            XCTAssertFalse(complete, "should be false")
-            XCTAssertNotNil(packet, "packet should not be nil")
-            XCTAssertNil(error, "error should be nil")
-            initiatorFirstPacket = packet!
-            firstInitiatorCallback.fulfill()
-        }
-
-        self.wait(for: [firstInitiatorCallback], timeout: 10)
+        let initiatorFirstPacket = self.sendPairingExpectingReply(channel: initiator, packet: nil, reason: "session initiation")
 
         /* ACCEPTOR FIRST RTT EPOCH*/
 
         /* ACCEPTOR FIRST RTT EPOCH*/
-        var acceptorFirstPacket = Data()
-        let firstAcceptorCallback = self.expectation(description: "firstAcceptorCallback callback occurs")
-
-        acceptor.exchangePacket(initiatorFirstPacket) { complete, packet, error in
-            XCTAssertFalse(complete, "should be false")
-            XCTAssertNotNil(packet, "packet should not be nil")
-            XCTAssertNil(error, "error should be nil")
-            acceptorFirstPacket = packet!
-            firstAcceptorCallback.fulfill()
-        }
-        self.wait(for: [firstAcceptorCallback], timeout: 10)
+        let acceptorEpochPacket = self.sendPairingExpectingReply(channel: acceptor, packet: initiatorFirstPacket, reason: "epoch return")
 
         /* INITIATOR SECOND RTT PREPARE*/
 
         /* INITIATOR SECOND RTT PREPARE*/
-        var initiatorSecondPacket = Data()
-        let secondInitiatorCallback = self.expectation(description: "secondInitiatorCallback callback occurs")
-
-        initiator.exchangePacket(acceptorFirstPacket) { complete, packet, error in
-            XCTAssertFalse(complete, "should be false")
-            XCTAssertNotNil(packet, "packet should not be nil")
-            XCTAssertNil(error, "error should be nil")
-            initiatorSecondPacket = packet!
-            secondInitiatorCallback.fulfill()
-        }
-
-        self.wait(for: [secondInitiatorCallback], timeout: 10)
+        let initiatorPreparedIdentityPacket = self.sendPairingExpectingReply(channel: initiator, packet: acceptorEpochPacket, reason: "prepared identity")
 
         /* ACCEPTOR SECOND RTT */
 
         /* ACCEPTOR SECOND RTT */
-        var acceptorSecondPacket = Data()
-        let SecondAcceptorCallback = self.expectation(description: "SecondAcceptorCallback callback occurs")
-
-        acceptor.exchangePacket(initiatorSecondPacket) { complete, packet, error in
-            XCTAssertFalse(complete, "should be false")
-            XCTAssertNotNil(packet, "packet should not be nil")
-            XCTAssertNil(error, "error should be nil")
-            acceptorSecondPacket = packet!
-            SecondAcceptorCallback.fulfill()
-        }
-        self.wait(for: [SecondAcceptorCallback], timeout: 10)
-        XCTAssertNotNil(acceptorSecondPacket, "acceptor second packet should not be nil")
+        let acceptorVoucherPacket = self.sendPairingExpectingReply(channel: acceptor, packet: initiatorPreparedIdentityPacket, reason: "epoch return")
 
         /* INITIATOR THIRD STEP*/
 
         /* INITIATOR THIRD STEP*/
-        var initiatorThirdPacket: Data?
-        let thirdInitiatorCallback = self.expectation(description: "thirdInitiatorCallback callback occurs")
-
-        initiator.exchangePacket(acceptorSecondPacket) { complete, packet, error in
-            XCTAssertFalse(complete, "should be false")
-            XCTAssertNotNil(packet, "packet should not be nil")
-            XCTAssertNil(error, "error should be nil")
-            initiatorThirdPacket = packet!
-            thirdInitiatorCallback.fulfill()
-        }
-        self.wait(for: [thirdInitiatorCallback], timeout: 10)
-        XCTAssertNotNil(initiatorThirdPacket, "acceptor second packet should not be nil")
+        _ = self.sendPairingExpectingReply(channel: initiator, packet: acceptorVoucherPacket, reason: "intitiator third packet")
 
         assertAllCKKSViews(enter: SecCKKSZoneKeyStateReady, within: 10 * NSEC_PER_SEC)
 
 
         assertAllCKKSViews(enter: SecCKKSZoneKeyStateReady, within: 10 * NSEC_PER_SEC)
 
@@ -1153,14 +982,13 @@ extension OctagonPairingTests {
         self.verifyDatabaseMocks()
 
         let initiatorDumpCallback = self.expectation(description: "initiatorDumpCallback callback occurs")
         self.verifyDatabaseMocks()
 
         let initiatorDumpCallback = self.expectation(description: "initiatorDumpCallback callback occurs")
-        self.tphClient.dump(withContainer: self.cuttlefishContext.containerName, context: self.cuttlefishContext.contextID) {
-            dump, _ in
+        self.tphClient.dump(withContainer: self.cuttlefishContext.containerName, context: self.cuttlefishContext.contextID) { dump, _ in
             XCTAssertNotNil(dump, "dump should not be nil")
             XCTAssertNotNil(dump, "dump should not be nil")
-            let egoSelf = dump!["self"] as? Dictionary<String, AnyObject>
+            let egoSelf = dump!["self"] as? [String: AnyObject]
             XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
             XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
-            let dynamicInfo = egoSelf!["dynamicInfo"] as? Dictionary<String, AnyObject>
+            let dynamicInfo = egoSelf!["dynamicInfo"] as? [String: AnyObject]
             XCTAssertNotNil(dynamicInfo, "dynamicInfo should not be nil")
             XCTAssertNotNil(dynamicInfo, "dynamicInfo should not be nil")
-            let included = dynamicInfo!["included"] as? Array<String>
+            let included = dynamicInfo!["included"] as? [String]
             XCTAssertNotNil(included, "included should not be nil")
             XCTAssertEqual(included!.count, 2, "should be 2 peer ids")
 
             XCTAssertNotNil(included, "included should not be nil")
             XCTAssertEqual(included!.count, 2, "should be 2 peer ids")
 
@@ -1169,14 +997,13 @@ extension OctagonPairingTests {
         self.wait(for: [initiatorDumpCallback], timeout: 10)
 
         let acceptorDumpCallback = self.expectation(description: "acceptorDumpCallback callback occurs")
         self.wait(for: [initiatorDumpCallback], timeout: 10)
 
         let acceptorDumpCallback = self.expectation(description: "acceptorDumpCallback callback occurs")
-        self.tphClient.dump(withContainer: self.cuttlefishContext.containerName, context: self.contextForAcceptor) {
-            dump, _ in
+        self.tphClient.dump(withContainer: self.cuttlefishContext.containerName, context: self.contextForAcceptor) { dump, _ in
             XCTAssertNotNil(dump, "dump should not be nil")
             XCTAssertNotNil(dump, "dump should not be nil")
-            let egoSelf = dump!["self"] as? Dictionary<String, AnyObject>
+            let egoSelf = dump!["self"] as? [String: AnyObject]
             XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
             XCTAssertNotNil(egoSelf, "egoSelf should not be nil")
-            let dynamicInfo = egoSelf!["dynamicInfo"] as? Dictionary<String, AnyObject>
+            let dynamicInfo = egoSelf!["dynamicInfo"] as? [String: AnyObject]
             XCTAssertNotNil(dynamicInfo, "dynamicInfo should not be nil")
             XCTAssertNotNil(dynamicInfo, "dynamicInfo should not be nil")
-            let included = dynamicInfo!["included"] as? Array<String>
+            let included = dynamicInfo!["included"] as? [String]
             XCTAssertNotNil(included, "included should not be nil")
             XCTAssertEqual(included!.count, 2, "should be 2 peer ids")
             acceptorDumpCallback.fulfill()
             XCTAssertNotNil(included, "included should not be nil")
             XCTAssertEqual(included!.count, 2, "should be 2 peer ids")
             acceptorDumpCallback.fulfill()
@@ -1218,16 +1045,7 @@ extension OctagonPairingTests {
         self.wait(for: [signInCallback], timeout: 10)
 
         /* INITIATOR FIRST RTT JOINING MESSAGE*/
         self.wait(for: [signInCallback], timeout: 10)
 
         /* INITIATOR FIRST RTT JOINING MESSAGE*/
-        let firstInitiatorCallback = self.expectation(description: "firstInitiatorCallback callback occurs")
-
-        initiator.exchangePacket(nil) { complete, packet, error in
-            XCTAssertNil(error, "should be no error")
-            XCTAssertTrue(complete, "should be true")
-            XCTAssertNil(packet, "packet should be nil")
-            firstInitiatorCallback.fulfill()
-        }
-
-        self.wait(for: [firstInitiatorCallback], timeout: 10)
+        self.sendPairingExpectingCompletion(channel: initiator, packet: nil, reason: "error on first message")
     }
 
     func testProximitySetupOctagonAndSOSWithOctagonAcceptorMessage1Failure() {
     }
 
     func testProximitySetupOctagonAndSOSWithOctagonAcceptorMessage1Failure() {
@@ -1260,18 +1078,7 @@ extension OctagonPairingTests {
         self.wait(for: [signInCallback], timeout: 10)
 
         /* INITIATOR FIRST RTT JOINING MESSAGE*/
         self.wait(for: [signInCallback], timeout: 10)
 
         /* INITIATOR FIRST RTT JOINING MESSAGE*/
-        var initiatorFirstPacket = Data()
-        let firstInitiatorCallback = self.expectation(description: "firstInitiatorCallback callback occurs")
-
-        initiator.exchangePacket(nil) { complete, packet, error in
-            XCTAssertFalse(complete, "should be false")
-            XCTAssertNotNil(packet, "packet should not be nil")
-            XCTAssertNil(error, "error should be nil")
-            initiatorFirstPacket = packet!
-            firstInitiatorCallback.fulfill()
-        }
-
-        self.wait(for: [firstInitiatorCallback], timeout: 10)
+        let initiatorFirstPacket = self.sendPairingExpectingReply(channel: initiator, packet: nil, reason: "session initiation")
 
         acceptor.setOctagonMessageFailForTesting(true)
 
 
         acceptor.setOctagonMessageFailForTesting(true)
 
@@ -1286,8 +1093,8 @@ extension OctagonPairingTests {
         }
         self.wait(for: [firstAcceptorCallback], timeout: 10)
     }
         }
         self.wait(for: [firstAcceptorCallback], timeout: 10)
     }
-    func testProximitySetupOctagonAndSOSWithOctagonInitiatorMessage2Failure() {
 
 
+    func testProximitySetupOctagonAndSOSWithOctagonInitiatorMessage2Failure() {
         OctagonSetPlatformSupportsSOS(true)
         OctagonSetIsEnabled(true)
         self.startCKAccountStatusMock()
         OctagonSetPlatformSupportsSOS(true)
         OctagonSetIsEnabled(true)
         self.startCKAccountStatusMock()
@@ -1316,31 +1123,10 @@ extension OctagonPairingTests {
         self.wait(for: [signInCallback], timeout: 10)
 
         /* INITIATOR FIRST RTT JOINING MESSAGE*/
         self.wait(for: [signInCallback], timeout: 10)
 
         /* INITIATOR FIRST RTT JOINING MESSAGE*/
-        var initiatorFirstPacket = Data()
-        let firstInitiatorCallback = self.expectation(description: "firstInitiatorCallback callback occurs")
-
-        initiator.exchangePacket(nil) { complete, packet, error in
-            XCTAssertFalse(complete, "should be false")
-            XCTAssertNotNil(packet, "packet should not be nil")
-            XCTAssertNil(error, "error should be nil")
-            initiatorFirstPacket = packet!
-            firstInitiatorCallback.fulfill()
-        }
-
-        self.wait(for: [firstInitiatorCallback], timeout: 10)
+        let initiatorFirstPacket = self.sendPairingExpectingReply(channel: initiator, packet: nil, reason: "session initiation")
 
         /* ACCEPTOR FIRST RTT EPOCH*/
 
         /* ACCEPTOR FIRST RTT EPOCH*/
-        var acceptorFirstPacket = Data()
-        let firstAcceptorCallback = self.expectation(description: "firstAcceptorCallback callback occurs")
-
-        acceptor.exchangePacket(initiatorFirstPacket) { complete, packet, error in
-            XCTAssertFalse(complete, "should be false")
-            XCTAssertNotNil(packet, "packet should not be nil")
-            XCTAssertNil(error, "error should be nil")
-            acceptorFirstPacket = packet!
-            firstAcceptorCallback.fulfill()
-        }
-        self.wait(for: [firstAcceptorCallback], timeout: 10)
+        let acceptorEpochPacket = self.sendPairingExpectingReply(channel: acceptor, packet: initiatorFirstPacket, reason: "epoch return")
 
         /* INITIATOR SECOND RTT PREPARE*/
         let secondInitiatorCallback = self.expectation(description: "secondInitiatorCallback callback occurs")
 
         /* INITIATOR SECOND RTT PREPARE*/
         let secondInitiatorCallback = self.expectation(description: "secondInitiatorCallback callback occurs")
@@ -1348,7 +1134,7 @@ extension OctagonPairingTests {
         //set up initiator's message 2 to fail
         initiator.setOctagonMessageFailForTesting(true)
 
         //set up initiator's message 2 to fail
         initiator.setOctagonMessageFailForTesting(true)
 
-        initiator.exchangePacket(acceptorFirstPacket) { complete, packet, error in
+        initiator.exchangePacket(acceptorEpochPacket) { complete, packet, error in
             XCTAssertNil(error, "should be no error")
             XCTAssertTrue(complete, "should be true")
             XCTAssertNil(packet, "packet should not be nil")
             XCTAssertNil(error, "should be no error")
             XCTAssertTrue(complete, "should be true")
             XCTAssertNil(packet, "packet should not be nil")
@@ -1387,52 +1173,20 @@ extension OctagonPairingTests {
         self.wait(for: [signInCallback], timeout: 10)
 
         /* INITIATOR FIRST RTT JOINING MESSAGE*/
         self.wait(for: [signInCallback], timeout: 10)
 
         /* INITIATOR FIRST RTT JOINING MESSAGE*/
-        var initiatorFirstPacket = Data()
-        let firstInitiatorCallback = self.expectation(description: "firstInitiatorCallback callback occurs")
-
-        initiator.exchangePacket(nil) { complete, packet, error in
-            XCTAssertFalse(complete, "should be false")
-            XCTAssertNotNil(packet, "packet should not be nil")
-            XCTAssertNil(error, "error should be nil")
-            initiatorFirstPacket = packet!
-            firstInitiatorCallback.fulfill()
-        }
-
-        self.wait(for: [firstInitiatorCallback], timeout: 10)
+        let initiatorFirstPacket = self.sendPairingExpectingReply(channel: initiator, packet: nil, reason: "session initiation")
 
         /* ACCEPTOR FIRST RTT EPOCH*/
 
         /* ACCEPTOR FIRST RTT EPOCH*/
-        var acceptorFirstPacket = Data()
-        let firstAcceptorCallback = self.expectation(description: "firstAcceptorCallback callback occurs")
-
-        acceptor.exchangePacket(initiatorFirstPacket) { complete, packet, error in
-            XCTAssertFalse(complete, "should be false")
-            XCTAssertNotNil(packet, "packet should not be nil")
-            XCTAssertNil(error, "error should be nil")
-            acceptorFirstPacket = packet!
-            firstAcceptorCallback.fulfill()
-        }
-        self.wait(for: [firstAcceptorCallback], timeout: 10)
+        let acceptorEpochPacket = self.sendPairingExpectingReply(channel: acceptor, packet: initiatorFirstPacket, reason: "epoch return")
 
         /* INITIATOR SECOND RTT PREPARE*/
 
         /* INITIATOR SECOND RTT PREPARE*/
-        var initiatorSecondPacket = Data()
-        let secondInitiatorCallback = self.expectation(description: "secondInitiatorCallback callback occurs")
-
-        initiator.exchangePacket(acceptorFirstPacket) { complete, packet, error in
-            XCTAssertFalse(complete, "should be false")
-            XCTAssertNotNil(packet, "packet should not be nil")
-            XCTAssertNil(error, "error should be nil")
-            initiatorSecondPacket = packet!
-            secondInitiatorCallback.fulfill()
-        }
-
-        self.wait(for: [secondInitiatorCallback], timeout: 10)
+        let initiatorPreparedIdentityPacket = self.sendPairingExpectingReply(channel: initiator, packet: acceptorEpochPacket, reason: "prepared identity")
 
         /* ACCEPTOR SECOND RTT */
         let SecondAcceptorCallback = self.expectation(description: "SecondAcceptorCallback callback occurs")
 
         acceptor.setOctagonMessageFailForTesting(true)
 
 
         /* ACCEPTOR SECOND RTT */
         let SecondAcceptorCallback = self.expectation(description: "SecondAcceptorCallback callback occurs")
 
         acceptor.setOctagonMessageFailForTesting(true)
 
-        acceptor.exchangePacket(initiatorSecondPacket) { complete, packet, error in
+        acceptor.exchangePacket(initiatorPreparedIdentityPacket) { complete, packet, error in
             XCTAssertNil(error, "should be no error")
             XCTAssertTrue(complete, "should be true")
             XCTAssertNil(packet, "packet should be nil")
             XCTAssertNil(error, "should be no error")
             XCTAssertTrue(complete, "should be true")
             XCTAssertNil(packet, "packet should be nil")
@@ -1470,66 +1224,23 @@ extension OctagonPairingTests {
         self.wait(for: [signInCallback], timeout: 10)
 
         /* INITIATOR FIRST RTT JOINING MESSAGE*/
         self.wait(for: [signInCallback], timeout: 10)
 
         /* INITIATOR FIRST RTT JOINING MESSAGE*/
-        var initiatorFirstPacket = Data()
-        let firstInitiatorCallback = self.expectation(description: "firstInitiatorCallback callback occurs")
-
-        initiator.exchangePacket(nil) { complete, packet, error in
-            XCTAssertFalse(complete, "should be false")
-            XCTAssertNotNil(packet, "packet should not be nil")
-            XCTAssertNil(error, "error should be nil")
-            initiatorFirstPacket = packet!
-            firstInitiatorCallback.fulfill()
-        }
-
-        self.wait(for: [firstInitiatorCallback], timeout: 10)
+        let initiatorFirstPacket = self.sendPairingExpectingReply(channel: initiator, packet: nil, reason: "session initiation")
 
         /* ACCEPTOR FIRST RTT EPOCH*/
 
         /* ACCEPTOR FIRST RTT EPOCH*/
-        var acceptorFirstPacket = Data()
-        let firstAcceptorCallback = self.expectation(description: "firstAcceptorCallback callback occurs")
-
-        acceptor.exchangePacket(initiatorFirstPacket) { complete, packet, error in
-            XCTAssertFalse(complete, "should be false")
-            XCTAssertNotNil(packet, "packet should not be nil")
-            XCTAssertNil(error, "error should be nil")
-            acceptorFirstPacket = packet!
-            firstAcceptorCallback.fulfill()
-        }
-        self.wait(for: [firstAcceptorCallback], timeout: 10)
+        let acceptorEpochPacket = self.sendPairingExpectingReply(channel: acceptor, packet: initiatorFirstPacket, reason: "epoch return")
 
         /* INITIATOR SECOND RTT PREPARE*/
 
         /* INITIATOR SECOND RTT PREPARE*/
-        var initiatorSecondPacket = Data()
-        let secondInitiatorCallback = self.expectation(description: "secondInitiatorCallback callback occurs")
-
-        initiator.exchangePacket(acceptorFirstPacket) { complete, packet, error in
-            XCTAssertFalse(complete, "should be false")
-            XCTAssertNotNil(packet, "packet should not be nil")
-            XCTAssertNil(error, "error should be nil")
-            initiatorSecondPacket = packet!
-            secondInitiatorCallback.fulfill()
-        }
-
-        self.wait(for: [secondInitiatorCallback], timeout: 10)
+        let initiatorPreparedIdentityPacket = self.sendPairingExpectingReply(channel: initiator, packet: acceptorEpochPacket, reason: "prepared identity")
 
         /* ACCEPTOR SECOND RTT */
 
         /* ACCEPTOR SECOND RTT */
-        var acceptorSecondPacket = Data()
-        let SecondAcceptorCallback = self.expectation(description: "SecondAcceptorCallback callback occurs")
-
-        acceptor.exchangePacket(initiatorSecondPacket) { complete, packet, error in
-            XCTAssertFalse(complete, "should be false")
-            XCTAssertNotNil(packet, "packet should not be nil")
-            XCTAssertNil(error, "error should be nil")
-            acceptorSecondPacket = packet!
-            SecondAcceptorCallback.fulfill()
-        }
-        self.wait(for: [SecondAcceptorCallback], timeout: 10)
-        XCTAssertNotNil(acceptorSecondPacket, "acceptor second packet should not be nil")
+        let acceptorVoucherPacket = self.sendPairingExpectingReply(channel: acceptor, packet: initiatorPreparedIdentityPacket, reason: "epoch return")
 
         /* INITIATOR THIRD STEP*/
         let thirdInitiatorCallback = self.expectation(description: "thirdInitiatorCallback callback occurs")
 
         initiator.setOctagonMessageFailForTesting(true)
 
 
         /* INITIATOR THIRD STEP*/
         let thirdInitiatorCallback = self.expectation(description: "thirdInitiatorCallback callback occurs")
 
         initiator.setOctagonMessageFailForTesting(true)
 
-        initiator.exchangePacket(acceptorSecondPacket) { complete, packet, error in
+        initiator.exchangePacket(acceptorVoucherPacket) { complete, packet, error in
             XCTAssertNil(error, "should be no error")
             XCTAssertTrue(complete, "should be true")
             XCTAssertNil(packet, "packet should be nil")
             XCTAssertNil(error, "should be no error")
             XCTAssertTrue(complete, "should be true")
             XCTAssertNil(packet, "packet should be nil")
@@ -1611,75 +1322,19 @@ extension OctagonPairingTests {
         self.wait(for: [signInCallback], timeout: 10)
 
         /* INITIATOR FIRST RTT JOINING MESSAGE*/
         self.wait(for: [signInCallback], timeout: 10)
 
         /* INITIATOR FIRST RTT JOINING MESSAGE*/
-        var initiatorFirstPacket = Data()
-        let firstInitiatorCallback = self.expectation(description: "firstInitiatorCallback callback occurs")
-
-        initiator.exchangePacket(nil) { complete, packet, error in
-            XCTAssertFalse(complete, "should be false")
-            XCTAssertNotNil(packet, "packet should not be nil")
-            XCTAssertNil(error, "error should be nil")
-            initiatorFirstPacket = packet!
-            firstInitiatorCallback.fulfill()
-        }
-
-        self.wait(for: [firstInitiatorCallback], timeout: 10)
+        let initiatorFirstPacket = self.sendPairingExpectingReply(channel: initiator, packet: nil, reason: "session initiation")
 
         /* ACCEPTOR FIRST RTT EPOCH*/
 
         /* ACCEPTOR FIRST RTT EPOCH*/
-        var acceptorFirstPacket = Data()
-        let firstAcceptorCallback = self.expectation(description: "firstAcceptorCallback callback occurs")
-
-        acceptor.exchangePacket(initiatorFirstPacket) { complete, packet, error in
-            XCTAssertFalse(complete, "should be false")
-            XCTAssertNotNil(packet, "packet should not be nil")
-            XCTAssertNil(error, "error should be nil")
-            acceptorFirstPacket = packet!
-            firstAcceptorCallback.fulfill()
-        }
-        self.wait(for: [firstAcceptorCallback], timeout: 10)
-
-        initiator.setSessionSupportsOctagonForTesting(false)
+        let acceptorEpochPacket = self.sendPairingExpectingReply(channel: acceptor, packet: initiatorFirstPacket, reason: "epoch return")
 
         /* INITIATOR SECOND RTT PREPARE*/
 
         /* INITIATOR SECOND RTT PREPARE*/
-        var initiatorSecondPacket = Data()
-        let secondInitiatorCallback = self.expectation(description: "secondInitiatorCallback callback occurs")
-
-        initiator.exchangePacket(acceptorFirstPacket) { complete, packet, error in
-            XCTAssertFalse(complete, "should be false")
-            XCTAssertNotNil(packet, "packet should not be nil")
-            XCTAssertNil(error, "error should be nil")
-            initiatorSecondPacket = packet!
-            secondInitiatorCallback.fulfill()
-        }
-
-        self.wait(for: [secondInitiatorCallback], timeout: 10)
+        let initiatorPreparedIdentityPacket = self.sendPairingExpectingReply(channel: initiator, packet: acceptorEpochPacket, reason: "prepared identity")
 
         /* ACCEPTOR SECOND RTT */
 
         /* ACCEPTOR SECOND RTT */
-        var acceptorSecondPacket = Data()
-        let SecondAcceptorCallback = self.expectation(description: "SecondAcceptorCallback callback occurs")
-
-        acceptor.exchangePacket(initiatorSecondPacket) { complete, packet, error in
-            XCTAssertFalse(complete, "should be false")
-            XCTAssertNotNil(packet, "packet should not be nil")
-            XCTAssertNil(error, "error should be nil")
-            acceptorSecondPacket = packet!
-            SecondAcceptorCallback.fulfill()
-        }
-        self.wait(for: [SecondAcceptorCallback], timeout: 10)
-        XCTAssertNotNil(acceptorSecondPacket, "acceptor second packet should not be nil")
+        let acceptorVoucherPacket = self.sendPairingExpectingReply(channel: acceptor, packet: initiatorPreparedIdentityPacket, reason: "epoch return")
 
         /* INITIATOR THIRD STEP*/
 
         /* INITIATOR THIRD STEP*/
-        var initiatorThirdPacket: Data?
-        let thirdInitiatorCallback = self.expectation(description: "thirdInitiatorCallback callback occurs")
-
-        initiator.exchangePacket(acceptorSecondPacket) { complete, packet, error in
-            XCTAssertFalse(complete, "should be false")
-            XCTAssertNotNil(packet, "packet should not be nil")
-            XCTAssertNil(error, "error should be nil")
-            initiatorThirdPacket = packet!
-            thirdInitiatorCallback.fulfill()
-        }
-        self.wait(for: [thirdInitiatorCallback], timeout: 10)
-        XCTAssertNotNil(initiatorThirdPacket, "acceptor second packet should not be nil")
+        _ = self.sendPairingExpectingReply(channel: initiator, packet: acceptorVoucherPacket, reason: "intitiator third packet")
 /*
         need to fix attempting sos upgrade in the tests when pairing/piggybacking and then kicking off an upgrade
         let initiatorContext = self.manager.context(forContainerName: OTCKContainerName, contextID: OTDefaultContext)
 /*
         need to fix attempting sos upgrade in the tests when pairing/piggybacking and then kicking off an upgrade
         let initiatorContext = self.manager.context(forContainerName: OTCKContainerName, contextID: OTDefaultContext)
@@ -1721,73 +1376,19 @@ extension OctagonPairingTests {
         self.wait(for: [signInCallback], timeout: 10)
 
         /* INITIATOR FIRST RTT JOINING MESSAGE*/
         self.wait(for: [signInCallback], timeout: 10)
 
         /* INITIATOR FIRST RTT JOINING MESSAGE*/
-        var initiatorFirstPacket = Data()
-        let firstInitiatorCallback = self.expectation(description: "firstInitiatorCallback callback occurs")
-
-        initiator.exchangePacket(nil) { complete, packet, error in
-            XCTAssertFalse(complete, "should be false")
-            XCTAssertNotNil(packet, "packet should not be nil")
-            XCTAssertNil(error, "error should be nil")
-            initiatorFirstPacket = packet!
-            firstInitiatorCallback.fulfill()
-        }
-
-        self.wait(for: [firstInitiatorCallback], timeout: 10)
+        let initiatorFirstPacket = self.sendPairingExpectingReply(channel: initiator, packet: nil, reason: "session initiation")
 
         /* ACCEPTOR FIRST RTT EPOCH*/
 
         /* ACCEPTOR FIRST RTT EPOCH*/
-        var acceptorFirstPacket = Data()
-        let firstAcceptorCallback = self.expectation(description: "firstAcceptorCallback callback occurs")
-
-        acceptor.exchangePacket(initiatorFirstPacket) { complete, packet, error in
-            XCTAssertFalse(complete, "should be false")
-            XCTAssertNotNil(packet, "packet should not be nil")
-            XCTAssertNil(error, "error should be nil")
-            acceptorFirstPacket = packet!
-            firstAcceptorCallback.fulfill()
-        }
-        self.wait(for: [firstAcceptorCallback], timeout: 10)
+        let acceptorEpochPacket = self.sendPairingExpectingReply(channel: acceptor, packet: initiatorFirstPacket, reason: "epoch return")
 
         /* INITIATOR SECOND RTT PREPARE*/
 
         /* INITIATOR SECOND RTT PREPARE*/
-        var initiatorSecondPacket = Data()
-        let secondInitiatorCallback = self.expectation(description: "secondInitiatorCallback callback occurs")
-
-        initiator.exchangePacket(acceptorFirstPacket) { complete, packet, error in
-            XCTAssertFalse(complete, "should be false")
-            XCTAssertNotNil(packet, "packet should not be nil")
-            XCTAssertNil(error, "error should be nil")
-            initiatorSecondPacket = packet!
-            secondInitiatorCallback.fulfill()
-        }
-
-        self.wait(for: [secondInitiatorCallback], timeout: 10)
+        let initiatorPreparedIdentityPacket = self.sendPairingExpectingReply(channel: initiator, packet: acceptorEpochPacket, reason: "prepared identity")
 
         /* ACCEPTOR SECOND RTT */
 
         /* ACCEPTOR SECOND RTT */
-        var acceptorSecondPacket = Data()
-        let SecondAcceptorCallback = self.expectation(description: "SecondAcceptorCallback callback occurs")
-
-        acceptor.exchangePacket(initiatorSecondPacket) { complete, packet, error in
-            XCTAssertFalse(complete, "should be false")
-            XCTAssertNotNil(packet, "packet should not be nil")
-            XCTAssertNil(error, "error should be nil")
-            acceptorSecondPacket = packet!
-            SecondAcceptorCallback.fulfill()
-        }
-        self.wait(for: [SecondAcceptorCallback], timeout: 10)
-        XCTAssertNotNil(acceptorSecondPacket, "acceptor second packet should not be nil")
+        let acceptorVoucherPacket = self.sendPairingExpectingReply(channel: acceptor, packet: initiatorPreparedIdentityPacket, reason: "epoch return")
 
         /* INITIATOR THIRD STEP*/
 
         /* INITIATOR THIRD STEP*/
-        var initiatorThirdPacket: Data?
-        let thirdInitiatorCallback = self.expectation(description: "thirdInitiatorCallback callback occurs")
-
-        initiator.exchangePacket(acceptorSecondPacket) { complete, packet, error in
-            XCTAssertFalse(complete, "should be false")
-            XCTAssertNotNil(packet, "packet should not be nil")
-            XCTAssertNil(error, "error should be nil")
-            initiatorThirdPacket = packet!
-            thirdInitiatorCallback.fulfill()
-        }
-        self.wait(for: [thirdInitiatorCallback], timeout: 10)
-        XCTAssertNotNil(initiatorThirdPacket, "acceptor second packet should not be nil")
+        _ = self.sendPairingExpectingReply(channel: initiator, packet: acceptorVoucherPacket, reason: "intitiator third packet")
 
         /*
          need to fix attempting sos upgrade in the tests when pairing/piggybacking and then kicking off an upgrade
 
         /*
          need to fix attempting sos upgrade in the tests when pairing/piggybacking and then kicking off an upgrade
index ea84fc767014117a67adf94d4231949e8429f016..5eda3686a79587a1813978fc076de3f908be68a7 100644 (file)
@@ -4,7 +4,7 @@ func GenerateFullECKey(keySize: Int) -> (SecKey) {
 
     let keyPair = _SFECKeyPair.init(randomKeyPairWith: _SFECKeySpecifier.init(curve: SFEllipticCurve.nistp384))!
 
 
     let keyPair = _SFECKeyPair.init(randomKeyPairWith: _SFECKeySpecifier.init(curve: SFEllipticCurve.nistp384))!
 
-    var keyAttributes: Dictionary<String, String> = [:]
+    var keyAttributes: [String: String] = [:]
     keyAttributes[kSecAttrKeyClass as String] = kSecAttrKeyClassPrivate as String
     keyAttributes[kSecAttrKeyType as String] = kSecAttrKeyTypeEC as String
 
     keyAttributes[kSecAttrKeyClass as String] = kSecAttrKeyClassPrivate as String
     keyAttributes[kSecAttrKeyType as String] = kSecAttrKeyTypeEC as String
 
@@ -35,7 +35,7 @@ class KCJoiningRequestTestDelegate: NSObject, KCJoiningRequestSecretDelegate, KC
         XCTAssertNotNil(octagonSigningKey, "signing key should not be nil")
         XCTAssertNotNil(octagonEncryptionKey, "encryption key should not be nil")
 
         XCTAssertNotNil(octagonSigningKey, "signing key should not be nil")
         XCTAssertNotNil(octagonEncryptionKey, "encryption key should not be nil")
 
-        var gestalt: Dictionary<String, String> = [:]
+        var gestalt: [String: String] = [:]
         gestalt[kPIUserDefinedDeviceNameKey as String] = "Fakey"
 
         let newPeerInfo = SOSPeerInfoCreate(nil, gestalt as CFDictionary, nil, signingKey, octagonSigningKey, octagonEncryptionKey, nil)
         gestalt[kPIUserDefinedDeviceNameKey as String] = "Fakey"
 
         let newPeerInfo = SOSPeerInfoCreate(nil, gestalt as CFDictionary, nil, signingKey, octagonSigningKey, octagonEncryptionKey, nil)
@@ -47,7 +47,7 @@ class KCJoiningRequestTestDelegate: NSObject, KCJoiningRequestSecretDelegate, KC
     }
 
     func nextSecret() -> String {
     }
 
     func nextSecret() -> String {
-        if (self.incorrectTries > 0) {
+        if self.incorrectTries > 0 {
             self.incorrectTries -= 1
             return self.incorrectSecret
         }
             self.incorrectTries -= 1
             return self.incorrectSecret
         }
@@ -79,7 +79,7 @@ class KCJoiningRequestTestDelegate: NSObject, KCJoiningRequestSecretDelegate, KC
 
 class KCJoiningAcceptTestDelegate: NSObject, KCJoiningAcceptSecretDelegate, KCJoiningAcceptCircleDelegate {
 
 
 class KCJoiningAcceptTestDelegate: NSObject, KCJoiningAcceptSecretDelegate, KCJoiningAcceptCircleDelegate {
 
-    var secrets: Array<String> = []
+    var secrets: [String] = []
     var currentSecret: Int = 0
     var retriesLeft: Int = 0
     var retriesPerSecret: Int = 0
     var currentSecret: Int = 0
     var retriesLeft: Int = 0
     var retriesPerSecret: Int = 0
@@ -87,7 +87,7 @@ class KCJoiningAcceptTestDelegate: NSObject, KCJoiningAcceptSecretDelegate, KCJo
     var circleJoinData = Data()
     var peerInfo: SOSPeerInfoRef?
 
     var circleJoinData = Data()
     var peerInfo: SOSPeerInfoRef?
 
-    class func acceptDelegateWithSecrets(secrets: Array<String>, retries: Int, code: String) -> KCJoiningAcceptTestDelegate {
+    class func acceptDelegateWithSecrets(secrets: [String], retries: Int, code: String) -> KCJoiningAcceptTestDelegate {
         return KCJoiningAcceptTestDelegate(withSecrets: secrets, retries: retries, code: code)
     }
 
         return KCJoiningAcceptTestDelegate(withSecrets: secrets, retries: retries, code: code)
     }
 
@@ -96,12 +96,12 @@ class KCJoiningAcceptTestDelegate: NSObject, KCJoiningAcceptSecretDelegate, KCJo
     }
 
     class func initWithSecret(secret: String, code: String) -> KCJoiningAcceptTestDelegate {
     }
 
     class func initWithSecret(secret: String, code: String) -> KCJoiningAcceptTestDelegate {
-        var secretArray: Array<String> = Array()
+        var secretArray: [String] = Array()
         secretArray.append(secret)
         return KCJoiningAcceptTestDelegate(withSecrets: secretArray, retries: 3, code: code)
     }
 
         secretArray.append(secret)
         return KCJoiningAcceptTestDelegate(withSecrets: secretArray, retries: 3, code: code)
     }
 
-    init(withSecrets secrets: Array<String>, retries: Int, code: String) {
+    init(withSecrets secrets: [String], retries: Int, code: String) {
 
         self.secrets = secrets
         self.currentSecret = 0
 
         self.secrets = secrets
         self.currentSecret = 0
@@ -119,9 +119,9 @@ class KCJoiningAcceptTestDelegate: NSObject, KCJoiningAcceptSecretDelegate, KCJo
     }
 
     func advanceSecret() -> KCRetryOrNot {
     }
 
     func advanceSecret() -> KCRetryOrNot {
-        if (self.retriesLeft == 0) {
+        if self.retriesLeft == 0 {
             self.currentSecret += 1
             self.currentSecret += 1
-            if (self.currentSecret >= self.secrets.count) {
+            if self.currentSecret >= self.secrets.count {
                 self.currentSecret = self.secrets.count - 1
             }
             self.retriesLeft = self.retriesPerSecret
                 self.currentSecret = self.secrets.count - 1
             }
             self.retriesLeft = self.retriesPerSecret
@@ -163,7 +163,203 @@ class KCJoiningAcceptTestDelegate: NSObject, KCJoiningAcceptSecretDelegate, KCJo
     }
 }
 
     }
 }
 
-@objcMembers class OctagonPairingTests: OctagonTestsBase {
+// Similar to the helpers below, but more accessible
+extension OctagonTestsBase {
+    func joiningConfigurations(initiator: OTCuttlefishContext, initiatorDeviceID: String, sponsor: OTCuttlefishContext, sponsorDeviceID: String) -> (OTJoiningConfiguration, OTJoiningConfiguration) {
+        let acceptorConfig = OTJoiningConfiguration(protocolType: OTProtocolPairing,
+                                                    uniqueDeviceID: sponsorDeviceID,
+                                                    uniqueClientID: initiatorDeviceID,
+                                                    pairingUUID: UUID().uuidString,
+                                                    containerName: OTCKContainerName,
+                                                    contextID: sponsor.contextID,
+                                                    epoch: 1,
+                                                    isInitiator: false)
+
+        let initiatorConfig = OTJoiningConfiguration(protocolType: OTProtocolPairing,
+                                                     uniqueDeviceID: initiatorDeviceID,
+                                                     uniqueClientID: initiatorDeviceID,
+                                                     pairingUUID: UUID().uuidString,
+                                                     containerName: OTCKContainerName,
+                                                     contextID: initiator.contextID,
+                                                     epoch: 1,
+                                                     isInitiator: true)
+
+        return (acceptorConfig, initiatorConfig)
+    }
+
+    func setupPairingChannels(initiator: OTCuttlefishContext, sponsor: OTCuttlefishContext) -> (KCPairingChannel, KCPairingChannel) {
+        let sponsorChannelContext = KCPairingChannelContext()
+        sponsorChannelContext.model = sponsor.deviceAdapter.modelID()
+        sponsorChannelContext.osVersion = sponsor.deviceAdapter.osVersion()
+        sponsorChannelContext.modelClass = "AcceptorModelClass"
+
+        sponsorChannelContext.uniqueDeviceID = UUID().uuidString
+        sponsorChannelContext.uniqueClientID = UUID().uuidString
+
+        let initiatorChannelContext = KCPairingChannelContext()
+        initiatorChannelContext.model = initiator.deviceAdapter.modelID()
+        initiatorChannelContext.osVersion = initiator.deviceAdapter.osVersion()
+        initiatorChannelContext.modelClass = "InitiatorModelClass"
+
+        // The initiator's client ID is equivalent to the sponsor's client ID
+        initiatorChannelContext.uniqueDeviceID = sponsorChannelContext.uniqueClientID
+
+        let sponsorPairingChannel = KCPairingChannel(acceptor: sponsorChannelContext)
+        let initiatorPairingChannel = KCPairingChannel(initiator: initiatorChannelContext)
+
+        XCTAssertNotNil(sponsorPairingChannel, "Should have a sponsor pairing channel")
+        XCTAssertNotNil(initiatorPairingChannel, "Should have a initiator pairing channel")
+
+        sponsorPairingChannel?.setControlObject(self.otControl)
+        initiatorPairingChannel?.setControlObject(self.otControl)
+
+        let (acceptorPairingConfig, initiatorPairingConfig) = self.joiningConfigurations(initiator: initiator,
+                                                                                         initiatorDeviceID: initiatorChannelContext.uniqueDeviceID,
+                                                                                         sponsor: sponsor,
+                                                                                         sponsorDeviceID: sponsorChannelContext.uniqueDeviceID)
+
+        sponsorPairingChannel?.setConfiguration(acceptorPairingConfig)
+        initiatorPairingChannel?.setConfiguration(initiatorPairingConfig)
+
+        let fakeCircle = SOSCircleCreate(kCFAllocatorDefault, "TEST DOMAIN" as CFString, nil) as SOSCircleRef
+
+        sponsorPairingChannel?.setXPCConnectionObject(FakeNSXPCConnectionSOS(withSOSControl: FCPairingFakeSOSControl(randomAccountKey: true, circle: fakeCircle)))
+        initiatorPairingChannel?.setXPCConnectionObject(FakeNSXPCConnectionSOS(withSOSControl: FCPairingFakeSOSControl(randomAccountKey: true, circle: fakeCircle)))
+
+        return (sponsorPairingChannel!, initiatorPairingChannel!)
+    }
+
+    func setupPiggybackingSessions(initiator: OTCuttlefishContext,
+                                   sponsor: OTCuttlefishContext) throws -> (KCJoiningRequestTestDelegate, KCJoiningAcceptTestDelegate, KCJoiningAcceptSession, KCJoiningRequestSecretSession) {
+        let (acceptorJoiningConfig, _) = self.joiningConfigurations(initiator: initiator,
+                                                                    initiatorDeviceID: UUID().uuidString,
+                                                                    sponsor: sponsor,
+                                                                    sponsorDeviceID: UUID().uuidString)
+
+        return try self.setupKCJoiningSessionObjects(dsid: 0x123456,
+                                                     sponsorConfiguration: acceptorJoiningConfig)
+    }
+
+    func setupKCJoiningSessionObjects(dsid: UInt64,
+                                      sponsorConfiguration: OTJoiningConfiguration) throws -> (KCJoiningRequestTestDelegate, KCJoiningAcceptTestDelegate, KCJoiningAcceptSession, KCJoiningRequestSecretSession) {
+        let secret = "123456"
+        let code = "987654"
+
+        let requestDelegate = KCJoiningRequestTestDelegate.requestDelegate(withSecret: secret)
+        let acceptDelegate = KCJoiningAcceptTestDelegate.acceptDelegateWithSecret(secret: secret, code: code)
+
+        let requestSession = try KCJoiningRequestSecretSession(secretDelegate: requestDelegate as KCJoiningRequestSecretDelegate, dsid: dsid, rng: ccDRBGGetRngState())
+
+        let acceptSession = try KCJoiningAcceptSession(secretDelegate: acceptDelegate as KCJoiningAcceptSecretDelegate,
+                                                       circleDelegate: acceptDelegate as KCJoiningAcceptCircleDelegate,
+                                                       dsid: dsid,
+                                                       rng: ccDRBGGetRngState())
+        requestSession.setControlObject(self.otControl)
+        acceptSession.setControlObject(self.otControl)
+
+        // requestSessions don't need control objects
+        acceptSession.setConfiguration(sponsorConfiguration)
+
+        return (requestDelegate, acceptDelegate, acceptSession, requestSession)
+    }
+
+    func octagonPiggypackingMessage(in message: KCJoiningMessage) throws -> OTPairingMessage {
+        guard let octagonData = message.secondData else {
+            throw NSError(domain: "missing octagon data" as String, code: -1, userInfo: nil)
+        }
+
+        return OTPairingMessage(data: octagonData)
+    }
+
+    func unpackPiggybackingInitialMessage(identityMessage: Data, session: KCAESGCMDuplexSession) throws -> OTPairingMessage {
+        let encryptedJoiningIdentityMessage = try KCJoiningMessage(der: identityMessage)
+        let joiningIdentityMessage = try session.decryptAndVerify(encryptedJoiningIdentityMessage.firstData)
+
+        let initialMessageProtobuf = KCInitialMessageData(data: joiningIdentityMessage)
+        XCTAssertNotNil(initialMessageProtobuf, "should have an initial message container")
+        let initiatorIdentityMessageOpt = OTPairingMessage(data: initialMessageProtobuf!.prepareMessage)
+        XCTAssertNotNil(initiatorIdentityMessageOpt, "should have an Octagon message container")
+        return initiatorIdentityMessageOpt!
+    }
+
+    func makePiggybackingPacket(combining currentMessage: KCJoiningMessage, octagonMessage: OTPairingMessage) throws -> Data {
+        let newMessage = try KCJoiningMessage(type: currentMessage.type,
+                                              data: currentMessage.firstData,
+                                              payload: octagonMessage.data)
+        return newMessage.der
+    }
+
+    func pairingPacketToPlist(packet: Data) throws -> [String: Any] {
+        let binaryPlist = KCPairingChannel.pairingChannelDecompressData(packet)!
+        let plist = (try PropertyListSerialization.propertyList(from: binaryPlist, options: [], format: nil)) as! [String: Any]
+        return plist
+    }
+
+    func octagonPairingMessage(in packet: Data) throws -> OTPairingMessage {
+        let plist = try self.pairingPacketToPlist(packet: packet)
+
+        guard let octagonData = plist["o"] as? Data else {
+            throw NSError(domain: "missing octagon data" as String, code: -1, userInfo: nil)
+        }
+
+        return OTPairingMessage(data: octagonData)
+    }
+
+    func makePairingPacket(combining currentPacket: Data, octagonMessage: OTPairingMessage) throws -> Data {
+        var plist = try self.pairingPacketToPlist(packet: currentPacket)
+        plist["o"] = octagonMessage.data
+
+        return KCPairingChannel.pairingChannelCompressData(try PropertyListSerialization.data(fromPropertyList: plist, format: .binary, options: 0))
+    }
+
+    func sendPairingExpectingReply(channel: KCPairingChannel, packet: Data?, reason: String) -> Data {
+        var packetResponse = Data()
+        let callback = self.expectation(description: "callback occurs (\(reason))")
+
+        channel.exchangePacket(packet) { complete, response, error in
+            XCTAssertNil(error, "error should be nil (\(reason))")
+
+            XCTAssertFalse(complete, "Expected no completion (\(reason))")
+            XCTAssertNotNil(response, "packet should not be nil (\(reason))")
+            packetResponse = response!
+            callback.fulfill()
+        }
+        self.wait(for: [callback], timeout: 10)
+        return packetResponse
+    }
+
+    func sendPairingExpectingCompletionAndReply(channel: KCPairingChannel, packet: Data?, reason: String) -> Data {
+        var packetResponse = Data()
+        let callback = self.expectation(description: "callback occurs (\(reason))")
+
+        channel.exchangePacket(packet) { complete, response, error in
+            XCTAssertNil(error, "error should be nil (\(reason))")
+
+            XCTAssertTrue(complete, "Expected channel completion (\(reason))")
+            XCTAssertNotNil(response, "response should be present (\(reason))")
+            packetResponse = response!
+            callback.fulfill()
+        }
+        self.wait(for: [callback], timeout: 10)
+        return packetResponse
+    }
+
+    func sendPairingExpectingCompletion(channel: KCPairingChannel, packet: Data?, reason: String) {
+        let callback = self.expectation(description: "callback occurs (\(reason))")
+
+        channel.exchangePacket(packet) { complete, response, error in
+            XCTAssertNil(error, "error should be nil (\(reason))")
+
+            XCTAssertTrue(complete, "Expected channel completion (\(reason))")
+            XCTAssertNil(response, "response should be nil (\(reason))")
+            callback.fulfill()
+        }
+        self.wait(for: [callback], timeout: 10)
+    }
+}
+
+@objcMembers
+class OctagonPairingTests: OctagonTestsBase {
 
     var sosAdapterForAcceptor: CKKSMockSOSPresentAdapter!
     var cuttlefishContextForAcceptor: OTCuttlefishContext!
 
     var sosAdapterForAcceptor: CKKSMockSOSPresentAdapter!
     var cuttlefishContextForAcceptor: OTCuttlefishContext!
@@ -197,11 +393,39 @@ class KCJoiningAcceptTestDelegate: NSObject, KCJoiningAcceptSecretDelegate, KCJo
                                                 accountStateTracker: self.accountStateTracker,
                                                 deviceInformationAdapter: OTMockDeviceInfoAdapter(modelID: "iPhone9,1", deviceName: "test-SOS-iphone", serialNumber: "456", osVersion: "iOS (fake version)"))
 
                                                 accountStateTracker: self.accountStateTracker,
                                                 deviceInformationAdapter: OTMockDeviceInfoAdapter(modelID: "iPhone9,1", deviceName: "test-SOS-iphone", serialNumber: "456", osVersion: "iOS (fake version)"))
 
-        self.acceptorPiggybackingConfig = OTJoiningConfiguration(protocolType: OTProtocolPiggybacking, uniqueDeviceID: "acceptor", uniqueClientID: self.initiatorName, containerName: OTCKContainerName, contextID: self.contextForAcceptor, epoch: 1, isInitiator: false)
-        self.initiatorPiggybackingConfig = OTJoiningConfiguration(protocolType: OTProtocolPiggybacking, uniqueDeviceID: "initiator", uniqueClientID: "acceptor", containerName: OTCKContainerName, contextID: OTDefaultContext, epoch: 1, isInitiator: true)
-
-        self.acceptorPairingConfig = OTJoiningConfiguration(protocolType: OTProtocolPairing, uniqueDeviceID: "acceptor", uniqueClientID: self.initiatorName, containerName: OTCKContainerName, contextID: self.contextForAcceptor, epoch: 1, isInitiator: false)
-        self.initiatorPairingConfig = OTJoiningConfiguration(protocolType: OTProtocolPairing, uniqueDeviceID: "initiator", uniqueClientID: "acceptor", containerName: OTCKContainerName, contextID: OTDefaultContext, epoch: 1, isInitiator: true)
+        self.acceptorPiggybackingConfig = OTJoiningConfiguration(protocolType: OTProtocolPiggybacking,
+                                                                 uniqueDeviceID: "acceptor",
+                                                                 uniqueClientID: self.initiatorName,
+                                                                 pairingUUID: UUID().uuidString,
+                                                                 containerName: OTCKContainerName,
+                                                                 contextID: self.contextForAcceptor,
+                                                                 epoch: 1,
+                                                                 isInitiator: false)
+        self.initiatorPiggybackingConfig = OTJoiningConfiguration(protocolType: OTProtocolPiggybacking,
+                                                                  uniqueDeviceID: "initiator",
+                                                                  uniqueClientID: "acceptor",
+                                                                  pairingUUID: UUID().uuidString,
+                                                                  containerName: OTCKContainerName,
+                                                                  contextID: OTDefaultContext,
+                                                                  epoch: 1,
+                                                                  isInitiator: true)
+
+        self.acceptorPairingConfig = OTJoiningConfiguration(protocolType: OTProtocolPairing,
+                                                            uniqueDeviceID: "acceptor",
+                                                            uniqueClientID: self.initiatorName,
+                                                            pairingUUID: UUID().uuidString,
+                                                            containerName: OTCKContainerName,
+                                                            contextID: self.contextForAcceptor,
+                                                            epoch: 1,
+                                                            isInitiator: false)
+        self.initiatorPairingConfig = OTJoiningConfiguration(protocolType: OTProtocolPairing,
+                                                             uniqueDeviceID: "initiator",
+                                                             uniqueClientID: "acceptor",
+                                                             pairingUUID: UUID().uuidString,
+                                                             containerName: OTCKContainerName,
+                                                             contextID: OTDefaultContext,
+                                                             epoch: 1,
+                                                             isInitiator: true)
     }
 
     func getAcceptorInCircle() {
     }
 
     func getAcceptorInCircle() {
@@ -237,7 +461,6 @@ class KCJoiningAcceptTestDelegate: NSObject, KCJoiningAcceptSecretDelegate, KCJo
         initiatorContext.osVersion = "InitiatorOsVersion"
         initiatorContext.modelClass = "InitiatorModelClass"
         initiatorContext.uniqueDeviceID = initiatorUniqueID
         initiatorContext.osVersion = "InitiatorOsVersion"
         initiatorContext.modelClass = "InitiatorModelClass"
         initiatorContext.uniqueDeviceID = initiatorUniqueID
-        initiatorContext.uniqueDeviceID = initiatorUniqueID
 
         let acceptor = acceptorClique!.setupPairingChannel(asAcceptor: acceptorContext)
         let initiator = initiatorClique!.setupPairingChannel(asInitiator: initiatorContext)
 
         let acceptor = acceptorClique!.setupPairingChannel(asAcceptor: acceptorContext)
         let initiator = initiatorClique!.setupPairingChannel(asInitiator: initiatorContext)
@@ -248,8 +471,22 @@ class KCJoiningAcceptTestDelegate: NSObject, KCJoiningAcceptSecretDelegate, KCJo
         acceptor.setControlObject(self.otControl)
         initiator.setControlObject(self.otControl)
 
         acceptor.setControlObject(self.otControl)
         initiator.setControlObject(self.otControl)
 
-        let acceptorPairingConfig = OTJoiningConfiguration(protocolType: OTProtocolPairing, uniqueDeviceID: acceptorUniqueID, uniqueClientID: initiatorUniqueID, containerName: OTCKContainerName, contextID: acceptorContextID, epoch: 1, isInitiator: false)
-        let initiatorPairingConfig = OTJoiningConfiguration(protocolType: OTProtocolPairing, uniqueDeviceID: initiatorUniqueID, uniqueClientID: initiatorUniqueID, containerName: OTCKContainerName, contextID: initiatorContextID, epoch: 1, isInitiator: true)
+        let acceptorPairingConfig = OTJoiningConfiguration(protocolType: OTProtocolPairing,
+                                                           uniqueDeviceID: acceptorUniqueID,
+                                                           uniqueClientID: initiatorUniqueID,
+                                                           pairingUUID: UUID().uuidString,
+                                                           containerName: OTCKContainerName,
+                                                           contextID: acceptorContextID,
+                                                           epoch: 1,
+                                                           isInitiator: false)
+        let initiatorPairingConfig = OTJoiningConfiguration(protocolType: OTProtocolPairing,
+                                                            uniqueDeviceID: initiatorUniqueID,
+                                                            uniqueClientID: initiatorUniqueID,
+                                                            pairingUUID: UUID().uuidString,
+                                                            containerName: OTCKContainerName,
+                                                            contextID: initiatorContextID,
+                                                            epoch: 1,
+                                                            isInitiator: true)
 
         acceptor.setConfiguration(acceptorPairingConfig)
         initiator.setConfiguration(initiatorPairingConfig)
 
         acceptor.setConfiguration(acceptorPairingConfig)
         initiator.setConfiguration(initiatorPairingConfig)
@@ -278,53 +515,30 @@ class KCJoiningAcceptTestDelegate: NSObject, KCJoiningAcceptSecretDelegate, KCJo
         XCTAssertNotNil(acceptorAnalytics, "acceptorAnalytics should not be nil")
         secondAcceptorData.analytics = acceptorAnalytics
 
         XCTAssertNotNil(acceptorAnalytics, "acceptorAnalytics should not be nil")
         secondAcceptorData.analytics = acceptorAnalytics
 
-        do {
-            let acceptor = try OTClique(contextData: secondAcceptorData)
-            XCTAssertNotNil(acceptor, "Clique should not be nil")
-            acceptor.setPairingDefault(true)
+        let acceptor = OTClique(contextData: secondAcceptorData)
+        XCTAssertNotNil(acceptor, "Clique should not be nil")
+        acceptor.setPairingDefault(true)
 
 
-            let secondInitiatorData = OTConfigurationContext()
-            secondInitiatorData.context = "secondInitiator"
-            secondInitiatorData.dsid = "i-"+count
-            secondInitiatorData.altDSID = "alt-i-"+count
+        let secondInitiatorData = OTConfigurationContext()
+        secondInitiatorData.context = "secondInitiator"
+        secondInitiatorData.dsid = "i-"+count
+        secondInitiatorData.altDSID = "alt-i-"+count
 
 
-            let initiatorAnalytics = SFSignInAnalytics(signInUUID: "uuid", category: "com.apple.cdp", eventName: "signed in")
-            XCTAssertNotNil(initiatorAnalytics, "initiatorAnalytics should not be nil")
-            secondInitiatorData.analytics = initiatorAnalytics
-            let initiator = try OTClique(contextData: secondInitiatorData)
-            XCTAssertNotNil(initiator, "Clique should not be nil")
-            initiator.setPairingDefault(true)
+        let initiatorAnalytics = SFSignInAnalytics(signInUUID: "uuid", category: "com.apple.cdp", eventName: "signed in")
+        XCTAssertNotNil(initiatorAnalytics, "initiatorAnalytics should not be nil")
+        secondInitiatorData.analytics = initiatorAnalytics
+        let initiator = OTClique(contextData: secondInitiatorData)
+        XCTAssertNotNil(initiator, "Clique should not be nil")
+        initiator.setPairingDefault(true)
 
 
-            return (acceptor, initiator)
-
-        } catch {
-            XCTFail("error creating test clique: \(error)")
-        }
-        return(nil, nil)
+        return (acceptor, initiator)
     }
 
     func setupKCJoiningSessionObjects() -> (KCJoiningRequestTestDelegate?, KCJoiningAcceptTestDelegate?, KCJoiningAcceptSession?, KCJoiningRequestSecretSession?) {
     }
 
     func setupKCJoiningSessionObjects() -> (KCJoiningRequestTestDelegate?, KCJoiningAcceptTestDelegate?, KCJoiningAcceptSession?, KCJoiningRequestSecretSession?) {
-
-        let secret = "123456"
-        let code = "987654"
         let dsid: UInt64 = 0x1234567887654321
 
         let dsid: UInt64 = 0x1234567887654321
 
-        let requestDelegate = KCJoiningRequestTestDelegate.requestDelegate(withSecret: secret)
-        let acceptDelegate = KCJoiningAcceptTestDelegate.acceptDelegateWithSecret(secret: secret, code: code)
-
         do {
         do {
-            let requestSession = try KCJoiningRequestSecretSession(secretDelegate: requestDelegate as KCJoiningRequestSecretDelegate, dsid: dsid, rng: ccDRBGGetRngState())
-
-            let acceptSession = try KCJoiningAcceptSession(secretDelegate: acceptDelegate as KCJoiningAcceptSecretDelegate,
-                                                           circleDelegate: acceptDelegate as KCJoiningAcceptCircleDelegate,
-                                                           dsid: dsid,
-                                                           rng: ccDRBGGetRngState())
-            requestSession.setControlObject(self.otControl)
-            acceptSession.setControlObject(self.otControl)
-            requestSession.setConfiguration(self.initiatorPiggybackingConfig)
-            acceptSession.setConfiguration(self.acceptorPiggybackingConfig)
-
-            return (requestDelegate, acceptDelegate, acceptSession, requestSession)
+            return try self.setupKCJoiningSessionObjects(dsid: dsid, sponsorConfiguration: self.acceptorPiggybackingConfig)
         } catch {
             XCTFail("error creating test clique: \(error)")
             return (nil, nil, nil, nil)
         } catch {
             XCTFail("error creating test clique: \(error)")
             return (nil, nil, nil, nil)
index a62f3bce6f49e71b6034b95114903c9f68a96fb4..382933355e65a5770bf3dfc67bdb7cf1a818d386 100644 (file)
@@ -21,6 +21,8 @@ NS_ASSUME_NONNULL_BEGIN
 
 - (long)resetOctagon:(NSString*)container context:(NSString*)contextID altDSID:(NSString*)altDSID;
 
 
 - (long)resetOctagon:(NSString*)container context:(NSString*)contextID altDSID:(NSString*)altDSID;
 
+- (long)resetProtectedData:(NSString*)container context:(NSString*)contextID altDSID:(NSString*)altDSID appleID:(NSString*)appleID dsid:(NSString*)dsid;
+
 - (long)status:(NSString* _Nullable)container context:(NSString*)contextID json:(bool)json;
 
 - (long)recoverUsingBottleID:(NSString*)bottleID
 - (long)status:(NSString* _Nullable)container context:(NSString*)contextID json:(bool)json;
 
 - (long)recoverUsingBottleID:(NSString*)bottleID
@@ -36,6 +38,7 @@ NS_ASSUME_NONNULL_BEGIN
                 control:(OTControl*)control;
 
 - (long)healthCheck:(NSString* _Nullable)container context:(NSString*)contextID skipRateLimitingCheck:(BOOL)skipRateLimitingCheck;
                 control:(OTControl*)control;
 
 - (long)healthCheck:(NSString* _Nullable)container context:(NSString*)contextID skipRateLimitingCheck:(BOOL)skipRateLimitingCheck;
+- (long)refetchCKKSPolicy:(NSString*)container context:(NSString*)contextID;
 
 - (long)tapToRadar:(NSString *)action description:(NSString *)description radar:(NSString *)radar;
 
 
 - (long)tapToRadar:(NSString *)action description:(NSString *)description radar:(NSString *)radar;
 
index 5fa418c48739eb76cbb944f458bfe02c3fc7e20f..5637dec991ad9725437afd0bcabc7b3f7e1aa3f3 100644 (file)
 #include "utilities/SecInternalReleasePriv.h"
 #import "utilities/debugging.h"
 
 #include "utilities/SecInternalReleasePriv.h"
 #import "utilities/debugging.h"
 
+#import "keychain/ot/OTClique.h"
 #import "keychain/ot/OT.h"
 #import "keychain/ot/OTConstants.h"
 #import "keychain/ot/OTControl.h"
 #import "keychain/otctl/OTControlCLI.h"
 
 #import "keychain/ot/OT.h"
 #import "keychain/ot/OTConstants.h"
 #import "keychain/ot/OTControl.h"
 #import "keychain/otctl/OTControlCLI.h"
 
+
+#import <AuthKit/AKAppleIDAuthenticationController.h>
+#import <AuthKit/AKAppleIDAuthenticationContext.h>
+#import <AuthKit/AKAppleIDAuthenticationContext_Private.h>
+
+static NSString* fetch_pet(NSString* appleID, NSString* dsid)
+{
+    if(!appleID && !dsid) {
+        NSLog(@"Must provide either an AppleID or a DSID to fetch a PET");
+        exit(1);
+    }
+
+    AKAppleIDAuthenticationContext* authContext = [[AKAppleIDAuthenticationContext alloc] init];
+    authContext.username = appleID;
+
+    authContext.authenticationType = AKAppleIDAuthenticationTypeSilent;
+    authContext.isUsernameEditable = NO;
+
+    __block NSString* pet = nil;
+
+    dispatch_semaphore_t s = dispatch_semaphore_create(0);
+
+    AKAppleIDAuthenticationController *authenticationController = [[AKAppleIDAuthenticationController alloc] init];
+    [authenticationController authenticateWithContext:authContext
+                                           completion:^(AKAuthenticationResults authenticationResults, NSError *error) {
+                                               if(error) {
+                                                   NSLog(@"error fetching PET: %@", error);
+                                                   exit(1);
+                                               }
+
+                                               pet = authenticationResults[AKAuthenticationPasswordKey];
+                                               dispatch_semaphore_signal(s);
+    }];
+    dispatch_semaphore_wait(s, DISPATCH_TIME_FOREVER);
+
+    return pet;
+}
+
 // Mutual recursion to set up an object for jsonification
 static NSDictionary* cleanDictionaryForJSON(NSDictionary* dict);
 
 // Mutual recursion to set up an object for jsonification
 static NSDictionary* cleanDictionaryForJSON(NSDictionary* dict);
 
@@ -201,6 +240,30 @@ static void print_json(NSDictionary* dict)
 #endif
 }
 
 #endif
 }
 
+
+- (long)resetProtectedData:(NSString*)container context:(NSString*)contextID altDSID:(NSString*)altDSID appleID:(NSString*)appleID dsid:(NSString*)dsid
+{
+#if OCTAGON
+    __block long ret = -1;
+
+    NSError* error = nil;
+    OTConfigurationContext *data = [[OTConfigurationContext alloc] init];
+    data.passwordEquivalentToken = fetch_pet(appleID, dsid);
+    data.authenticationAppleID = appleID;
+    data.altDSID = altDSID;
+    data.context = contextID;
+
+    OTClique* clique = [OTClique resetProtectedData:data error:&error];
+    if(clique != nil && error == nil) {
+        ret = 0;
+    }
+    return ret;
+#else
+    printf("Unimplemented.\n");
+    return -1;
+#endif
+}
+
 - (void)printPeer:(NSDictionary*)peerInformation prefix:(NSString* _Nullable)prefix {
     NSString* peerID = peerInformation[@"peerID"];
     NSString* model = peerInformation[@"permanentInfo"][@"model_id"];
 - (void)printPeer:(NSDictionary*)peerInformation prefix:(NSString* _Nullable)prefix {
     NSString* peerID = peerInformation[@"peerID"];
     NSString* model = peerInformation[@"permanentInfo"][@"model_id"];
@@ -451,6 +514,28 @@ static void print_json(NSDictionary* dict)
 #endif
 }
 
 #endif
 }
 
+- (long)refetchCKKSPolicy:(NSString*)container context:(NSString*)contextID
+{
+    #if OCTAGON
+        __block long ret = 1;
+
+        [self.control refetchCKKSPolicy:container
+                              contextID:contextID
+                                  reply:^(NSError * _Nullable error) {
+            if(error) {
+                printf("Error refetching CKKS policy: %s\n", [[error description] UTF8String]);
+            } else {
+                printf("CKKS refetch completed.\n");
+                ret = 0;
+            }
+        }];
+        return ret;
+    #else
+        printf("Unimplemented.\n");
+        return 1;
+    #endif
+}
+
 - (long)tapToRadar:(NSString *)action description:(NSString *)description radar:(NSString *)radar
 {
 #if OCTAGON
 - (long)tapToRadar:(NSString *)action description:(NSString *)description radar:(NSString *)radar
 {
 #if OCTAGON
index 472e6b4237caa0b8dd7151886418a88edb999df7..311579d3c7c888805ca9cf810650e752a3933c20 100644 (file)
@@ -2,6 +2,14 @@
 <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
 <plist version="1.0">
 <dict>
 <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
 <plist version="1.0">
 <dict>
+       <key>com.apple.securebackupd.access</key>
+       <true/>
+       <key>keychain-cloud-circle</key>
+       <true/>
+       <key>com.apple.private.ckks</key>
+       <true/>
+       <key>com.apple.authkit.client.private</key>
+       <true/>
        <key>com.apple.private.octagon</key>
        <true/>
        <key>com.apple.private.escrow-update</key>
        <key>com.apple.private.octagon</key>
        <true/>
        <key>com.apple.private.escrow-update</key>
index d569f7eaa0cb128861fd73a2faa6d9ee3e9d3365..dd0cb2cb9bfab04957429028acd8a17bffaefff6 100644 (file)
@@ -22,6 +22,7 @@ static int start = false;
 static int signIn = false;
 static int signOut = false;
 static int resetoctagon = false;
 static int signIn = false;
 static int signOut = false;
 static int resetoctagon = false;
+static int resetProtectedData = false;
 
 static int fetchAllBottles = false;
 static int recover = false;
 
 static int fetchAllBottles = false;
 static int recover = false;
@@ -33,6 +34,7 @@ static int er_trigger = false;
 static int er_status = false;
 static int er_reset = false;
 static int er_store = false;
 static int er_status = false;
 static int er_reset = false;
 static int er_store = false;
+static int ckks_policy_flag = false;
 
 static int ttr_flag = false;
 
 
 static int ttr_flag = false;
 
@@ -51,6 +53,8 @@ static int json = false;
 static char* altDSIDArg = NULL;
 static char* containerStr = NULL;
 static char* radarNumber = NULL;
 static char* altDSIDArg = NULL;
 static char* containerStr = NULL;
 static char* radarNumber = NULL;
+static char* appleIDArg = NULL;
+static char* dsidArg = NULL;
 
 static void internalOnly(void)
 {
 
 static void internalOnly(void)
 {
@@ -71,6 +75,9 @@ int main(int argc, char** argv)
         {.longname = "altDSID", .argument = &altDSIDArg, .description = "altDSID (for sign-in/out)"},
         {.longname = "entropy", .argument = &secretArg, .description = "escrowed entropy in JSON"},
 
         {.longname = "altDSID", .argument = &altDSIDArg, .description = "altDSID (for sign-in/out)"},
         {.longname = "entropy", .argument = &secretArg, .description = "escrowed entropy in JSON"},
 
+        {.longname = "appleID", .argument = &appleIDArg, .description = "AppleID"},
+        {.longname = "dsid", .argument = &dsidArg, .description = "DSID"},
+
         {.longname = "container", .argument = &containerStr, .description = "CloudKit container name"},
         {.longname = "radar", .argument = &radarNumber, .description = "Radar number"},
 
         {.longname = "container", .argument = &containerStr, .description = "CloudKit container name"},
         {.longname = "radar", .argument = &radarNumber, .description = "Radar number"},
 
@@ -78,7 +85,10 @@ int main(int argc, char** argv)
         {.command = "sign-in", .flag = &signIn, .flagval = true, .description = "Inform Cuttlefish container of sign in"},
         {.command = "sign-out", .flag = &signOut, .flagval = true, .description = "Inform Cuttlefish container of sign out"},
         {.command = "status", .flag = &status, .flagval = true, .description = "Report Octagon status"},
         {.command = "sign-in", .flag = &signIn, .flagval = true, .description = "Inform Cuttlefish container of sign in"},
         {.command = "sign-out", .flag = &signOut, .flagval = true, .description = "Inform Cuttlefish container of sign out"},
         {.command = "status", .flag = &status, .flagval = true, .description = "Report Octagon status"},
+
         {.command = "resetoctagon", .flag = &resetoctagon, .flagval = true, .description = "Reset and establish new Octagon trust"},
         {.command = "resetoctagon", .flag = &resetoctagon, .flagval = true, .description = "Reset and establish new Octagon trust"},
+        {.command = "resetProtectedData", .flag = &resetProtectedData, .flagval = true, .description = "Reset ProtectedData"},
+
         {.command = "allBottles", .flag = &fetchAllBottles, .flagval = true, .description = "Fetch all viable bottles"},
         {.command = "recover", .flag = &recover, .flagval = true, .description = "Recover using this bottle"},
         {.command = "depart", .flag = &depart, .flagval = true, .description = "Depart from Octagon Trust"},
         {.command = "allBottles", .flag = &fetchAllBottles, .flagval = true, .description = "Fetch all viable bottles"},
         {.command = "recover", .flag = &recover, .flagval = true, .description = "Recover using this bottle"},
         {.command = "depart", .flag = &depart, .flagval = true, .description = "Depart from Octagon Trust"},
@@ -89,9 +99,11 @@ int main(int argc, char** argv)
         {.command = "er-store", .flag = &er_store, .flagval = true, .description = "Store any pending Escrow Request prerecords"},
 
         {.command = "health", .flag = &health, .flagval = true, .description = "Check Octagon Health status"},
         {.command = "er-store", .flag = &er_store, .flagval = true, .description = "Store any pending Escrow Request prerecords"},
 
         {.command = "health", .flag = &health, .flagval = true, .description = "Check Octagon Health status"},
+        {.command = "ckks-policy", .flag = &ckks_policy_flag, .flagval = true, .description = "Trigger a refetch of the CKKS policy"},
 
         {.command = "taptoradar", .flag = &ttr_flag, .flagval = true, .description = "Trigger a TapToRadar"},
 
 
         {.command = "taptoradar", .flag = &ttr_flag, .flagval = true, .description = "Trigger a TapToRadar"},
 
+
 #if TARGET_OS_WATCH
         {.command = "pairme", .flag = &pairme, .flagval = true, .description = "Perform pairing (watchOS only)"},
 #endif /* TARGET_OS_WATCH */
 #if TARGET_OS_WATCH
         {.command = "pairme", .flag = &pairme, .flagval = true, .description = "Perform pairing (watchOS only)"},
 #endif /* TARGET_OS_WATCH */
@@ -121,6 +133,9 @@ int main(int argc, char** argv)
         NSString* context = contextNameArg ? [NSString stringWithCString:contextNameArg encoding:NSUTF8StringEncoding] : OTDefaultContext;
         NSString* container = containerStr ? [NSString stringWithCString:containerStr encoding:NSUTF8StringEncoding] : nil;
         NSString* altDSID = altDSIDArg ? [NSString stringWithCString:altDSIDArg encoding:NSUTF8StringEncoding] : nil;
         NSString* context = contextNameArg ? [NSString stringWithCString:contextNameArg encoding:NSUTF8StringEncoding] : OTDefaultContext;
         NSString* container = containerStr ? [NSString stringWithCString:containerStr encoding:NSUTF8StringEncoding] : nil;
         NSString* altDSID = altDSIDArg ? [NSString stringWithCString:altDSIDArg encoding:NSUTF8StringEncoding] : nil;
+        NSString* dsid = dsidArg ? [NSString stringWithCString:dsidArg encoding:NSUTF8StringEncoding] : nil;
+        NSString* appleID = appleIDArg ? [NSString stringWithCString:appleIDArg encoding:NSUTF8StringEncoding] : nil;
+
         NSString* skipRateLimitingCheck = skipRateLimitingCheckArg ? [NSString stringWithCString:skipRateLimitingCheckArg encoding:NSUTF8StringEncoding] : @"NO";
 
         OTControlCLI* ctl = [[OTControlCLI alloc] initWithOTControl:rpc];
         NSString* skipRateLimitingCheck = skipRateLimitingCheckArg ? [NSString stringWithCString:skipRateLimitingCheckArg encoding:NSUTF8StringEncoding] : @"NO";
 
         OTControlCLI* ctl = [[OTControlCLI alloc] initWithOTControl:rpc];
@@ -134,6 +149,11 @@ int main(int argc, char** argv)
             long ret = [ctl resetOctagon:container context:context altDSID:altDSID];
             return (int)ret;
         }
             long ret = [ctl resetOctagon:container context:context altDSID:altDSID];
             return (int)ret;
         }
+        if(resetProtectedData) {
+            internalOnly();
+            long ret = [ctl resetProtectedData:container context:context altDSID:altDSID appleID:appleID dsid:dsid];
+            return (int)ret;
+        }
         if(fetchAllBottles) {
             return (int)[ctl fetchAllBottles:altDSID containerName:container context:context control:rpc];
         }
         if(fetchAllBottles) {
             return (int)[ctl fetchAllBottles:altDSID containerName:container context:context control:rpc];
         }
@@ -188,12 +208,16 @@ int main(int argc, char** argv)
             }
             return (int)[ctl healthCheck:container context:context skipRateLimitingCheck:skip];
         }
             }
             return (int)[ctl healthCheck:container context:context skipRateLimitingCheck:skip];
         }
+        if(ckks_policy_flag) {
+            return (int)[ctl refetchCKKSPolicy:container context:context];
+        }
         if (ttr_flag) {
             if (radarNumber == NULL) {
                 radarNumber = "1";
             }
             return (int)[ctl tapToRadar:@"action" description:@"description" radar:[NSString stringWithUTF8String:radarNumber]];
         }
         if (ttr_flag) {
             if (radarNumber == NULL) {
                 radarNumber = "1";
             }
             return (int)[ctl tapToRadar:@"action" description:@"description" radar:[NSString stringWithUTF8String:radarNumber]];
         }
+
         if(er_trigger) {
             internalOnly();
             return (int)[escrowctl trigger];
         if(er_trigger) {
             internalOnly();
             return (int)[escrowctl trigger];
index f2d431ee6895e272e03ea2cabd3d8e7f2b4b8657..413b75cb37cdc4af2de446b13f760cfd080bbfc7 100644 (file)
@@ -116,6 +116,41 @@ static inline SOSViewResultCode SOSAccountUpdateView_wTxn(SOSAccount* acct, CFSt
     return result;
 }
 
     return result;
 }
 
+static inline bool SOSAccountIsMyPeerInBackupAndCurrentInView_wTxn(SOSAccount *account, CFStringRef viewname) {
+    __block bool result = false;
+    [account performTransaction:^(SOSAccountTransaction * _Nonnull txn) {
+        result = SOSAccountIsMyPeerInBackupAndCurrentInView(account, viewname);
+    }];
+    return result;
+}
+
+static inline bool SOSAccountIsPeerInBackupAndCurrentInView_wTxn(SOSAccount *account, SOSPeerInfoRef peerInfo, CFStringRef viewname) {
+    __block bool result = false;
+    [account performTransaction:^(SOSAccountTransaction * _Nonnull txn) {
+        result = SOSAccountIsPeerInBackupAndCurrentInView(account, peerInfo, viewname);
+    }];
+    return result;
+}
+
+static inline bool SOSAccountRecoveryKeyIsInBackupAndCurrentInView_wTxn(SOSAccount *account, CFStringRef viewname) {
+    __block bool result = false;
+    [account performTransaction:^(SOSAccountTransaction * _Nonnull txn) {
+        result = SOSAccountRecoveryKeyIsInBackupAndCurrentInView(account, viewname);
+    }];
+    return result;
+}
+
+static inline SOSBackupSliceKeyBagRef SOSAccountBackupSliceKeyBagForView_wTxn(SOSAccount *account, CFStringRef viewname, CFErrorRef *error) {
+    __block SOSBackupSliceKeyBagRef result = NULL;
+    [account performTransaction:^(SOSAccountTransaction * _Nonnull txn) {
+        result = SOSAccountBackupSliceKeyBagForView(account, viewname, error);
+    }];
+    return result;
+}
+
+
+
+
 //
 // Account comparison
 //
 //
 // Account comparison
 //
@@ -141,7 +176,9 @@ static void SOSAccountResetToTest(SOSAccount* a, CFStringRef accountName) {
     a.key_transport = nil;
     a.kvs_message_transport = nil;
 
     a.key_transport = nil;
     a.kvs_message_transport = nil;
 
-    SOSAccountEnsureFactoryCirclesTest(a, accountName);
+    [a performTransaction:^(SOSAccountTransaction * _Nonnull txn) {
+        SOSAccountEnsureFactoryCirclesTest(a, accountName);
+    }];
 }
 
 
 }
 
 
@@ -810,7 +847,10 @@ static inline bool testAccountPersistence(SOSAccount* account) {
     SOSAccount* reinflatedAccount = NULL;
     NSError* error = nil;
 
     SOSAccount* reinflatedAccount = NULL;
     NSError* error = nil;
 
-    require(retval, errOut);
+    if(!retval) {
+        error = nil;
+        return retval;
+    }
 
     // Re-inflate to "inflated"
     reinflatedAccount = [SOSAccount accountFromData:accountDER
 
     // Re-inflate to "inflated"
     reinflatedAccount = [SOSAccount accountFromData:accountDER
@@ -822,16 +862,20 @@ static inline bool testAccountPersistence(SOSAccount* account) {
     ok(CFEqualSafe((__bridge CFTypeRef)reinflatedAccount, (__bridge CFTypeRef)account), "Compares");
 
     // Repeat through SOSAccountCopyEncodedData() interface - this is the normally called combined interface
     ok(CFEqualSafe((__bridge CFTypeRef)reinflatedAccount, (__bridge CFTypeRef)account), "Compares");
 
     // Repeat through SOSAccountCopyEncodedData() interface - this is the normally called combined interface
-    accountDER = [account encodedData:&error];
+    [account performTransaction:^(SOSAccountTransaction * _Nonnull txn) {
+        NSError* error = nil;
+        accountDER = [account encodedData:&error];
+    }];
+
     error = nil;
     error = nil;
-    reinflatedAccount = [SOSAccount accountFromData:accountDER factory:test_factory error:&error];
-    ok(reinflatedAccount, "inflated2: %@", error);
-    ok(CFEqual((__bridge CFTypeRef)account, (__bridge CFTypeRef)reinflatedAccount), "Compares");
+    SOSAccount* reinflatedAccount2 = NULL;
+
+    reinflatedAccount2 = [SOSAccount accountFromData:accountDER factory:test_factory error:&error];
+    ok(reinflatedAccount2, "inflated2: %@", error);
+    ok(CFEqual((__bridge CFTypeRef)account, (__bridge CFTypeRef)reinflatedAccount2), "Compares");
 
     retval = true;
 
     retval = true;
-errOut:
     error = nil;
     error = nil;
-
     return retval;
 }
 
     return retval;
 }
 
index f62cc0224b6338e7e5be432070e3f16b349d7d31..41934fda203dafd88a0fbc7c1e70688cf98b01ec 100644 (file)
@@ -165,9 +165,6 @@ static void tests(void)
     ok(SOSAccountIsMyPeerInBackupAndCurrentInView(alice_account, kTestView1), "Is alice in backup after sync?");
     
     ok(SOSAccountIsMyPeerInBackupAndCurrentInView(bob_account, kTestView1), "IS bob in the backup after sync");
     ok(SOSAccountIsMyPeerInBackupAndCurrentInView(alice_account, kTestView1), "Is alice in backup after sync?");
     
     ok(SOSAccountIsMyPeerInBackupAndCurrentInView(bob_account, kTestView1), "IS bob in the backup after sync");
-    
-    ok(!SOSAccountIsLastBackupPeer(alice_account, &error), "Alice is not last backup peer");
-    CFReleaseNull(error);
 
     //
     //Bob leaves the circle
 
     //
     //Bob leaves the circle
@@ -179,11 +176,6 @@ static void tests(void)
     is(ProcessChangesUntilNoChange(changes, alice_account, bob_account, NULL), 2, "updates");
     
     ok(SOSAccountIsMyPeerInBackupAndCurrentInView(alice_account, kTestView1), "Bob left the circle, Alice is not in the backup");
     is(ProcessChangesUntilNoChange(changes, alice_account, bob_account, NULL), 2, "updates");
     
     ok(SOSAccountIsMyPeerInBackupAndCurrentInView(alice_account, kTestView1), "Bob left the circle, Alice is not in the backup");
-    
-    ok(SOSAccountIsLastBackupPeer(alice_account, &error), "Alice is last backup peer");
-    CFReleaseNull(error);
-    ok(!SOSAccountIsLastBackupPeer(bob_account, &error), "Bob is not last backup peer");
-    CFReleaseNull(error);
 
     ok(testAccountPersistence(alice_account), "Test Account->DER->Account Equivalence");
 
 
     ok(testAccountPersistence(alice_account), "Test Account->DER->Account Equivalence");
 
@@ -211,16 +203,10 @@ static void tests(void)
 
     ok(!SOSAccountIsMyPeerInBackupAndCurrentInView(bob_account, kTestView1), "Bob isn't in the backup yet");
 
 
     ok(!SOSAccountIsMyPeerInBackupAndCurrentInView(bob_account, kTestView1), "Bob isn't in the backup yet");
 
-    ok(!SOSAccountIsLastBackupPeer(alice_account, &error), "Alice is the not the last backup peer - Bob still registers as one");
-    CFReleaseNull(error);
-
     ok(SOSAccountSetBackupPublicKey_wTxn(bob_account, bob_backup_key, &error), "Set backup public key, bob (%@)", error);
 
     is(ProcessChangesUntilNoChange(changes, alice_account, bob_account, NULL), 3, "updates");
 
     ok(SOSAccountSetBackupPublicKey_wTxn(bob_account, bob_backup_key, &error), "Set backup public key, bob (%@)", error);
 
     is(ProcessChangesUntilNoChange(changes, alice_account, bob_account, NULL), 3, "updates");
 
-    ok(!SOSAccountIsLastBackupPeer(alice_account, &error), "Alice is not last backup peer");
-    CFReleaseNull(error);
-
     //
     //removing backup key for bob account
     //
     //
     //removing backup key for bob account
     //
index a406f2ab77d8d208a3fd971991f63783d7038e66..5db81a50677b06b01d9eb68416acd13904073dbd 100644 (file)
@@ -122,10 +122,12 @@ static void tests(void)
 
         return 2;
     }, ^{
 
         return 2;
     }, ^{
-        NSError *ns_error = nil;
-        frozen_alice = (CFDataRef) CFBridgingRetain([alice_account encodedData:&ns_error]);
-        ok(frozen_alice, "Copy encoded %@", ns_error);
-        ns_error = nil;
+        [alice_account performTransaction:^(SOSAccountTransaction * _Nonnull txn) {
+            NSError *ns_error = nil;
+            frozen_alice = (CFDataRef) CFBridgingRetain([alice_account encodedData:&ns_error]);
+            ok(frozen_alice, "Copy encoded %@", ns_error);
+            ns_error = nil;
+        }];
 
         SOSAccountPurgePrivateCredential(alice_account);
 
 
         SOSAccountPurgePrivateCredential(alice_account);
 
index 2a2fa0baf6aa61781972491e73431bf309f1b35c..57ffd281c6c24ff43c4844365ffa7149f4f5cd8a 100644 (file)
@@ -51,7 +51,7 @@
 #include <unistd.h>
 
 #include "secd_regressions.h"
 #include <unistd.h>
 
 #include "secd_regressions.h"
-#include "SOSTestDataSource.h"
+#include "keychain/SecureObjectSync/Regressions/SOSTestDataSource.h"
 
 #include "SOSRegressionUtilities.h"
 #include <Security/SecRecoveryKey.h>
 
 #include "SOSRegressionUtilities.h"
 #include <Security/SecRecoveryKey.h>
@@ -209,10 +209,10 @@ static void tests(bool recKeyFirst)
     ok([bob_account.trust checkForRings:&error], "Bob_account is good");
     CFReleaseNull(error);
     
     ok([bob_account.trust checkForRings:&error], "Bob_account is good");
     CFReleaseNull(error);
     
-    ok(SOSAccountIsMyPeerInBackupAndCurrentInView(alice_account, kTestView1), "Is alice is in backup before sync?");
+    ok(SOSAccountIsMyPeerInBackupAndCurrentInView_wTxn(alice_account, kTestView1), "Is alice is in backup before sync?");
     
     if(!recKeyFirst) {
     
     if(!recKeyFirst) {
-        SOSBackupSliceKeyBagRef bskb = SOSAccountBackupSliceKeyBagForView(alice_account, kTestView1, &error);
+        SOSBackupSliceKeyBagRef bskb = SOSAccountBackupSliceKeyBagForView_wTxn(alice_account, kTestView1, &error);
         CFReleaseNull(error);
         ok(!SOSBSKBHasRecoveryKey(bskb), "BSKB should not have recovery key");
         CFReleaseNull(bskb);
         CFReleaseNull(error);
         ok(!SOSBSKBHasRecoveryKey(bskb), "BSKB should not have recovery key");
         CFReleaseNull(bskb);
@@ -221,7 +221,7 @@ static void tests(bool recKeyFirst)
     ok([alice_account.trust checkForRings:&error], "Alice_account is good");
     CFReleaseNull(error);
     
     ok([alice_account.trust checkForRings:&error], "Alice_account is good");
     CFReleaseNull(error);
     
-    ok(SOSAccountIsMyPeerInBackupAndCurrentInView(bob_account, kTestView1), "Is bob in the backup after sync? - 1");
+    ok(SOSAccountIsMyPeerInBackupAndCurrentInView_wTxn(bob_account, kTestView1), "Is bob in the backup after sync? - 1");
     
     ok([bob_account.trust checkForRings:&error], "Alice_account is good");
     CFReleaseNull(error);
     
     ok([bob_account.trust checkForRings:&error], "Alice_account is good");
     CFReleaseNull(error);
@@ -232,12 +232,9 @@ static void tests(bool recKeyFirst)
     ok([alice_account.trust checkForRings:&error], "Alice_account is good");
     CFReleaseNull(error);
     
     ok([alice_account.trust checkForRings:&error], "Alice_account is good");
     CFReleaseNull(error);
     
-    ok(SOSAccountIsMyPeerInBackupAndCurrentInView(alice_account, kTestView1), "Is alice is in backup after sync?");
+    ok(SOSAccountIsMyPeerInBackupAndCurrentInView_wTxn(alice_account, kTestView1), "Is alice is in backup after sync?");
     
     
-    ok(SOSAccountIsMyPeerInBackupAndCurrentInView(bob_account, kTestView1), "IS bob in the backup after sync");
-    
-    ok(!SOSAccountIsLastBackupPeer(alice_account, &error), "Alice is not last backup peer");
-    CFReleaseNull(error);
+    ok(SOSAccountIsMyPeerInBackupAndCurrentInView_wTxn(bob_account, kTestView1), "IS bob in the backup after sync");
     
     //
     //Bob leaves the circle
     
     //
     //Bob leaves the circle
@@ -248,16 +245,11 @@ static void tests(bool recKeyFirst)
     //Alice should kick Bob out of the backup!
     is(ProcessChangesUntilNoChange(changes, alice_account, bob_account, NULL), 2, "updates");
     
     //Alice should kick Bob out of the backup!
     is(ProcessChangesUntilNoChange(changes, alice_account, bob_account, NULL), 2, "updates");
     
-    ok(SOSAccountIsMyPeerInBackupAndCurrentInView(alice_account, kTestView1), "Bob left the circle, Alice is in the backup");
-    
-    ok(SOSAccountIsLastBackupPeer(alice_account, &error), "Alice is last backup peer");
-    CFReleaseNull(error);
-    ok(!SOSAccountIsLastBackupPeer(bob_account, &error), "Bob is not last backup peer");
-    CFReleaseNull(error);
-    
+    ok(SOSAccountIsMyPeerInBackupAndCurrentInView_wTxn(alice_account, kTestView1), "Bob left the circle, Alice is in the backup");
+
     //ok(testAccountPersistence(alice_account), "Test Account->DER->Account Equivalence");
     SOSAccountTrustClassic* bobTrust = bob_account.trust;
     //ok(testAccountPersistence(alice_account), "Test Account->DER->Account Equivalence");
     SOSAccountTrustClassic* bobTrust = bob_account.trust;
-    ok(!SOSAccountIsPeerInBackupAndCurrentInView(alice_account, bobTrust.peerInfo, kTestView1), "Bob is still in the backup!");
+    ok(!SOSAccountIsPeerInBackupAndCurrentInView_wTxn(alice_account, bobTrust.peerInfo, kTestView1), "Bob is still in the backup!");
     
     //Bob gets back into the circle
     ok(SOSTestJoinWithApproval(cfpassword, cfaccount, changes, alice_account, bob_account, KEEP_USERKEY, 2, false), "Bob Re-Joins");
     
     //Bob gets back into the circle
     ok(SOSTestJoinWithApproval(cfpassword, cfaccount, changes, alice_account, bob_account, KEEP_USERKEY, 2, false), "Bob Re-Joins");
@@ -266,18 +258,12 @@ static void tests(bool recKeyFirst)
     is([bob_account.trust updateView:bob_account name:kTestView1 code:kSOSCCViewEnable err:&error], kSOSCCViewMember, "Enable view (%@)", error);
     CFReleaseNull(error);
     
     is([bob_account.trust updateView:bob_account name:kTestView1 code:kSOSCCViewEnable err:&error], kSOSCCViewMember, "Enable view (%@)", error);
     CFReleaseNull(error);
     
-    ok(!SOSAccountIsMyPeerInBackupAndCurrentInView(bob_account, kTestView1), "Bob isn't in the backup yet");
-    
-    ok(!SOSAccountIsLastBackupPeer(alice_account, &error), "Alice is the not the last backup peer - Bob still registers as one");
-    CFReleaseNull(error);
-    
+    ok(!SOSAccountIsMyPeerInBackupAndCurrentInView_wTxn(bob_account, kTestView1), "Bob isn't in the backup yet");
+
     ok(SOSAccountSetBackupPublicKey_wTxn(bob_account, bob_backup_key, &error), "Set backup public key, alice (%@)", error);
     
     is(ProcessChangesUntilNoChange(changes, alice_account, bob_account, NULL), 3, "updates");
     
     ok(SOSAccountSetBackupPublicKey_wTxn(bob_account, bob_backup_key, &error), "Set backup public key, alice (%@)", error);
     
     is(ProcessChangesUntilNoChange(changes, alice_account, bob_account, NULL), 3, "updates");
     
-    ok(!SOSAccountIsLastBackupPeer(alice_account, &error), "Alice is not last backup peer");
-    CFReleaseNull(error);
-    
     //
     //removing backup key for bob account
     //
     //
     //removing backup key for bob account
     //
@@ -286,8 +272,8 @@ static void tests(bool recKeyFirst)
     int nchanges = (recKeyFirst) ? 2: 2;
     is(ProcessChangesUntilNoChange(changes, alice_account, bob_account, NULL), nchanges, "updates");
 
     int nchanges = (recKeyFirst) ? 2: 2;
     is(ProcessChangesUntilNoChange(changes, alice_account, bob_account, NULL), nchanges, "updates");
 
-    ok(!SOSAccountIsMyPeerInBackupAndCurrentInView(bob_account, kTestView1), "Bob's backup key is in the backup - should not be so!");
-    ok(!SOSAccountIsPeerInBackupAndCurrentInView(alice_account, bobTrust.peerInfo, kTestView1), "Bob is up to date in the backup!");
+    ok(!SOSAccountIsMyPeerInBackupAndCurrentInView_wTxn(bob_account, kTestView1), "Bob's backup key is in the backup - should not be so!");
+    ok(!SOSAccountIsPeerInBackupAndCurrentInView_wTxn(alice_account, bobTrust.peerInfo, kTestView1), "Bob is up to date in the backup!");
     
     //
     // Setting new backup public key for Bob
     
     //
     // Setting new backup public key for Bob
@@ -300,21 +286,21 @@ static void tests(bool recKeyFirst)
     ok(SOSAccountNewBKSBForView(bob_account, kTestView1, &error), "Setting new backup public key for bob account failed: (%@)", error);
     
     //bob is in his own backup
     ok(SOSAccountNewBKSBForView(bob_account, kTestView1, &error), "Setting new backup public key for bob account failed: (%@)", error);
     
     //bob is in his own backup
-    ok(SOSAccountIsMyPeerInBackupAndCurrentInView(bob_account, kTestView1), "Bob's backup key is not in the backup");
+    ok(SOSAccountIsMyPeerInBackupAndCurrentInView_wTxn(bob_account, kTestView1), "Bob's backup key is not in the backup");
     //alice does not have bob in her backup
     //alice does not have bob in her backup
-    ok(!SOSAccountIsPeerInBackupAndCurrentInView(alice_account, bobTrust.peerInfo, kTestView1), "Bob is up to date in the backup - should not be so!");
+    ok(!SOSAccountIsPeerInBackupAndCurrentInView_wTxn(alice_account, bobTrust.peerInfo, kTestView1), "Bob is up to date in the backup - should not be so!");
     
     is(ProcessChangesUntilNoChange(changes, alice_account, bob_account, NULL), 5, "updates");
     
     
     is(ProcessChangesUntilNoChange(changes, alice_account, bob_account, NULL), 5, "updates");
     
-    ok(SOSAccountIsMyPeerInBackupAndCurrentInView(bob_account, kTestView1), "Bob's backup key should be in the backup");
-    ok(SOSAccountIsMyPeerInBackupAndCurrentInView(alice_account, kTestView1), "Alice is in the backup");
+    ok(SOSAccountIsMyPeerInBackupAndCurrentInView_wTxn(bob_account, kTestView1), "Bob's backup key should be in the backup");
+    ok(SOSAccountIsMyPeerInBackupAndCurrentInView_wTxn(alice_account, kTestView1), "Alice is in the backup");
     
     if(!recKeyFirst) registerRecoveryKeyNow(changes, alice_account, bob_account, pubKeyBytes, recKeyFirst);
     
     
     if(!recKeyFirst) registerRecoveryKeyNow(changes, alice_account, bob_account, pubKeyBytes, recKeyFirst);
     
-    ok(SOSAccountRecoveryKeyIsInBackupAndCurrentInView(alice_account, kTestView1), "Recovery Key is also in the backup");
-    ok(SOSAccountRecoveryKeyIsInBackupAndCurrentInView(bob_account, kTestView1), "Recovery Key is also in the backup");
+    ok(SOSAccountRecoveryKeyIsInBackupAndCurrentInView_wTxn(alice_account, kTestView1), "Recovery Key is also in the backup");
+    ok(SOSAccountRecoveryKeyIsInBackupAndCurrentInView_wTxn(bob_account, kTestView1), "Recovery Key is also in the backup");
     
     
-    SOSBackupSliceKeyBagRef bskb = SOSAccountBackupSliceKeyBagForView(alice_account, kTestView1, &error);
+    SOSBackupSliceKeyBagRef bskb = SOSAccountBackupSliceKeyBagForView_wTxn(alice_account, kTestView1, &error);
     CFReleaseNull(error);
     
     ok(SOSBSKBHasRecoveryKey(bskb), "BSKB should have recovery key");
     CFReleaseNull(error);
     
     ok(SOSBSKBHasRecoveryKey(bskb), "BSKB should have recovery key");
@@ -332,10 +318,10 @@ static void tests(bool recKeyFirst)
 
     registerRecoveryKeyNow(changes, alice_account, bob_account, NULL, recKeyFirst);
     
 
     registerRecoveryKeyNow(changes, alice_account, bob_account, NULL, recKeyFirst);
     
-    ok(!SOSAccountRecoveryKeyIsInBackupAndCurrentInView(alice_account, kTestView1), "Recovery Key is not in the backup");
-    ok(!SOSAccountRecoveryKeyIsInBackupAndCurrentInView(bob_account, kTestView1), "Recovery Key is not in the backup");
+    ok(!SOSAccountRecoveryKeyIsInBackupAndCurrentInView_wTxn(alice_account, kTestView1), "Recovery Key is not in the backup");
+    ok(!SOSAccountRecoveryKeyIsInBackupAndCurrentInView_wTxn(bob_account, kTestView1), "Recovery Key is not in the backup");
 
 
-    bskb = SOSAccountBackupSliceKeyBagForView(alice_account, kTestView1, &error);
+    bskb = SOSAccountBackupSliceKeyBagForView_wTxn(alice_account, kTestView1, &error);
     CFReleaseNull(error);
     
     ok(!SOSBSKBHasRecoveryKey(bskb), "BSKB should not have recovery key");
     CFReleaseNull(error);
     
     ok(!SOSBSKBHasRecoveryKey(bskb), "BSKB should not have recovery key");
diff --git a/keychain/securityd/Regressions/secd-76-idstransport.m b/keychain/securityd/Regressions/secd-76-idstransport.m
deleted file mode 100644 (file)
index 164c56f..0000000
+++ /dev/null
@@ -1,315 +0,0 @@
-//
-//  secd-76-idstransport.c
-//  sec
-//
-//
-
-/*
- * Copyright (c) 2012-2014 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@
- */
-
-
-#include <stdio.h>
-#include <Security/SecBase.h>
-#include <Security/SecItem.h>
-
-#include <Security/SecureObjectSync/SOSAccount.h>
-#include <Security/SecureObjectSync/SOSCloudCircle.h>
-#include <Security/SecureObjectSync/SOSInternal.h>
-#include <Security/SecureObjectSync/SOSFullPeerInfo.h>
-#include <Security/SecureObjectSync/SOSUserKeygen.h>
-#include <Security/SecureObjectSync/SOSAccountTrustClassic+Circle.h>
-#include <stdlib.h>
-#include <unistd.h>
-
-#include "secd_regressions.h"
-#include "SOSTestDataSource.h"
-
-#include "SOSRegressionUtilities.h"
-#include <utilities/SecCFWrappers.h>
-
-#include <securityd/SOSCloudCircleServer.h>
-#include "SecdTestKeychainUtilities.h"
-#import "SOSAccountTesting.h"
-#import "SOSTransportTestTransports.h"
-#include <Security/SecureObjectSync/SOSTransportMessageIDS.h>
-#include <SOSCircle/CKBridge/SOSCloudKeychainConstants.h>
-#include "SOSTestDevice.h"
-
-
-
-static int kTestTestCount = 73;
-
-static void tests()
-{
-    CFErrorRef error = NULL;
-        
-    CFMutableDictionaryRef changes = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
-    CFDataRef cfpassword = CFDataCreate(NULL, (uint8_t *) "FooFooFoo", 10);
-    CFStringRef cfaccount = CFSTR("test@test.org");
-    
-    SOSAccount* alice_account = CreateAccountForLocalChanges(CFSTR("Alice"), CFSTR("ak"));
-    SOSAccount* bob_account = CreateAccountForLocalChanges(CFSTR("Bob"), CFSTR("ak"));
-    SOSAccountTrustClassic *aliceTrust = alice_account.trust;
-    SOSAccountTrustClassic *bobTrust = bob_account.trust;
-
-    ok(SOSAccountAssertUserCredentialsAndUpdate(bob_account, cfaccount, cfpassword, &error), "Credential setting (%@)", error);
-    
-    // Bob wins writing at this point, feed the changes back to alice.
-    is(ProcessChangesUntilNoChange(changes, alice_account, bob_account, NULL), 1, "updates");
-    
-    ok(SOSAccountAssertUserCredentialsAndUpdate(alice_account, cfaccount, cfpassword, &error), "Credential setting (%@)", error);
-    CFReleaseNull(cfpassword);
-    CFReleaseNull(error);
-    
-    ok(NULL != alice_account, "Alice Created");
-    ok(NULL != bob_account, "Bob Created");
-    
-    ok(SOSAccountResetToOffering_wTxn(alice_account, &error), "Reset to offering (%@)", error);
-    CFReleaseNull(error);
-    
-    is(ProcessChangesUntilNoChange(changes, alice_account, bob_account, NULL), 2, "updates");
-    
-    ok(SOSAccountJoinCircles_wTxn(bob_account, &error), "Bob Applies (%@)", error);
-    CFReleaseNull(error);
-    
-    is(ProcessChangesUntilNoChange(changes, alice_account, bob_account, NULL), 2, "updates");
-    
-    {
-        CFArrayRef applicants = SOSAccountCopyApplicants(alice_account, &error);
-        
-        ok(applicants && CFArrayGetCount(applicants) == 1, "See one applicant %@ (%@)", applicants, error);
-        ok(SOSAccountAcceptApplicants(alice_account, applicants, &error), "Alice accepts (%@)", error);
-        CFReleaseNull(error);
-        CFReleaseNull(applicants);
-    }
-    
-    is(ProcessChangesUntilNoChange(changes, alice_account, bob_account, NULL), 3, "updates");
-    
-    accounts_agree("bob&alice pair", bob_account, alice_account);
-    
-    CFArrayRef peers = SOSAccountCopyPeers(alice_account, &error);
-    ok(peers && CFArrayGetCount(peers) == 2, "See two peers %@ (%@)", peers, error);
-    CFReleaseNull(peers);
-    
-    //creating test devices
-    CFIndex version = 0;
-    
-    // Optionally prefix each peer with name to make them more unique.
-    CFArrayRef deviceIDs = CFArrayCreateForCFTypes(kCFAllocatorDefault,alice_account.peerID, bob_account.peerID, NULL);
-    CFSetRef views = SOSViewsCopyTestV2Default();
-    CFMutableArrayRef peerMetas = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
-    CFStringRef deviceID;
-    CFArrayForEachC(deviceIDs, deviceID) {
-        SOSPeerMetaRef peerMeta = SOSPeerMetaCreateWithComponents(deviceID, views, NULL);
-        CFArrayAppendValue(peerMetas, peerMeta);
-        CFReleaseNull(peerMeta);
-    }
-    
-    CFReleaseNull(views);
-    CFArrayForEachC(deviceIDs, deviceID) {
-        SOSTestDeviceRef device = SOSTestDeviceCreateWithDbNamed(kCFAllocatorDefault, deviceID, deviceID);
-        SOSTestDeviceSetPeerIDs(device, peerMetas, version, NULL);
-        
-        if(CFEqualSafe(deviceID, (__bridge CFTypeRef)(alice_account.peerID))){
-            alice_account.factory = device->dsf;
-            SOSTestDeviceAddGenericItem(device, CFSTR("Alice"), CFSTR("Alice-add"));
-        }
-        else{
-            bob_account.factory = device->dsf;
-            SOSTestDeviceAddGenericItem(device, CFSTR("Bob"), CFSTR("Bob-add"));
-        }
-        CFReleaseNull(device);
-    }
-    CFReleaseNull(deviceIDs);
-    CFReleaseNull(peerMetas);
-    
-    SOSUnregisterAllTransportMessages();
-    CFArrayRemoveAllValues(message_transports);
-
-    alice_account.ids_message_transport =  (SOSMessageIDS*)[[SOSMessageIDSTest alloc] initWithAccount:alice_account andAccountName:CFSTR("Alice") andCircleName:SOSCircleGetName(aliceTrust.trustedCircle) err:&error];
-
-
-    bob_account.ids_message_transport = (SOSMessageIDS*)[[SOSMessageIDSTest alloc] initWithAccount:bob_account andAccountName:CFSTR("Bob") andCircleName:SOSCircleGetName(bobTrust.trustedCircle) err:&error];
-    ok(alice_account.ids_message_transport != NULL, "Alice Account, Created IDS Test Transport");
-    ok(bob_account.ids_message_transport != NULL, "Bob Account, Created IDS Test Transport");
-    
-    bool result = [alice_account.trust modifyCircle:alice_account.circle_transport err:&error action:^(SOSCircleRef circle) {
-        CFErrorRef localError = NULL;
-        
-        SOSFullPeerInfoUpdateTransportType(aliceTrust.fullPeerInfo, SOSTransportMessageTypeIDSV2, &localError);
-        SOSFullPeerInfoUpdateTransportPreference(aliceTrust.fullPeerInfo, kCFBooleanFalse, &localError);
-        SOSFullPeerInfoUpdateTransportFragmentationPreference(aliceTrust.fullPeerInfo, kCFBooleanTrue, &localError);
-        SOSFullPeerInfoUpdateTransportAckModelPreference(aliceTrust.fullPeerInfo, kCFBooleanTrue, &localError);
-
-        return SOSCircleHasPeer(circle, aliceTrust.peerInfo, NULL);
-    }];
-    
-    ok(result, "Alice account update circle with transport type");
-    
-    is(ProcessChangesUntilNoChange(changes, alice_account, bob_account, NULL), 2, "updates");
-    
-    result &= [bob_account.trust modifyCircle:bob_account.circle_transport err:&error action:^(SOSCircleRef circle) {
-        CFErrorRef localError = NULL;
-        
-        SOSFullPeerInfoUpdateTransportType(bobTrust.fullPeerInfo, SOSTransportMessageTypeIDSV2, &localError);
-        SOSFullPeerInfoUpdateTransportPreference(bobTrust.fullPeerInfo, kCFBooleanFalse, &localError);
-        SOSFullPeerInfoUpdateTransportFragmentationPreference(bobTrust.fullPeerInfo, kCFBooleanTrue, &localError);
-        SOSFullPeerInfoUpdateTransportAckModelPreference(bobTrust.fullPeerInfo, kCFBooleanTrue, &localError);
-
-        return SOSCircleHasPeer(circle, bobTrust.peerInfo, NULL);
-    }];
-    
-    ok(result, "Bob account update circle with transport type");
-    is(ProcessChangesUntilNoChange(changes, alice_account, bob_account, NULL), 2, "updates");
-    
-    CFStringRef alice_transportType =SOSPeerInfoCopyTransportType(alice_account.peerInfo);
-    CFStringRef bob_accountTransportType = SOSPeerInfoCopyTransportType(bob_account.peerInfo);
-    ok(CFEqualSafe(alice_transportType, CFSTR("IDS2.0")), "Alice transport type not IDS");
-    ok(CFEqualSafe(bob_accountTransportType, CFSTR("IDS2.0")), "Bob transport type not IDS");
-    
-    CFReleaseNull(alice_transportType);
-    CFReleaseNull(bob_accountTransportType);
-    
-    SOSTransportMessageIDSTestSetName((SOSMessageIDSTest*)alice_account.ids_message_transport, CFSTR("Alice Account"));
-    ok(SOSTransportMessageIDSTestGetName((SOSMessageIDSTest*)alice_account.ids_message_transport) != NULL, "retrieved getting account name");
-    ok(SOSAccountRetrieveDeviceIDFromKeychainSyncingOverIDSProxy(alice_account, &error) != false, "device ID from KeychainSyncingOverIDSProxy");
-
-    SOSTransportMessageIDSTestSetName((SOSMessageIDSTest*)bob_account.ids_message_transport, CFSTR("Bob Account"));
-    ok(SOSTransportMessageIDSTestGetName((SOSMessageIDSTest*)bob_account.ids_message_transport) != NULL, "retrieved getting account name");
-    ok(SOSAccountRetrieveDeviceIDFromKeychainSyncingOverIDSProxy(bob_account, &error) != false, "device ID from KeychainSyncingOverIDSProxy");
-    
-    ok(SOSAccountSetMyDSID_wTxn(alice_account, CFSTR("Alice"),&error), "Setting IDS device ID");
-    CFStringRef alice_dsid = SOSAccountCopyDeviceID(alice_account, &error);
-    ok(CFEqualSafe(alice_dsid, CFSTR("Alice")), "Getting IDS device ID");
-    
-    ok(SOSAccountSetMyDSID_wTxn(bob_account, CFSTR("Bob"),&error), "Setting IDS device ID");
-    CFStringRef bob_dsid = SOSAccountCopyDeviceID(bob_account, &error);
-    ok(CFEqualSafe(bob_dsid, CFSTR("Bob")), "Getting IDS device ID");
-    
-    is(ProcessChangesUntilNoChange(changes, alice_account, bob_account, NULL), 3, "updates");
-    
-    SOSTransportMessageIDSTestSetName((SOSMessageIDSTest*)alice_account.ids_message_transport, CFSTR("Alice Account"));
-    ok(SOSTransportMessageIDSTestGetName((SOSMessageIDSTest*)alice_account.ids_message_transport) != NULL, "retrieved getting account name");
-    ok(SOSAccountRetrieveDeviceIDFromKeychainSyncingOverIDSProxy(alice_account, &error) != false, "device ID from KeychainSyncingOverIDSProxy");
-    
-    ok(SOSAccountSetMyDSID_wTxn(alice_account, CFSTR("DSID"),&error), "Setting IDS device ID");
-    CFStringRef dsid = SOSAccountCopyDeviceID(alice_account, &error);
-    ok(CFEqualSafe(dsid, CFSTR("DSID")), "Getting IDS device ID");
-    CFReleaseNull(dsid);
-    
-    ok(SOSAccountStartPingTest(alice_account, CFSTR("hai there!"), &error), "Ping test");
-    ok(CFDictionaryGetCount(SOSTransportMessageIDSTestGetChanges((SOSMessageIDSTest*)alice_account.ids_message_transport)) != 0, "ping message made it to transport");
-    SOSTransportMessageIDSTestClearChanges((SOSMessageIDSTest*)alice_account.ids_message_transport);
-    
-    ok(SOSAccountSendIDSTestMessage(alice_account, CFSTR("hai again!"), &error), "Send Test Message");
-    ok(CFDictionaryGetCount(SOSTransportMessageIDSTestGetChanges((SOSMessageIDSTest*)alice_account.ids_message_transport)) != 0, "ping message made it to transport");
-
-    CFStringRef dataKey = CFStringCreateWithCString(kCFAllocatorDefault, kMessageKeyIDSDataMessage, kCFStringEncodingASCII);
-    CFStringRef deviceIDKey = CFStringCreateWithCString(kCFAllocatorDefault, kMessageKeyDeviceID, kCFStringEncodingASCII);
-    CFStringRef sendersPeerIDKey = CFStringCreateWithCString(kCFAllocatorDefault, kMessageKeySendersPeerID, kCFStringEncodingASCII);
-
-    //test IDS message handling
-    CFMutableDictionaryRef messageDict = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
-
-    ok([alice_account.ids_message_transport  SOSTransportMessageIDSHandleMessage:alice_account m:messageDict err:&error]
-       == kHandleIDSMessageDontHandle, "sending empty message dictionary");
-    
-    CFDictionaryAddValue(messageDict, deviceIDKey, CFSTR("Alice Account"));
-    ok([alice_account.ids_message_transport  SOSTransportMessageIDSHandleMessage:alice_account m:messageDict err:&error]  == kHandleIDSMessageDontHandle, "sending device ID only");
-
-    CFReleaseNull(messageDict);
-    messageDict = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
-    CFDictionaryAddValue(messageDict, sendersPeerIDKey, CFSTR("Alice Account"));
-    ok([alice_account.ids_message_transport  SOSTransportMessageIDSHandleMessage:alice_account m:messageDict err:&error]   == kHandleIDSMessageDontHandle, "sending peer ID only");
-
-    CFReleaseNull(messageDict);
-    messageDict = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
-    CFDataRef data = CFDataCreate(kCFAllocatorDefault, 0, 0);
-    CFDictionaryAddValue(messageDict, dataKey, data);
-    ok( [alice_account.ids_message_transport  SOSTransportMessageIDSHandleMessage:alice_account m:messageDict err:&error] == kHandleIDSMessageDontHandle, "sending data only");
-    
-    CFReleaseNull(messageDict);
-    CFReleaseNull(data);
-    messageDict = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
-    data = CFDataCreate(kCFAllocatorDefault, 0, 0);
-    CFDictionaryAddValue(messageDict, dataKey, data);
-    CFDictionaryAddValue(messageDict, sendersPeerIDKey, CFSTR("Alice Account"));
-    ok([(SOSMessageIDS*)alice_account.ids_message_transport  SOSTransportMessageIDSHandleMessage:alice_account m:messageDict err:&error]== kHandleIDSMessageDontHandle, "sending data and peerid only");
-    
-    CFReleaseNull(messageDict);
-    CFReleaseNull(data);
-    messageDict = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
-    data = CFDataCreate(kCFAllocatorDefault, 0, 0);
-    CFDictionaryAddValue(messageDict, dataKey, data);
-    CFDictionaryAddValue(messageDict, deviceIDKey, CFSTR("Alice Account"));
-    ok([(SOSMessageIDS*)alice_account.ids_message_transport  SOSTransportMessageIDSHandleMessage:alice_account m:messageDict err:&error] == kHandleIDSMessageDontHandle, "sending data and deviceid only");
-    
-    CFReleaseNull(messageDict);
-    CFReleaseNull(data);
-    messageDict = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
-    CFDictionaryAddValue(messageDict, deviceIDKey, CFSTR("Alice Account"));
-    CFDictionaryAddValue(messageDict, sendersPeerIDKey, CFSTR("Alice Account"));
-    ok([(SOSMessageIDS*)alice_account.ids_message_transport  SOSTransportMessageIDSHandleMessage:alice_account m:messageDict err:&error] == kHandleIDSMessageDontHandle, "sending peerid and deviceid only");
-    
-    CFReleaseNull(messageDict);
-    CFReleaseNull(data);
-    messageDict = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
-    data = CFDataCreate(kCFAllocatorDefault, 0, 0);
-    CFDictionaryAddValue(messageDict, dataKey, data);
-    CFDictionaryAddValue(messageDict, deviceIDKey, CFSTR("Alice Account"));
-    CFDictionaryAddValue(messageDict, sendersPeerIDKey, SOSPeerInfoGetPeerID(bob_account.peerInfo));
-    ok([(SOSMessageIDS*)alice_account.ids_message_transport  SOSTransportMessageIDSHandleMessage:alice_account m:messageDict err:&error]== kHandleIDSMessageDontHandle, "sending peerid and deviceid and data");
-    
-    CFReleaseNull(messageDict);
-    CFReleaseNull(data);
-
-    messageDict = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
-    data = CFDataCreate(kCFAllocatorDefault, 0, 0);
-    CFDictionaryAddValue(messageDict, dataKey, data);
-    CFStringRef BobDeviceID = SOSPeerInfoCopyDeviceID(bob_account.peerInfo);
-    CFDictionaryAddValue(messageDict, deviceIDKey, BobDeviceID);
-    CFReleaseNull(BobDeviceID);
-    CFDictionaryAddValue(messageDict, sendersPeerIDKey, CFSTR("Alice Account"));
-    ok([(SOSMessageIDS*)alice_account.ids_message_transport  SOSTransportMessageIDSHandleMessage:alice_account m:messageDict err:&error]== kHandleIDSMessageDontHandle, "sending peerid and deviceid and data");
-    
-    CFReleaseNull(data);
-    CFReleaseNull(dataKey);
-    CFReleaseNull(deviceIDKey);
-    CFReleaseNull(sendersPeerIDKey);
-
-    CFReleaseNull(alice_dsid);
-    CFReleaseNull(bob_dsid);
-    CFReleaseNull(changes);
-
-    SOSTestCleanup();
-}
-int secd_76_idstransport(int argc, char *const *argv)
-{
-    plan_tests(kTestTestCount);
-    
-    secd_test_setup_temp_keychain(__FUNCTION__, NULL);
-    
-    tests();
-    
-    return 0;
-}
diff --git a/keychain/securityd/Regressions/secd-95-escrow-persistence.m b/keychain/securityd/Regressions/secd-95-escrow-persistence.m
deleted file mode 100644 (file)
index 80c23ae..0000000
+++ /dev/null
@@ -1,178 +0,0 @@
-/*
- * Copyright (c) 2014 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@
- */
-
-
-#include <Security/SecBase.h>
-#include <Security/SecItem.h>
-
-#include <CoreFoundation/CFDictionary.h>
-
-#include "keychain/SecureObjectSync/SOSAccount.h"
-#include <Security/SecureObjectSync/SOSCloudCircle.h>
-#include "keychain/SecureObjectSync/SOSInternal.h"
-#include "keychain/SecureObjectSync/SOSUserKeygen.h"
-#include "keychain/SecureObjectSync/SOSTransport.h"
-
-#include <stdlib.h>
-#include <unistd.h>
-
-#include "secd_regressions.h"
-#include "SOSTestDataSource.h"
-
-#include "SOSRegressionUtilities.h"
-#include <utilities/SecCFWrappers.h>
-#include <Security/SecKeyPriv.h>
-
-#include "keychain/securityd/SOSCloudCircleServer.h"
-
-#include "SOSAccountTesting.h"
-
-#include "SecdTestKeychainUtilities.h"
-
-static void tests(void)
-{
-    CFErrorRef error = NULL;
-    
-    CFDataRef cfpassword = CFDataCreate(NULL, (uint8_t *) "FooFooFoo", 10);
-    CFStringRef cfaccount = CFSTR("test@test.org");
-    
-    CFMutableDictionaryRef changes = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
-    
-    SOSAccount* alice_account = CreateAccountForLocalChanges(CFSTR("Alice"), CFSTR("TestSource"));
-    SOSAccount* bob_account = CreateAccountForLocalChanges(CFSTR("Bob"), CFSTR("TestSource"));
-    
-    ok(SOSAccountAssertUserCredentialsAndUpdate(bob_account, cfaccount, cfpassword, &error), "Credential setting (%@)", error);
-    
-    // Bob wins writing at this point, feed the changes back to alice.
-    is(ProcessChangesUntilNoChange(changes, alice_account, bob_account, NULL), 1, "updates");
-    
-    ok(SOSAccountAssertUserCredentialsAndUpdate(alice_account, cfaccount, cfpassword, &error), "Credential setting (%@)", error);
-    CFReleaseNull(error);
-    
-    CFReleaseNull(cfpassword);
-    CFReleaseNull(error);
-    
-    ok(SOSAccountResetToOffering_wTxn(alice_account, &error), "Reset to offering (%@)", error);
-    CFReleaseNull(error);
-    
-    is(ProcessChangesUntilNoChange(changes, alice_account, bob_account, NULL), 2, "updates");
-    
-    ok(SOSAccountJoinCircles_wTxn(bob_account, &error), "Bob Applies (%@)", error);
-    CFReleaseNull(error);
-    
-    is(ProcessChangesUntilNoChange(changes, alice_account, bob_account, NULL), 2, "updates");
-    {
-        CFArrayRef applicants = SOSAccountCopyApplicants(alice_account, &error);
-        
-        ok(applicants && CFArrayGetCount(applicants) == 1, "See one applicants %@ (%@)", applicants, error);
-        CFReleaseNull(error);
-        CFReleaseSafe(applicants);
-    }
-    
-    is(ProcessChangesUntilNoChange(changes, alice_account, bob_account, NULL), 1, "updates");
-    CFMutableStringRef timeDescription = CFStringCreateMutableCopy(kCFAllocatorDefault, 0, CFSTR("["));
-    CFAbsoluteTime currentTimeAndDate = CFAbsoluteTimeGetCurrent();
-    
-    withStringOfAbsoluteTime(currentTimeAndDate, ^(CFStringRef decription) {
-        CFStringAppend(timeDescription, decription);
-    });
-    CFStringAppend(timeDescription, CFSTR("]"));
-    
-    int tries = 5;
-    
-    CFNumberRef attempts = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &tries);
-    
-    CFMutableArrayRef escrowTimeAndTries = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
-    CFArrayAppendValue(escrowTimeAndTries, timeDescription);
-    CFArrayAppendValue(escrowTimeAndTries, attempts);
-    CFDictionaryRef escrowRecord = CFDictionaryCreateForCFTypes(kCFAllocatorDefault, CFSTR("account label"), escrowTimeAndTries, NULL);
-    
-    CFMutableDictionaryRef record = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
-    CFDictionaryAddValue(record, CFSTR("12345"), escrowRecord);
-    
-    SOSFullPeerInfoRef alice_fpi = alice_account.fullPeerInfo;
-    ok(SOSFullPeerInfoAddEscrowRecord(alice_fpi, CFSTR("12345"), escrowRecord, &error), "Adding Escrow records to Alice FPI(%@)", error);
-    CFDictionaryRef fpi_escrow = SOSPeerInfoCopyEscrowRecord(SOSFullPeerInfoGetPeerInfo(alice_fpi));
-    ok(CFEqualSafe(CFDictionaryGetValue(fpi_escrow, CFSTR("12345")), escrowRecord), "Alice's FPI has escrow (%@)", error);
-       
-    ok(SOSAccountAddEscrowRecords(bob_account, CFSTR("12345"), escrowRecord, &error), "Adding escrow to Bob's account (%@)", error);
-    CFReleaseNull(fpi_escrow);
-
-    fpi_escrow = (CFDictionaryRef)SOSAccountGetValue(bob_account, kSOSEscrowRecord, NULL);
-    ok(CFEqualSafe(CFDictionaryGetValue(fpi_escrow, CFSTR("12345")), escrowRecord), "Bob has escrow records in account (%@)", error);
-    ok(SOSAccountHasPublicKey(alice_account, &error), "Has Public Key" );
-
-    ok([alice_account.trust resetAccountToEmpty:alice_account transport:alice_account.circle_transport err:&error], "Reset to offering (%@)", error);
-    is(ProcessChangesUntilNoChange(changes, alice_account, bob_account, NULL), 2, "updates");
-    ok(SOSAccountHasPublicKey(bob_account, &error), "Has Public Key" );
-
-    ok([bob_account.trust resetAccountToEmpty:bob_account transport:bob_account.circle_transport err:&error], "Reset to offering (%@)", error);
-    is(ProcessChangesUntilNoChange(changes, alice_account, bob_account, NULL), 2, "updates");
-
-    ok(SOSAccountAddEscrowRecords(bob_account, CFSTR("12345"), escrowRecord, &error), "Adding escrow to Bob's account (%@)", error);
-
-    ok(SOSAccountResetToOffering_wTxn(alice_account, &error), "Reset to offering (%@)", error);
-    is(ProcessChangesUntilNoChange(changes, alice_account, bob_account, NULL), 2, "updates");
-
-    SOSAccountTrustClassic *bobTrust = bob_account.trust;
-    CFDictionaryRef bob_fpi_escrow = SOSPeerInfoCopyEscrowRecord(SOSFullPeerInfoGetPeerInfo(bobTrust.fullPeerInfo));
-    ok(bob_fpi_escrow == NULL, "Bob's FPI escrow should be null");
-    CFReleaseNull(bob_fpi_escrow);
-
-    ok(SOSAccountJoinCircles_wTxn(bob_account, &error), "Bob Applies (%@)", error);
-    bob_fpi_escrow = SOSPeerInfoCopyEscrowRecord(SOSFullPeerInfoGetPeerInfo(bobTrust.fullPeerInfo));
-    ok(bob_fpi_escrow && CFEqualSafe(CFDictionaryGetValue(bob_fpi_escrow, CFSTR("12345")), escrowRecord), "Bob has escrow records in account (%@)", error);
-    CFReleaseNull(bob_fpi_escrow);
-    
-    is(ProcessChangesUntilNoChange(changes, alice_account, bob_account, NULL), 2, "updates");
-    
-    {
-        CFArrayRef applicants = SOSAccountCopyApplicants(alice_account, &error);
-        ok(applicants && CFArrayGetCount(applicants) == 1, "See one applicants %@ (%@)", applicants, error);
-        ok(SOSAccountAcceptApplicants(alice_account, applicants, &error), "Accept bob into the fold");
-        CFReleaseNull(error);
-        CFReleaseNull(applicants);
-    }
-    is(ProcessChangesUntilNoChange(changes, alice_account, bob_account, NULL), 3, "updates");
-
-    fpi_escrow = (CFDictionaryRef)SOSAccountGetValue(bob_account, kSOSEscrowRecord, NULL);
-    ok(isNull(fpi_escrow), "Bob's escrow records in the account object should be gone");
-    
-    CFReleaseNull(record);
-    CFReleaseNull(escrowRecord);
-    CFReleaseNull(timeDescription);
-    CFReleaseNull(attempts);
-    SOSTestCleanup();
-
-}
-
-int secd_95_escrow_persistence(int argc, char *const *argv)
-{
-    plan_tests(41);
-    
-    secd_test_setup_temp_keychain(__FUNCTION__, NULL);
-
-    tests();
-    
-    return 0;
-}
diff --git a/keychain/securityd/Regressions/secd_77_ids_messaging.m b/keychain/securityd/Regressions/secd_77_ids_messaging.m
deleted file mode 100644 (file)
index a23cdab..0000000
+++ /dev/null
@@ -1,296 +0,0 @@
-//
-//  secd_77_ids_messaging.c
-//  sec
-//
-
-/*
- * Copyright (c) 2012-2014 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@
- */
-
-
-#include <stdio.h>
-#include <Security/SecBase.h>
-#include <Security/SecItem.h>
-
-#include <Security/SecureObjectSync/SOSAccount.h>
-#include <Security/SecureObjectSync/SOSCloudCircle.h>
-#include <Security/SecureObjectSync/SOSInternal.h>
-#include <Security/SecureObjectSync/SOSFullPeerInfo.h>
-#include <Security/SecureObjectSync/SOSUserKeygen.h>
-#include <stdlib.h>
-#include <unistd.h>
-
-#include "secd_regressions.h"
-#include "SOSTestDataSource.h"
-
-#include "SOSRegressionUtilities.h"
-#include <utilities/SecCFWrappers.h>
-
-#include <securityd/SOSCloudCircleServer.h>
-#include "SecdTestKeychainUtilities.h"
-#include "SOSAccountTesting.h"
-#import "SOSTransportTestTransports.h"
-#include "SOSTestDevice.h"
-#include "SOSTestDataSource.h"
-#include <Security/SecureObjectSync/SOSTransportMessageIDS.h>
-#include <Security/SecureObjectSync/SOSAccountTrustClassic+Circle.h>
-
-static bool SOSAccountIsThisPeerIDMe(SOSAccount* account, CFStringRef peerID) {
-    SOSAccountTrustClassic* trust = account.trust;
-    SOSPeerInfoRef mypi = trust.peerInfo;
-    CFStringRef myPeerID = SOSPeerInfoGetPeerID(mypi);
-    
-    return myPeerID && CFEqualSafe(myPeerID, peerID);
-}
-
-__unused static void ids_test_sync(SOSAccount* alice_account, SOSAccount* bob_account){
-    
-    CFMutableDictionaryRef changes = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
-    __block bool SyncingCompletedOverIDS = false;
-    __block CFErrorRef localError = NULL;
-    __block bool done = false;
-    SOSAccountTrustClassic* aliceTrust = alice_account.trust;
-    SOSAccountTrustClassic* bobTrust = bob_account.trust;
-
-    do{
-        SOSCircleForEachValidPeer(aliceTrust.trustedCircle, alice_account.accountKey, ^(SOSPeerInfoRef peer) {
-            if (!SOSAccountIsThisPeerIDMe(alice_account, SOSPeerInfoGetPeerID(peer))) {
-                if(SOSPeerInfoShouldUseIDSTransport(aliceTrust.peerInfo, peer) &&
-                   SOSPeerInfoShouldUseIDSMessageFragmentation(aliceTrust.peerInfo, peer)){
-                    secnotice("IDS Transport","Syncing with IDS capable peers using IDS!");
-                    
-                    CFMutableSetRef ids = CFSetCreateMutableForCFTypes(kCFAllocatorDefault);
-                    CFSetAddValue(ids, SOSPeerInfoGetPeerID(peer));
-
-                    SyncingCompletedOverIDS = [alice_account.ids_message_transport SOSTransportMessageSyncWithPeers:alice_account.ids_message_transport p:ids err:&localError];
-                    CFReleaseNull(ids);
-                }
-            }
-        });
-     
-        ProcessChangesUntilNoChange(changes, alice_account, bob_account, NULL);
-        
-        SOSCircleForEachValidPeer(bobTrust.trustedCircle, bob_account.accountKey, ^(SOSPeerInfoRef peer) {
-            if (!SOSAccountIsThisPeerIDMe(bob_account, SOSPeerInfoGetPeerID(peer))) {
-                if(SOSPeerInfoShouldUseIDSTransport(bobTrust.peerInfo, peer) &&
-                   SOSPeerInfoShouldUseIDSMessageFragmentation(bobTrust.peerInfo, peer)){
-                    secnotice("IDS Transport","Syncing with IDS capable peers using IDS!");
-
-                    CFMutableSetRef ids = CFSetCreateMutableForCFTypes(kCFAllocatorDefault);
-                    CFSetAddValue(ids, SOSPeerInfoGetPeerID(peer));
-
-                    SyncingCompletedOverIDS = [(SOSMessageIDSTest*)bob_account.ids_message_transport SOSTransportMessageSyncWithPeers:(SOSMessageIDSTest*)bob_account.ids_message_transport p:ids err:&localError];
-                    CFReleaseNull(ids);
-                }
-            }
-        });
-        
-        ok(SyncingCompletedOverIDS, "synced items over IDS");
-        if(CFDictionaryGetCount(SOSTransportMessageIDSTestGetChanges((SOSMessageIDSTest*)alice_account.ids_message_transport)) == 0 && CFDictionaryGetCount(SOSTransportMessageIDSTestGetChanges((SOSMessageIDSTest*)bob_account.ids_message_transport)) == 0){
-            done = true;
-            break;
-        }
-
-        ProcessChangesUntilNoChange(changes, alice_account, bob_account, NULL);
-        
-    }while(done == false);
-    CFReleaseNull(changes);
-}
-
-static void tests()
-{
-    CFErrorRef error = NULL;
-    
-    CFMutableDictionaryRef changes = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
-    CFDataRef cfpassword = CFDataCreate(NULL, (uint8_t *) "FooFooFoo", 10);
-    CFStringRef cfaccount = CFSTR("test@test.org");
-    CFStringRef dsName = CFSTR("Test");
-    
-    SOSAccount* alice_account = CreateAccountForLocalChanges(CFSTR("Alice"), dsName);
-    SOSAccount* bob_account = CreateAccountForLocalChanges(CFSTR("Bob"), dsName);
-    
-    ok(SOSAccountAssertUserCredentialsAndUpdate(bob_account, cfaccount, cfpassword, &error), "Credential setting (%@)", error);
-    
-    // Bob wins writing at this point, feed the changes back to alice.
-    is(ProcessChangesUntilNoChange(changes, alice_account, bob_account, NULL), 1, "updates");
-    
-    ok(SOSAccountAssertUserCredentialsAndUpdate(alice_account, cfaccount, cfpassword, &error), "Credential setting (%@)", error);
-    CFReleaseNull(cfpassword);
-    CFReleaseNull(error);
-
-    ok(NULL != alice_account, "Alice Created");
-    ok(NULL != bob_account, "Bob Created");
-
-    ok(SOSAccountResetToOffering_wTxn(alice_account, &error), "Reset to offering (%@)", error);
-    CFReleaseNull(error);
-    
-    is(ProcessChangesUntilNoChange(changes, alice_account, bob_account, NULL), 2, "updates");
-    
-    ok(SOSAccountJoinCircles_wTxn(bob_account, &error), "Bob Applies (%@)", error);
-    CFReleaseNull(error);
-    
-    is(ProcessChangesUntilNoChange(changes, alice_account, bob_account, NULL), 2, "updates");
-    
-    {
-        CFArrayRef applicants = SOSAccountCopyApplicants(alice_account, &error);
-        
-        ok(applicants && CFArrayGetCount(applicants) == 1, "See one applicant %@ (%@)", applicants, error);
-        ok(SOSAccountAcceptApplicants(alice_account, applicants, &error), "Alice accepts (%@)", error);
-        CFReleaseNull(error);
-        CFReleaseNull(applicants);
-    }
-    
-    is(ProcessChangesUntilNoChange(changes, alice_account, bob_account, NULL), 3, "updates");
-    
-    accounts_agree("bob&alice pair", bob_account, alice_account);
-    
-    CFArrayRef peers = SOSAccountCopyPeers(alice_account, &error);
-    ok(peers && CFArrayGetCount(peers) == 2, "See two peers %@ (%@)", peers, error);
-    CFReleaseNull(peers);
-
-    //creating test devices
-    CFIndex version = 0;
-    
-    // Optionally prefix each peer with name to make them more unique.
-    CFArrayRef deviceIDs = CFArrayCreateForCFTypes(kCFAllocatorDefault,alice_account.peerID, bob_account.peerID, NULL);
-    CFSetRef views = SOSViewsCopyTestV2Default();
-    CFMutableArrayRef peerMetas = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
-    CFStringRef deviceID;
-    CFArrayForEachC(deviceIDs, deviceID) {
-        SOSPeerMetaRef peerMeta = SOSPeerMetaCreateWithComponents(deviceID, views, NULL);
-        CFArrayAppendValue(peerMetas, peerMeta);
-        CFReleaseNull(peerMeta);
-    }
-    
-    CFReleaseNull(views);
-    CFArrayForEachC(deviceIDs, deviceID) {
-        SOSTestDeviceRef device = SOSTestDeviceCreateWithDbNamed(kCFAllocatorDefault, deviceID, deviceID);
-        SOSTestDeviceSetPeerIDs(device, peerMetas, version, NULL);
-        
-        if([alice_account.peerID isEqual: (__bridge id)(deviceID)]){
-            alice_account.factory = device->dsf;
-            SOSTestDeviceAddGenericItem(device, CFSTR("Alice"), CFSTR("Alice-add"));
-        }
-        else{
-            bob_account.factory = device->dsf;
-            SOSTestDeviceAddGenericItem(device, CFSTR("Bob"), CFSTR("Bob-add"));
-        }
-        
-        CFReleaseNull(device);
-    }
-    CFReleaseNull(deviceIDs);
-    CFReleaseNull(peerMetas);
-    
-    SOSUnregisterAllTransportMessages();
-    CFArrayRemoveAllValues(message_transports);
-
-    SOSAccountTrustClassic* aliceTrust = alice_account.trust;
-    SOSAccountTrustClassic* bobTrust = bob_account.trust;
-
-    alice_account.ids_message_transport = (SOSMessageIDS*)[[SOSMessageIDSTest alloc] initWithAccount:alice_account andAccountName:CFSTR("Alice") andCircleName:SOSCircleGetName(aliceTrust.trustedCircle) err:&error ];
-
-    bob_account.ids_message_transport = (SOSMessageIDS*)[[SOSMessageIDSTest alloc] initWithAccount:bob_account andAccountName:CFSTR("Bob") andCircleName:SOSCircleGetName(bobTrust.trustedCircle) err:&error];
-    
-    ok(alice_account.ids_message_transport != NULL, "Alice Account, Created IDS Test Transport");
-    ok(bob_account.ids_message_transport != NULL, "Bob Account, Created IDS Test Transport");
-    
-    bool result = [alice_account.trust modifyCircle:alice_account.circle_transport err:&error action:^bool(SOSCircleRef circle) {
-        CFErrorRef localError = NULL;
-        
-        SOSFullPeerInfoUpdateTransportType(aliceTrust.fullPeerInfo, SOSTransportMessageTypeIDSV2, &localError);
-        SOSFullPeerInfoUpdateTransportPreference(aliceTrust.fullPeerInfo, kCFBooleanFalse, &localError);
-        SOSFullPeerInfoUpdateTransportFragmentationPreference(aliceTrust.fullPeerInfo, kCFBooleanTrue, &localError);
-        SOSFullPeerInfoUpdateTransportAckModelPreference(aliceTrust.fullPeerInfo, kCFBooleanTrue, &localError);
-
-        return SOSCircleHasPeer(circle, aliceTrust.peerInfo, NULL);
-    }];
-
-    ok(result, "Alice account update circle with transport type");
-    
-    is(ProcessChangesUntilNoChange(changes, alice_account, bob_account, NULL), 2, "updates");
-
-    result = [bob_account.trust modifyCircle:bob_account.circle_transport err:&error action:^bool(SOSCircleRef circle) {
-        CFErrorRef localError = NULL;
-        
-        SOSFullPeerInfoUpdateTransportType(bobTrust.fullPeerInfo, SOSTransportMessageTypeIDSV2, &localError);
-        SOSFullPeerInfoUpdateTransportPreference(bobTrust.fullPeerInfo, kCFBooleanFalse, &localError);
-        SOSFullPeerInfoUpdateTransportFragmentationPreference(bobTrust.fullPeerInfo, kCFBooleanTrue, &localError);
-        SOSFullPeerInfoUpdateTransportAckModelPreference(bobTrust.fullPeerInfo, kCFBooleanTrue, &localError);
-
-        return SOSCircleHasPeer(circle, bobTrust.peerInfo, NULL);
-    }];
-    
-    ok(result, "Bob account update circle with transport type");
-    is(ProcessChangesUntilNoChange(changes, alice_account, bob_account, NULL), 2, "updates");
-
-    CFStringRef alice_transportType =SOSPeerInfoCopyTransportType(alice_account.peerInfo);
-    CFStringRef bob_accountTransportType = SOSPeerInfoCopyTransportType(bob_account.peerInfo);
-    ok(CFEqualSafe(alice_transportType, CFSTR("IDS2.0")), "Alice transport type not IDS");
-    ok(CFEqualSafe(bob_accountTransportType, CFSTR("IDS2.0")), "Bob transport type not IDS");
-    
-    CFReleaseNull(alice_transportType);
-    CFReleaseNull(bob_accountTransportType);
-    
-    SOSTransportMessageIDSTestSetName((SOSMessageIDSTest*)alice_account.ids_message_transport, CFSTR("Alice Account"));
-    ok(SOSTransportMessageIDSTestGetName((SOSMessageIDSTest*)alice_account.ids_message_transport) != NULL, "retrieved getting account name");
-    ok(SOSAccountRetrieveDeviceIDFromKeychainSyncingOverIDSProxy(alice_account, &error) != false, "device ID from KeychainSyncingOverIDSProxy");
-
-    SOSTransportMessageIDSTestSetName((SOSMessageIDSTest*)bob_account.ids_message_transport, CFSTR("Bob Account"));
-    ok(SOSTransportMessageIDSTestGetName((SOSMessageIDSTest*)bob_account.ids_message_transport) != NULL, "retrieved getting account name");
-    ok(SOSAccountRetrieveDeviceIDFromKeychainSyncingOverIDSProxy(bob_account, &error) != false, "device ID from KeychainSyncingOverIDSProxy");
-
-    
-    ok(SOSAccountSetMyDSID_wTxn(alice_account, CFSTR("Alice"),&error), "Setting IDS device ID");
-    CFStringRef alice_dsid = SOSAccountCopyDeviceID(alice_account, &error);
-    ok(CFEqualSafe(alice_dsid, CFSTR("Alice")), "Getting IDS device ID");
-
-    ok(SOSAccountSetMyDSID_wTxn(bob_account, CFSTR("Bob"),&error), "Setting IDS device ID");
-    CFStringRef bob_dsid = SOSAccountCopyDeviceID(bob_account, &error);
-    ok(CFEqualSafe(bob_dsid, CFSTR("Bob")), "Getting IDS device ID");
-    
-    is(ProcessChangesUntilNoChange(changes, alice_account, bob_account, NULL), 3, "updates");
-
-    
-    ok(SOSAccountEnsurePeerRegistration(alice_account, NULL), "ensure peer registration - alice");
-
-    ok(SOSAccountEnsurePeerRegistration(bob_account, NULL), "ensure peer registration - bob");
-    
-    
-    //ids_test_sync(alice_account, bob_account);
-    
-    CFReleaseNull(bob_dsid);
-    CFReleaseNull(alice_dsid);
-    CFReleaseNull(changes);
-
-    SOSTestCleanup();
-}
-
-int secd_77_ids_messaging(int argc, char *const *argv)
-{
-    plan_tests(100);
-    
-    secd_test_setup_temp_keychain(__FUNCTION__, NULL);
-    
-    tests();
-    
-    return 0;
-}
index d3de4a06e643234586d41e9821d37db58c9ba6d4..422b0cdeb118471e9dfd4821067589b991737d5d 100644 (file)
@@ -86,7 +86,6 @@ ONE_TEST(secd_82_persistent_ref)
 ONE_TEST(secd_83_item_match_policy)
 ONE_TEST(secd_83_item_match_valid_on_date)
 ONE_TEST(secd_83_item_match_trusted)
 ONE_TEST(secd_83_item_match_policy)
 ONE_TEST(secd_83_item_match_valid_on_date)
 ONE_TEST(secd_83_item_match_trusted)
-ONE_TEST(secd_95_escrow_persistence)
 ONE_TEST(secd_154_engine_backoff)
 ONE_TEST(secd_100_initialsync)
 ONE_TEST(secd_130_other_peer_views)
 ONE_TEST(secd_154_engine_backoff)
 ONE_TEST(secd_100_initialsync)
 ONE_TEST(secd_130_other_peer_views)
index 3adf34ebc69c7af61e931250709b342603828062..0bb84b8be754b8f7c7ddb3f2d57e7380b3b0bf49 100644 (file)
@@ -55,15 +55,6 @@ bool SOSCCRemovePeersFromCircle_Server(CFArrayRef peers, CFErrorRef* error);
 bool SOSCCRemovePeersFromCircleWithAnalytics_Server(CFArrayRef peers, CFDataRef parentEvent, CFErrorRef* error);
 bool SOSCCLoggedOutOfAccount_Server(CFErrorRef *error);
 bool SOSCCBailFromCircle_Server(uint64_t limit_in_seconds, CFErrorRef* error);
 bool SOSCCRemovePeersFromCircleWithAnalytics_Server(CFArrayRef peers, CFDataRef parentEvent, CFErrorRef* error);
 bool SOSCCLoggedOutOfAccount_Server(CFErrorRef *error);
 bool SOSCCBailFromCircle_Server(uint64_t limit_in_seconds, CFErrorRef* error);
-bool SOSCCRequestEnsureFreshParameters_Server(CFErrorRef* error);
-
-
-bool SOSCCApplyToARing_Server(CFStringRef ringName, CFErrorRef *error);
-bool SOSCCWithdrawlFromARing_Server(CFStringRef ringName, CFErrorRef *error);
-SOSRingStatus SOSCCRingStatus_Server(CFStringRef ringName, CFErrorRef *error);
-CF_RETURNS_RETAINED CFStringRef SOSCCGetAllTheRings_Server(CFErrorRef *error);
-bool SOSCCEnableRing_Server(CFStringRef ringName, CFErrorRef *error);
-
 
 CFArrayRef SOSCCCopyGenerationPeerInfo_Server(CFErrorRef* error);
 CFArrayRef SOSCCCopyApplicantPeerInfo_Server(CFErrorRef* error);
 
 CFArrayRef SOSCCCopyGenerationPeerInfo_Server(CFErrorRef* error);
 CFArrayRef SOSCCCopyApplicantPeerInfo_Server(CFErrorRef* error);
@@ -81,8 +72,6 @@ CFArrayRef SOSCCCopyEngineState_Server(CFErrorRef* error);
 
 CFArrayRef SOSCCCopyPeerPeerInfo_Server(CFErrorRef* error);
 CFArrayRef SOSCCCopyConcurringPeerPeerInfo_Server(CFErrorRef* error);
 
 CFArrayRef SOSCCCopyPeerPeerInfo_Server(CFErrorRef* error);
 CFArrayRef SOSCCCopyConcurringPeerPeerInfo_Server(CFErrorRef* error);
-bool SOSCCkSecXPCOpIsThisDeviceLastBackup_Server(CFErrorRef *error);
-bool SOSCCkSecXPCOpIsThisDeviceLastBackup_Server(CFErrorRef *error);
 bool SOSCCAccountSetToNew_Server(CFErrorRef *error);
 bool SOSCCResetToOffering_Server(CFErrorRef* error);
 bool SOSCCResetToEmpty_Server(CFErrorRef* error);
 bool SOSCCAccountSetToNew_Server(CFErrorRef *error);
 bool SOSCCResetToOffering_Server(CFErrorRef* error);
 bool SOSCCResetToEmpty_Server(CFErrorRef* error);
@@ -94,7 +83,6 @@ SOSViewResultCode SOSCCView_Server(CFStringRef view, SOSViewActionCode action, C
 bool SOSCCViewSetWithAnalytics_Server(CFSetRef enabledViews, CFSetRef disabledViews, CFDataRef parentEvent);
 bool SOSCCViewSet_Server(CFSetRef enabledViews, CFSetRef disabledViews);
 
 bool SOSCCViewSetWithAnalytics_Server(CFSetRef enabledViews, CFSetRef disabledViews, CFDataRef parentEvent);
 bool SOSCCViewSet_Server(CFSetRef enabledViews, CFSetRef disabledViews);
 
-CFStringRef SOSCCCopyIncompatibilityInfo_Server(CFErrorRef* error);
 enum DepartureReason SOSCCGetLastDepartureReason_Server(CFErrorRef* error);
 bool SOSCCSetLastDepartureReason_Server(enum DepartureReason reason, CFErrorRef *error);
 
 enum DepartureReason SOSCCGetLastDepartureReason_Server(CFErrorRef* error);
 bool SOSCCSetLastDepartureReason_Server(enum DepartureReason reason, CFErrorRef *error);
 
@@ -108,12 +96,6 @@ bool SOSCCRegisterSingleRecoverySecret_Server(CFDataRef backupSlice, bool setupV
 
 bool SOSCCWaitForInitialSync_Server(CFErrorRef*);
 bool SOSCCWaitForInitialSyncWithAnalytics_Server(CFDataRef parentEvent, CFErrorRef* error);
 
 bool SOSCCWaitForInitialSync_Server(CFErrorRef*);
 bool SOSCCWaitForInitialSyncWithAnalytics_Server(CFDataRef parentEvent, CFErrorRef* error);
-CFArrayRef SOSCCCopyYetToSyncViewsList_Server(CFErrorRef*);
-
-bool SOSWrapToBackupSliceKeyBagForView_Server(CFStringRef viewName, CFDataRef input, CFDataRef* output, CFDataRef* bskbEncoded, CFErrorRef* error);
-
-SOSBackupSliceKeyBagRef SOSBackupSliceKeyBagForView(CFStringRef viewName, CFErrorRef* error);
-CF_RETURNS_RETAINED CFDataRef SOSWrapToBackupSliceKeyBag(SOSBackupSliceKeyBagRef bskb, CFDataRef input, CFErrorRef* error);
 
 //
 // MARK: Internal kicks.
 
 //
 // MARK: Internal kicks.
@@ -147,11 +129,6 @@ CFTypeRef SOSKeychainAccountGetSharedAccount(void);
 
 void SOSCCSetGestalt_Server(CFStringRef name, CFStringRef version, CFStringRef model, CFStringRef serial);
 CFStringRef SOSCCCopyOSVersion(void);
 
 void SOSCCSetGestalt_Server(CFStringRef name, CFStringRef version, CFStringRef model, CFStringRef serial);
 CFStringRef SOSCCCopyOSVersion(void);
-CFDataRef SOSCCCopyAccountState_Server(CFErrorRef* error);
-CFDataRef SOSCCCopyEngineData_Server(CFErrorRef* error);
-bool SOSCCDeleteEngineState_Server(CFErrorRef* error);
-bool SOSCCDeleteAccountState_Server(CFErrorRef* error);
-
 
 //
 // MARK: Testing operations, dangerous to call in normal operation.
 
 //
 // MARK: Testing operations, dangerous to call in normal operation.
@@ -168,13 +145,9 @@ extern CFStringRef kSOSPeerDataLabel;
 CFDataRef SOSItemCopy(CFStringRef label, CFErrorRef* error);
 bool SOSItemUpdateOrAdd(CFStringRef label, CFStringRef accessibility, CFDataRef data, CFErrorRef *error);
 
 CFDataRef SOSItemCopy(CFStringRef label, CFErrorRef* error);
 bool SOSItemUpdateOrAdd(CFStringRef label, CFStringRef accessibility, CFDataRef data, CFErrorRef *error);
 
-bool SOSCCSetEscrowRecord_Server(CFStringRef escrow_label, uint64_t tries, CFErrorRef *error);
-CFDictionaryRef SOSCCCopyEscrowRecord_Server(CFErrorRef *error);
 bool SOSCCRegisterRecoveryPublicKey_Server(CFDataRef recovery_key, CFErrorRef *error);
 CFDataRef SOSCCCopyRecoveryPublicKey_Server(CFErrorRef *error);
 
 bool SOSCCRegisterRecoveryPublicKey_Server(CFDataRef recovery_key, CFErrorRef *error);
 CFDataRef SOSCCCopyRecoveryPublicKey_Server(CFErrorRef *error);
 
-CFDictionaryRef SOSCCCopyBackupInformation_Server(CFErrorRef *error);
-
 SOSPeerInfoRef SOSCCCopyApplication_Server(CFErrorRef *error);
 CFDataRef SOSCCCopyCircleJoiningBlob_Server(SOSPeerInfoRef applicant, CFErrorRef *error);
 bool SOSCCJoinWithCircleJoiningBlob_Server(CFDataRef joiningBlob, PiggyBackProtocolVersion version, CFErrorRef *error);
 SOSPeerInfoRef SOSCCCopyApplication_Server(CFErrorRef *error);
 CFDataRef SOSCCCopyCircleJoiningBlob_Server(SOSPeerInfoRef applicant, CFErrorRef *error);
 bool SOSCCJoinWithCircleJoiningBlob_Server(CFDataRef joiningBlob, PiggyBackProtocolVersion version, CFErrorRef *error);
@@ -182,8 +155,6 @@ CFDataRef SOSCCCopyInitialSyncData_Server(uint32_t flags, CFErrorRef *error);
 bool SOSCCCleanupKVSKeys_Server(CFErrorRef *error);
 
 bool SOSCCAccountHasPublicKey_Server(CFErrorRef *error);
 bool SOSCCCleanupKVSKeys_Server(CFErrorRef *error);
 
 bool SOSCCAccountHasPublicKey_Server(CFErrorRef *error);
-bool SOSCCAccountIsNew_Server(CFErrorRef *error);
-bool SOSCCTestPopulateKVSWithBadKeys_Server(CFErrorRef *error);
 
 void sync_the_last_data_to_kvs(CFTypeRef account, bool waitForeverForSynchronization);
 
 
 void sync_the_last_data_to_kvs(CFTypeRef account, bool waitForeverForSynchronization);
 
index 51bccb51f6b70718e03235c520c9440d1b31c693..71677a8a45a352d8f592044fed59d8d0548d1dec 100644 (file)
@@ -45,7 +45,6 @@
 #include "keychain/SecureObjectSync/SOSInternal.h"
 #include "keychain/SecureObjectSync/SOSUserKeygen.h"
 #include "keychain/SecureObjectSync/SOSMessage.h"
 #include "keychain/SecureObjectSync/SOSInternal.h"
 #include "keychain/SecureObjectSync/SOSUserKeygen.h"
 #include "keychain/SecureObjectSync/SOSMessage.h"
-#include "keychain/SecureObjectSync/SOSBackupInformation.h"
 #include "keychain/SecureObjectSync/SOSDataSource.h"
 #include "keychain/SecureObjectSync/SOSKVSKeys.h"
 #import "keychain/SecureObjectSync/SOSAccountTrustClassic.h"
 #include "keychain/SecureObjectSync/SOSDataSource.h"
 #include "keychain/SecureObjectSync/SOSKVSKeys.h"
 #import "keychain/SecureObjectSync/SOSAccountTrustClassic.h"
@@ -258,7 +257,7 @@ static SOSAccount* SOSKeychainAccountCreateSharedAccount(CFDictionaryRef our_ges
             secerror("Got NULL creating account");
     }
 
             secerror("Got NULL creating account");
     }
 
-    [account startStateMachine];
+    //[account startStateMachine];
 
 done:
     CFReleaseNull(savedAccount);
 
 done:
     CFReleaseNull(savedAccount);
@@ -1152,21 +1151,6 @@ bool SOSCCAccountHasPublicKey_Server(CFErrorRef *error)
     return hasPublicKey;
 }
 
     return hasPublicKey;
 }
 
-bool SOSCCAccountIsNew_Server(CFErrorRef *error)
-{
-    __block bool result = true;
-    __block CFErrorRef localError = NULL;
-    
-    (void) do_with_account_while_unlocked(error, ^bool (SOSAccountTransaction* txn, CFErrorRef* block_error) {
-        result = SOSAccountIsNew(txn.account, &localError);
-        return result;
-    });
-    
-    if(error != NULL && localError != NULL)
-        *error = localError;
-    
-    return result;
-}
 bool SOSCCRequestToJoinCircleAfterRestore_Server(CFErrorRef* error)
 {
     __block bool result = true;
 bool SOSCCRequestToJoinCircleAfterRestore_Server(CFErrorRef* error)
 {
     __block bool result = true;
@@ -1209,99 +1193,6 @@ bool SOSCCRequestToJoinCircleAfterRestoreWithAnalytics_Server(CFDataRef parentEv
 
 }
 
 
 }
 
-bool SOSCCRequestEnsureFreshParameters_Server(CFErrorRef* error)
-{
-    bool returned = false;
-    returned = do_with_account_while_unlocked(error, ^bool (SOSAccountTransaction* txn, CFErrorRef* block_error) {
-            return SyncKVSAndWait(block_error);
-        });
-    if (returned) {
-        returned = Flush(error);
-    }
-    return returned;
-}
-
-bool SOSCCApplyToARing_Server(CFStringRef ringName, CFErrorRef *error){
-    __block bool result = true;
-    bool returned = false;
-    returned = do_with_account_while_unlocked(error, ^bool (SOSAccountTransaction* txn, CFErrorRef* block_error) {
-        SOSFullPeerInfoRef fpi = txn.account.fullPeerInfo;
-        SOSRingRef ring = [txn.account.trust copyRing:ringName err:error];
-
-        if(fpi && ring) {
-            result = SOSRingApply(ring, txn.account.accountKey, fpi , error);
-        }
-        CFReleaseNull(ring);
-        return result;
-    });
-    return returned;
-}
-
-bool SOSCCWithdrawlFromARing_Server(CFStringRef ringName, CFErrorRef *error){
-    __block bool result = true;
-    bool returned = false;
-    returned = do_with_account_while_unlocked(error, ^bool (SOSAccountTransaction* txn, CFErrorRef* block_error) {
-        SOSFullPeerInfoRef fpi = txn.account.fullPeerInfo;
-        SOSRingRef ring = [txn.account.trust copyRing:ringName err:error];
-        if(fpi && ring) {
-            result = SOSRingWithdraw(ring, txn.account.accountKey, fpi , error);
-        }
-        CFReleaseNull(ring);
-        return result;
-    });
-    return returned;
-}
-
-bool SOSCCEnableRing_Server(CFStringRef ringName, CFErrorRef *error){
-    __block bool result = true;
-    bool returned = false;
-    returned = do_with_account_while_unlocked(error, ^bool (SOSAccountTransaction* txn, CFErrorRef* block_error) {
-        SOSFullPeerInfoRef fpi = txn.account.fullPeerInfo;
-        SOSRingRef ring = [txn.account.trust copyRing:ringName err:error];
-        if(fpi && ring) {
-            result = SOSRingResetToOffering(ring, NULL, fpi, error);
-        }
-        CFReleaseNull(ring);
-        return result;
-    });
-    return returned;
-}
-
-CFStringRef SOSCCGetAllTheRings_Server(CFErrorRef *error){
-    __block CFMutableDictionaryRef result = NULL;
-    __block CFMutableStringRef description = CFStringCreateMutable(kCFAllocatorDefault, 0);
-    
-    (void) do_with_account_while_unlocked(error, ^bool(SOSAccountTransaction* txn, CFErrorRef *error) {
-        SOSAccountForEachRing(txn.account, ^SOSRingRef(CFStringRef name, SOSRingRef ring) {
-            CFStringAppendFormat(description, NULL, CFSTR("%@\n"), ring);
-            return NULL;
-        });
-        if(result)
-            return true;
-        return false;
-    });
-    
-    return description;
-}
-
-SOSRingStatus SOSCCRingStatus_Server(CFStringRef ringName, CFErrorRef *error){
-    __block bool result = true;
-    SOSRingStatus returned;
-    returned = do_with_account_while_unlocked(error, ^bool (SOSAccountTransaction* txn, CFErrorRef* block_error) {
-        SOSFullPeerInfoRef fpi = txn.account.fullPeerInfo;
-        SOSPeerInfoRef myPeer = SOSFullPeerInfoGetPeerInfo(fpi);
-        
-        SOSRingRef ring = [txn.account.trust copyRing:ringName err:error];
-        if(myPeer && ring) {
-            result = SOSRingDeviceIsInRing(ring, SOSPeerInfoGetPeerID(myPeer));
-        }
-        CFReleaseNull(ring);
-
-        return result;
-    });
-    return returned;
-}
-
 bool SOSCCAccountSetToNew_Server(CFErrorRef *error)
 {
        return do_with_account_if_after_first_unlock(error, ^bool (SOSAccountTransaction* txn, CFErrorRef* block_error) {
 bool SOSCCAccountSetToNew_Server(CFErrorRef *error)
 {
        return do_with_account_if_after_first_unlock(error, ^bool (SOSAccountTransaction* txn, CFErrorRef* block_error) {
@@ -1440,7 +1331,7 @@ bool SOSCCBailFromCircle_Server(uint64_t limit_in_seconds, CFErrorRef* error)
 {
     return do_with_account_while_unlocked(error, ^bool (SOSAccountTransaction* txn, CFErrorRef* block_error) {
         bool waitForeverForSynchronization = false;
 {
     return do_with_account_while_unlocked(error, ^bool (SOSAccountTransaction* txn, CFErrorRef* block_error) {
         bool waitForeverForSynchronization = false;
-        
+
         bool result = SOSAccountBail(txn.account, limit_in_seconds, block_error);
 
         [txn restart]; // Make sure this gets finished before we set to new.
         bool result = SOSAccountBail(txn.account, limit_in_seconds, block_error);
 
         [txn restart]; // Make sure this gets finished before we set to new.
@@ -1764,220 +1655,6 @@ fail:
     return result;
 }
 
     return result;
 }
 
-
-static CFArrayRef SOSAccountCopyYetToSyncViews(SOSAccount* account, CFErrorRef *error) {
-    __block CFArrayRef result = NULL;
-
-    CFTypeRef valueFetched = SOSAccountGetValue(account, kSOSUnsyncedViewsKey, error);
-    if (valueFetched == kCFBooleanTrue) {
-        SOSPeerInfoRef myPI = account.peerInfo;
-        if (myPI) {
-            SOSPeerInfoWithEnabledViewSet(myPI, ^(CFSetRef enabled) {
-                result = CFSetCopyValues(enabled);
-            });
-        }
-    } else if (isSet(valueFetched)) {
-        result = CFSetCopyValues((CFSetRef)valueFetched);
-    }
-
-    if (result == NULL) {
-        result = CFArrayCreateForCFTypes(kCFAllocatorDefault, NULL);
-    }
-
-    return result;
-}
-
-CFArrayRef SOSCCCopyYetToSyncViewsList_Server(CFErrorRef* error) {
-
-    __block CFArrayRef views = NULL;
-
-    (void) do_with_account_if_after_first_unlock(error, ^bool (SOSAccountTransaction* txn, CFErrorRef* block_error) {
-        views = SOSAccountCopyYetToSyncViews(txn.account, error);
-
-        return true;
-    });
-
-    return views;
-}
-
-bool SOSWrapToBackupSliceKeyBagForView_Server(CFStringRef viewName, CFDataRef input, CFDataRef* output, CFDataRef* bskbEncoded, CFErrorRef* error) {
-    CFErrorRef localerror = NULL;
-    SOSBackupSliceKeyBagRef bskb = SOSBackupSliceKeyBagForView(viewName, &localerror);
-
-    if(bskbEncoded && bskb) {
-        *bskbEncoded = SOSBSKBCopyEncoded(bskb, &localerror);
-    }
-
-    if(output) {
-        *output = SOSWrapToBackupSliceKeyBag(bskb, input, &localerror);
-    }
-
-    if(error) {
-        *error = localerror;
-    }
-    return localerror == NULL;
-}
-
-SOSBackupSliceKeyBagRef SOSBackupSliceKeyBagForView(CFStringRef viewName, CFErrorRef* error){
-    __block SOSBackupSliceKeyBagRef bskb = NULL;
-    (void) do_with_account(^ (SOSAccountTransaction* txn) {
-            bskb = SOSAccountBackupSliceKeyBagForView(txn.account, viewName, error);
-            });
-    return bskb;
-}
-
-CFDataRef SOSWrapToBackupSliceKeyBag(SOSBackupSliceKeyBagRef bskb, CFDataRef input, CFErrorRef* error) {
-    CFDataRef encrypted = NULL;
-    bskb_keybag_handle_t bskb_handle = 0;
-
-    require_quiet(bskb, exit);
-
-    bskb_handle = SOSBSKBLoadLocked(bskb, error);
-    require_quiet(bskb_handle, exit);
-
-    SecAccessControlRef access = NULL;
-    require_quiet(access = SecAccessControlCreate(kCFAllocatorDefault, error), exit);
-    require_quiet(SecAccessControlSetProtection(access, kSecAttrAccessibleWhenUnlocked, error), exit);
-
-    // ks_encrypt_data takes a dictionary as its plaintext.
-    CFMutableDictionaryRef plaintext = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
-    CFDictionarySetValue(plaintext, CFSTR("data"), input);
-
-    require_quiet(ks_encrypt_data_legacy(bskb_handle, access, NULL, plaintext, NULL, &encrypted, false, error), exit);
-
-exit:
-    CFReleaseNull(bskb);
-    if(bskb_handle != 0) {
-        ks_close_keybag(bskb_handle, error);
-    }
-    if(error && *error) {
-        secnotice("backup", "Failed to wrap to a BKSB: %@", *error);
-    }
-    return encrypted;
-
-}
-
-CFDictionaryRef SOSCCCopyEscrowRecord_Server(CFErrorRef *error){
-    
-    __block CFDictionaryRef result = NULL;
-
-    (void) do_with_account_if_after_first_unlock(error, ^bool(SOSAccountTransaction* txn, CFErrorRef *error) {
-       CFErrorRef localError = NULL;
-       SOSCCStatus status = [txn.account getCircleStatus:&localError];
-
-        CFStringRef dsid = SOSAccountGetValue(txn.account, kSOSDSIDKey, error);
-        CFDictionaryRef escrowRecords = NULL;
-        CFDictionaryRef record = NULL;
-        switch(status) {
-            case kSOSCCInCircle:
-                //get the escrow record in the peer info!
-                escrowRecords = SOSPeerInfoCopyEscrowRecord(txn.account.peerInfo);
-                if(escrowRecords){
-                    record = CFDictionaryGetValue(escrowRecords, dsid);
-                    if(record)
-                        result = CFRetainSafe(record);
-                }
-                CFReleaseNull(escrowRecords);
-                break;
-            case kSOSCCRequestPending:
-                //set the escrow record in the peer info/application?
-                break;
-            case kSOSCCNotInCircle:
-            case kSOSCCCircleAbsent:
-                //set the escrow record in the account expansion!
-                escrowRecords = SOSAccountGetValue(txn.account, kSOSEscrowRecord, error);
-                if(escrowRecords){
-                    record = CFDictionaryGetValue(escrowRecords, dsid);
-                    if(record)
-                        result = CFRetainSafe(record);
-                }
-                break;
-            default:
-                secdebug("account", "no circle status!");
-                break;
-        }
-        return true;
-    });
-    
-    return result;
-}
-
-CFDictionaryRef SOSCCCopyBackupInformation_Server(CFErrorRef *error) {
-    __block CFDictionaryRef result = NULL;
-    
-    (void) do_with_account_if_after_first_unlock(error, ^bool(SOSAccountTransaction* txn, CFErrorRef *error) {
-        result = SOSBackupInformation(txn, error);
-        return true;
-    });
-    return result;
-}
-
-bool SOSCCSetEscrowRecord_Server(CFStringRef escrow_label, uint64_t tries, CFErrorRef *error){
-   
-    if (escrow_label == NULL) {
-        return false;
-    }
-
-    __block bool result = true;
-    __block CFErrorRef block_error = NULL;
-    
-    (void) do_with_account_if_after_first_unlock(error, ^bool(SOSAccountTransaction* txn, CFErrorRef *error) {
-        SOSCCStatus status = [txn.account getCircleStatus:&block_error];
-
-        CFStringRef dsid = SOSAccountGetValue(txn.account, kSOSDSIDKey, error);
-
-        CFMutableStringRef timeDescription = CFStringCreateMutableCopy(kCFAllocatorDefault, 0, CFSTR("["));
-        CFAbsoluteTime currentTimeAndDate = CFAbsoluteTimeGetCurrent();
-        
-        withStringOfAbsoluteTime(currentTimeAndDate, ^(CFStringRef decription) {
-            CFStringAppend(timeDescription, decription);
-        });
-        CFStringAppend(timeDescription, CFSTR("]"));
-       
-        CFNumberRef attempts = CFNumberCreate(kCFAllocatorDefault, kCFNumberLongLongType, (const void*)&tries);
-        
-        CFMutableDictionaryRef escrowTimeAndTries = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
-        CFDictionaryAddValue(escrowTimeAndTries, kSOSBurnedRecoveryAttemptCount, attempts);
-        CFDictionaryAddValue(escrowTimeAndTries, kSOSBurnedRecoveryAttemptAttestationDate, timeDescription);
-       
-        CFMutableDictionaryRef escrowRecord = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
-        CFDictionaryAddValue(escrowRecord, escrow_label, escrowTimeAndTries);
-
-        switch(status) {
-            case kSOSCCInCircle:
-                //set the escrow record in the peer info!
-                if(!SOSFullPeerInfoAddEscrowRecord(txn.account.fullPeerInfo, dsid, escrowRecord, error)){
-                    secdebug("accout", "Could not set escrow record in the full peer info");
-                    result = false;
-                }
-                break;
-            case kSOSCCRequestPending:
-                //set the escrow record in the peer info/application?
-                break;
-            case kSOSCCNotInCircle:
-            case kSOSCCCircleAbsent:
-                //set the escrow record in the account expansion!
-                
-                if(!SOSAccountAddEscrowRecords(txn.account, dsid, escrowRecord, error)) {
-                    secdebug("account", "Could not set escrow record in expansion data");
-                    result = false;
-                }
-                break;
-            default:
-                secdebug("account", "no circle status!");
-                break;
-        }
-        CFReleaseNull(attempts);
-        CFReleaseNull(timeDescription);
-        CFReleaseNull(escrowTimeAndTries);
-        CFReleaseNull(escrowRecord);
-        
-        return true;
-    });
-    
-    return result;
-}
-
 bool SOSCCAcceptApplicants_Server(CFArrayRef applicants, CFErrorRef* error)
 {
     OctagonSignpost signPost = OctagonSignpostBegin(SOSSignpostNameSOSCCAcceptApplicants);
 bool SOSCCAcceptApplicants_Server(CFArrayRef applicants, CFErrorRef* error)
 {
     OctagonSignpost signPost = OctagonSignpostBegin(SOSSignpostNameSOSCCAcceptApplicants);
@@ -2043,60 +1720,6 @@ SOSPeerInfoRef SOSCCCopyMyPeerInfo_Server(CFErrorRef* error)
     return result;
 }
 
     return result;
 }
 
-CFDataRef SOSCCCopyAccountState_Server(CFErrorRef* error)
-{
-    __block CFDataRef accountState = NULL;
-    
-    (void) do_with_account_if_after_first_unlock(error, ^bool (SOSAccountTransaction* txn, CFErrorRef* block_error) {
-        // Copy account state from the keychain
-        accountState = SOSAccountCopyAccountStateFromKeychain(block_error);
-        return accountState != NULL;
-    });
-    
-    return accountState;
-}
-
-bool SOSCCDeleteAccountState_Server(CFErrorRef* error)
-{
-    __block bool result = NULL;
-    
-    (void) do_with_account_if_after_first_unlock(error, ^bool (SOSAccountTransaction* txn, CFErrorRef* block_error) {
-        // Delete account state from the keychain
-        result = SOSAccountDeleteAccountStateFromKeychain(block_error);
-        return result;
-    });
-    
-    return result;
-}
-
-CFDataRef SOSCCCopyEngineData_Server(CFErrorRef* error)
-{
-    __block CFDataRef engineState = NULL;
-    
-    (void) do_with_account_if_after_first_unlock(error, ^bool (SOSAccountTransaction* txn, CFErrorRef* block_error) {
-        // Copy engine state from the keychain
-        engineState = SOSAccountCopyEngineStateFromKeychain(block_error);
-        return engineState != NULL;
-    });
-    
-    return engineState;
-}
-
-bool SOSCCDeleteEngineState_Server(CFErrorRef* error)
-{
-    __block bool result = NULL;
-    
-    (void) do_with_account_if_after_first_unlock(error, ^bool (SOSAccountTransaction* txn, CFErrorRef* block_error) {
-        // Delete engine state from the keychain
-        result = SOSAccountDeleteEngineStateFromKeychain(block_error);
-        return result;
-    });
-    
-    return result;
-}
-
-
-
 SOSPeerInfoRef SOSCCSetNewPublicBackupKey_Server(CFDataRef newPublicBackup, CFErrorRef *error){
     __block SOSPeerInfoRef result = NULL;
     OctagonSignpost signPost = OctagonSignpostBegin(SOSSignpostNameSOSCCSetNewPublicBackupKey);
 SOSPeerInfoRef SOSCCSetNewPublicBackupKey_Server(CFDataRef newPublicBackup, CFErrorRef *error){
     __block SOSPeerInfoRef result = NULL;
     OctagonSignpost signPost = OctagonSignpostBegin(SOSSignpostNameSOSCCSetNewPublicBackupKey);
@@ -2134,25 +1757,6 @@ bool SOSCCRegisterSingleRecoverySecret_Server(CFDataRef aks_bag, bool setupV0Onl
     return registerResult;
 }
 
     return registerResult;
 }
 
-CFStringRef SOSCCCopyIncompatibilityInfo_Server(CFErrorRef* error)
-{
-    __block CFStringRef result = NULL;
-
-    (void) do_with_account_if_after_first_unlock(error, ^bool (SOSAccountTransaction* txn, CFErrorRef* block_error) {
-        result = SOSAccountCopyIncompatibilityInfo(txn.account, block_error);
-        return result != NULL;
-    });
-
-    return result;
-}
-
-bool SOSCCkSecXPCOpIsThisDeviceLastBackup_Server(CFErrorRef *error) {
-    bool result = do_with_account_while_unlocked(error, ^bool (SOSAccountTransaction* txn, CFErrorRef* block_error) {
-        return SOSAccountIsLastBackupPeer(txn.account, block_error);
-    });
-    return result;
-}
-
 enum DepartureReason SOSCCGetLastDepartureReason_Server(CFErrorRef* error)
 {
     __block enum DepartureReason result = kSOSDepartureReasonError;
 enum DepartureReason SOSCCGetLastDepartureReason_Server(CFErrorRef* error)
 {
     __block enum DepartureReason result = kSOSDepartureReasonError;
@@ -2351,14 +1955,6 @@ bool SOSCCCleanupKVSKeys_Server(CFErrorRef *error) {
     return result;
 }
 
     return result;
 }
 
-bool SOSCCTestPopulateKVSWithBadKeys_Server(CFErrorRef *error)
-{
-    __block bool result = false;
-    do_with_account_while_unlocked(error, ^bool(SOSAccountTransaction* txn, CFErrorRef *error) {
-        return SOSAccountPopulateKVSWithBadKeys(txn.account, error);
-    });
-    return result;
-}
 CFDataRef SOSCCCopyCircleJoiningBlob_Server(SOSPeerInfoRef applicant, CFErrorRef *error) {
     __block CFDataRef pbblob = NULL;
     OctagonSignpost signPost = OctagonSignpostBegin(SOSSignpostNameSOSCCCopyCircleJoiningBlob);
 CFDataRef SOSCCCopyCircleJoiningBlob_Server(SOSPeerInfoRef applicant, CFErrorRef *error) {
     __block CFDataRef pbblob = NULL;
     OctagonSignpost signPost = OctagonSignpostBegin(SOSSignpostNameSOSCCCopyCircleJoiningBlob);
index b86f63cbcd28da97b45a91ec5f0ea01145caf49b..bfdd3c2ca36c9088c37f2eb62020ef9d0742c61e 100644 (file)
@@ -25,7 +25,7 @@
 }
 - (BOOL)hasKeyClass
 {
 }
 - (BOOL)hasKeyClass
 {
-    return _has.keyClass;
+    return _has.keyClass != 0;
 }
 - (BOOL)hasPublicKey
 {
 }
 - (BOOL)hasPublicKey
 {
index eae21ba43807dc20ff2014e0748be3de08b5cec4..0da4f8ecb747808fb51c6f55bed8527456bae5dc 100644 (file)
@@ -25,7 +25,7 @@
 }
 - (BOOL)hasKeyClass
 {
 }
 - (BOOL)hasKeyClass
 {
-    return _has.keyClass;
+    return _has.keyClass != 0;
 }
 - (BOOL)hasBackupWrappedMetadataKey
 {
 }
 - (BOOL)hasBackupWrappedMetadataKey
 {
index 1621b0d2c89e9b864537cafad7f8c8e745a136b2..9dd58a5ec6ab0b79f1c587c60e716052574b5644 100644 (file)
@@ -27,7 +27,7 @@
 }
 - (BOOL)hasRecoveryType
 {
 }
 - (BOOL)hasRecoveryType
 {
-    return _has.recoveryType;
+    return _has.recoveryType != 0;
 }
 - (BOOL)hasBagIdentity
 {
 }
 - (BOOL)hasBagIdentity
 {
index 7e52fc36d9e66ab2e8586dc5c11297351819eb11..4ad9c89248f34a45ec1e24fb775502ef9542c263 100644 (file)
@@ -71,7 +71,7 @@ static CFDataRef kc_copy_protection_data(SecAccessControlRef access_control);
 static CFTypeRef kc_copy_protection_from(const uint8_t *der, const uint8_t *der_end);
 static CF_RETURNS_RETAINED CFMutableDictionaryRef s3dl_item_v2_decode(CFDataRef plain, CFErrorRef *error);
 static CF_RETURNS_RETAINED CFMutableDictionaryRef s3dl_item_v3_decode(CFDataRef plain, CFErrorRef *error);
 static CFTypeRef kc_copy_protection_from(const uint8_t *der, const uint8_t *der_end);
 static CF_RETURNS_RETAINED CFMutableDictionaryRef s3dl_item_v2_decode(CFDataRef plain, CFErrorRef *error);
 static CF_RETURNS_RETAINED CFMutableDictionaryRef s3dl_item_v3_decode(CFDataRef plain, CFErrorRef *error);
-#if USE_KEYSTORE
+#if USE_KEYSTORE && !TARGET_OS_SIMULATOR
 static bool kc_attribs_key_encrypted_data_from_blob(keybag_handle_t keybag, const SecDbClass *class, const void *blob_data, size_t blob_data_len, SecAccessControlRef access_control, uint32_t version,
                                                     CFMutableDictionaryRef *authenticated_attributes, aks_ref_key_t *ref_key, CFDataRef *encrypted_data, CFErrorRef *error);
 static CFDataRef kc_create_auth_data(SecAccessControlRef access_control, CFDictionaryRef auth_attributes);
 static bool kc_attribs_key_encrypted_data_from_blob(keybag_handle_t keybag, const SecDbClass *class, const void *blob_data, size_t blob_data_len, SecAccessControlRef access_control, uint32_t version,
                                                     CFMutableDictionaryRef *authenticated_attributes, aks_ref_key_t *ref_key, CFDataRef *encrypted_data, CFErrorRef *error);
 static CFDataRef kc_create_auth_data(SecAccessControlRef access_control, CFDictionaryRef auth_attributes);
@@ -155,7 +155,7 @@ bool ks_encrypt_data_legacy(keybag_handle_t keybag, SecAccessControlRef access_c
             CFRelease(attributes_dict);
         }
     } else {
             CFRelease(attributes_dict);
         }
     } else {
-#if USE_KEYSTORE
+#if USE_KEYSTORE && !TARGET_OS_SIMULATOR
         if (attributes) {
             plainText = CFPropertyListCreateDERData(kCFAllocatorDefault, attributes, error);
         }
         if (attributes) {
             plainText = CFPropertyListCreateDERData(kCFAllocatorDefault, attributes, error);
         }
@@ -195,7 +195,7 @@ bool ks_encrypt_data_legacy(keybag_handle_t keybag, SecAccessControlRef access_c
     if (!keyclass)
         goto out;
 
     if (!keyclass)
         goto out;
 
-#if USE_KEYSTORE
+#if USE_KEYSTORE && !TARGET_OS_SIMULATOR
     if (version >= 4) {
         auth_data = kc_create_auth_data(access_control, authenticated_attributes);
         require_quiet(ok = ks_encrypt_acl(keybag, keyclass, bulkKeySize, bulkKey, bulkKeyWrapped, auth_data, acm_context, access_control, error), out);
     if (version >= 4) {
         auth_data = kc_create_auth_data(access_control, authenticated_attributes);
         require_quiet(ok = ks_encrypt_acl(keybag, keyclass, bulkKeySize, bulkKey, bulkKeyWrapped, auth_data, acm_context, access_control, error), out);
@@ -603,7 +603,7 @@ bool ks_decrypt_data(keybag_handle_t keybag, CFTypeRef cryptoOp, SecAccessContro
         }
     }
 
         }
     }
 
-#if USE_KEYSTORE
+#if USE_KEYSTORE && !TARGET_OS_SIMULATOR
     if (hasProtectionData) {
         if (caller_access_groups) {
             caller_access_groups_data = kc_copy_access_groups_data(caller_access_groups, error);
     if (hasProtectionData) {
         if (caller_access_groups) {
             caller_access_groups_data = kc_copy_access_groups_data(caller_access_groups, error);
@@ -777,7 +777,8 @@ static CFTypeRef kc_encode_keyclass(keyclass_t keyclass) {
     }
 }
 
     }
 }
 
-#if USE_KEYSTORE
+// Simulator fakes security, this code is unused on it
+#if USE_KEYSTORE && !TARGET_OS_SIMULATOR
 static bool kc_attribs_key_encrypted_data_from_blob(keybag_handle_t keybag, const SecDbClass *class, const void *blob_data, size_t blob_data_len, SecAccessControlRef access_control, uint32_t version,
                                              CFMutableDictionaryRef *authenticated_attributes, aks_ref_key_t *ref_key, CFDataRef *encrypted_data, CFErrorRef *error)
 {
 static bool kc_attribs_key_encrypted_data_from_blob(keybag_handle_t keybag, const SecDbClass *class, const void *blob_data, size_t blob_data_len, SecAccessControlRef access_control, uint32_t version,
                                              CFMutableDictionaryRef *authenticated_attributes, aks_ref_key_t *ref_key, CFDataRef *encrypted_data, CFErrorRef *error)
 {
index 1845b5bb1afafa6345e30b2d38006ee82ca2407d..d5a3a283ab01216c5e38b0373d7f334c36d3796e 100644 (file)
@@ -894,10 +894,23 @@ static SOSDataSourceFactoryRef SecItemDataSourceFactoryCreate(SecDbRef db) {
     return &dsf->factory;
 }
 
     return &dsf->factory;
 }
 
+
+static dispatch_once_t sDSFQueueOnce;
+static dispatch_queue_t sDSFQueue;
+static CFMutableDictionaryRef sDSTable = NULL;
+
+void SecItemDataSourceFactoryReleaseAll() {
+    // Ensure that the queue is set up
+    (void) SecItemDataSourceFactoryGetShared(nil);
+
+    dispatch_sync(sDSFQueue, ^{
+        if(sDSTable) {
+            CFDictionaryRemoveAllValues(sDSTable);
+        }
+    });
+}
+
 SOSDataSourceFactoryRef SecItemDataSourceFactoryGetShared(SecDbRef db) {
 SOSDataSourceFactoryRef SecItemDataSourceFactoryGetShared(SecDbRef db) {
-    static dispatch_once_t sDSFQueueOnce;
-    static dispatch_queue_t sDSFQueue;
-    static CFMutableDictionaryRef sDSTable = NULL;
     
     dispatch_once(&sDSFQueueOnce, ^{
         sDSFQueue = dispatch_queue_create("dataSourceFactory queue", DISPATCH_QUEUE_SERIAL);
     
     dispatch_once(&sDSFQueueOnce, ^{
         sDSFQueue = dispatch_queue_create("dataSourceFactory queue", DISPATCH_QUEUE_SERIAL);
index fab2678e38248d4453f941ab190fb45cd413f156..3ba61dd777a7eddafd8dd9a2975006264de47af3 100644 (file)
@@ -44,6 +44,8 @@ SOSManifestRef SOSCreateManifestWithBackup(CFDictionaryRef backup, CFErrorRef *e
 // Hack to log objects from inside SOS code
 void SecItemServerAppendItemDescription(CFMutableStringRef desc, CFDictionaryRef object);
 
 // Hack to log objects from inside SOS code
 void SecItemServerAppendItemDescription(CFMutableStringRef desc, CFDictionaryRef object);
 
+// Are you a test? Call this to drop all data sources.
+void SecItemDataSourceFactoryReleaseAll(void);
 
 __END_DECLS
 
 
 __END_DECLS
 
index 039b82528c03426c299a18c3eb67d785f5ee20d1..cd9c7b9707074a970d08cfd8a7531346272a1af8 100644 (file)
@@ -1157,19 +1157,16 @@ SecDbRef SecKeychainDbCreate(CFStringRef path, CFErrorRef* error) {
 SecDbRef SecKeychainDbInitialize(SecDbRef db) {
 
 #if OCTAGON
 SecDbRef SecKeychainDbInitialize(SecDbRef db) {
 
 #if OCTAGON
-    if(SecCKKSIsEnabled()) {
-        // This needs to be async, otherwise we get hangs between securityd, cloudd, and apsd
-        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
-            SecCKKSInitialize(db);
-
-        });
-    }
-
-    if(OctagonIsEnabled() && OctagonShouldPerformInitialization()) {
-        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
+    // This needs to be async, otherwise we get hangs between securityd, cloudd, and apsd
+    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
+        if(OctagonIsEnabled() && OctagonShouldPerformInitialization()) {
             OctagonInitialize();
             OctagonInitialize();
-        });
-    }
+        }
+
+        if(SecCKKSIsEnabled()) {
+            SecCKKSInitialize(db);
+        }
+    });
 
     if(EscrowRequestServerIsEnabled()) {
         dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
 
     if(EscrowRequestServerIsEnabled()) {
         dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
@@ -1221,6 +1218,16 @@ static SecDbRef kc_dbhandle(CFErrorRef* error)
     return _kc_dbhandle;
 }
 
     return _kc_dbhandle;
 }
 
+/* whitebox testing only, and I really hope you call DbReset soon */
+void SecKeychainDbForceClose(void)
+{
+    dispatch_sync(get_kc_dbhandle_dispatch(), ^{
+        if(_kc_dbhandle) {
+            SecDbForceClose(_kc_dbhandle);
+        }
+    });
+}
+
 /* For whitebox testing only */
 void SecKeychainDbReset(dispatch_block_t inbetween)
 {
 /* For whitebox testing only */
 void SecKeychainDbReset(dispatch_block_t inbetween)
 {
index 91fcda27733b6494e7bcd956754a5e2ebd60a60e..0c1637786929acaa26201c90d85d4acfc26b3295 100644 (file)
@@ -87,7 +87,9 @@ bool kc_with_dbt_non_item_tables(bool writeAndRead, CFErrorRef* error, bool (^pe
 bool kc_with_custom_db(bool writeAndRead, bool usesItemTables, SecDbRef db, CFErrorRef *error, bool (^perform)(SecDbConnectionRef dbt));
 
 
 bool kc_with_custom_db(bool writeAndRead, bool usesItemTables, SecDbRef db, CFErrorRef *error, bool (^perform)(SecDbConnectionRef dbt));
 
 
+
 /* For whitebox testing only */
 /* For whitebox testing only */
+void SecKeychainDbForceClose(void);
 void SecKeychainDbReset(dispatch_block_t inbetween);
 
 
 void SecKeychainDbReset(dispatch_block_t inbetween);
 
 
index 80b10d3fda5ce9430cfbef7199309972d85508b9..367f2274d741c7c1a7ff4f87df00e9d5a91ad4d8 100644 (file)
@@ -87,12 +87,6 @@ static struct securityd securityd_spi = {
     .soscc_RequestToJoinCircle              = SOSCCRequestToJoinCircle_Server,
     .soscc_RequestToJoinCircleAfterRestore  = SOSCCRequestToJoinCircleAfterRestore_Server,
     .soscc_RequestToJoinCircleAfterRestoreWithAnalytics  = SOSCCRequestToJoinCircleAfterRestoreWithAnalytics_Server,
     .soscc_RequestToJoinCircle              = SOSCCRequestToJoinCircle_Server,
     .soscc_RequestToJoinCircleAfterRestore  = SOSCCRequestToJoinCircleAfterRestore_Server,
     .soscc_RequestToJoinCircleAfterRestoreWithAnalytics  = SOSCCRequestToJoinCircleAfterRestoreWithAnalytics_Server,
-    .soscc_RequestEnsureFreshParameters     = SOSCCRequestEnsureFreshParameters_Server,
-    .soscc_GetAllTheRings                   = SOSCCGetAllTheRings_Server,
-    .soscc_ApplyToARing                     = SOSCCApplyToARing_Server,
-    .soscc_WithdrawlFromARing               = SOSCCWithdrawlFromARing_Server,
-    .soscc_EnableRing                       = SOSCCEnableRing_Server,
-    .soscc_RingStatus                       = SOSCCRingStatus_Server,
     .soscc_SetToNew                         = SOSCCAccountSetToNew_Server,
     .soscc_ResetToOffering                  = SOSCCResetToOffering_Server,
     .soscc_ResetToEmpty                     = SOSCCResetToEmpty_Server,
     .soscc_SetToNew                         = SOSCCAccountSetToNew_Server,
     .soscc_ResetToOffering                  = SOSCCResetToOffering_Server,
     .soscc_ResetToEmpty                     = SOSCCResetToEmpty_Server,
@@ -131,21 +125,10 @@ static struct securityd securityd_spi = {
     .soscc_RegisterSingleRecoverySecret     = SOSCCRegisterSingleRecoverySecret_Server,
     .soscc_WaitForInitialSync               = SOSCCWaitForInitialSync_Server,
     .soscc_WaitForInitialSyncWithAnalytics  = SOSCCWaitForInitialSyncWithAnalytics_Server,
     .soscc_RegisterSingleRecoverySecret     = SOSCCRegisterSingleRecoverySecret_Server,
     .soscc_WaitForInitialSync               = SOSCCWaitForInitialSync_Server,
     .soscc_WaitForInitialSyncWithAnalytics  = SOSCCWaitForInitialSyncWithAnalytics_Server,
-    .soscc_CopyYetToSyncViewsList           = SOSCCCopyYetToSyncViewsList_Server,
-    .soscc_SetEscrowRecords                 = SOSCCSetEscrowRecord_Server,
-    .soscc_CopyEscrowRecords                = SOSCCCopyEscrowRecord_Server,
-    .sosbskb_WrapToBackupSliceKeyBagForView = SOSWrapToBackupSliceKeyBagForView_Server,
-    .soscc_CopyAccountState                 = SOSCCCopyAccountState_Server,
-    .soscc_DeleteAccountState               = SOSCCDeleteAccountState_Server,
-    .soscc_CopyEngineData                   = SOSCCCopyEngineData_Server,
-    .soscc_DeleteEngineState                = SOSCCDeleteEngineState_Server,
     .soscc_AccountHasPublicKey              = SOSCCAccountHasPublicKey_Server,
     .soscc_AccountHasPublicKey              = SOSCCAccountHasPublicKey_Server,
-    .soscc_AccountIsNew                     = SOSCCAccountIsNew_Server,
-    .soscc_IsThisDeviceLastBackup           = SOSCCkSecXPCOpIsThisDeviceLastBackup_Server,
     .soscc_SOSCCPeersHaveViewsEnabled       = SOSCCPeersHaveViewsEnabled_Server,
     .soscc_RegisterRecoveryPublicKey        = SOSCCRegisterRecoveryPublicKey_Server,
     .soscc_CopyRecoveryPublicKey            = SOSCCCopyRecoveryPublicKey_Server,
     .soscc_SOSCCPeersHaveViewsEnabled       = SOSCCPeersHaveViewsEnabled_Server,
     .soscc_RegisterRecoveryPublicKey        = SOSCCRegisterRecoveryPublicKey_Server,
     .soscc_CopyRecoveryPublicKey            = SOSCCCopyRecoveryPublicKey_Server,
-    .soscc_CopyBackupInformation            = SOSCCCopyBackupInformation_Server,
     .soscc_SOSCCMessageFromPeerIsPending    = SOSCCMessageFromPeerIsPending_Server,
     .soscc_SOSCCSendToPeerIsPending         = SOSCCSendToPeerIsPending_Server,
 #endif /* SECUREOBJECTSYNC */
     .soscc_SOSCCMessageFromPeerIsPending    = SOSCCMessageFromPeerIsPending_Server,
     .soscc_SOSCCSendToPeerIsPending         = SOSCCSendToPeerIsPending_Server,
 #endif /* SECUREOBJECTSYNC */
index fd98b35381f8db56c1f5c23478d5f3e67bdf7f4a..5ba4a914a470cb6a9769e28646df022be2e6e3ae 100644 (file)
@@ -24,7 +24,6 @@ var preapprovedKeys: [Data]?
 var deviceName: String?
 var serialNumber: String?
 var osVersion: String?
 var deviceName: String?
 var serialNumber: String?
 var osVersion: String?
-var policyVersion: NSNumber?
 var policySecrets: [String: Data]?
 
 enum Command {
 var policySecrets: [String: Data]?
 
 enum Command {
@@ -209,11 +208,11 @@ while let arg = argIterator.next() {
         osVersion = newOsVersion
 
     case "--policy-version":
         osVersion = newOsVersion
 
     case "--policy-version":
-        guard let newPolicyVersion = UInt64(argIterator.next() ?? "") else {
+        guard let _ = UInt64(argIterator.next() ?? "") else {
             print("Error: --policy-version takes an integer argument")
             exitUsage(1)
         }
             print("Error: --policy-version takes an integer argument")
             exitUsage(1)
         }
-        policyVersion = NSNumber(value: newPolicyVersion)
+        // Option ignored for now
 
     case "--policy-secret":
         guard let name = argIterator.next(), let dataBase64 = argIterator.next() else {
 
     case "--policy-secret":
         guard let name = argIterator.next(), let dataBase64 = argIterator.next() else {
@@ -451,7 +450,7 @@ while let arg = argIterator.next() {
         var machineIDs = Set<String>()
         var performIDMS = false
         while let arg = argIterator.next() {
         var machineIDs = Set<String>()
         var performIDMS = false
         while let arg = argIterator.next() {
-            if(arg == "--idms") {
+            if arg == "--idms" {
                 performIDMS = true
             } else {
                 machineIDs.insert(arg)
                 performIDMS = true
             } else {
                 machineIDs.insert(arg)
@@ -465,7 +464,7 @@ while let arg = argIterator.next() {
     }
 }
 
     }
 }
 
-if commands.count == 0 {
+if commands.isEmpty {
     exitUsage(0)
 }
 
     exitUsage(0)
 }
 
@@ -543,7 +542,7 @@ for command in commands {
                       voucherSig: voucherSig,
                       ckksKeys: [],
                       tlkShares: [],
                       voucherSig: voucherSig,
                       ckksKeys: [],
                       tlkShares: [],
-                      preapprovedKeys: preapprovedKeys ?? []) { peerID, _, error in
+                      preapprovedKeys: preapprovedKeys ?? []) { peerID, _, _, _, error in
                         guard error == nil else {
                             print("Error joining:", error!)
                             return
                         guard error == nil else {
                             print("Error joining:", error!)
                             return
@@ -634,11 +633,10 @@ for command in commands {
                          deviceName: deviceName ?? deviceInfo.deviceName(),
                          serialNumber: serialNumber ?? deviceInfo.serialNumber(),
                          osVersion: osVersion ?? deviceInfo.osVersion(),
                          deviceName: deviceName ?? deviceInfo.deviceName(),
                          serialNumber: serialNumber ?? deviceInfo.serialNumber(),
                          osVersion: osVersion ?? deviceInfo.osVersion(),
-                         policyVersion: policyVersion,
+                         policyVersion: nil,
                          policySecrets: policySecrets,
                          signingPrivKeyPersistentRef: nil,
                          policySecrets: policySecrets,
                          signingPrivKeyPersistentRef: nil,
-                         encPrivKeyPersistentRef: nil) {
-                            peerID, permanentInfo, permanentInfoSig, stableInfo, stableInfoSig, error in
+                         encPrivKeyPersistentRef: nil) { peerID, permanentInfo, permanentInfoSig, stableInfo, stableInfoSig, views, _, error in
                             guard error == nil else {
                                 print("Error preparing:", error!)
                                 return
                             guard error == nil else {
                                 print("Error preparing:", error!)
                                 return
@@ -651,7 +649,8 @@ for command in commands {
                                 "stableInfo": stableInfo!.base64EncodedString(),
                                 "stableInfoSig": stableInfoSig!.base64EncodedString(),
                                 "machineID": machineID!,
                                 "stableInfo": stableInfo!.base64EncodedString(),
                                 "stableInfoSig": stableInfoSig!.base64EncodedString(),
                                 "machineID": machineID!,
-                                ]
+                                "views": Array(views ?? Set()),
+                                ] as [String: Any]
                             do {
                                 print(try TPCTLObjectiveC.jsonSerialize(cleanDictionaryForJSON(result)))
                             } catch {
                             do {
                                 print(try TPCTLObjectiveC.jsonSerialize(cleanDictionaryForJSON(result)))
                             } catch {
@@ -666,7 +665,7 @@ for command in commands {
                         deviceName: deviceName,
                         serialNumber: serialNumber,
                         osVersion: osVersion,
                         deviceName: deviceName,
                         serialNumber: serialNumber,
                         osVersion: osVersion,
-                        policyVersion: policyVersion,
+                        policyVersion: nil,
                         policySecrets: policySecrets) { _, error in
                             guard error == nil else {
                                 print("Error updating:", error!)
                         policySecrets: policySecrets) { _, error in
                             guard error == nil else {
                                 print("Error updating:", error!)
@@ -754,7 +753,7 @@ for command in commands {
                                  bottleID: bottleID,
                                  entropy: entropy,
                                  bottleSalt: salt,
                                  bottleID: bottleID,
                                  entropy: entropy,
                                  bottleSalt: salt,
-                                 tlkShares: []) { voucher, voucherSig, error in
+                                 tlkShares: []) { voucher, voucherSig, _, _, error in
                                     guard error == nil else {
                                         print("Error during vouchWithBottle", error!)
                                         return
                                     guard error == nil else {
                                         print("Error during vouchWithBottle", error!)
                                         return
@@ -771,8 +770,9 @@ for command in commands {
         os_log("allow-listing (%@, %@)", log: tplogDebug, type: .default, container, context)
 
         var idmsDeviceIDs: Set<String> = Set()
         os_log("allow-listing (%@, %@)", log: tplogDebug, type: .default, container, context)
 
         var idmsDeviceIDs: Set<String> = Set()
+        var accountIsDemo: Bool = false
 
 
-        if(performIDMS) {
+        if performIDMS {
             let store = ACAccountStore()
             guard let account = store.aa_primaryAppleAccount() else {
                 print("Unable to fetch primary Apple account!")
             let store = ACAccountStore()
             guard let account = store.aa_primaryAppleAccount() else {
                 print("Unable to fetch primary Apple account!")
@@ -783,6 +783,12 @@ for command in commands {
             requestArguments.altDSID = account.aa_altDSID
             requestArguments.services = [AKServiceNameiCloud]
 
             requestArguments.altDSID = account.aa_altDSID
             requestArguments.services = [AKServiceNameiCloud]
 
+            let akManager = AKAccountManager.sharedInstance
+            let authKitAccount = akManager.authKitAccount(withAltDSID: account.aa_altDSID)
+            if let account = authKitAccount {
+                accountIsDemo = akManager.demoAccount(for: account)
+            }
+
             guard let controller = AKAppleIDAuthenticationController() else {
                 print("Unable to create AKAppleIDAuthenticationController!")
                 abort()
             guard let controller = AKAppleIDAuthenticationController() else {
                 print("Unable to create AKAppleIDAuthenticationController!")
                 abort()
@@ -804,10 +810,9 @@ for command in commands {
             }
             semaphore.wait()
         }
             }
             semaphore.wait()
         }
-
         let allMachineIDs = machineIDs.union(idmsDeviceIDs)
         print("Setting allowed machineIDs to \(allMachineIDs)")
         let allMachineIDs = machineIDs.union(idmsDeviceIDs)
         print("Setting allowed machineIDs to \(allMachineIDs)")
-        tpHelper.setAllowedMachineIDsWithContainer(container, context: context, allowedMachineIDs: allMachineIDs) { listChanged, error in
+        tpHelper.setAllowedMachineIDsWithContainer(container, context: context, allowedMachineIDs: allMachineIDs, honorIDMSListChanges: accountIsDemo) { listChanged, error in
             guard error == nil else {
                 print("Error during allow:", error!)
                 return
             guard error == nil else {
                 print("Error during allow:", error!)
                 return
index e31f88f6210b8525dd2f038e275cce3e1b391dc5..9968af8068009cda8554a4b3e5c47ee4437f6fcc 100644 (file)
 #include <security_asn1/secerr.h>
 #include <security_asn1/secport.h>
 
 #include <security_asn1/secerr.h>
 #include <security_asn1/secport.h>
 
-#if USE_CDSA_CRYPTO
-#include <Security/cssmapi.h>
-#else
 #include <CommonCrypto/CommonDigest.h>
 #include <CommonCrypto/CommonDigest.h>
-#endif
 
 #include <Security/SecCmsDigestContext.h>
 
 
 #include <Security/SecCmsDigestContext.h>
 
@@ -60,11 +56,7 @@ struct SecCmsDigestContextStr {
     PLArenaPool *      poolp;
     Boolean            saw_contents;
     int                 digcnt;
     PLArenaPool *      poolp;
     Boolean            saw_contents;
     int                 digcnt;
-#if USE_CDSA_CRYPTO
-    CSSM_CC_HANDLE *   digobjs;
-#else
     void **             digobjs;
     void **             digobjs;
-#endif
     SECAlgorithmID **   digestalgs;
 };
 
     SECAlgorithmID **   digestalgs;
 };
 
@@ -77,17 +69,14 @@ SecCmsDigestContextStartMultiple(SECAlgorithmID **digestalgs)
 {
     PLArenaPool *poolp;
     SecCmsDigestContextRef cmsdigcx;
 {
     PLArenaPool *poolp;
     SecCmsDigestContextRef cmsdigcx;
-#if USE_CDSA_CRYPTO
-    CSSM_CC_HANDLE digobj;
-#else
     void * digobj;
     void * digobj;
-#endif
     int digcnt;
     int i;
 
     poolp = PORT_NewArena(1024);
     int digcnt;
     int i;
 
     poolp = PORT_NewArena(1024);
-    if (poolp == NULL)
-       goto loser;
+    if (poolp == NULL) {
+        goto loser;
+    }
 
     digcnt = (digestalgs == NULL) ? 0 : SecCmsArrayCount((void **)digestalgs);
 
 
     digcnt = (digestalgs == NULL) ? 0 : SecCmsArrayCount((void **)digestalgs);
 
@@ -98,27 +87,18 @@ SecCmsDigestContextStartMultiple(SECAlgorithmID **digestalgs)
     cmsdigcx->poolp = poolp;
 
     if (digcnt > 0) {
     cmsdigcx->poolp = poolp;
 
     if (digcnt > 0) {
-#if USE_CDSA_CRYPTO
-       /* Security check to prevent under-allocation */
-       if (digcnt >= (int)((INT_MAX/(MAX(sizeof(CSSM_CC_HANDLE),sizeof(SECAlgorithmID *))))-1)) {
-               goto loser;
-       }
-       cmsdigcx->digobjs = (CSSM_CC_HANDLE *)PORT_ArenaAlloc(poolp, digcnt * sizeof(CSSM_CC_HANDLE));
-       if (cmsdigcx->digobjs == NULL)
-           goto loser;
-#else
-       /* Security check to prevent under-allocation */
-       if (digcnt >= (int)((INT_MAX/(MAX(sizeof(void *),sizeof(SECAlgorithmID *))))-1)) {
-               goto loser;
-       }
-       cmsdigcx->digobjs = (void**)PORT_ArenaAlloc(poolp, digcnt * sizeof(void *));
-       if (cmsdigcx->digobjs == NULL)
-           goto loser;
-#endif
-       cmsdigcx->digestalgs = (SECAlgorithmID **)PORT_ArenaZAlloc(poolp,
-           (digcnt + 1) * sizeof(SECAlgorithmID *));
-       if (cmsdigcx->digestalgs == NULL)
-           goto loser;
+        /* Security check to prevent under-allocation */
+        if (digcnt >= (int)((INT_MAX/(MAX(sizeof(void *),sizeof(SECAlgorithmID *))))-1)) {
+            goto loser;
+        }
+        cmsdigcx->digobjs = (void**)PORT_ArenaAlloc(poolp, digcnt * sizeof(void *));
+        if (cmsdigcx->digobjs == NULL) {
+            goto loser;
+        }
+        cmsdigcx->digestalgs = (SECAlgorithmID **)PORT_ArenaZAlloc(poolp, (digcnt + 1) * sizeof(SECAlgorithmID *));
+        if (cmsdigcx->digestalgs == NULL) {
+            goto loser;
+        }
     }
 
     cmsdigcx->digcnt = 0;
     }
 
     cmsdigcx->digcnt = 0;
@@ -127,31 +107,27 @@ SecCmsDigestContextStartMultiple(SECAlgorithmID **digestalgs)
      * Create a digest object context for each algorithm.
      */
     for (i = 0; i < digcnt; i++) {
      * Create a digest object context for each algorithm.
      */
     for (i = 0; i < digcnt; i++) {
-       digobj = SecCmsUtilGetHashObjByAlgID(digestalgs[i]);
-       /*
-        * Skip any algorithm we do not even recognize; obviously,
-        * this could be a problem, but if it is critical then the
-        * result will just be that the signature does not verify.
-        * We do not necessarily want to error out here, because
-        * the particular algorithm may not actually be important,
-        * but we cannot know that until later.
-        */
-#if USE_CDSA_CRYPTO
-       if (digobj)
-           if (CSSM_DigestDataInit(digobj))
-               goto loser;
-#endif
-
-       cmsdigcx->digobjs[cmsdigcx->digcnt] = digobj;
-       cmsdigcx->digestalgs[cmsdigcx->digcnt] = PORT_ArenaAlloc(poolp, sizeof(SECAlgorithmID));
-       if (SECITEM_CopyItem(poolp,
-           &(cmsdigcx->digestalgs[cmsdigcx->digcnt]->algorithm),
-           &(digestalgs[i]->algorithm))
-           || SECITEM_CopyItem(poolp,
-           &(cmsdigcx->digestalgs[cmsdigcx->digcnt]->parameters),
-           &(digestalgs[i]->parameters)))
-           goto loser;
-       cmsdigcx->digcnt++;
+        digobj = SecCmsUtilGetHashObjByAlgID(digestalgs[i]);
+        /*
+         * Skip any algorithm we do not even recognize; obviously,
+         * this could be a problem, but if it is critical then the
+         * result will just be that the signature does not verify.
+         * We do not necessarily want to error out here, because
+         * the particular algorithm may not actually be important,
+         * but we cannot know that until later.
+         */
+
+        cmsdigcx->digobjs[cmsdigcx->digcnt] = digobj;
+        cmsdigcx->digestalgs[cmsdigcx->digcnt] = PORT_ArenaAlloc(poolp, sizeof(SECAlgorithmID));
+        if (SECITEM_CopyItem(poolp,
+                             &(cmsdigcx->digestalgs[cmsdigcx->digcnt]->algorithm),
+                             &(digestalgs[i]->algorithm))
+            || SECITEM_CopyItem(poolp,
+                                &(cmsdigcx->digestalgs[cmsdigcx->digcnt]->parameters),
+                                &(digestalgs[i]->parameters))) {
+            goto loser;
+        }
+        cmsdigcx->digcnt++;
     }
 
     cmsdigcx->saw_contents = PR_FALSE;
     }
 
     cmsdigcx->saw_contents = PR_FALSE;
@@ -159,8 +135,9 @@ SecCmsDigestContextStartMultiple(SECAlgorithmID **digestalgs)
     return cmsdigcx;
 
 loser:
     return cmsdigcx;
 
 loser:
-    if (poolp)
-       PORT_FreeArena(poolp, PR_FALSE);
+    if (poolp) {
+        PORT_FreeArena(poolp, PR_FALSE);
+    }
 
     return NULL;
 }
 
     return NULL;
 }
@@ -191,35 +168,31 @@ SecCmsDigestContextUpdate(SecCmsDigestContextRef cmsdigcx, const unsigned char *
     dataBuf.Data = (uint8_t *)data;
     cmsdigcx->saw_contents = PR_TRUE;
     for (i = 0; i < cmsdigcx->digcnt; i++) {
     dataBuf.Data = (uint8_t *)data;
     cmsdigcx->saw_contents = PR_TRUE;
     for (i = 0; i < cmsdigcx->digcnt; i++) {
-       if (cmsdigcx->digobjs[i]) {
-#if USE_CDSA_CRYPTO
-           CSSM_DigestDataUpdate(cmsdigcx->digobjs[i], &dataBuf, 1);
-#else
+        if (cmsdigcx->digobjs[i]) {
             /* 64 bits cast: worst case is we truncate the length and we dont hash all the data.
             /* 64 bits cast: worst case is we truncate the length and we dont hash all the data.
-               This may cause an invalid CMS blob larger than 4GB to be validated. Unlikely, but
-               possible security issue. There is no way to return an error here, but a check at
-               the upper level may happen. */
-           /*
-             rdar://problem/20642513
-             Let's just die a horrible death rather than have the security issue.
-             CMS blob over 4GB?  Oh well.
-           */
-           if (len > UINT32_MAX) {
-             /* Ugh. */
-             abort();
-           }
+             This may cause an invalid CMS blob larger than 4GB to be validated. Unlikely, but
+             possible security issue. There is no way to return an error here, but a check at
+             the upper level may happen. */
+            /*
+             rdar://problem/20642513
+             Let's just die a horrible death rather than have the security issue.
+             CMS blob over 4GB?  Oh well.
+             */
+            if (len > UINT32_MAX) {
+                /* Ugh. */
+                abort();
+            }
             assert(len<=UINT32_MAX); /* Debug check. Correct as long as CC_LONG is uint32_t */
             switch (SECOID_GetAlgorithmTag(cmsdigcx->digestalgs[i])) {
             assert(len<=UINT32_MAX); /* Debug check. Correct as long as CC_LONG is uint32_t */
             switch (SECOID_GetAlgorithmTag(cmsdigcx->digestalgs[i])) {
-            case SEC_OID_SHA1: CC_SHA1_Update((CC_SHA1_CTX *)cmsdigcx->digobjs[i], data, (CC_LONG)len); break;
-            case SEC_OID_MD5: CC_MD5_Update((CC_MD5_CTX *)cmsdigcx->digobjs[i], data, (CC_LONG)len); break;
-            case SEC_OID_SHA224: CC_SHA224_Update((CC_SHA256_CTX *)cmsdigcx->digobjs[i], data, (CC_LONG)len); break;
-            case SEC_OID_SHA256: CC_SHA256_Update((CC_SHA256_CTX *)cmsdigcx->digobjs[i], data, (CC_LONG)len); break;
-            case SEC_OID_SHA384: CC_SHA384_Update((CC_SHA512_CTX *)cmsdigcx->digobjs[i], data, (CC_LONG)len); break;
-            case SEC_OID_SHA512: CC_SHA512_Update((CC_SHA512_CTX *)cmsdigcx->digobjs[i], data, (CC_LONG)len); break;
-            default:
-                break;
+                case SEC_OID_SHA1: CC_SHA1_Update((CC_SHA1_CTX *)cmsdigcx->digobjs[i], data, (CC_LONG)len); break;
+                case SEC_OID_MD5: CC_MD5_Update((CC_MD5_CTX *)cmsdigcx->digobjs[i], data, (CC_LONG)len); break;
+                case SEC_OID_SHA224: CC_SHA224_Update((CC_SHA256_CTX *)cmsdigcx->digobjs[i], data, (CC_LONG)len); break;
+                case SEC_OID_SHA256: CC_SHA256_Update((CC_SHA256_CTX *)cmsdigcx->digobjs[i], data, (CC_LONG)len); break;
+                case SEC_OID_SHA384: CC_SHA384_Update((CC_SHA512_CTX *)cmsdigcx->digobjs[i], data, (CC_LONG)len); break;
+                case SEC_OID_SHA512: CC_SHA512_Update((CC_SHA512_CTX *)cmsdigcx->digobjs[i], data, (CC_LONG)len); break;
+                default:
+                    break;
             }
             }
-#endif
         }
     }
 }
         }
     }
 }
@@ -232,13 +205,12 @@ SecCmsDigestContextCancel(SecCmsDigestContextRef cmsdigcx)
 {
     int i;
 
 {
     int i;
 
-    for (i = 0; i < cmsdigcx->digcnt; i++)
-       if (cmsdigcx->digobjs[i])
-#if USE_CDSA_CRYPTO
-           CSSM_DeleteContext(cmsdigcx->digobjs[i]);
-#else
+    for (i = 0; i < cmsdigcx->digcnt; i++) {
+        if (cmsdigcx->digobjs && cmsdigcx->digobjs[i]) {
             free(cmsdigcx->digobjs[i]);
             free(cmsdigcx->digobjs[i]);
-#endif
+            cmsdigcx->digobjs[i] = NULL;
+        }
+    }
 
     PORT_FreeArena(cmsdigcx->poolp, PR_TRUE);
 }
 
     PORT_FreeArena(cmsdigcx->poolp, PR_TRUE);
 }
@@ -254,17 +226,16 @@ SecCmsDigestContextDestroy(SecCmsDigestContextRef cmsdigcx)
 
 /*
  * SecCmsDigestContextFinishMultiple - finish the digests
 
 /*
  * SecCmsDigestContextFinishMultiple - finish the digests
+ * Note that on iOS, this call only frees the digest objects and requires a call to SecCmsDisgestContextDestroy
+ * or SecCmsDisgestContextCancel (because the digests are allocated out of the context's pool).
+ * The macOS version cancels and frees the digest context (because the digests are allocated from an input arena pool).
  */
 OSStatus
 SecCmsDigestContextFinishMultiple(SecCmsDigestContextRef cmsdigcx,
  */
 OSStatus
 SecCmsDigestContextFinishMultiple(SecCmsDigestContextRef cmsdigcx,
-                           SECAlgorithmID ***digestalgsp,
-                           SecAsn1Item * **digestsp)
+                                  SECAlgorithmID ***digestalgsp,
+                                  SecAsn1Item * **digestsp)
 {
 {
-#if USE_CDSA_CRYPTO
-    CSSM_CC_HANDLE digboj;
-#else
     void * digobj;
     void * digobj;
-#endif
     SecAsn1Item **digests, *digest;
     SECAlgorithmID **digestalgs;
     int i;
     SecAsn1Item **digests, *digest;
     SECAlgorithmID **digestalgs;
     int i;
@@ -277,17 +248,16 @@ SecCmsDigestContextFinishMultiple(SecCmsDigestContextRef cmsdigcx,
 #if 0
     /* no contents? do not update digests */
     if (digestsp == NULL || !cmsdigcx->saw_contents) {
 #if 0
     /* no contents? do not update digests */
     if (digestsp == NULL || !cmsdigcx->saw_contents) {
-       for (i = 0; i < cmsdigcx->digcnt; i++)
-           if (cmsdigcx->digobjs[i])
-#if USE_CDSA_CRYPTO
-               CSSM_DeleteContext(cmsdigcx->digobjs[i]);
-#else
+        for (i = 0; i < cmsdigcx->digcnt; i++) {
+            if (cmsdigcx->digobjs[i]) {
                 free(cmsdigcx->digobjs[i]);
                 free(cmsdigcx->digobjs[i]);
-#endif
-       rv = SECSuccess;
-       if (digestsp)
-           *digestsp = NULL;
-       goto cleanup;
+            }
+        }
+        rv = SECSuccess;
+        if (digestsp) {
+            *digestsp = NULL;
+        }
+        goto cleanup;
     }
 #endif
 
     }
 #endif
 
@@ -305,14 +275,13 @@ SecCmsDigestContextFinishMultiple(SecCmsDigestContextRef cmsdigcx,
     digests = (SecAsn1Item * *)PORT_ArenaZAlloc(cmsdigcx->poolp, (cmsdigcx->digcnt+1) * sizeof(SecAsn1Item *));
     digest = (SecAsn1Item *)PORT_ArenaZAlloc(cmsdigcx->poolp, cmsdigcx->digcnt * sizeof(SecAsn1Item));
     if (digestalgs == NULL || digests == NULL || digest == NULL) {
     digests = (SecAsn1Item * *)PORT_ArenaZAlloc(cmsdigcx->poolp, (cmsdigcx->digcnt+1) * sizeof(SecAsn1Item *));
     digest = (SecAsn1Item *)PORT_ArenaZAlloc(cmsdigcx->poolp, cmsdigcx->digcnt * sizeof(SecAsn1Item));
     if (digestalgs == NULL || digests == NULL || digest == NULL) {
-       goto loser;
+        goto loser;
     }
 
     for (i = 0; i < cmsdigcx->digcnt; i++, digest++) {
     }
 
     for (i = 0; i < cmsdigcx->digcnt; i++, digest++) {
-    
         SECOidTag hash_alg = SECOID_GetAlgorithmTag(cmsdigcx->digestalgs[i]);
         SECOidTag hash_alg = SECOID_GetAlgorithmTag(cmsdigcx->digestalgs[i]);
-       int diglength = 0;
-    
+        int diglength = 0;
+
         switch (hash_alg) {
             case SEC_OID_SHA1: diglength = CC_SHA1_DIGEST_LENGTH; break;
             case SEC_OID_MD5: diglength = CC_MD5_DIGEST_LENGTH; break;
         switch (hash_alg) {
             case SEC_OID_SHA1: diglength = CC_SHA1_DIGEST_LENGTH; break;
             case SEC_OID_MD5: diglength = CC_MD5_DIGEST_LENGTH; break;
@@ -323,17 +292,12 @@ SecCmsDigestContextFinishMultiple(SecCmsDigestContextRef cmsdigcx,
             default: goto loser;
         }
         
             default: goto loser;
         }
         
-       digobj = cmsdigcx->digobjs[i];
-       if (digobj)
-       {
-           digest->Data = (unsigned char*)PORT_ArenaAlloc(cmsdigcx->poolp, diglength);
-           if (digest->Data == NULL)
-               goto loser;
-           digest->Length = diglength;
-#if USE_CDSA_CRYPTO
-           CSSM_DigestDataFinal(digobj, digest);
-           CSSM_DeleteContext(digobj);
-#else
+        digobj = cmsdigcx->digobjs[i];
+        if (digobj) {
+            digest->Data = (unsigned char*)PORT_ArenaAlloc(cmsdigcx->poolp, diglength);
+            if (digest->Data == NULL)
+                goto loser;
+            digest->Length = diglength;
             switch (hash_alg) {
                 case SEC_OID_SHA1: CC_SHA1_Final(digest->Data, digobj); break;
                 case SEC_OID_MD5: CC_MD5_Final(digest->Data, digobj); break;
             switch (hash_alg) {
                 case SEC_OID_SHA1: CC_SHA1_Final(digest->Data, digobj); break;
                 case SEC_OID_MD5: CC_MD5_Final(digest->Data, digobj); break;
@@ -345,15 +309,12 @@ SecCmsDigestContextFinishMultiple(SecCmsDigestContextRef cmsdigcx,
             }
 
             free(digobj);
             }
 
             free(digobj);
-#endif
-           digestalgs[i] = cmsdigcx->digestalgs[i];
-           digests[i] = digest;
-       }
-       else
-       {
-           digest->Data = NULL;
-           digest->Length = 0;
-       }
+            digestalgs[i] = cmsdigcx->digestalgs[i];
+            digests[i] = digest;
+        } else {
+            digest->Data = NULL;
+            digest->Length = 0;
+        }
     }
     digestalgs[i] = NULL;
     digests[i] = NULL;
     }
     digestalgs[i] = NULL;
     digests[i] = NULL;
@@ -363,13 +324,14 @@ SecCmsDigestContextFinishMultiple(SecCmsDigestContextRef cmsdigcx,
     rv = SECSuccess;
 
 loser:
     rv = SECSuccess;
 
 loser:
-    if (rv == SECSuccess)
-       PORT_ArenaUnmark(cmsdigcx->poolp, mark);
-    else
-       PORT_ArenaRelease(cmsdigcx->poolp, mark);
+    if (rv == SECSuccess) {
+        PORT_ArenaUnmark(cmsdigcx->poolp, mark);
+    } else {
+        PORT_ArenaRelease(cmsdigcx->poolp, mark);
+    }
 
 
-/*cleanup:*/
-    /* Set things up so SecCmsDigestContextDestroy won't call CSSM_DeleteContext again. */ 
+    /*cleanup:*/
+    /* Set things up so SecCmsDigestContextDestroy won't call CSSM_DeleteContext again. */
     cmsdigcx->digcnt = 0;
 
     return rv;
     cmsdigcx->digcnt = 0;
 
     return rv;
@@ -381,15 +343,16 @@ loser:
  */
 OSStatus
 SecCmsDigestContextFinishSingle(SecCmsDigestContextRef cmsdigcx,
  */
 OSStatus
 SecCmsDigestContextFinishSingle(SecCmsDigestContextRef cmsdigcx,
-                           SecAsn1Item * digest)
+                                SecAsn1Item * digest)
 {
     OSStatus rv = SECFailure;
     SecAsn1Item * *dp;
     SECAlgorithmID **ap;
 
     /* get the digests into arena, then copy the first digest into poolp */
 {
     OSStatus rv = SECFailure;
     SecAsn1Item * *dp;
     SECAlgorithmID **ap;
 
     /* get the digests into arena, then copy the first digest into poolp */
-    if (SecCmsDigestContextFinishMultiple(cmsdigcx, &ap, &dp) != SECSuccess)
-       goto loser;
+    if (SecCmsDigestContextFinishMultiple(cmsdigcx, &ap, &dp) != SECSuccess) {
+        goto loser;
+    }
 
     /* Return the first element in the digest array. */
     if (digest) {
 
     /* Return the first element in the digest array. */
     if (digest) {
index 1c309f03f745b2b255472334e2f2457f9f8387b6..9d2597a59badd659698b9c94546cdf539f8a9cb5 100644 (file)
@@ -95,11 +95,8 @@ SecCmsAlgArrayGetIndexByAlgID(SECAlgorithmID **algorithmArray, SECAlgorithmID *a
 extern int
 SecCmsAlgArrayGetIndexByAlgTag(SECAlgorithmID **algorithmArray, SECOidTag algtag);
 
 extern int
 SecCmsAlgArrayGetIndexByAlgTag(SECAlgorithmID **algorithmArray, SECOidTag algtag);
 
-#if USE_CDSA_CRYPTO
-extern CSSM_CC_HANDLE
-#else
+
 extern void *
 extern void *
-#endif
 SecCmsUtilGetHashObjByAlgID(SECAlgorithmID *algid);
 
 /*
 SecCmsUtilGetHashObjByAlgID(SECAlgorithmID *algid);
 
 /*
index 9ed5d17332c27001d640945c8cc51b6e285450c2..e462cab5a97503d42df362d6bf7132198c6c3151 100644 (file)
@@ -227,58 +227,42 @@ SecCmsAlgArrayGetIndexByAlgTag(SECAlgorithmID **algorithmArray,
     return i;
 }
 
     return i;
 }
 
-#if USE_CDSA_CRYPTO
-CSSM_CC_HANDLE
-#else
 void *
 void *
-#endif
 SecCmsUtilGetHashObjByAlgID(SECAlgorithmID *algid)
 {
     SECOidData *oidData = SECOID_FindOID(&(algid->algorithm));
     if (oidData)
     {
 SecCmsUtilGetHashObjByAlgID(SECAlgorithmID *algid)
 {
     SECOidData *oidData = SECOID_FindOID(&(algid->algorithm));
     if (oidData)
     {
-#if USE_CDSA_CRYPTO
-       CSSM_ALGORITHMS alg = oidData->cssmAlgorithm;
-       if (alg)
-       {
-           CSSM_CC_HANDLE digobj;
-           CSSM_CSP_HANDLE cspHandle = SecCspHandleForAlgorithm(alg);
-
-           if (!CSSM_CSP_CreateDigestContext(cspHandle, alg, &digobj))
-               return digobj;
-       }
-#else
         void *digobj = NULL;
         switch (oidData->offset) {
         void *digobj = NULL;
         switch (oidData->offset) {
-        case SEC_OID_SHA1:
-            digobj = calloc(1, sizeof(CC_SHA1_CTX));
-            CC_SHA1_Init(digobj);
-            break;
-        case SEC_OID_MD5:
-            digobj = calloc(1, sizeof(CC_MD5_CTX));
-            CC_MD5_Init(digobj);
-            break;
-        case SEC_OID_SHA224:
-            digobj = calloc(1, sizeof(CC_SHA256_CTX));
-            CC_SHA224_Init(digobj);
-            break;
-        case SEC_OID_SHA256:
-            digobj = calloc(1, sizeof(CC_SHA256_CTX));
-            CC_SHA256_Init(digobj);
-            break;
-        case SEC_OID_SHA384:
-            digobj = calloc(1, sizeof(CC_SHA512_CTX));
-            CC_SHA384_Init(digobj);
-            break;
-        case SEC_OID_SHA512:
-            digobj = calloc(1, sizeof(CC_SHA512_CTX));
-            CC_SHA512_Init(digobj);
-            break;
-        default:
-            break;
+            case SEC_OID_SHA1:
+                digobj = calloc(1, sizeof(CC_SHA1_CTX));
+                CC_SHA1_Init(digobj);
+                break;
+            case SEC_OID_MD5:
+                digobj = calloc(1, sizeof(CC_MD5_CTX));
+                CC_MD5_Init(digobj);
+                break;
+            case SEC_OID_SHA224:
+                digobj = calloc(1, sizeof(CC_SHA256_CTX));
+                CC_SHA224_Init(digobj);
+                break;
+            case SEC_OID_SHA256:
+                digobj = calloc(1, sizeof(CC_SHA256_CTX));
+                CC_SHA256_Init(digobj);
+                break;
+            case SEC_OID_SHA384:
+                digobj = calloc(1, sizeof(CC_SHA512_CTX));
+                CC_SHA384_Init(digobj);
+                break;
+            case SEC_OID_SHA512:
+                digobj = calloc(1, sizeof(CC_SHA512_CTX));
+                CC_SHA512_Init(digobj);
+                break;
+            default:
+                break;
         }
         return digobj;
         }
         return digobj;
-#endif
     }
 
     return 0;
     }
 
     return 0;
index b209bc6f383611643897a284997594a3dd28ec62..25395fecaa4daaa4a7da637facd53bcde2a7b30e 100644 (file)
@@ -690,7 +690,7 @@ sec_protocol_metadata_get_tls_negotiated_group(sec_protocol_metadata_t metadata)
  *
  *      Note: this SPI is meant to be called by libnetcore. It should not be called in any other circumstances.
  *
  *
  *      Note: this SPI is meant to be called by libnetcore. It should not be called in any other circumstances.
  *
- * @param options
+ * @param metadata
  *      A `sec_protocol_metadata_t` instance.
  *
  * @return The identifier for a secure connection experiment, or NULL if none was specified.
  *      A `sec_protocol_metadata_t` instance.
  *
  * @return The identifier for a secure connection experiment, or NULL if none was specified.
diff --git a/rio.yml b/rio.yml
deleted file mode 100644 (file)
index e69de29..0000000
index c59ca4d27d92f20e111fd8d904c02a8ac0462161..26672e55bcd076d468ba62bb60a1a5298db8bdc8 100644 (file)
@@ -49,7 +49,8 @@
        (global-name "com.apple.ocspd")
        (global-name "com.apple.PowerManagement.control")
        (global-name "com.apple.security.syspolicy")
        (global-name "com.apple.ocspd")
        (global-name "com.apple.PowerManagement.control")
        (global-name "com.apple.security.syspolicy")
-       (global-name "com.apple.security.agent"))
+       (global-name "com.apple.security.agent")
+       (global-name "com.apple.security.agent.login"))
 
 (allow ipc-posix-shm 
     (ipc-posix-name "com.apple.AppleDatabaseChanged")
 
 (allow ipc-posix-shm 
     (ipc-posix-name "com.apple.AppleDatabaseChanged")
index ad6cc5e7c63f48171a4c1e01b8610aabe7273fd0..43ebb292be4a918ff0043342ff76e1ff4d2d2556 100644 (file)
                189D462D166AC95C001D8533 /* Project object */ = {
                        isa = PBXProject;
                        attributes = {
                189D462D166AC95C001D8533 /* Project object */ = {
                        isa = PBXProject;
                        attributes = {
-                               LastUpgradeCheck = 1000;
+                               LastUpgradeCheck = 1120;
                                ORGANIZATIONNAME = Apple;
                        };
                        buildConfigurationList = 189D4630166AC95C001D8533 /* Build configuration list for PBXProject "securityd_service" */;
                                ORGANIZATIONNAME = Apple;
                        };
                        buildConfigurationList = 189D4630166AC95C001D8533 /* Build configuration list for PBXProject "securityd_service" */;
                        developmentRegion = English;
                        hasScannedForEncodings = 0;
                        knownRegions = (
                        developmentRegion = English;
                        hasScannedForEncodings = 0;
                        knownRegions = (
+                               English,
                                en,
                        );
                        mainGroup = 189D462C166AC95C001D8533;
                                en,
                        );
                        mainGroup = 189D462C166AC95C001D8533;
                        buildSettings = {
                                ALWAYS_SEARCH_USER_PATHS = NO;
                                ASSETCATALOG_COMPRESSION = lossless;
                        buildSettings = {
                                ALWAYS_SEARCH_USER_PATHS = NO;
                                ASSETCATALOG_COMPRESSION = lossless;
+                               CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
                                CLANG_ENABLE_OBJC_ARC = YES;
                                CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
                                CLANG_WARN_BOOL_CONVERSION = YES;
                                CLANG_ENABLE_OBJC_ARC = YES;
                                CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
                                CLANG_WARN_BOOL_CONVERSION = YES;
                        buildSettings = {
                                ALWAYS_SEARCH_USER_PATHS = NO;
                                ASSETCATALOG_COMPRESSION = "respect-asset-catalog";
                        buildSettings = {
                                ALWAYS_SEARCH_USER_PATHS = NO;
                                ASSETCATALOG_COMPRESSION = "respect-asset-catalog";
+                               CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
                                CLANG_ENABLE_OBJC_ARC = YES;
                                CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
                                CLANG_WARN_BOOL_CONVERSION = YES;
                                CLANG_ENABLE_OBJC_ARC = YES;
                                CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
                                CLANG_WARN_BOOL_CONVERSION = YES;
index fc35bbc1a28c8000940c9f5e4849cc4a37f4c860..943df37195fb2123d1727d2cc085c59939f2a43a 100644 (file)
@@ -40,7 +40,9 @@ KeychainKey::KeychainKey(Database &db, const KeyBlob *blob)
        : LocalKey(db, n2h(blob->header.attributes()))
 {
     // perform basic validation on the incoming blob
        : LocalKey(db, n2h(blob->header.attributes()))
 {
     // perform basic validation on the incoming blob
-       assert(blob);
+       if (blob == NULL) {
+               CssmError::throwMe(CSSMERR_APPLEDL_INVALID_KEY_BLOB);
+       }
     blob->validate(CSSMERR_APPLEDL_INVALID_KEY_BLOB);
     if (blob->startCryptoBlob > blob->totalLength) {
         CssmError::throwMe(CSSMERR_APPLEDL_INVALID_KEY_BLOB);
     blob->validate(CSSMERR_APPLEDL_INVALID_KEY_BLOB);
     if (blob->startCryptoBlob > blob->totalLength) {
         CssmError::throwMe(CSSMERR_APPLEDL_INVALID_KEY_BLOB);
index ec8b2bc0c4df45b548394be540953c5ab3ee2d8a..f569603b32cf419c85d547ab7b9269399e5f9f80 100644 (file)
@@ -8,5 +8,7 @@
        <array>
                <string>kTCCServiceSystemPolicyAllFiles</string>
        </array>
        <array>
                <string>kTCCServiceSystemPolicyAllFiles</string>
        </array>
+       <key>com.apple.private.security.storage.SystemKeychain</key>
+       <true/>
 </dict>
 </plist>
 </dict>
 </plist>
index fa4ce7533efb2e07f8ea1230b82f64cde99376c2..c73a8267a202be29c169c3475512aa77f1b56f5d 100644 (file)
@@ -23,6 +23,7 @@
 
 #import <XCTest/XCTest.h>
 #import <Security/SFAnalytics.h>
 
 #import <XCTest/XCTest.h>
 #import <Security/SFAnalytics.h>
+#import "SFAnalytics+Internal.h"
 #import "SFAnalyticsDefines.h"
 #import "SFAnalyticsSQLiteStore.h"
 #import "NSDate+SFAnalytics.h"
 #import "SFAnalyticsDefines.h"
 #import "SFAnalyticsSQLiteStore.h"
 #import "NSDate+SFAnalytics.h"
@@ -68,6 +69,7 @@ static NSString* _path;
 static NSInteger _testnum;
 static NSString* build = NULL;
 static NSString* product = NULL;
 static NSInteger _testnum;
 static NSString* build = NULL;
 static NSString* product = NULL;
+static NSString* modelID = nil;
 
 // MARK: Test helper methods
 
 
 // MARK: Test helper methods
 
@@ -153,6 +155,7 @@ static NSString* product = NULL;
     XCTAssertTrue([rowdata[SFAnalyticsEventClassKey] isKindOfClass:[NSNumber class]] && [rowdata[SFAnalyticsEventClassKey] intValue] == class, @"eventClass is %ld", (long)class);
     XCTAssertTrue([rowdata[@"build"] isEqualToString:build], @"event row includes build");
     XCTAssertTrue([rowdata[@"product"] isEqualToString:product], @"event row includes product");
     XCTAssertTrue([rowdata[SFAnalyticsEventClassKey] isKindOfClass:[NSNumber class]] && [rowdata[SFAnalyticsEventClassKey] intValue] == class, @"eventClass is %ld", (long)class);
     XCTAssertTrue([rowdata[@"build"] isEqualToString:build], @"event row includes build");
     XCTAssertTrue([rowdata[@"product"] isEqualToString:product], @"event row includes product");
+    XCTAssertTrue([rowdata[@"modelid"] isEqualToString:modelID], @"event row includes modelid");
     XCTAssertTrue(rowdata[@"internal"], @"event row includes internal");
 }
 
     XCTAssertTrue(rowdata[@"internal"], @"event row includes internal");
 }
 
@@ -222,6 +225,8 @@ static NSString* product = NULL;
         NSLog(@"could not get build version/product, tests should fail");
     }
 
         NSLog(@"could not get build version/product, tests should fail");
     }
 
+    modelID = [SFAnalytics hwModelID];
+
     [TestResourceUsage monitorTestResourceUsage];
 }
 
     [TestResourceUsage monitorTestResourceUsage];
 }
 
index 0b8695009c49890eb8e5253f2d68653e750c1e77..20c99ffbe4712887e23472d5ea95fe309b6dee85 100644 (file)
@@ -294,7 +294,7 @@ static NSInteger _reporterWrites;
 {
     dispatch_semaphore_t sema = dispatch_semaphore_create(0);
     __block NSDictionary* data;
 {
     dispatch_semaphore_t sema = dispatch_semaphore_create(0);
     __block NSDictionary* data;
-    [_supd getLoggingJSON:YES topic:topic reply:^(NSData *json, NSError *error) {
+    [_supd createLoggingJSON:YES topic:topic reply:^(NSData *json, NSError *error) {
         XCTAssertNil(error);
         XCTAssertNotNil(json);
         if (!error) {
         XCTAssertNil(error);
         XCTAssertNotNil(json);
         if (!error) {
@@ -448,7 +448,8 @@ static NSInteger _reporterWrites;
     [_sosAnalytics logHardFailureForEventNamed:@"unittestevent" withAttributes:utAttrs];
     [_sosAnalytics logSoftFailureForEventNamed:@"unittestevent" withAttributes:utAttrs];
 
     [_sosAnalytics logHardFailureForEventNamed:@"unittestevent" withAttributes:utAttrs];
     [_sosAnalytics logSoftFailureForEventNamed:@"unittestevent" withAttributes:utAttrs];
 
-    NSDictionary* data = [self getJSONDataFromSupd];
+    NSDictionary *data =  [self getJSONDataFromSupd];
+
     [self inspectDataBlobStructure:data];
 
     // TODO: inspect health summaries
     [self inspectDataBlobStructure:data];
 
     // TODO: inspect health summaries
@@ -763,6 +764,106 @@ static NSInteger _reporterWrites;
     XCTAssertEqual(foundErrorEvents, 1);
 }
 
     XCTAssertEqual(foundErrorEvents, 1);
 }
 
+- (void)testUploadSizeLimits
+{
+    SFAnalyticsTopic *trustTopic = [self TrustTopic];
+    XCTAssertEqual(1000000, trustTopic.uploadSizeLimit);
+
+    SFAnalyticsTopic *keySyncTopic = [self keySyncTopic];
+    XCTAssertEqual(1000000, keySyncTopic.uploadSizeLimit);
+}
+
+- (NSArray<NSDictionary *> *)createRandomEventList:(size_t)count
+{
+    NSMutableArray<NSDictionary *> *eventSet = [[NSMutableArray<NSDictionary *> alloc] init];
+
+    const size_t dataSize = 100;
+    uint8_t backingBuffer[dataSize] = {};
+    for (size_t i = 0; i < count; i++) {
+        NSData *data = [[NSData alloc] initWithBytes:backingBuffer length:dataSize];
+        NSDictionary *entry = @{@"key" : [data base64EncodedStringWithOptions:0]};
+        [eventSet addObject:entry];
+    }
+
+    return eventSet;
+}
+
+- (void)testCreateLoggingJSON
+{
+    NSArray<NSDictionary *> *summaries = [self createRandomEventList:5];
+    NSArray<NSDictionary *> *failures = [self createRandomEventList:100];
+    NSMutableArray<NSDictionary *> *visitedEvents = [[NSMutableArray<NSDictionary *> alloc] init];
+
+    SFAnalyticsTopic *topic = [self TrustTopic];
+    const size_t sizeLimit = 10000; // total size of the encoded data
+    topic.uploadSizeLimit = sizeLimit;
+
+    NSError *error = nil;
+    NSArray<NSDictionary *> *eventSet = [topic createChunkedLoggingJSON:summaries failures:failures error:&error];
+    XCTAssertNil(error);
+
+    for (NSDictionary *event in eventSet) {
+        XCTAssertNotNil([event objectForKey:@"events"]);
+        XCTAssertNotNil([event objectForKey:SFAnalyticsPostTime]);
+        NSArray *events = [event objectForKey:@"events"];
+        for (NSDictionary *summary in summaries) {
+            BOOL foundSummary = NO;
+            for (NSDictionary *innerEvent in events) {
+                if ([summary isEqualToDictionary:innerEvent]) {
+                    foundSummary = YES;
+                    break;
+                }
+            }
+            XCTAssertTrue(foundSummary);
+        }
+
+        // Record the events we've seen so far
+        for (NSDictionary *innerEvent in events) {
+            [visitedEvents addObject:innerEvent];
+        }
+    }
+
+    // Check that each summary and failure is in the visitedEvents
+    for (NSDictionary *summary in summaries) {
+        BOOL foundSummary = NO;
+        for (NSDictionary *innerEvent in visitedEvents) {
+            if ([summary isEqualToDictionary:innerEvent]) {
+                foundSummary = YES;
+                break;
+            }
+        }
+        XCTAssertTrue(foundSummary);
+    }
+    for (NSDictionary *failure in failures) {
+        BOOL foundFailure = NO;
+        for (NSDictionary *innerEvent in visitedEvents) {
+            if ([failure isEqualToDictionary:innerEvent]) {
+                foundFailure = YES;
+                break;
+            }
+        }
+        XCTAssertTrue(foundFailure);
+    }
+}
+
+- (void)testEventSetChunking
+{
+    NSArray<NSDictionary *> *eventSet = [self createRandomEventList:100];
+    SFAnalyticsTopic *topic = [self TrustTopic];
+
+    const size_t sizeLimit = 10000; // total size of the encoded data
+    size_t encodedEventSize = [topic serializedEventSize:eventSet[0] error:nil];
+    topic.uploadSizeLimit = sizeLimit; // fix the upload limit
+
+    // Chunk up the set, assuming that each chunk already has one event in it.
+    // In practice, this is the health summary.
+    NSError *error = nil;
+    NSArray<NSArray *> *chunkedEvents = [topic chunkFailureSet:(sizeLimit - encodedEventSize) events:eventSet error:nil];
+    XCTAssertNil(error);
+
+    // There should be two resulting chunks, since the set of chunks overflows.
+    XCTAssertEqual(2, [chunkedEvents count]);
+}
 
 // TODO
 - (void)testGetSysdiagnoseDump
 
 // TODO
 - (void)testGetSysdiagnoseDump
index f9fdce960d58c610231a1e9a863631d770256c99..9e4bf941a0a092dcb530b39263edbe5f64b44a6a 100644 (file)
@@ -35,6 +35,7 @@
 @property NSString* splunkTopicName;
 @property NSURL* splunkBagURL;
 @property NSString *internalTopicName;
 @property NSString* splunkTopicName;
 @property NSURL* splunkBagURL;
 @property NSString *internalTopicName;
+@property NSUInteger uploadSizeLimit;
 
 @property NSArray<SFAnalyticsClient*>* topicClients;
 
 
 @property NSArray<SFAnalyticsClient*>* topicClients;
 
@@ -42,6 +43,9 @@
 // Things below are for unit testing
 - (instancetype)initWithDictionary:(NSDictionary *)dictionary name:(NSString *)topicName samplingRates:(NSDictionary *)rates;
 - (BOOL)haveEligibleClients;
 // Things below are for unit testing
 - (instancetype)initWithDictionary:(NSDictionary *)dictionary name:(NSString *)topicName samplingRates:(NSDictionary *)rates;
 - (BOOL)haveEligibleClients;
+- (NSArray<NSDictionary *> *)createChunkedLoggingJSON:(NSArray<NSDictionary *> *)healthSummaries failures:(NSArray<NSDictionary *> *)failures error:(NSError **)error;
+- (NSArray<NSArray *> *)chunkFailureSet:(size_t)sizeCapacity events:(NSArray<NSDictionary *> *)events error:(NSError **)error;
+- (size_t)serializedEventSize:(NSObject *)event error:(NSError**)error;
 + (NSString*)databasePathForCKKS;
 + (NSString*)databasePathForSOS;
 + (NSString*)databasePathForPCS;
 + (NSString*)databasePathForCKKS;
 + (NSString*)databasePathForSOS;
 + (NSString*)databasePathForPCS;
index 194e1591bb2664b7af18590b52d416fed2b89c9b..9fe33d8773009a4e8a7852819cac6692877eea59 100644 (file)
@@ -61,7 +61,6 @@
 
 
 NSString* const SFAnalyticsSplunkTopic = @"topic";
 
 
 NSString* const SFAnalyticsSplunkTopic = @"topic";
-NSString* const SFAnalyticsSplunkPostTime = @"postTime";
 NSString* const SFAnalyticsClientId = @"clientId";
 NSString* const SFAnalyticsInternal = @"internal";
 
 NSString* const SFAnalyticsClientId = @"clientId";
 NSString* const SFAnalyticsInternal = @"internal";
 
@@ -69,6 +68,8 @@ NSString* const SFAnalyticsMetricsBase = @"metricsBase";
 NSString* const SFAnalyticsDeviceID = @"ckdeviceID";
 NSString* const SFAnalyticsAltDSID = @"altDSID";
 
 NSString* const SFAnalyticsDeviceID = @"ckdeviceID";
 NSString* const SFAnalyticsAltDSID = @"altDSID";
 
+NSString* const SFAnalyticsEventCorrelationID = @"eventLinkID";
+
 NSString* const SFAnalyticsSecondsCustomerKey = @"SecondsBetweenUploadsCustomer";
 NSString* const SFAnalyticsSecondsInternalKey = @"SecondsBetweenUploadsInternal";
 NSString* const SFAnalyticsSecondsSeedKey = @"SecondsBetweenUploadsSeed";
 NSString* const SFAnalyticsSecondsCustomerKey = @"SecondsBetweenUploadsCustomer";
 NSString* const SFAnalyticsSecondsInternalKey = @"SecondsBetweenUploadsInternal";
 NSString* const SFAnalyticsSecondsSeedKey = @"SecondsBetweenUploadsSeed";
@@ -369,6 +370,8 @@ _isiCloudAnalyticsEnabled()
         __splunkUploadURL = [NSURL URLWithString:dictionary[@"splunk_uploadURL"]];
         _splunkBagURL = [NSURL URLWithString:dictionary[@"splunk_bagURL"]];
         _allowInsecureSplunkCert = [[dictionary valueForKey:@"splunk_allowInsecureCertificate"] boolValue];
         __splunkUploadURL = [NSURL URLWithString:dictionary[@"splunk_uploadURL"]];
         _splunkBagURL = [NSURL URLWithString:dictionary[@"splunk_bagURL"]];
         _allowInsecureSplunkCert = [[dictionary valueForKey:@"splunk_allowInsecureCertificate"] boolValue];
+        _uploadSizeLimit = [[dictionary valueForKey:@"uploadSizeLimit"] unsignedIntegerValue];
+
         NSString* splunkEndpoint = dictionary[@"splunk_endpointDomain"];
         if (dictionary[@"disableClientId"]) {
             _disableClientId = YES;
         NSString* splunkEndpoint = dictionary[@"splunk_endpointDomain"];
         if (dictionary[@"disableClientId"]) {
             _disableClientId = YES;
@@ -390,6 +393,11 @@ _isiCloudAnalyticsEnabled()
             _splunkBagURL = userDefaultsSplunkBagURL;
         }
 
             _splunkBagURL = userDefaultsSplunkBagURL;
         }
 
+        NSInteger userDefaultsUploadSizeLimit = [defaults integerForKey:@"uploadSizeLimit"];
+        if (userDefaultsUploadSizeLimit > 0) {
+            _uploadSizeLimit = userDefaultsUploadSizeLimit;
+        }
+
         BOOL userDefaultsAllowInsecureSplunkCert = [defaults boolForKey:@"splunk_allowInsecureCertificate"];
         _allowInsecureSplunkCert |= userDefaultsAllowInsecureSplunkCert;
 
         BOOL userDefaultsAllowInsecureSplunkCert = [defaults boolForKey:@"splunk_allowInsecureCertificate"];
         _allowInsecureSplunkCert |= userDefaultsAllowInsecureSplunkCert;
 
@@ -536,7 +544,8 @@ _isiCloudAnalyticsEnabled()
     }];
 }
 
     }];
 }
 
-- (BOOL)prepareEventForUpload:(NSMutableDictionary*)event {
+- (BOOL)prepareEventForUpload:(NSMutableDictionary*)event
+                   linkedUUID:(NSUUID *)linkedUUID {
     if ([self eventIsBlacklisted:event]) {
         return NO;
     }
     if ([self eventIsBlacklisted:event]) {
         return NO;
     }
@@ -547,10 +556,13 @@ _isiCloudAnalyticsEnabled()
         event[SFAnalyticsClientId] = @(0);
     }
     event[SFAnalyticsSplunkTopic] = self->_splunkTopicName ?: [NSNull null];
         event[SFAnalyticsClientId] = @(0);
     }
     event[SFAnalyticsSplunkTopic] = self->_splunkTopicName ?: [NSNull null];
+    if (linkedUUID) {
+        event[SFAnalyticsEventCorrelationID] = [linkedUUID UUIDString];
+    }
     return YES;
 }
 
     return YES;
 }
 
-- (void)addFailures:(NSMutableArray<NSArray*>*)failures toUploadRecords:(NSMutableArray*)records threshold:(NSUInteger)threshold
+- (void)addFailures:(NSMutableArray<NSArray*>*)failures toUploadRecords:(NSMutableArray*)records threshold:(NSUInteger)threshold linkedUUID:(NSUUID *)linkedUUID
 {
     // The first 0 through 'threshold' items are getting uploaded in any case (which might be 0 for lower priority data)
 
 {
     // The first 0 through 'threshold' items are getting uploaded in any case (which might be 0 for lower priority data)
 
@@ -561,7 +573,7 @@ _isiCloudAnalyticsEnabled()
                 *stop = YES;
                 return;
             }
                 *stop = YES;
                 return;
             }
-            if ([self prepareEventForUpload:event]) {
+            if ([self prepareEventForUpload:event linkedUUID:linkedUUID]) {
                 if ([NSJSONSerialization isValidJSONObject:event]) {
                     [records addObject:event];
                 } else {
                 if ([NSJSONSerialization isValidJSONObject:event]) {
                     [records addObject:event];
                 } else {
@@ -594,7 +606,7 @@ _isiCloudAnalyticsEnabled()
                 NSRange range = NSMakeRange(threshold, (client.count - threshold) * scale);
                 NSArray* sub = [client subarrayWithRange:range];
                 [sub enumerateObjectsUsingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
                 NSRange range = NSMakeRange(threshold, (client.count - threshold) * scale);
                 NSArray* sub = [client subarrayWithRange:range];
                 [sub enumerateObjectsUsingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
-                    if ([self prepareEventForUpload:obj]) {
+                    if ([self prepareEventForUpload:obj linkedUUID:linkedUUID]) {
                         [records addObject:obj];
                     }
                 }];
                         [records addObject:obj];
                     }
                 }];
@@ -651,7 +663,7 @@ _isiCloudAnalyticsEnabled()
     return statistics;
 }
 
     return statistics;
 }
 
-- (NSMutableDictionary*)healthSummaryWithName:(NSString*)name store:(SFAnalyticsSQLiteStore*)store
+- (NSMutableDictionary*)healthSummaryWithName:(NSString*)name store:(SFAnalyticsSQLiteStore*)store uuid:(NSUUID *)uuid
 {
     __block NSMutableDictionary* summary = [NSMutableDictionary new];
 
 {
     __block NSMutableDictionary* summary = [NSMutableDictionary new];
 
@@ -703,7 +715,7 @@ _isiCloudAnalyticsEnabled()
     }];
 
     // Should always return yes because we already checked for event blacklisting specifically (unless summary itself is blacklisted)
     }];
 
     // Should always return yes because we already checked for event blacklisting specifically (unless summary itself is blacklisted)
-    if (![self prepareEventForUpload:summary]) {
+    if (![self prepareEventForUpload:summary linkedUUID:uuid]) {
         secwarning("supd: health summary for %@ blacklisted", name);
         return nil;
     }
         secwarning("supd: health summary for %@ blacklisted", name);
         return nil;
     }
@@ -731,19 +743,147 @@ _isiCloudAnalyticsEnabled()
     }
 }
 
     }
 }
 
-- (NSData*)getLoggingJSON:(bool)pretty
-                forUpload:(BOOL)upload
-     participatingClients:(NSMutableArray<SFAnalyticsClient*>**)clients
-                    force:(BOOL)force                                       // supdctl uploads ignore privacy settings and recency
-                    error:(NSError**)error
+- (size_t)serializedEventSize:(NSObject *)event
+                        error:(NSError**)error
+{
+    if (![NSJSONSerialization isValidJSONObject:event]) {
+        secnotice("serializedEventSize", "invalid JSON object");
+        return 0;
+    }
+
+    NSData *json = [NSJSONSerialization dataWithJSONObject:event
+                                                   options:0
+                                                     error:error];
+    if (json) {
+        return [json length];
+    } else {
+        secnotice("serializedEventSize", "failed to serialize event");
+        return 0;
+    }
+}
+
+- (NSArray<NSArray *> *)chunkFailureSet:(size_t)sizeCapacity
+                                 events:(NSArray<NSDictionary *> *)events
+                                  error:(NSError **)error
+{
+    const size_t postBodyLimit = 1000; // 1000 events in a single upload
+    size_t currentSize = 0;
+    size_t currentEventCount = 0;
+
+    NSMutableArray<NSArray<NSDictionary *> *> *eventChunks = [[NSMutableArray<NSArray<NSDictionary *> *> alloc] init];
+    NSMutableArray<NSDictionary *> *currentEventChunk = [[NSMutableArray<NSDictionary *> alloc] init];
+    for (NSDictionary *event in events) {
+        NSError *localError = nil;
+        size_t eventSize = [self serializedEventSize:event error:&localError];
+        if (localError != nil) {
+            if (error) {
+                *error = localError;
+            }
+            secemergency("Unable to serialize event JSON: %@", [localError localizedDescription]);
+            return nil;
+        }
+
+        BOOL countLessThanLimit = currentEventCount < postBodyLimit;
+        BOOL sizeLessThanCapacity = (currentSize + eventSize) <= sizeCapacity;
+        if (!countLessThanLimit || !sizeLessThanCapacity) {
+            [eventChunks addObject:currentEventChunk];
+            currentEventChunk = [[NSMutableArray<NSDictionary *> alloc] init];
+            currentEventCount = 0;
+            currentSize = 0;
+        }
+
+        [currentEventChunk addObject:event];
+        currentEventCount++;
+        currentSize += eventSize;
+    }
+
+    if ([currentEventChunk count] > 0) {
+        [eventChunks addObject:currentEventChunk];
+    }
+
+    return eventChunks;
+}
+
+- (NSDictionary *)createEventDictionary:(NSArray *)healthSummaries
+                               failures:(NSArray<NSDictionary *> *)failures
+                                  error:(NSError **)error
+{
+    NSMutableArray *events = [[NSMutableArray alloc] init];
+    [events addObjectsFromArray:healthSummaries];
+    if (failures) {
+        [events addObjectsFromArray:failures];
+    }
+
+    NSDictionary *eventDictionary = @{
+        SFAnalyticsPostTime : @([[NSDate date] timeIntervalSince1970] * 1000),
+        @"events" : events,
+    };
+
+    if (![NSJSONSerialization isValidJSONObject:eventDictionary]) {
+        secemergency("json: final dictionary invalid JSON.");
+        if (error) {
+            *error = [NSError errorWithDomain:SupdErrorDomain code:SupdInvalidJSONError
+                                     userInfo:@{NSLocalizedDescriptionKey : [NSString localizedStringWithFormat:@"Final dictionary for upload is invalid JSON: %@", eventDictionary]}];
+        }
+        return nil;
+    }
+
+    return eventDictionary;
+}
+
+- (NSArray<NSDictionary *> *)createChunkedLoggingJSON:(NSArray<NSDictionary *> *)healthSummaries
+                                             failures:(NSArray<NSDictionary *> *)failures
+                                                error:(NSError **)error
 {
 {
-    NSMutableArray<SFAnalyticsClient*>* localClients = [NSMutableArray new];
-    __block NSMutableArray* uploadRecords = [NSMutableArray arrayWithCapacity:_maxEventsToReport];
-    __block NSError *localError;
-    __block NSMutableArray<NSArray*>* hardFailures = [NSMutableArray new];
-    __block NSMutableArray<NSArray*>* softFailures = [NSMutableArray new];
-    NSString* ckdeviceID = nil;
-    NSString* accountID = nil;
+    NSError *localError = nil;
+    size_t baseSize = [self serializedEventSize:healthSummaries error:&localError];
+    if (localError != nil) {
+        secemergency("Unable to serialize health summary JSON");
+        if (error) {
+            *error = localError;
+        }
+        return nil;
+    }
+
+    NSArray<NSArray *> *chunkedEvents = [self chunkFailureSet:(self.uploadSizeLimit - baseSize) events:failures error:&localError];
+
+    NSMutableArray<NSDictionary *> *jsonResults = [[NSMutableArray<NSDictionary *> alloc] init];
+    for (NSArray<NSDictionary *> *failureSet in chunkedEvents) {
+        NSDictionary *eventDictionary = [self createEventDictionary:healthSummaries failures:failureSet error:error];
+        if (eventDictionary) {
+            [jsonResults addObject:eventDictionary];
+        } else {
+            return nil;
+        }
+    }
+
+    if ([jsonResults count] == 0) {
+        NSDictionary *eventDictionary = [self createEventDictionary:healthSummaries failures:nil error:error];
+        if (eventDictionary) {
+            [jsonResults addObject:eventDictionary];
+        } else {
+            return nil;
+        }
+    }
+
+    return jsonResults;
+}
+
+- (BOOL)copyEvents:(NSMutableArray<NSDictionary *> **)healthSummaries
+          failures:(NSMutableArray<NSDictionary *> **)failures
+         forUpload:(BOOL)upload
+participatingClients:(NSMutableArray<SFAnalyticsClient*>**)clients
+             force:(BOOL)force
+        linkedUUID:(NSUUID *)linkedUUID
+             error:(NSError**)error
+{
+    NSMutableArray<SFAnalyticsClient*> *localClients = [[NSMutableArray alloc] init];
+    NSMutableArray<NSDictionary *> *localHealthSummaries = [[NSMutableArray<NSDictionary *> alloc] init];
+    NSMutableArray<NSDictionary *> *localFailures = [[NSMutableArray<NSDictionary *> alloc] init];
+    NSMutableArray<NSArray*> *hardFailures = [[NSMutableArray alloc] init];
+    NSMutableArray<NSArray*> *softFailures = [[NSMutableArray alloc] init];
+    NSString *ckdeviceID = nil;
+    NSString *accountID = nil;
 
     if (os_variant_has_internal_diagnostics("com.apple.security") && [_internalTopicName isEqualToString:SFAnalyticsTopicKeySync]) {
         ckdeviceID = [self askSecurityForCKDeviceID];
 
     if (os_variant_has_internal_diagnostics("com.apple.security") && [_internalTopicName isEqualToString:SFAnalyticsTopicKeySync]) {
         ckdeviceID = [self askSecurityForCKDeviceID];
@@ -751,12 +891,12 @@ _isiCloudAnalyticsEnabled()
     }
     for (SFAnalyticsClient* client in self->_topicClients) {
         if (!force && [client requireDeviceAnalytics] && !_isDeviceAnalyticsEnabled()) {
     }
     for (SFAnalyticsClient* client in self->_topicClients) {
         if (!force && [client requireDeviceAnalytics] && !_isDeviceAnalyticsEnabled()) {
-            // Client required device analytics, yet the user did not opt in. 
+            // Client required device analytics, yet the user did not opt in.
             secnotice("getLoggingJSON", "Client '%@' requires device analytics yet user did not opt in.", [client name]);
             continue;
             secnotice("getLoggingJSON", "Client '%@' requires device analytics yet user did not opt in.", [client name]);
             continue;
-        } 
+        }
         if (!force && [client requireiCloudAnalytics] && !_isiCloudAnalyticsEnabled()) {
         if (!force && [client requireiCloudAnalytics] && !_isiCloudAnalyticsEnabled()) {
-            // Client required iCloud analytics, yet the user did not opt in. 
+            // Client required iCloud analytics, yet the user did not opt in.
             secnotice("getLoggingJSON", "Client '%@' requires iCloud analytics yet user did not opt in.", [client name]);
             continue;
         }
             secnotice("getLoggingJSON", "Client '%@' requires iCloud analytics yet user did not opt in.", [client name]);
             continue;
         }
@@ -779,7 +919,7 @@ _isiCloudAnalyticsEnabled()
             [localClients addObject:client];
         }
 
             [localClients addObject:client];
         }
 
-        NSMutableDictionary* healthSummary = [self healthSummaryWithName:client.name store:store];
+        NSMutableDictionary* healthSummary = [self healthSummaryWithName:client.name store:store uuid:linkedUUID];
         if (healthSummary) {
             if (ckdeviceID) {
                 healthSummary[SFAnalyticsDeviceID] = ckdeviceID;
         if (healthSummary) {
             if (ckdeviceID) {
                 healthSummary[SFAnalyticsDeviceID] = ckdeviceID;
@@ -787,7 +927,7 @@ _isiCloudAnalyticsEnabled()
             if (accountID) {
                 healthSummary[SFAnalyticsAltDSID] = accountID;
             }
             if (accountID) {
                 healthSummary[SFAnalyticsAltDSID] = accountID;
             }
-            [uploadRecords addObject:healthSummary];
+            [localHealthSummaries addObject:healthSummary];
         }
 
         [hardFailures addObject:store.hardFailures];
         }
 
         [hardFailures addObject:store.hardFailures];
@@ -801,40 +941,93 @@ _isiCloudAnalyticsEnabled()
                                          code:-10
                                      userInfo:@{NSLocalizedDescriptionKey : description}];
         }
                                          code:-10
                                      userInfo:@{NSLocalizedDescriptionKey : description}];
         }
-        return nil;
+        return NO;
     }
 
     if (clients) {
         *clients = localClients;
     }
 
     }
 
     if (clients) {
         *clients = localClients;
     }
 
-    [self addFailures:hardFailures toUploadRecords:uploadRecords threshold:_maxEventsToReport/10];
-    [self addFailures:softFailures toUploadRecords:uploadRecords threshold:0];
+    if (failures) {
+        [self addFailures:hardFailures toUploadRecords:localFailures threshold:_maxEventsToReport/10 linkedUUID:linkedUUID];
+        [self addFailures:softFailures toUploadRecords:localFailures threshold:0 linkedUUID:linkedUUID];
+        [*failures addObjectsFromArray:localFailures];
+    }
+
+    if (healthSummaries) {
+        [*healthSummaries addObjectsFromArray:localHealthSummaries];
+    }
 
 
-    NSDictionary* jsonDict = @{
-                               SFAnalyticsSplunkPostTime : @([[NSDate date] timeIntervalSince1970] * 1000),
-                               @"events" : uploadRecords
-                               };
+    return YES;
+}
 
 
-    // This check is "belt and suspenders" because we already checked each event separately
-    if (![NSJSONSerialization isValidJSONObject:jsonDict]) {
-        secemergency("json: final dictionary invalid JSON. This is terrible!");
+- (NSArray<NSDictionary *> *)createChunkedLoggingJSON:(bool)pretty
+                                            forUpload:(BOOL)upload
+                                 participatingClients:(NSMutableArray<SFAnalyticsClient*>**)clients
+                                                force:(BOOL)force                                       // supdctl uploads ignore privacy settings and recency
+                                                error:(NSError**)error
+{
+    NSUUID *linkedUUID = [NSUUID UUID];
+    NSError *localError = nil;
+    NSMutableArray *failures = [[NSMutableArray alloc] init];
+    NSMutableArray *healthSummaries = [[NSMutableArray alloc] init];
+    BOOL copied = [self copyEvents:&healthSummaries
+                          failures:&failures
+                         forUpload:upload
+              participatingClients:clients
+                             force:force
+                        linkedUUID:linkedUUID
+                             error:&localError];
+    if (!copied || localError) {
         if (error) {
         if (error) {
-            *error = [NSError errorWithDomain:SupdErrorDomain code:SupdInvalidJSONError
-                                     userInfo:@{NSLocalizedDescriptionKey : [NSString localizedStringWithFormat:@"Final dictionary for upload is invalid JSON: %@", jsonDict]}];
+            *error = localError;
         }
         return nil;
     }
 
         }
         return nil;
     }
 
-    NSData *json = [NSJSONSerialization dataWithJSONObject:jsonDict
-                                                   options:(pretty ? NSJSONWritingPrettyPrinted : 0)
-                                                     error:&localError];
-    
-    if (error) {
-        *error = localError;
+    // Trim failures to the max count, based on health summary count
+    if ([failures count] > (_maxEventsToReport - [healthSummaries count])) {
+        NSRange range;
+        range.location = 0;
+        range.length = _maxEventsToReport - [healthSummaries count];
+        failures = [[failures subarrayWithRange:range] mutableCopy];
     }
 
     }
 
-    return json;
+    return [self createChunkedLoggingJSON:healthSummaries failures:failures error:error];
+}
+
+- (NSDictionary *)createLoggingJSON:(bool)pretty
+                          forUpload:(BOOL)upload
+               participatingClients:(NSMutableArray<SFAnalyticsClient*>**)clients
+                              force:(BOOL)force                                       // supdctl uploads ignore privacy settings and recency
+                              error:(NSError**)error
+{
+    NSError *localError = nil;
+    NSMutableArray *failures = [[NSMutableArray alloc] init];
+    NSMutableArray *healthSummaries = [[NSMutableArray alloc] init];
+    BOOL copied = [self copyEvents:&healthSummaries
+                          failures:&failures
+                         forUpload:upload
+              participatingClients:clients
+                             force:force
+                        linkedUUID:nil
+                             error:&localError];
+    if (!copied || localError) {
+        if (error) {
+            *error = localError;
+        }
+        return nil;
+    }
+
+    // Trim failures to the max count, based on health summary count
+    if ([failures count] > (_maxEventsToReport - [healthSummaries count])) {
+        NSRange range;
+        range.location = 0;
+        range.length = _maxEventsToReport - [healthSummaries count];
+        failures = [[failures subarrayWithRange:range] mutableCopy];
+    }
+
+    return [self createEventDictionary:healthSummaries failures:failures error:error];
 }
 
 // Is at least one client eligible for data collection based on user consent? Otherwise callers should NOT reach off-device.
 }
 
 // Is at least one client eligible for data collection based on user consent? Otherwise callers should NOT reach off-device.
@@ -1252,6 +1445,30 @@ static bool ShouldInitializeWithAsset(NSBundle *trustStoreBundle, NSURL *directo
     }
 }
 
     }
 }
 
+- (NSArray<NSData *> *)serializeLoggingEvents:(NSArray<NSDictionary *> *)events
+                                        error:(NSError **)error
+{
+    if (!events) {
+        return nil;
+    }
+
+    NSMutableArray<NSData *> *serializedEvents = [[NSMutableArray<NSData *> alloc] init];
+    for (NSDictionary *event in events) {
+        NSError *serializationError = nil;
+        NSData* serializedEvent = [NSJSONSerialization dataWithJSONObject:event
+                                                                  options:0
+                                                                    error:&serializationError];
+        if (serializedEvent && !serializationError) {
+            [serializedEvents addObject:serializedEvent];
+        } else if (error) {
+            *error = serializationError;
+            return nil;
+        }
+    }
+
+    return serializedEvents;
+}
+
 - (BOOL)uploadAnalyticsWithError:(NSError**)error force:(BOOL)force {
     [self sendNotificationForOncePerReportSamplers];
     
 - (BOOL)uploadAnalyticsWithError:(NSError**)error force:(BOOL)force {
     [self sendNotificationForOncePerReportSamplers];
     
@@ -1272,9 +1489,32 @@ static bool ShouldInitializeWithAsset(NSBundle *trustStoreBundle, NSURL *directo
             }
 
             NSMutableArray<SFAnalyticsClient*>* clients = [NSMutableArray new];
             }
 
             NSMutableArray<SFAnalyticsClient*>* clients = [NSMutableArray new];
-            NSData* json = [topic getLoggingJSON:false forUpload:YES participatingClients:&clients force:force error:&localError];
-            if (json) {
-                if ([topic isSampledUpload]) {
+            NSArray<NSDictionary *> *jsonEvents = [topic createChunkedLoggingJSON:false forUpload:YES participatingClients:&clients force:force error:&localError];
+            if (!jsonEvents || localError) {
+                if ([[localError domain] isEqualToString:SupdErrorDomain] && [localError code] == SupdInvalidJSONError) {
+                    // Pretend this was a success because at least we'll get rid of bad data.
+                    // If someone keeps logging bad data and we only catch it here then
+                    // this causes sustained data loss for the entire topic.
+                    [topic updateUploadDateForClients:clients date:[NSDate date] clearData:YES];
+                }
+                secerror("upload: failed to create chunked log events for logging topic %@: %@", [topic internalTopicName], localError);
+                continue;
+            }
+
+            NSArray<NSData *> *serializedEvents = [self serializeLoggingEvents:jsonEvents error:&localError];
+            if (!serializedEvents || localError) {
+                if ([[localError domain] isEqualToString:SupdErrorDomain] && [localError code] == SupdInvalidJSONError) {
+                    // Pretend this was a success because at least we'll get rid of bad data.
+                    // If someone keeps logging bad data and we only catch it here then
+                    // this causes sustained data loss for the entire topic.
+                    [topic updateUploadDateForClients:clients date:[NSDate date] clearData:YES];
+                }
+                secerror("upload: failed to serialized chunked log events for logging topic %@: %@", [topic internalTopicName], localError);
+                continue;
+            }
+
+            if ([topic isSampledUpload]) {
+                for (NSData *json in serializedEvents) {
                     if (![self->_reporter saveReport:json fileName:[topic internalTopicName]]) {
                         secerror("upload: failed to write analytics data to log");
                     }
                     if (![self->_reporter saveReport:json fileName:[topic internalTopicName]]) {
                         secerror("upload: failed to write analytics data to log");
                     }
@@ -1285,20 +1525,12 @@ static bool ShouldInitializeWithAsset(NSBundle *trustStoreBundle, NSURL *directo
                     } else {
                         secerror("upload: Failed to post JSON for %@: %@", [topic internalTopicName], localError);
                     }
                     } else {
                         secerror("upload: Failed to post JSON for %@: %@", [topic internalTopicName], localError);
                     }
-                } else {
-                    /* If we didn't sample this report, update date to prevent trying to upload again sooner
-                     * than we should. Clear data so that per-day calculations remain consistent. */
-                    secnotice("upload", "skipping unsampled upload for %@ and clearing data", [topic internalTopicName]);
-                    [topic updateUploadDateForClients:clients date:[NSDate date] clearData:YES];
                 }
             } else {
                 }
             } else {
-                if ([[localError domain] isEqualToString:SupdErrorDomain] && [localError code] == SupdInvalidJSONError) {
-                    // Pretend this was a success because at least we'll get rid of bad data.
-                    // If someone keeps logging bad data and we only catch it here then
-                    // this causes sustained data loss for the entire topic.
-                    [topic updateUploadDateForClients:clients date:[NSDate date] clearData:YES];
-                }
-                secerror("upload: failed to get logging JSON for topic %@: %@", [topic internalTopicName], localError);
+                /* If we didn't sample this report, update date to prevent trying to upload again sooner
+                 * than we should. Clear data so that per-day calculations remain consistent. */
+                secnotice("upload", "skipping unsampled upload for %@ and clearing data", [topic internalTopicName]);
+                [topic updateUploadDateForClients:clients date:[NSDate date] clearData:YES];
             }
         }
         if (error && localError) {
             }
         }
         if (error && localError) {
@@ -1382,8 +1614,6 @@ static bool ShouldInitializeWithAsset(NSBundle *trustStoreBundle, NSURL *directo
     reply(info, nil);
 }
 
     reply(info, nil);
 }
 
-
-
 - (NSString*)stringForEventClass:(SFAnalyticsEventClass)eventClass
 {
     if (eventClass == SFAnalyticsEventClassNote) {
 - (NSString*)stringForEventClass:(SFAnalyticsEventClass)eventClass
 {
     if (eventClass == SFAnalyticsEventClassNote) {
@@ -1409,20 +1639,51 @@ static bool ShouldInitializeWithAsset(NSBundle *trustStoreBundle, NSURL *directo
     reply([self getSysdiagnoseDump]);
 }
 
     reply([self getSysdiagnoseDump]);
 }
 
-- (void)getLoggingJSON:(bool)pretty topic:(NSString *)topicName reply:(void (^)(NSData*, NSError*))reply {
-    secnotice("rpcGetLoggingJSON", "Building a JSON blob resembling the one we would have uploaded");
+- (void)createLoggingJSON:(bool)pretty topic:(NSString *)topicName reply:(void (^)(NSData *, NSError*))reply {
+    secnotice("rpcCreateLoggingJSON", "Building a JSON blob resembling the one we would have uploaded");
     NSError* error = nil;
     [self sendNotificationForOncePerReportSamplers];
     NSError* error = nil;
     [self sendNotificationForOncePerReportSamplers];
-    NSData* json = nil;
+    NSDictionary *eventDictionary = nil;
     for (SFAnalyticsTopic* topic in self->_analyticsTopics) {
         if ([topic.internalTopicName isEqualToString:topicName]) {
     for (SFAnalyticsTopic* topic in self->_analyticsTopics) {
         if ([topic.internalTopicName isEqualToString:topicName]) {
-            json = [topic getLoggingJSON:pretty forUpload:NO participatingClients:nil force:!runningTests error:&error];
+            eventDictionary = [topic createLoggingJSON:pretty forUpload:NO participatingClients:nil force:!runningTests error:&error];
         }
     }
         }
     }
-    if (!json) {
+
+    NSData *data = nil;
+    if (!eventDictionary) {
         secerror("Unable to obtain JSON: %@", error);
         secerror("Unable to obtain JSON: %@", error);
+    } else {
+        data = [NSJSONSerialization dataWithJSONObject:eventDictionary
+                                               options:(pretty ? NSJSONWritingPrettyPrinted : 0)
+                                                 error:&error];
+    }
+
+    reply(data, error);
+}
+
+- (void)createChunkedLoggingJSON:(bool)pretty topic:(NSString *)topicName reply:(void (^)(NSData *, NSError*))reply
+{
+    secnotice("rpcCreateChunkedLoggingJSON", "Building an array of JSON blobs resembling the one we would have uploaded");
+    NSError* error = nil;
+    [self sendNotificationForOncePerReportSamplers];
+    NSArray<NSDictionary *> *events = nil;
+    for (SFAnalyticsTopic* topic in self->_analyticsTopics) {
+        if ([topic.internalTopicName isEqualToString:topicName]) {
+            events = [topic createChunkedLoggingJSON:pretty forUpload:NO participatingClients:nil force:!runningTests error:&error];
+        }
+    }
+
+    NSData *data = nil;
+    if (!events) {
+        secerror("Unable to obtain JSON: %@", error);
+    } else {
+        data = [NSJSONSerialization dataWithJSONObject:events
+                                               options:(pretty ? NSJSONWritingPrettyPrinted : 0)
+                                                 error:&error];
     }
     }
-    reply(json, error);
+
+    reply(data, error);
 }
 
 - (void)forceUploadWithReply:(void (^)(BOOL, NSError*))reply {
 }
 
 - (void)forceUploadWithReply:(void (^)(BOOL, NSError*))reply {
index 98371ac245034a7ae68fc14e27c1baf37d014c32..b190a34bc2086469f4da075101e7ab4bf2b86294 100644 (file)
@@ -25,7 +25,8 @@
 
 @protocol supdProtocol
 - (void)getSysdiagnoseDumpWithReply:(void (^)(NSString*))reply;
 
 @protocol supdProtocol
 - (void)getSysdiagnoseDumpWithReply:(void (^)(NSString*))reply;
-- (void)getLoggingJSON:(bool)pretty topic:(NSString *)topicName reply:(void (^)(NSData*, NSError*))reply;
+- (void)createLoggingJSON:(bool)pretty topic:(NSString *)topicName reply:(void (^)(NSData *, NSError*))reply;
+- (void)createChunkedLoggingJSON:(bool)pretty topic:(NSString *)topicName reply:(void (^)(NSData *, NSError*))reply;
 - (void)forceUploadWithReply:(void (^)(BOOL, NSError*))reply;
 - (void)setUploadDateWith:(NSDate *)date reply:(void (^)(BOOL, NSError*))reply;
 - (void)clientStatus:(void (^)(NSDictionary<NSString *, id> *, NSError *))reply;
 - (void)forceUploadWithReply:(void (^)(BOOL, NSError*))reply;
 - (void)setUploadDateWith:(NSDate *)date reply:(void (^)(BOOL, NSError*))reply;
 - (void)clientStatus:(void (^)(NSDictionary<NSString *, id> *, NSError *))reply;
index 3c920bdf8c0101e767a49c49ad88806cba1750cc..2920fd4c988d38f93c1ab7a58dc44e4d527bb3b9 100644 (file)
@@ -73,7 +73,7 @@ static void getSysdiagnoseDump(void)
     [connection invalidate];
 }
 
     [connection invalidate];
 }
 
-static void getLoggingJSON(char *topicName)
+static void createLoggingJSON(char *topicName)
 {
     NSString *topic = topicName ? [NSString stringWithUTF8String:topicName] : SFAnalyticsTopicKeySync;
     dispatch_semaphore_t sema = dispatch_semaphore_create(0);
 {
     NSString *topic = topicName ? [NSString stringWithUTF8String:topicName] : SFAnalyticsTopicKeySync;
     dispatch_semaphore_t sema = dispatch_semaphore_create(0);
@@ -81,7 +81,31 @@ static void getLoggingJSON(char *topicName)
     [[connection remoteObjectProxyWithErrorHandler:^(NSError * _Nonnull error) {
         nsprintf(@"Could not communicate with supd: %@", error);
         dispatch_semaphore_signal(sema);
     [[connection remoteObjectProxyWithErrorHandler:^(NSError * _Nonnull error) {
         nsprintf(@"Could not communicate with supd: %@", error);
         dispatch_semaphore_signal(sema);
-    }] getLoggingJSON:YES topic:topic reply:^(NSData* data, NSError* error) {
+    }] createLoggingJSON:YES topic:topic reply:^(NSData* data, NSError* error) {
+        if (data) {
+            // Success! Only print the JSON blob to make output easier to parse
+            nsprintf(@"%@", [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);
+        } else {
+            nsprintf(@"supd gave us an error: %@", error);
+        }
+        dispatch_semaphore_signal(sema);
+    }];
+
+    if(dispatch_semaphore_wait(sema, dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC * 20)) != 0) {
+        printf("\n\nError: timed out waiting for response from supd\n");
+    }
+    [connection invalidate];
+}
+
+static void createChunkedLoggingJSON(char *topicName)
+{
+    NSString *topic = topicName ? [NSString stringWithUTF8String:topicName] : SFAnalyticsTopicKeySync;
+    dispatch_semaphore_t sema = dispatch_semaphore_create(0);
+    NSXPCConnection* connection = getConnection();
+    [[connection remoteObjectProxyWithErrorHandler:^(NSError * _Nonnull error) {
+        nsprintf(@"Could not communicate with supd: %@", error);
+        dispatch_semaphore_signal(sema);
+    }] createChunkedLoggingJSON:YES topic:topic reply:^(NSData* data, NSError* error) {
         if (data) {
             // Success! Only print the JSON blob to make output easier to parse
             nsprintf(@"%@", [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);
         if (data) {
             // Success! Only print the JSON blob to make output easier to parse
             nsprintf(@"%@", [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);
@@ -168,6 +192,7 @@ forceOldUploadDate(void)
 
 static int forceUpload = false;
 static int getJSON = false;
 
 static int forceUpload = false;
 static int getJSON = false;
+static int getChunkedJSON = false;
 static int getSysdiagnose = false;
 static int getInfo = false;
 static int setOldUploadDate = false;
 static int getSysdiagnose = false;
 static int getInfo = false;
 static int setOldUploadDate = false;
@@ -177,9 +202,9 @@ int main(int argc, char **argv)
 {
     static struct argument options[] = {
         { .shortname='t', .longname="topicName", .argument=&topicName, .description="Operate on a non-default topic"},
 {
     static struct argument options[] = {
         { .shortname='t', .longname="topicName", .argument=&topicName, .description="Operate on a non-default topic"},
-
         { .command="sysdiagnose", .flag=&getSysdiagnose, .flagval=true, .description="Retrieve the current sysdiagnose dump for security analytics"},
         { .command="get", .flag=&getJSON, .flagval=true, .description="Get the JSON blob we would upload to the server if an upload were due"},
         { .command="sysdiagnose", .flag=&getSysdiagnose, .flagval=true, .description="Retrieve the current sysdiagnose dump for security analytics"},
         { .command="get", .flag=&getJSON, .flagval=true, .description="Get the JSON blob we would upload to the server if an upload were due"},
+        { .command="getChunked", .flag=&getChunkedJSON, .flagval=true, .description="Chunk the JSON blob"},
         { .command="upload", .flag=&forceUpload, .flagval=true, .description="Force an upload of analytics data to server (ignoring privacy settings)"},
         { .command="info", .flag=&getInfo, .flagval=true, .description="Request info about clients"},
         { .command="set-old-upload-date", .flag=&setOldUploadDate, .flagval=true, .description="Clear last upload date"},
         { .command="upload", .flag=&forceUpload, .flagval=true, .description="Force an upload of analytics data to server (ignoring privacy settings)"},
         { .command="info", .flag=&getInfo, .flagval=true, .description="Request info about clients"},
         { .command="set-old-upload-date", .flag=&setOldUploadDate, .flagval=true, .description="Clear last upload date"},
@@ -206,7 +231,9 @@ int main(int argc, char **argv)
         if (forceUpload) {
             forceUploadAnalytics();
         } else if (getJSON) {
         if (forceUpload) {
             forceUploadAnalytics();
         } else if (getJSON) {
-            getLoggingJSON(topicName);
+            createLoggingJSON(topicName);
+        } else if (getChunkedJSON) {
+            createChunkedLoggingJSON(topicName);
         } else if (getSysdiagnose) {
             getSysdiagnoseDump();
         } else if (getInfo) {
         } else if (getSysdiagnose) {
             getSysdiagnoseDump();
         } else if (getInfo) {
diff --git a/tests/TrustTests/EvaluationTests/ExceptionTests.m b/tests/TrustTests/EvaluationTests/ExceptionTests.m
new file mode 100644 (file)
index 0000000..3e2bb6a
--- /dev/null
@@ -0,0 +1,331 @@
+/*
+* Copyright (c) 2006-2010,2012-2019 Apple Inc. All Rights Reserved.
+*/
+
+#include <AssertMacros.h>
+#import <XCTest/XCTest.h>
+#import <Security/SecCertificatePriv.h>
+#include <Security/SecTrustPriv.h>
+#include <Security/SecPolicyPriv.h>
+#include "OSX/utilities/array_size.h"
+#include "OSX/utilities/SecCFWrappers.h"
+
+#import "../TestMacroConversions.h"
+#import "TrustEvaluationTestCase.h"
+
+#import "ExceptionTests_data.h"
+
+@interface TrustExceptionTests : TrustEvaluationTestCase
+@end
+
+@implementation TrustExceptionTests
+
+static NSArray *certs = nil;
+static NSDate *date = nil;
+
++ (void)setUp
+{
+    [super setUp];
+    SecCertificateRef cert0 = SecCertificateCreateWithBytes(NULL, _exception_cert0, sizeof(_exception_cert0));
+    SecCertificateRef cert1 = SecCertificateCreateWithBytes(NULL, _exception_cert1, sizeof(_exception_cert1));
+    certs = @[ (__bridge id)cert0, (__bridge id)cert1 ];
+    date = [NSDate dateWithTimeIntervalSinceReferenceDate:545000000.0]; /* April 9, 2018 at 1:53:20 PM PDT */
+
+    CFReleaseNull(cert0);
+    CFReleaseNull(cert1);
+}
+
+#if !TARGET_OS_BRIDGE
+// bridgeOS doesn't have a system root store
+- (void)testPassingTrust
+{
+    SecTrustRef trust = NULL;
+    SecPolicyRef policy = SecPolicyCreateSSL(true, CFSTR("store.apple.com"));
+    ok_status(SecTrustCreateWithCertificates((__bridge CFArrayRef)certs, policy, &trust), "create trust");
+    ok_status(SecTrustSetVerifyDate(trust, (__bridge CFDateRef)date), "set date");
+
+    SecTrustResultType trustResult;
+    ok(SecTrustEvaluateWithError(trust, NULL), "evaluate trust");
+    ok_status(SecTrustGetTrustResult(trust, &trustResult));
+    is_status(trustResult, kSecTrustResultUnspecified,
+            "trust is kSecTrustResultUnspecified");
+    CFDataRef exceptions;
+    ok(exceptions = SecTrustCopyExceptions(trust), "create exceptions");
+    ok(SecTrustSetExceptions(trust, exceptions), "set exceptions");
+    ok_status(SecTrustGetTrustResult(trust, &trustResult), "evaluate trust");
+    is_status(trustResult, kSecTrustResultProceed, "trust is kSecTrustResultProceed");
+
+    CFReleaseNull(trust);
+    CFReleaseNull(policy);
+    CFReleaseNull(exceptions);
+}
+#endif
+
+- (void)testFailingTrust
+{
+    SecTrustRef trust = NULL;
+    SecPolicyRef policy = SecPolicyCreateSSL(true, CFSTR("badstore.apple.com"));
+    ok_status(SecTrustCreateWithCertificates((__bridge CFArrayRef)certs, policy, &trust), "create trust");
+    ok_status(SecTrustSetVerifyDate(trust, (__bridge CFDateRef)date), "set date");
+
+    SecTrustResultType trustResult;
+    XCTAssertFalse(SecTrustEvaluateWithError(trust, NULL), "evaluate trust");
+    ok_status(SecTrustGetTrustResult(trust, &trustResult));
+    is_status(trustResult, kSecTrustResultRecoverableTrustFailure);
+    CFDataRef exceptions;
+    ok(exceptions = SecTrustCopyExceptions(trust), "create exceptions");
+    ok(SecTrustSetExceptions(trust, exceptions), "set exceptions");
+    ok_status(SecTrustGetTrustResult(trust, &trustResult), "evaluate trust");
+    is_status(trustResult, kSecTrustResultProceed, "trust is kSecTrustResultProceed");
+
+    CFReleaseNull(trust);
+    CFReleaseNull(policy);
+    CFReleaseNull(exceptions);
+}
+
+- (void)testNewTrustObjectSameFailure
+{
+    SecTrustRef trust = NULL;
+    SecPolicyRef policy = SecPolicyCreateSSL(true, CFSTR("badstore.apple.com"));
+    ok_status(SecTrustCreateWithCertificates((__bridge CFArrayRef)certs, policy, &trust), "create trust");
+    ok_status(SecTrustSetVerifyDate(trust, (__bridge CFDateRef)date), "set date");
+
+    SecTrustResultType trustResult;
+    XCTAssertFalse(SecTrustEvaluateWithError(trust, NULL), "evaluate trust");
+    ok_status(SecTrustGetTrustResult(trust, &trustResult));
+    is_status(trustResult, kSecTrustResultRecoverableTrustFailure);
+    CFDataRef exceptions;
+    ok(exceptions = SecTrustCopyExceptions(trust), "create exceptions");
+    ok(SecTrustSetExceptions(trust, exceptions), "set exceptions");
+    ok_status(SecTrustGetTrustResult(trust, &trustResult), "evaluate trust");
+    is_status(trustResult, kSecTrustResultProceed, "trust is kSecTrustResultProceed");
+
+    /* new trust with the same failing policy and certs should pass */
+    CFReleaseNull(trust);
+    ok_status(SecTrustCreateWithCertificates((__bridge CFArrayRef)certs, policy, &trust), "create trust");
+    ok_status(SecTrustSetVerifyDate(trust, (__bridge CFDateRef)date), "set date");
+    ok(SecTrustSetExceptions(trust, exceptions), "set exceptions");
+    ok_status(SecTrustGetTrustResult(trust, &trustResult), "evaluate trust");
+    is_status(trustResult, kSecTrustResultProceed, "trust is kSecTrustResultProceed");
+
+    CFReleaseNull(trust);
+    CFReleaseNull(policy);
+    CFReleaseNull(exceptions);
+}
+
+#if !TARGET_OS_BRIDGE
+// bridgeOS always has an AnchorTrusted error due to lack of a system root store
+- (void)testIntroduceNewAnchorTrustedFailure
+{
+    SecTrustRef trust = NULL;
+    SecPolicyRef policy = SecPolicyCreateSSL(true, CFSTR("badstore.apple.com"));
+    ok_status(SecTrustCreateWithCertificates((__bridge CFArrayRef)certs, policy, &trust), "create trust");
+    ok_status(SecTrustSetVerifyDate(trust, (__bridge CFDateRef)date), "set date");
+
+    SecTrustResultType trustResult;
+    XCTAssertFalse(SecTrustEvaluateWithError(trust, NULL), "evaluate trust");
+    ok_status(SecTrustGetTrustResult(trust, &trustResult));
+    is_status(trustResult, kSecTrustResultRecoverableTrustFailure);
+    CFDataRef exceptions;
+    ok(exceptions = SecTrustCopyExceptions(trust), "create exceptions");
+    ok(SecTrustSetExceptions(trust, exceptions), "set exceptions");
+    ok_status(SecTrustGetTrustResult(trust, &trustResult), "evaluate trust");
+    is_status(trustResult, kSecTrustResultProceed, "trust is kSecTrustResultProceed");
+
+    // new AnchorTrusted failure
+    ok_status(SecTrustSetAnchorCertificates(trust, (__bridge CFArrayRef)@[]), "set empty anchor list");
+    ok_status(SecTrustGetTrustResult(trust, &trustResult), "evaluate trust");
+    is_status(trustResult, kSecTrustResultRecoverableTrustFailure, "trust is kSecTrustResultRecoverableTrustFailure");
+
+    // fix AnchorTrusted failure
+    ok_status(SecTrustSetAnchorCertificatesOnly(trust, false), "trust passed in anchors and system anchors");
+    ok_status(SecTrustGetTrustResult(trust, &trustResult), "evaluate trust");
+    is_status(trustResult, kSecTrustResultProceed, "trust is kSecTrustResultProceed");
+
+    CFReleaseNull(trust);
+    CFReleaseNull(policy);
+    CFReleaseNull(exceptions);
+}
+#endif
+
+- (void)testIntroduceNewExpiredFailure
+{
+    SecTrustRef trust = NULL;
+    SecPolicyRef policy = SecPolicyCreateSSL(true, CFSTR("badstore.apple.com"));
+    ok_status(SecTrustCreateWithCertificates((__bridge CFArrayRef)certs, policy, &trust), "create trust");
+    ok_status(SecTrustSetVerifyDate(trust, (__bridge CFDateRef)date), "set date");
+
+    SecTrustResultType trustResult;
+    XCTAssertFalse(SecTrustEvaluateWithError(trust, NULL), "evaluate trust");
+    ok_status(SecTrustGetTrustResult(trust, &trustResult));
+    is_status(trustResult, kSecTrustResultRecoverableTrustFailure);
+    CFDataRef exceptions;
+    ok(exceptions = SecTrustCopyExceptions(trust), "create exceptions");
+    ok(SecTrustSetExceptions(trust, exceptions), "set exceptions");
+    ok_status(SecTrustGetTrustResult(trust, &trustResult), "evaluate trust");
+    is_status(trustResult, kSecTrustResultProceed, "trust is kSecTrustResultProceed");
+
+    // new expiry failure
+    ok_status(SecTrustSetVerifyDate(trust, (__bridge CFDateRef)[NSDate dateWithTimeIntervalSinceReferenceDate:667680000.0]),
+              "set date to far future so certs are expired");
+    ok_status(SecTrustGetTrustResult(trust, &trustResult), "evaluate trust");
+    is_status(trustResult, kSecTrustResultRecoverableTrustFailure, "trust is kSecTrustResultRecoverableTrustFailure");
+
+    CFReleaseNull(trust);
+    CFReleaseNull(policy);
+    CFReleaseNull(exceptions);
+}
+
+- (void)testNewTrustObjectNewHostnameFailure
+{
+    SecTrustRef trust = NULL;
+    SecPolicyRef policy = SecPolicyCreateSSL(true, CFSTR("store.apple.com"));
+    ok_status(SecTrustCreateWithCertificates((__bridge CFArrayRef)certs, policy, &trust), "create trust");
+    ok_status(SecTrustSetVerifyDate(trust, (__bridge CFDateRef)date), "set date");
+    CFDataRef exceptions;
+    ok(exceptions = SecTrustCopyExceptions(trust), "create exceptions");
+
+    CFReleaseNull(trust);
+    CFReleaseNull(policy);
+    policy = SecPolicyCreateSSL(true, CFSTR("badstore.apple.com"));
+    ok_status(SecTrustCreateWithCertificates((__bridge CFArrayRef)certs, policy, &trust), "create trust with hostname mismatch");
+    ok_status(SecTrustSetVerifyDate(trust, (__bridge CFDateRef)date), "set date");
+
+    /* exceptions from the old trust evaluation should fail */
+    SecTrustResultType trustResult;
+    ok(SecTrustSetExceptions(trust, exceptions), "set old exceptions");
+    XCTAssertFalse(SecTrustEvaluateWithError(trust, NULL), "evaluate trust");
+    ok_status(SecTrustGetTrustResult(trust, &trustResult));
+    is_status(trustResult, kSecTrustResultRecoverableTrustFailure);
+
+    /* we should be able to get new exceptions and pass */
+    CFReleaseNull(exceptions);
+    ok(exceptions = SecTrustCopyExceptions(trust), "create exceptions");
+    ok(SecTrustSetExceptions(trust, exceptions), "set exceptions");
+    ok_status(SecTrustGetTrustResult(trust, &trustResult), "evaluate trust");
+    is_status(trustResult, kSecTrustResultProceed, "trust is kSecTrustResultProceed");
+
+    CFReleaseNull(trust);
+    CFReleaseNull(policy);
+    CFReleaseNull(exceptions);
+}
+
+- (void)testClearExceptions
+{
+    SecTrustRef trust = NULL;
+    SecPolicyRef policy = SecPolicyCreateSSL(true, CFSTR("badstore.apple.com"));
+    ok_status(SecTrustCreateWithCertificates((__bridge CFArrayRef)certs, policy, &trust), "create trust");
+    ok_status(SecTrustSetVerifyDate(trust, (__bridge CFDateRef)date), "set date");
+
+    SecTrustResultType trustResult;
+    XCTAssertFalse(SecTrustEvaluateWithError(trust, NULL), "evaluate trust");
+    ok_status(SecTrustGetTrustResult(trust, &trustResult));
+    is_status(trustResult, kSecTrustResultRecoverableTrustFailure);
+    CFDataRef exceptions;
+    ok(exceptions = SecTrustCopyExceptions(trust), "create exceptions");
+    ok(SecTrustSetExceptions(trust, exceptions), "set exceptions");
+    ok_status(SecTrustGetTrustResult(trust, &trustResult), "evaluate trust");
+    is_status(trustResult, kSecTrustResultProceed, "trust is kSecTrustResultProceed");
+
+    XCTAssertFalse(SecTrustSetExceptions(trust, NULL));
+    ok_status(SecTrustGetTrustResult(trust, &trustResult));
+    is_status(trustResult, kSecTrustResultRecoverableTrustFailure);
+
+    CFReleaseNull(trust);
+    CFReleaseNull(policy);
+    CFReleaseNull(exceptions);
+}
+
+- (void)testWrongCertForExceptions
+{
+    SecTrustRef trust = NULL;
+    SecPolicyRef policy = SecPolicyCreateSSL(true, CFSTR("badstore.apple.com"));
+    ok_status(SecTrustCreateWithCertificates((__bridge CFArrayRef)certs, policy, &trust), "create trust");
+    ok_status(SecTrustSetVerifyDate(trust, (__bridge CFDateRef)date), "set date");
+
+    SecTrustResultType trustResult;
+    XCTAssertFalse(SecTrustEvaluateWithError(trust, NULL), "evaluate trust");
+    ok_status(SecTrustGetTrustResult(trust, &trustResult));
+    is_status(trustResult, kSecTrustResultRecoverableTrustFailure);
+    CFDataRef exceptions;
+    ok(exceptions = SecTrustCopyExceptions(trust), "create exceptions");
+    ok(SecTrustSetExceptions(trust, exceptions), "set exceptions");
+    ok_status(SecTrustGetTrustResult(trust, &trustResult), "evaluate trust");
+    is_status(trustResult, kSecTrustResultProceed, "trust is kSecTrustResultProceed");
+
+    /* new trust with the same failing policy and certs should pass */
+    CFReleaseNull(trust);
+    CFReleaseNull(policy);
+    SecCertificateRef sscert0 = SecCertificateCreateWithBytes(NULL, _exception_self_signed, sizeof(_exception_self_signed));
+    policy = SecPolicyCreateSSL(false, CFSTR("self-signed.ssltest.apple.com"));
+    ok_status(SecTrustCreateWithCertificates(sscert0, policy, &trust), "create trust");
+    ok_status(SecTrustSetVerifyDate(trust, (__bridge CFDateRef)date), "set date");
+    XCTAssertFalse(SecTrustSetExceptions(trust, exceptions), "set exceptions fails for other cert");
+    ok_status(SecTrustGetTrustResult(trust, &trustResult), "evaluate trust");
+    is_status(trustResult, kSecTrustResultRecoverableTrustFailure);
+
+    CFReleaseNull(trust);
+    CFReleaseNull(policy);
+    CFReleaseNull(exceptions);
+}
+
+#if TARGET_OS_IPHONE
+- (void)testExtensionsEpoch
+{
+    SecTrustRef trust = NULL;
+    SecTrustResultType trustResult;
+    CFDataRef exceptions = NULL;
+
+    SecPolicyRef policy = SecPolicyCreateSSL(true, CFSTR("badstore.apple.com"));
+    ok_status(SecTrustCreateWithCertificates((__bridge CFArrayRef)certs, policy, &trust), "create trust");
+    ok_status(SecTrustSetVerifyDate(trust, (__bridge CFDateRef)date), "set date");
+    ok(exceptions = SecTrustCopyExceptions(trust), "create exceptions");
+
+    /* Test the uninitialized extensions epoch. */
+    CFErrorRef exceptionResetCountError = NULL;
+    uint64_t exceptionResetCount = SecTrustGetExceptionResetCount(&exceptionResetCountError);
+    ok(exceptionResetCount == 0, "exception reset count is uninitialized");
+    is(SecTrustGetExceptionResetCount(&exceptionResetCountError), exceptionResetCount, "SecTrustGetExceptionResetCount is idempotent");
+    ok(SecTrustSetExceptions(trust, exceptions), "set exceptions");
+    ok_status(SecTrustGetTrustResult(trust, &trustResult), "evaluate trust");
+    is_status(trustResult, kSecTrustResultProceed, "trust is kSecTrustResultProceed");
+
+    /* Test increasing the extensions epoch. */
+    exceptionResetCountError = NULL;
+    ok_status(SecTrustIncrementExceptionResetCount(&exceptionResetCountError), "increase exception reset count");
+    exceptionResetCountError = NULL;
+    is(SecTrustGetExceptionResetCount(&exceptionResetCountError), 1 + exceptionResetCount, "exception reset count is 1 + previous count");
+
+    /* Test trust evaluation under a future extensions epoch. */
+    ok(!SecTrustSetExceptions(trust, exceptions), "set exceptions");
+    ok_status(SecTrustGetTrustResult(trust, &trustResult), "evaluate trust");
+    is_status(trustResult, kSecTrustResultRecoverableTrustFailure, "trust is kSecTrustResultRecoverableTrustFailure");
+
+    CFReleaseNull(trust);
+    CFReleaseNull(policy);
+    CFReleaseNull(exceptions);
+}
+#endif
+
+#if !TARGET_OS_BRIDGE
+// bridgeOS doesn't support Valid
+- (void)testFatalResultsNonOverride
+{
+    id root = [self SecCertificateCreateFromPEMResource:@"ca-ki" subdirectory:@"si-88-sectrust-valid-data"];
+    id revokedLeaf = [self SecCertificateCreateFromPEMResource:@"leaf-ki-revoked1" subdirectory:@"si-88-sectrust-valid-data"];
+    TestTrustEvaluation *eval = [[TestTrustEvaluation alloc] initWithCertificates:@[revokedLeaf, root] policies:nil];
+    [eval setVerifyDate:[NSDate dateWithTimeIntervalSinceReferenceDate:542400000.0]]; // March 10, 2018 at 10:40:00 AM PST
+    [eval setAnchors:@[root]];
+    XCTAssertFalse([eval evaluate:nil]);
+    XCTAssertEqual(eval.trustResult, kSecTrustResultFatalTrustFailure);
+
+    /* try to set exceptions on the trust and ensure it still fails */
+    NSData *exceptions = CFBridgingRelease(SecTrustCopyExceptions(eval.trust));
+    XCTAssertNotNil(exceptions);
+    XCTAssert(SecTrustSetExceptions(eval.trust, (__bridge CFDataRef)exceptions));
+    XCTAssertFalse([eval evaluate:nil]);
+    XCTAssertEqual(eval.trustResult, kSecTrustResultFatalTrustFailure);
+}
+#endif
+
+@end
diff --git a/tests/TrustTests/EvaluationTests/ExceptionTests_data.h b/tests/TrustTests/EvaluationTests/ExceptionTests_data.h
new file mode 100644 (file)
index 0000000..e6dc309
--- /dev/null
@@ -0,0 +1,269 @@
+/*
+* Copyright (c) 2019 Apple Inc. All Rights Reserved.
+*/
+
+#ifndef _TRUSTTESTS_EXCEPTION_TESTS_H_
+#define _TRUSTTESTS_EXCEPTION_TESTS_H_
+
+/*
+ Serial Number:
+ 01:f1:eb:db:ee:d3:1d:7a:b5:72:54:7d:34:43:4b:87
+ Signature Algorithm: sha256WithRSAEncryption
+ Issuer: C=US, O=DigiCert Inc, OU=www.digicert.com, CN=DigiCert SHA2 Extended Validation Server CA
+ Validity
+ Not Before: Mar 22 00:00:00 2018 GMT
+ Not After : Mar 23 12:00:00 2019 GMT
+ Subject: businessCategory=Private Organization/jurisdictionC=US/jurisdictionST=California/serialNumber=C0806592, C=US, ST=California, L=Cupertino, O=Apple Inc., OU=Internet Services for Akamai, CN=store.apple.com
+ */
+static unsigned char _exception_cert0[]={
+    0x30,0x82,0x06,0xF7,0x30,0x82,0x05,0xDF,0xA0,0x03,0x02,0x01,0x02,0x02,0x10,0x01,
+    0xF1,0xEB,0xDB,0xEE,0xD3,0x1D,0x7A,0xB5,0x72,0x54,0x7D,0x34,0x43,0x4B,0x87,0x30,
+    0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x0B,0x05,0x00,0x30,0x75,
+    0x31,0x0B,0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x55,0x53,0x31,0x15,0x30,
+    0x13,0x06,0x03,0x55,0x04,0x0A,0x13,0x0C,0x44,0x69,0x67,0x69,0x43,0x65,0x72,0x74,
+    0x20,0x49,0x6E,0x63,0x31,0x19,0x30,0x17,0x06,0x03,0x55,0x04,0x0B,0x13,0x10,0x77,
+    0x77,0x77,0x2E,0x64,0x69,0x67,0x69,0x63,0x65,0x72,0x74,0x2E,0x63,0x6F,0x6D,0x31,
+    0x34,0x30,0x32,0x06,0x03,0x55,0x04,0x03,0x13,0x2B,0x44,0x69,0x67,0x69,0x43,0x65,
+    0x72,0x74,0x20,0x53,0x48,0x41,0x32,0x20,0x45,0x78,0x74,0x65,0x6E,0x64,0x65,0x64,
+    0x20,0x56,0x61,0x6C,0x69,0x64,0x61,0x74,0x69,0x6F,0x6E,0x20,0x53,0x65,0x72,0x76,
+    0x65,0x72,0x20,0x43,0x41,0x30,0x1E,0x17,0x0D,0x31,0x38,0x30,0x33,0x32,0x32,0x30,
+    0x30,0x30,0x30,0x30,0x30,0x5A,0x17,0x0D,0x31,0x39,0x30,0x33,0x32,0x33,0x31,0x32,
+    0x30,0x30,0x30,0x30,0x5A,0x30,0x81,0xF0,0x31,0x1D,0x30,0x1B,0x06,0x03,0x55,0x04,
+    0x0F,0x0C,0x14,0x50,0x72,0x69,0x76,0x61,0x74,0x65,0x20,0x4F,0x72,0x67,0x61,0x6E,
+    0x69,0x7A,0x61,0x74,0x69,0x6F,0x6E,0x31,0x13,0x30,0x11,0x06,0x0B,0x2B,0x06,0x01,
+    0x04,0x01,0x82,0x37,0x3C,0x02,0x01,0x03,0x13,0x02,0x55,0x53,0x31,0x1B,0x30,0x19,
+    0x06,0x0B,0x2B,0x06,0x01,0x04,0x01,0x82,0x37,0x3C,0x02,0x01,0x02,0x13,0x0A,0x43,
+    0x61,0x6C,0x69,0x66,0x6F,0x72,0x6E,0x69,0x61,0x31,0x11,0x30,0x0F,0x06,0x03,0x55,
+    0x04,0x05,0x13,0x08,0x43,0x30,0x38,0x30,0x36,0x35,0x39,0x32,0x31,0x0B,0x30,0x09,
+    0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x55,0x53,0x31,0x13,0x30,0x11,0x06,0x03,0x55,
+    0x04,0x08,0x13,0x0A,0x43,0x61,0x6C,0x69,0x66,0x6F,0x72,0x6E,0x69,0x61,0x31,0x12,
+    0x30,0x10,0x06,0x03,0x55,0x04,0x07,0x13,0x09,0x43,0x75,0x70,0x65,0x72,0x74,0x69,
+    0x6E,0x6F,0x31,0x13,0x30,0x11,0x06,0x03,0x55,0x04,0x0A,0x13,0x0A,0x41,0x70,0x70,
+    0x6C,0x65,0x20,0x49,0x6E,0x63,0x2E,0x31,0x25,0x30,0x23,0x06,0x03,0x55,0x04,0x0B,
+    0x13,0x1C,0x49,0x6E,0x74,0x65,0x72,0x6E,0x65,0x74,0x20,0x53,0x65,0x72,0x76,0x69,
+    0x63,0x65,0x73,0x20,0x66,0x6F,0x72,0x20,0x41,0x6B,0x61,0x6D,0x61,0x69,0x31,0x18,
+    0x30,0x16,0x06,0x03,0x55,0x04,0x03,0x13,0x0F,0x73,0x74,0x6F,0x72,0x65,0x2E,0x61,
+    0x70,0x70,0x6C,0x65,0x2E,0x63,0x6F,0x6D,0x30,0x82,0x01,0x22,0x30,0x0D,0x06,0x09,
+    0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x01,0x05,0x00,0x03,0x82,0x01,0x0F,0x00,
+    0x30,0x82,0x01,0x0A,0x02,0x82,0x01,0x01,0x00,0xB5,0xDF,0x48,0x6A,0xE5,0x5F,0x0C,
+    0x42,0xBF,0x71,0x07,0xAB,0xE6,0x48,0xD2,0x2C,0x13,0x7C,0x29,0xF7,0x90,0xC2,0x45,
+    0x39,0x43,0x48,0x2E,0x16,0x22,0xBF,0xAB,0x48,0x20,0x14,0x27,0xE7,0x87,0x52,0x97,
+    0xF6,0x64,0x0C,0x62,0xF5,0x39,0x54,0x64,0x09,0xE8,0x29,0x56,0xF4,0x03,0x40,0x5A,
+    0xFA,0x9A,0x11,0x2B,0xD0,0x01,0x51,0x1E,0xED,0xB8,0x51,0xCB,0xAB,0x6B,0x7A,0x99,
+    0xF3,0xB0,0x6E,0xA8,0xC4,0xB6,0xAF,0x17,0xEC,0xA6,0xD2,0x4D,0xCA,0x63,0x3D,0x59,
+    0x30,0x25,0x30,0x54,0x86,0xDB,0x70,0x7A,0xEC,0x07,0x83,0x19,0xA0,0x44,0xE7,0x3C,
+    0x68,0xE0,0xFD,0xB9,0xEE,0x3F,0xAC,0xF3,0x32,0x5A,0x5F,0x42,0x31,0x94,0x70,0x2C,
+    0xC5,0x73,0xD2,0x79,0x23,0x5B,0x96,0x45,0xB1,0xB3,0x2A,0xA1,0x5A,0x69,0xFE,0xBE,
+    0x52,0x0D,0x5D,0x79,0x18,0xCA,0xF1,0x44,0x92,0xA0,0x27,0x1F,0xAA,0x6E,0x9D,0x6F,
+    0x1B,0x83,0x5B,0x73,0x28,0x1D,0x87,0xB5,0x70,0x0E,0x3D,0xED,0xE2,0xC2,0x34,0x8A,
+    0x81,0xB2,0x22,0x40,0x98,0x77,0x2F,0x34,0x1B,0x70,0xEC,0x96,0x3F,0x91,0xB9,0xFF,
+    0xC9,0xE5,0x7E,0xE7,0x25,0xEF,0xDB,0x9A,0x58,0x4E,0xB2,0x92,0x19,0xA5,0x8D,0xEB,
+    0x76,0xF8,0xA8,0x48,0x9F,0x3D,0x10,0x0C,0xE4,0x69,0x7B,0xE7,0xB7,0xCA,0xF6,0x14,
+    0x5E,0x93,0x1E,0x20,0x37,0x1B,0xB8,0xB1,0x2C,0xD1,0x46,0x5C,0xD7,0x85,0x4A,0x2E,
+    0x19,0xB2,0x3C,0x31,0xDC,0x3D,0xB5,0x3C,0xEC,0x49,0x8D,0x9C,0xCF,0x75,0x0B,0xFC,
+    0x03,0x31,0x37,0xE7,0xA5,0xD6,0x9D,0x19,0x0D,0x02,0x03,0x01,0x00,0x01,0xA3,0x82,
+    0x03,0x05,0x30,0x82,0x03,0x01,0x30,0x1F,0x06,0x03,0x55,0x1D,0x23,0x04,0x18,0x30,
+    0x16,0x80,0x14,0x3D,0xD3,0x50,0xA5,0xD6,0xA0,0xAD,0xEE,0xF3,0x4A,0x60,0x0A,0x65,
+    0xD3,0x21,0xD4,0xF8,0xF8,0xD6,0x0F,0x30,0x1D,0x06,0x03,0x55,0x1D,0x0E,0x04,0x16,
+    0x04,0x14,0x6F,0x89,0xAE,0x9B,0xE4,0x8A,0x21,0x3A,0x3B,0xEA,0xAA,0xBE,0x99,0x90,
+    0xC8,0xDB,0x34,0x80,0x21,0xB9,0x30,0x2F,0x06,0x03,0x55,0x1D,0x11,0x04,0x28,0x30,
+    0x26,0x82,0x0F,0x73,0x74,0x6F,0x72,0x65,0x2E,0x61,0x70,0x70,0x6C,0x65,0x2E,0x63,
+    0x6F,0x6D,0x82,0x13,0x77,0x77,0x77,0x2E,0x73,0x74,0x6F,0x72,0x65,0x2E,0x61,0x70,
+    0x70,0x6C,0x65,0x2E,0x63,0x6F,0x6D,0x30,0x0E,0x06,0x03,0x55,0x1D,0x0F,0x01,0x01,
+    0xFF,0x04,0x04,0x03,0x02,0x05,0xA0,0x30,0x1D,0x06,0x03,0x55,0x1D,0x25,0x04,0x16,
+    0x30,0x14,0x06,0x08,0x2B,0x06,0x01,0x05,0x05,0x07,0x03,0x01,0x06,0x08,0x2B,0x06,
+    0x01,0x05,0x05,0x07,0x03,0x02,0x30,0x75,0x06,0x03,0x55,0x1D,0x1F,0x04,0x6E,0x30,
+    0x6C,0x30,0x34,0xA0,0x32,0xA0,0x30,0x86,0x2E,0x68,0x74,0x74,0x70,0x3A,0x2F,0x2F,
+    0x63,0x72,0x6C,0x33,0x2E,0x64,0x69,0x67,0x69,0x63,0x65,0x72,0x74,0x2E,0x63,0x6F,
+    0x6D,0x2F,0x73,0x68,0x61,0x32,0x2D,0x65,0x76,0x2D,0x73,0x65,0x72,0x76,0x65,0x72,
+    0x2D,0x67,0x32,0x2E,0x63,0x72,0x6C,0x30,0x34,0xA0,0x32,0xA0,0x30,0x86,0x2E,0x68,
+    0x74,0x74,0x70,0x3A,0x2F,0x2F,0x63,0x72,0x6C,0x34,0x2E,0x64,0x69,0x67,0x69,0x63,
+    0x65,0x72,0x74,0x2E,0x63,0x6F,0x6D,0x2F,0x73,0x68,0x61,0x32,0x2D,0x65,0x76,0x2D,
+    0x73,0x65,0x72,0x76,0x65,0x72,0x2D,0x67,0x32,0x2E,0x63,0x72,0x6C,0x30,0x4B,0x06,
+    0x03,0x55,0x1D,0x20,0x04,0x44,0x30,0x42,0x30,0x37,0x06,0x09,0x60,0x86,0x48,0x01,
+    0x86,0xFD,0x6C,0x02,0x01,0x30,0x2A,0x30,0x28,0x06,0x08,0x2B,0x06,0x01,0x05,0x05,
+    0x07,0x02,0x01,0x16,0x1C,0x68,0x74,0x74,0x70,0x73,0x3A,0x2F,0x2F,0x77,0x77,0x77,
+    0x2E,0x64,0x69,0x67,0x69,0x63,0x65,0x72,0x74,0x2E,0x63,0x6F,0x6D,0x2F,0x43,0x50,
+    0x53,0x30,0x07,0x06,0x05,0x67,0x81,0x0C,0x01,0x01,0x30,0x81,0x88,0x06,0x08,0x2B,
+    0x06,0x01,0x05,0x05,0x07,0x01,0x01,0x04,0x7C,0x30,0x7A,0x30,0x24,0x06,0x08,0x2B,
+    0x06,0x01,0x05,0x05,0x07,0x30,0x01,0x86,0x18,0x68,0x74,0x74,0x70,0x3A,0x2F,0x2F,
+    0x6F,0x63,0x73,0x70,0x2E,0x64,0x69,0x67,0x69,0x63,0x65,0x72,0x74,0x2E,0x63,0x6F,
+    0x6D,0x30,0x52,0x06,0x08,0x2B,0x06,0x01,0x05,0x05,0x07,0x30,0x02,0x86,0x46,0x68,
+    0x74,0x74,0x70,0x3A,0x2F,0x2F,0x63,0x61,0x63,0x65,0x72,0x74,0x73,0x2E,0x64,0x69,
+    0x67,0x69,0x63,0x65,0x72,0x74,0x2E,0x63,0x6F,0x6D,0x2F,0x44,0x69,0x67,0x69,0x43,
+    0x65,0x72,0x74,0x53,0x48,0x41,0x32,0x45,0x78,0x74,0x65,0x6E,0x64,0x65,0x64,0x56,
+    0x61,0x6C,0x69,0x64,0x61,0x74,0x69,0x6F,0x6E,0x53,0x65,0x72,0x76,0x65,0x72,0x43,
+    0x41,0x2E,0x63,0x72,0x74,0x30,0x09,0x06,0x03,0x55,0x1D,0x13,0x04,0x02,0x30,0x00,
+    0x30,0x82,0x01,0x03,0x06,0x0A,0x2B,0x06,0x01,0x04,0x01,0xD6,0x79,0x02,0x04,0x02,
+    0x04,0x81,0xF4,0x04,0x81,0xF1,0x00,0xEF,0x00,0x75,0x00,0xA4,0xB9,0x09,0x90,0xB4,
+    0x18,0x58,0x14,0x87,0xBB,0x13,0xA2,0xCC,0x67,0x70,0x0A,0x3C,0x35,0x98,0x04,0xF9,
+    0x1B,0xDF,0xB8,0xE3,0x77,0xCD,0x0E,0xC8,0x0D,0xDC,0x10,0x00,0x00,0x01,0x62,0x4E,
+    0xBC,0xC7,0x43,0x00,0x00,0x04,0x03,0x00,0x46,0x30,0x44,0x02,0x20,0x3E,0xD3,0xFE,
+    0xB4,0x45,0x97,0xCE,0xA3,0x05,0xC9,0x29,0x70,0x55,0x3B,0x77,0x9E,0xCC,0x4B,0x06,
+    0xFD,0x76,0x21,0xD0,0x79,0x69,0xD6,0x60,0x01,0xBB,0xC7,0x43,0x5E,0x02,0x20,0x3D,
+    0xB7,0x73,0x91,0x51,0xB7,0xAC,0x40,0xFB,0xA7,0x36,0xCF,0x10,0xE8,0x63,0x79,0xE6,
+    0x06,0xEC,0xFA,0x60,0xFE,0x44,0x90,0x9A,0x53,0x26,0x04,0x27,0x1A,0x4B,0xD4,0x00,
+    0x76,0x00,0x56,0x14,0x06,0x9A,0x2F,0xD7,0xC2,0xEC,0xD3,0xF5,0xE1,0xBD,0x44,0xB2,
+    0x3E,0xC7,0x46,0x76,0xB9,0xBC,0x99,0x11,0x5C,0xC0,0xEF,0x94,0x98,0x55,0xD6,0x89,
+    0xD0,0xDD,0x00,0x00,0x01,0x62,0x4E,0xBC,0xC8,0x6A,0x00,0x00,0x04,0x03,0x00,0x47,
+    0x30,0x45,0x02,0x20,0x18,0xF4,0x40,0x99,0x83,0xA7,0x53,0x6D,0x11,0xBF,0x7C,0xA5,
+    0x64,0x21,0x52,0xD0,0x7C,0xF5,0x96,0xCD,0xB0,0x56,0xAE,0x1E,0x13,0x9C,0xFC,0x08,
+    0x3B,0x56,0x66,0x0F,0x02,0x21,0x00,0x98,0xBE,0xC7,0x01,0x8E,0x88,0xB7,0xB2,0xF9,
+    0xC7,0xE6,0x08,0x46,0x10,0xEA,0x8D,0x01,0x12,0x0D,0x7D,0xA6,0xBA,0xA6,0xD3,0x1F,
+    0xEC,0xA3,0xD6,0x7A,0xE4,0x85,0x89,0x30,0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,
+    0x0D,0x01,0x01,0x0B,0x05,0x00,0x03,0x82,0x01,0x01,0x00,0x6A,0x52,0xE8,0xB3,0x70,
+    0x25,0xD9,0x95,0x87,0xC4,0xF3,0x14,0xB1,0x8C,0x29,0x6F,0x11,0xA0,0x6D,0xD7,0xE9,
+    0x96,0xFA,0x06,0x57,0x4D,0x86,0xE8,0xD6,0x95,0xC0,0x3A,0x33,0xEC,0x67,0x6C,0x98,
+    0xA2,0x99,0x09,0x28,0x16,0x18,0xEC,0x21,0xED,0x93,0xEF,0xAB,0x69,0x5F,0x98,0x9C,
+    0x62,0x21,0x10,0xEB,0x1C,0xC4,0x20,0x5E,0x3E,0x6F,0x80,0x03,0xC9,0xC5,0xD5,0x2F,
+    0xDA,0x8C,0x86,0x40,0x59,0x71,0x34,0xEE,0xCF,0x54,0x83,0xE1,0x3E,0x11,0x1D,0x12,
+    0x66,0x3D,0x16,0x7F,0xD8,0x1B,0x42,0x35,0x50,0xA6,0xD6,0xE6,0x6B,0x32,0xDE,0xE2,
+    0xD9,0x01,0xD7,0xB1,0xF5,0xE8,0x72,0x44,0x03,0xB5,0xFA,0x19,0x0D,0xF2,0x8D,0x0B,
+    0x75,0xBA,0xD9,0x39,0xFF,0x73,0xF2,0xA0,0xD6,0x49,0xEF,0x9E,0xE9,0x23,0x5D,0xED,
+    0xC5,0x08,0x69,0x10,0xF7,0xF0,0x0B,0x8D,0x80,0xB6,0x43,0xE4,0x2A,0xEC,0x92,0xCF,
+    0x22,0xCA,0xAF,0x1A,0x8A,0x16,0xC7,0xC5,0x88,0xB7,0xCA,0xC6,0x19,0x1A,0xD1,0xFF,
+    0x13,0x93,0x56,0x29,0x1A,0x48,0xA8,0x92,0x21,0xE5,0x7F,0x3E,0x4C,0xB4,0x89,0xD6,
+    0x08,0xF0,0xB0,0x4B,0x22,0x7C,0x1F,0xEC,0xE7,0x09,0x5D,0x23,0xE2,0xD1,0xB8,0xA3,
+    0xDF,0xEC,0x2D,0x87,0x1F,0xCD,0x58,0x1C,0xDD,0x09,0xE8,0xB5,0xD4,0x30,0x1E,0x2A,
+    0x4D,0xA3,0xA1,0x84,0x43,0xD7,0x4A,0x7B,0x15,0x8E,0xEB,0xE2,0x53,0x2B,0x12,0xCB,
+    0xFB,0xF2,0x61,0x7E,0x92,0x0B,0x87,0x07,0x1C,0x8D,0x0A,0xED,0x62,0x10,0x27,0xE3,
+    0xDB,0xC4,0x65,0xDE,0x57,0xFB,0x6D,0x49,0xC8,0x7A,0xF8,
+};
+
+/* subject:/C=US/O=DigiCert Inc/OU=www.digicert.com/CN=DigiCert SHA2 Extended Validation Server CA */
+/* issuer :/C=US/O=DigiCert Inc/OU=www.digicert.com/CN=DigiCert High Assurance EV Root CA */
+static unsigned char _exception_cert1[]={
+    0x30,0x82,0x04,0xB6,0x30,0x82,0x03,0x9E,0xA0,0x03,0x02,0x01,0x02,0x02,0x10,0x0C,
+    0x79,0xA9,0x44,0xB0,0x8C,0x11,0x95,0x20,0x92,0x61,0x5F,0xE2,0x6B,0x1D,0x83,0x30,
+    0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x0B,0x05,0x00,0x30,0x6C,
+    0x31,0x0B,0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x55,0x53,0x31,0x15,0x30,
+    0x13,0x06,0x03,0x55,0x04,0x0A,0x13,0x0C,0x44,0x69,0x67,0x69,0x43,0x65,0x72,0x74,
+    0x20,0x49,0x6E,0x63,0x31,0x19,0x30,0x17,0x06,0x03,0x55,0x04,0x0B,0x13,0x10,0x77,
+    0x77,0x77,0x2E,0x64,0x69,0x67,0x69,0x63,0x65,0x72,0x74,0x2E,0x63,0x6F,0x6D,0x31,
+    0x2B,0x30,0x29,0x06,0x03,0x55,0x04,0x03,0x13,0x22,0x44,0x69,0x67,0x69,0x43,0x65,
+    0x72,0x74,0x20,0x48,0x69,0x67,0x68,0x20,0x41,0x73,0x73,0x75,0x72,0x61,0x6E,0x63,
+    0x65,0x20,0x45,0x56,0x20,0x52,0x6F,0x6F,0x74,0x20,0x43,0x41,0x30,0x1E,0x17,0x0D,
+    0x31,0x33,0x31,0x30,0x32,0x32,0x31,0x32,0x30,0x30,0x30,0x30,0x5A,0x17,0x0D,0x32,
+    0x38,0x31,0x30,0x32,0x32,0x31,0x32,0x30,0x30,0x30,0x30,0x5A,0x30,0x75,0x31,0x0B,
+    0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x55,0x53,0x31,0x15,0x30,0x13,0x06,
+    0x03,0x55,0x04,0x0A,0x13,0x0C,0x44,0x69,0x67,0x69,0x43,0x65,0x72,0x74,0x20,0x49,
+    0x6E,0x63,0x31,0x19,0x30,0x17,0x06,0x03,0x55,0x04,0x0B,0x13,0x10,0x77,0x77,0x77,
+    0x2E,0x64,0x69,0x67,0x69,0x63,0x65,0x72,0x74,0x2E,0x63,0x6F,0x6D,0x31,0x34,0x30,
+    0x32,0x06,0x03,0x55,0x04,0x03,0x13,0x2B,0x44,0x69,0x67,0x69,0x43,0x65,0x72,0x74,
+    0x20,0x53,0x48,0x41,0x32,0x20,0x45,0x78,0x74,0x65,0x6E,0x64,0x65,0x64,0x20,0x56,
+    0x61,0x6C,0x69,0x64,0x61,0x74,0x69,0x6F,0x6E,0x20,0x53,0x65,0x72,0x76,0x65,0x72,
+    0x20,0x43,0x41,0x30,0x82,0x01,0x22,0x30,0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,
+    0x0D,0x01,0x01,0x01,0x05,0x00,0x03,0x82,0x01,0x0F,0x00,0x30,0x82,0x01,0x0A,0x02,
+    0x82,0x01,0x01,0x00,0xD7,0x53,0xA4,0x04,0x51,0xF8,0x99,0xA6,0x16,0x48,0x4B,0x67,
+    0x27,0xAA,0x93,0x49,0xD0,0x39,0xED,0x0C,0xB0,0xB0,0x00,0x87,0xF1,0x67,0x28,0x86,
+    0x85,0x8C,0x8E,0x63,0xDA,0xBC,0xB1,0x40,0x38,0xE2,0xD3,0xF5,0xEC,0xA5,0x05,0x18,
+    0xB8,0x3D,0x3E,0xC5,0x99,0x17,0x32,0xEC,0x18,0x8C,0xFA,0xF1,0x0C,0xA6,0x64,0x21,
+    0x85,0xCB,0x07,0x10,0x34,0xB0,0x52,0x88,0x2B,0x1F,0x68,0x9B,0xD2,0xB1,0x8F,0x12,
+    0xB0,0xB3,0xD2,0xE7,0x88,0x1F,0x1F,0xEF,0x38,0x77,0x54,0x53,0x5F,0x80,0x79,0x3F,
+    0x2E,0x1A,0xAA,0xA8,0x1E,0x4B,0x2B,0x0D,0xAB,0xB7,0x63,0xB9,0x35,0xB7,0x7D,0x14,
+    0xBC,0x59,0x4B,0xDF,0x51,0x4A,0xD2,0xA1,0xE2,0x0C,0xE2,0x90,0x82,0x87,0x6A,0xAE,
+    0xEA,0xD7,0x64,0xD6,0x98,0x55,0xE8,0xFD,0xAF,0x1A,0x50,0x6C,0x54,0xBC,0x11,0xF2,
+    0xFD,0x4A,0xF2,0x9D,0xBB,0x7F,0x0E,0xF4,0xD5,0xBE,0x8E,0x16,0x89,0x12,0x55,0xD8,
+    0xC0,0x71,0x34,0xEE,0xF6,0xDC,0x2D,0xEC,0xC4,0x87,0x25,0x86,0x8D,0xD8,0x21,0xE4,
+    0xB0,0x4D,0x0C,0x89,0xDC,0x39,0x26,0x17,0xDD,0xF6,0xD7,0x94,0x85,0xD8,0x04,0x21,
+    0x70,0x9D,0x6F,0x6F,0xFF,0x5C,0xBA,0x19,0xE1,0x45,0xCB,0x56,0x57,0x28,0x7E,0x1C,
+    0x0D,0x41,0x57,0xAA,0xB7,0xB8,0x27,0xBB,0xB1,0xE4,0xFA,0x2A,0xEF,0x21,0x23,0x75,
+    0x1A,0xAD,0x2D,0x9B,0x86,0x35,0x8C,0x9C,0x77,0xB5,0x73,0xAD,0xD8,0x94,0x2D,0xE4,
+    0xF3,0x0C,0x9D,0xEE,0xC1,0x4E,0x62,0x7E,0x17,0xC0,0x71,0x9E,0x2C,0xDE,0xF1,0xF9,
+    0x10,0x28,0x19,0x33,0x02,0x03,0x01,0x00,0x01,0xA3,0x82,0x01,0x49,0x30,0x82,0x01,
+    0x45,0x30,0x12,0x06,0x03,0x55,0x1D,0x13,0x01,0x01,0xFF,0x04,0x08,0x30,0x06,0x01,
+    0x01,0xFF,0x02,0x01,0x00,0x30,0x0E,0x06,0x03,0x55,0x1D,0x0F,0x01,0x01,0xFF,0x04,
+    0x04,0x03,0x02,0x01,0x86,0x30,0x1D,0x06,0x03,0x55,0x1D,0x25,0x04,0x16,0x30,0x14,
+    0x06,0x08,0x2B,0x06,0x01,0x05,0x05,0x07,0x03,0x01,0x06,0x08,0x2B,0x06,0x01,0x05,
+    0x05,0x07,0x03,0x02,0x30,0x34,0x06,0x08,0x2B,0x06,0x01,0x05,0x05,0x07,0x01,0x01,
+    0x04,0x28,0x30,0x26,0x30,0x24,0x06,0x08,0x2B,0x06,0x01,0x05,0x05,0x07,0x30,0x01,
+    0x86,0x18,0x68,0x74,0x74,0x70,0x3A,0x2F,0x2F,0x6F,0x63,0x73,0x70,0x2E,0x64,0x69,
+    0x67,0x69,0x63,0x65,0x72,0x74,0x2E,0x63,0x6F,0x6D,0x30,0x4B,0x06,0x03,0x55,0x1D,
+    0x1F,0x04,0x44,0x30,0x42,0x30,0x40,0xA0,0x3E,0xA0,0x3C,0x86,0x3A,0x68,0x74,0x74,
+    0x70,0x3A,0x2F,0x2F,0x63,0x72,0x6C,0x34,0x2E,0x64,0x69,0x67,0x69,0x63,0x65,0x72,
+    0x74,0x2E,0x63,0x6F,0x6D,0x2F,0x44,0x69,0x67,0x69,0x43,0x65,0x72,0x74,0x48,0x69,
+    0x67,0x68,0x41,0x73,0x73,0x75,0x72,0x61,0x6E,0x63,0x65,0x45,0x56,0x52,0x6F,0x6F,
+    0x74,0x43,0x41,0x2E,0x63,0x72,0x6C,0x30,0x3D,0x06,0x03,0x55,0x1D,0x20,0x04,0x36,
+    0x30,0x34,0x30,0x32,0x06,0x04,0x55,0x1D,0x20,0x00,0x30,0x2A,0x30,0x28,0x06,0x08,
+    0x2B,0x06,0x01,0x05,0x05,0x07,0x02,0x01,0x16,0x1C,0x68,0x74,0x74,0x70,0x73,0x3A,
+    0x2F,0x2F,0x77,0x77,0x77,0x2E,0x64,0x69,0x67,0x69,0x63,0x65,0x72,0x74,0x2E,0x63,
+    0x6F,0x6D,0x2F,0x43,0x50,0x53,0x30,0x1D,0x06,0x03,0x55,0x1D,0x0E,0x04,0x16,0x04,
+    0x14,0x3D,0xD3,0x50,0xA5,0xD6,0xA0,0xAD,0xEE,0xF3,0x4A,0x60,0x0A,0x65,0xD3,0x21,
+    0xD4,0xF8,0xF8,0xD6,0x0F,0x30,0x1F,0x06,0x03,0x55,0x1D,0x23,0x04,0x18,0x30,0x16,
+    0x80,0x14,0xB1,0x3E,0xC3,0x69,0x03,0xF8,0xBF,0x47,0x01,0xD4,0x98,0x26,0x1A,0x08,
+    0x02,0xEF,0x63,0x64,0x2B,0xC3,0x30,0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,
+    0x01,0x01,0x0B,0x05,0x00,0x03,0x82,0x01,0x01,0x00,0x9D,0xB6,0xD0,0x90,0x86,0xE1,
+    0x86,0x02,0xED,0xC5,0xA0,0xF0,0x34,0x1C,0x74,0xC1,0x8D,0x76,0xCC,0x86,0x0A,0xA8,
+    0xF0,0x4A,0x8A,0x42,0xD6,0x3F,0xC8,0xA9,0x4D,0xAD,0x7C,0x08,0xAD,0xE6,0xB6,0x50,
+    0xB8,0xA2,0x1A,0x4D,0x88,0x07,0xB1,0x29,0x21,0xDC,0xE7,0xDA,0xC6,0x3C,0x21,0xE0,
+    0xE3,0x11,0x49,0x70,0xAC,0x7A,0x1D,0x01,0xA4,0xCA,0x11,0x3A,0x57,0xAB,0x7D,0x57,
+    0x2A,0x40,0x74,0xFD,0xD3,0x1D,0x85,0x18,0x50,0xDF,0x57,0x47,0x75,0xA1,0x7D,0x55,
+    0x20,0x2E,0x47,0x37,0x50,0x72,0x8C,0x7F,0x82,0x1B,0xD2,0x62,0x8F,0x2D,0x03,0x5A,
+    0xDA,0xC3,0xC8,0xA1,0xCE,0x2C,0x52,0xA2,0x00,0x63,0xEB,0x73,0xBA,0x71,0xC8,0x49,
+    0x27,0x23,0x97,0x64,0x85,0x9E,0x38,0x0E,0xAD,0x63,0x68,0x3C,0xBA,0x52,0x81,0x58,
+    0x79,0xA3,0x2C,0x0C,0xDF,0xDE,0x6D,0xEB,0x31,0xF2,0xBA,0xA0,0x7C,0x6C,0xF1,0x2C,
+    0xD4,0xE1,0xBD,0x77,0x84,0x37,0x03,0xCE,0x32,0xB5,0xC8,0x9A,0x81,0x1A,0x4A,0x92,
+    0x4E,0x3B,0x46,0x9A,0x85,0xFE,0x83,0xA2,0xF9,0x9E,0x8C,0xA3,0xCC,0x0D,0x5E,0xB3,
+    0x3D,0xCF,0x04,0x78,0x8F,0x14,0x14,0x7B,0x32,0x9C,0xC7,0x00,0xA6,0x5C,0xC4,0xB5,
+    0xA1,0x55,0x8D,0x5A,0x56,0x68,0xA4,0x22,0x70,0xAA,0x3C,0x81,0x71,0xD9,0x9D,0xA8,
+    0x45,0x3B,0xF4,0xE5,0xF6,0xA2,0x51,0xDD,0xC7,0x7B,0x62,0xE8,0x6F,0x0C,0x74,0xEB,
+    0xB8,0xDA,0xF8,0xBF,0x87,0x0D,0x79,0x50,0x91,0x90,0x9B,0x18,0x3B,0x91,0x59,0x27,
+    0xF1,0x35,0x28,0x13,0xAB,0x26,0x7E,0xD5,0xF7,0x7A,
+};
+
+/* subject:/CN=self-signed.ssltest.apple.com/C=US */
+/* issuer :/CN=self-signed.ssltest.apple.com/C=US */
+static unsigned char _exception_self_signed[]={
+    0x30,0x82,0x03,0x0F,0x30,0x82,0x01,0xF7,0xA0,0x03,0x02,0x01,0x02,0x02,0x01,0x01,
+    0x30,0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x0B,0x05,0x00,0x30,
+    0x35,0x31,0x26,0x30,0x24,0x06,0x03,0x55,0x04,0x03,0x0C,0x1D,0x73,0x65,0x6C,0x66,
+    0x2D,0x73,0x69,0x67,0x6E,0x65,0x64,0x2E,0x73,0x73,0x6C,0x74,0x65,0x73,0x74,0x2E,
+    0x61,0x70,0x70,0x6C,0x65,0x2E,0x63,0x6F,0x6D,0x31,0x0B,0x30,0x09,0x06,0x03,0x55,
+    0x04,0x06,0x13,0x02,0x55,0x53,0x30,0x1E,0x17,0x0D,0x31,0x36,0x30,0x36,0x30,0x37,
+    0x32,0x31,0x35,0x33,0x30,0x38,0x5A,0x17,0x0D,0x31,0x37,0x30,0x36,0x30,0x37,0x32,
+    0x31,0x35,0x33,0x30,0x38,0x5A,0x30,0x35,0x31,0x26,0x30,0x24,0x06,0x03,0x55,0x04,
+    0x03,0x0C,0x1D,0x73,0x65,0x6C,0x66,0x2D,0x73,0x69,0x67,0x6E,0x65,0x64,0x2E,0x73,
+    0x73,0x6C,0x74,0x65,0x73,0x74,0x2E,0x61,0x70,0x70,0x6C,0x65,0x2E,0x63,0x6F,0x6D,
+    0x31,0x0B,0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x55,0x53,0x30,0x82,0x01,
+    0x22,0x30,0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x01,0x05,0x00,
+    0x03,0x82,0x01,0x0F,0x00,0x30,0x82,0x01,0x0A,0x02,0x82,0x01,0x01,0x00,0xCC,0x72,
+    0x7D,0x09,0x36,0x5A,0x6A,0xED,0xC1,0x7A,0x2C,0xF4,0x7C,0x58,0x63,0x05,0x3E,0x91,
+    0x68,0x55,0xB1,0x2A,0x5D,0x57,0xF3,0xA4,0xA7,0x80,0x05,0x41,0x74,0xB2,0xAD,0x5A,
+    0x7F,0x38,0xF6,0xF7,0xFD,0xF9,0x64,0x4D,0xDE,0xF9,0x7A,0xD3,0x8C,0x78,0xE9,0x71,
+    0xCF,0x1D,0x3E,0xF0,0xDB,0x12,0x48,0x74,0x22,0xA8,0x1F,0x3F,0xB9,0xDD,0xB0,0xAD,
+    0x8C,0x10,0x64,0x05,0x0E,0xE2,0x59,0x9A,0xEB,0x3F,0xBF,0xA9,0x48,0x07,0xD9,0x2C,
+    0x07,0x44,0x70,0x14,0x16,0x56,0x9C,0x73,0x01,0x2E,0x0B,0xF1,0x2A,0x9F,0x1C,0xC6,
+    0x78,0x56,0xB7,0x0B,0xDA,0xA6,0xE6,0x99,0x87,0x2D,0x49,0xFB,0xF0,0x47,0x22,0xA6,
+    0x8B,0xF0,0x02,0x37,0x31,0xD0,0x34,0x9F,0x43,0xD1,0x24,0x49,0x94,0x7F,0xFD,0x48,
+    0x9C,0xBA,0x5D,0x6B,0xD4,0xF9,0x9E,0xB5,0x18,0xE4,0xB2,0x06,0x46,0xC3,0xD9,0xE7,
+    0x80,0xD8,0x61,0xA9,0x09,0x5E,0xBA,0x2E,0x58,0x56,0xAE,0x37,0x31,0x6E,0x87,0x98,
+    0xD5,0xC9,0x2B,0x31,0x5C,0x40,0x01,0xDF,0xD5,0x63,0x9E,0x05,0x18,0x21,0x53,0x70,
+    0x62,0x36,0x44,0xCD,0x02,0xC0,0xCC,0x6A,0x58,0xC6,0xF6,0xA4,0xDC,0x89,0x94,0xBD,
+    0x4E,0xC4,0xEE,0xEE,0x40,0x31,0x59,0xC3,0x43,0xAD,0x34,0x30,0xDE,0xA9,0xA7,0x0D,
+    0x85,0xF7,0x96,0x8C,0x45,0xC1,0x6E,0x85,0x39,0x97,0xA6,0x4F,0xEA,0xE8,0x2F,0x01,
+    0x3D,0xC0,0x3B,0x34,0x9F,0x8F,0xCB,0xD6,0x22,0x79,0x2C,0x8C,0x8C,0xE6,0xBB,0x1F,
+    0x89,0x87,0x93,0x3B,0x39,0x4E,0x64,0x7D,0xDA,0x4D,0x52,0x4C,0x97,0xE5,0x02,0x03,
+    0x01,0x00,0x01,0xA3,0x2A,0x30,0x28,0x30,0x0E,0x06,0x03,0x55,0x1D,0x0F,0x01,0x01,
+    0xFF,0x04,0x04,0x03,0x02,0x07,0x80,0x30,0x16,0x06,0x03,0x55,0x1D,0x25,0x01,0x01,
+    0xFF,0x04,0x0C,0x30,0x0A,0x06,0x08,0x2B,0x06,0x01,0x05,0x05,0x07,0x03,0x01,0x30,
+    0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x0B,0x05,0x00,0x03,0x82,
+    0x01,0x01,0x00,0x36,0x06,0xC9,0xE6,0x98,0xC2,0x84,0x1D,0x13,0x1E,0x54,0x35,0x6D,
+    0xE5,0xCB,0xC5,0xFD,0xD9,0x54,0x45,0x83,0x53,0xB3,0x3B,0xE7,0x30,0x6F,0xAE,0xEA,
+    0x63,0x3F,0xA8,0xFA,0xD9,0x6D,0x0F,0x7D,0xD4,0xB6,0x28,0x66,0xF9,0x57,0x87,0x3E,
+    0x57,0x27,0xB6,0x9A,0x56,0xAE,0xD7,0xE0,0x11,0x20,0x71,0xC1,0xEA,0xF6,0xED,0x74,
+    0x1A,0x5A,0xB1,0x74,0x6C,0xBE,0xAC,0x0E,0x3C,0xD9,0x3E,0xEC,0x17,0x6E,0xF0,0x69,
+    0xC9,0x4D,0xD2,0x7E,0xAE,0x8B,0x01,0xCC,0x1A,0x23,0x7C,0x58,0x07,0x30,0xE4,0x2A,
+    0x12,0xE8,0xA0,0x25,0x65,0x66,0xB5,0xC7,0x5D,0xD8,0x47,0xDF,0xD7,0x51,0xBC,0xA2,
+    0xAA,0xF0,0x2F,0xB5,0x9E,0x20,0x6D,0x1F,0x84,0x00,0xF0,0xD0,0xB8,0x42,0x6A,0x9A,
+    0xE7,0xCA,0x7B,0xE5,0x39,0x09,0x91,0xBF,0xCB,0x4D,0x7A,0x32,0x1E,0x00,0x6E,0xE5,
+    0xF7,0x44,0x80,0x82,0x38,0x53,0x64,0xB7,0x26,0x81,0xCB,0xCE,0xA1,0xAF,0x0C,0x67,
+    0x32,0xC6,0xE4,0x5D,0x09,0x7B,0x37,0xD7,0xC8,0x43,0x44,0xEF,0xC6,0xF8,0x72,0xFF,
+    0x65,0xD4,0x39,0x3D,0xEC,0x72,0xA5,0x28,0xFF,0x70,0x47,0x38,0xA3,0xC7,0xCC,0x5E,
+    0x0F,0xFF,0x43,0x83,0x78,0x49,0x68,0x90,0x48,0x89,0xAD,0xE1,0x2E,0xFA,0x8F,0x59,
+    0xB6,0x08,0x2A,0x72,0x2F,0x52,0x3F,0x73,0x84,0xCA,0xD8,0x18,0x6C,0xDA,0xA3,0x2E,
+    0xF2,0xD7,0x4C,0x21,0xD9,0xF8,0xB1,0x86,0xE9,0x35,0x78,0xE4,0x4F,0xD0,0x93,0x11,
+    0x8F,0xF4,0xB1,0x17,0x4F,0xDE,0xAC,0xBD,0xA9,0xBC,0x94,0xFC,0x2E,0x7D,0xF9,0x05,
+    0x26,0x90,0xF1,
+};
+
+#endif /* _TRUSTTESTS_EXCEPTION_TESTS_H_ */
index 1bdff251052b289534c67404b830bc4ad07cccbb..e3b239b102434b6d82423753e62c20a9b0d082f7 100644 (file)
@@ -638,6 +638,70 @@ errOut:
     CFReleaseNull(error);
 }
 
     CFReleaseNull(error);
 }
 
+- (void) test_revocation_checked_via_cache {
+    if (!ping_host("ocsp.digicert.com")) {
+        XCTAssert(false, "Unable to contact required network resource");
+        return;
+    }
+
+    SecCertificateRef leaf = NULL, subCA = NULL, root = NULL;
+    SecPolicyRef sslPolicy = NULL, ocspPolicy = NULL;
+    SecTrustRef trust = NULL;
+    CFArrayRef certs = NULL, anchors = NULL, policies = NULL;
+    CFDateRef verifyDate = NULL;
+    CFErrorRef error = NULL;
+
+    leaf = SecCertificateCreateWithBytes(NULL, _ocsp_c0, sizeof(_ocsp_c0));
+    subCA = SecCertificateCreateWithBytes(NULL, _ocsp_c1, sizeof(_ocsp_c1));
+    root = SecCertificateCreateWithBytes(NULL, _ocsp_c2, sizeof(_ocsp_c2));
+
+    sslPolicy = SecPolicyCreateSSL(true, CFSTR("www.apple.com"));
+    ocspPolicy = SecPolicyCreateRevocation(kSecRevocationOCSPMethod);
+
+    const void *v_certs[] = { leaf, subCA };
+    const void *v_anchors[] = { root };
+    const void *v_policies[] = { sslPolicy, ocspPolicy };
+
+    certs = CFArrayCreate(NULL, v_certs, 2, &kCFTypeArrayCallBacks);
+    policies = CFArrayCreate(NULL, v_policies, 2, &kCFTypeArrayCallBacks);
+    require_noerr_action(SecTrustCreateWithCertificates(certs, policies, &trust), errOut, fail("failed to create trust object"));
+
+    anchors = CFArrayCreate(NULL, v_anchors, 1, &kCFTypeArrayCallBacks);
+    require_noerr_action(SecTrustSetAnchorCertificates(trust, anchors), errOut, fail("failed to set anchors"));
+
+    verifyDate = CFDateCreate(NULL, 577000000.0); // April 14, 2019 at 10:46:40 PM PDT
+    require_noerr_action(SecTrustSetVerifyDate(trust, verifyDate), errOut, fail("failed to set verify date"));
+
+    is(SecTrustEvaluateWithError(trust, &error), true, "valid cert failed");
+
+    /* Set no fetch allowed, so we're relying on the cached response from above */
+    require_noerr_action(SecTrustSetNetworkFetchAllowed(trust, false), errOut, fail("failed to set network fetch disallowed"));
+
+    /* Evaluate trust. Cached response should tell us that it's revoked. */
+    is(SecTrustEvaluateWithError(trust, &error), true, "valid cert failed");
+
+    /* Verify that the results dictionary contains the kSecTrustRevocationChecked key for a valid cert where revocation checked */
+    CFDictionaryRef result = SecTrustCopyResult(trust);
+    isnt(result, NULL, "failed to copy result dictionary");
+    if (result) {
+        is(CFDictionaryGetValue(result, kSecTrustRevocationChecked), kCFBooleanTrue, "expected revocation checked flag");
+    }
+    CFReleaseNull(result);
+
+errOut:
+    CFReleaseNull(leaf);
+    CFReleaseNull(subCA);
+    CFReleaseNull(root);
+    CFReleaseNull(ocspPolicy);
+    CFReleaseNull(sslPolicy);
+    CFReleaseNull(trust);
+    CFReleaseNull(certs);
+    CFReleaseNull(anchors);
+    CFReleaseNull(policies);
+    CFReleaseNull(verifyDate);
+    CFReleaseNull(error);
+}
+
 #else  /* TARGET_OS_WATCH || TARGET_OS_BRIDGE */
 - (void)testNoNetworking
 {
 #else  /* TARGET_OS_WATCH || TARGET_OS_BRIDGE */
 - (void)testNoNetworking
 {
@@ -680,7 +744,89 @@ errOut:
     CFReleaseNull(verifyDate);
     CFReleaseNull(error);
 }
     CFReleaseNull(verifyDate);
     CFReleaseNull(error);
 }
-#endif
+#endif /* !TARGET_OS_WATCH && !TARGET_OS_BRIDGE */
+
+#if !TARGET_OS_BRIDGE
+/* bridgeOS doesn't use Valid */
+- (NSNumber *)runRevocationCheckNoNetwork:(SecCertificateRef)leaf
+                                    subCA:(SecCertificateRef)subCA
+{
+    CFArrayRef anchors = NULL;
+    SecPolicyRef smimePolicy = NULL, revocationPolicy = NULL;
+    CFArrayRef certs = NULL;
+    SecTrustRef trust = NULL;
+    CFDateRef date = NULL;
+    CFErrorRef error = NULL;
+    NSArray *policies = nil;
+    NSDictionary *result = nil;
+    NSNumber *revocationChecked = nil;
+
+    const void *v_certs[] = { leaf };
+    require_action(certs = CFArrayCreate(NULL, v_certs, array_size(v_certs), &kCFTypeArrayCallBacks), errOut,
+                   fail("unable to create certificates array"));
+    require_action(anchors = CFArrayCreate(NULL, (const void **)&subCA, 1, &kCFTypeArrayCallBacks), errOut,
+                   fail("unable to create anchors array"));
+
+    require_action(smimePolicy = SecPolicyCreateSMIME(kSecSignSMIMEUsage, NULL), errOut, fail("unable to create policy"));
+    revocationPolicy = SecPolicyCreateRevocation(kSecRevocationUseAnyAvailableMethod | kSecRevocationCheckIfTrusted);
+    policies = @[(__bridge id)smimePolicy, (__bridge id)revocationPolicy];
+    ok_status(SecTrustCreateWithCertificates(certs, (__bridge CFArrayRef)policies, &trust), "failed to create trust");
+    ok_status(SecTrustSetNetworkFetchAllowed(trust, false), "SecTrustSetNetworkFetchAllowed failed");
+
+    require_noerr_action(SecTrustSetAnchorCertificates(trust, anchors), errOut,
+                         fail("unable to set anchors"));
+    ok(SecTrustEvaluateWithError(trust, &error), "Not trusted");
+    result = CFBridgingRelease(SecTrustCopyResult(trust));
+    revocationChecked = result[(__bridge NSString *)kSecTrustRevocationChecked];
+
+errOut:
+    CFReleaseNull(anchors);
+    CFReleaseNull(smimePolicy);
+    CFReleaseNull(revocationPolicy);
+    CFReleaseNull(certs);
+    CFReleaseNull(trust);
+    CFReleaseNull(date);
+    CFReleaseNull(error);
+
+    return revocationChecked;
+}
+
+- (void) test_revocation_checked_via_valid {
+    SecCertificateRef leaf = NULL, subCA = NULL;
+    NSNumber *revocationChecked = NULL;
+
+    require_action(leaf = SecCertificateCreateWithBytes(NULL, _leaf_sha256_valid_cav2_complete_ok1, sizeof(_leaf_sha256_valid_cav2_complete_ok1)), errOut,
+                   fail("unable to create cert"));
+    require_action(subCA = SecCertificateCreateWithBytes(NULL, _ca_sha256_valid_cav2_complete, sizeof(_ca_sha256_valid_cav2_complete)), errOut, fail("unable to create cert"));
+
+    revocationChecked = [self runRevocationCheckNoNetwork:leaf
+                                                    subCA:subCA];
+    XCTAssert(revocationChecked != NULL, "kSecTrustRevocationChecked is not in the result dictionary");
+
+errOut:
+    CFReleaseNull(leaf);
+    CFReleaseNull(subCA);
+}
+
+- (void) test_revocation_not_checked_no_network {
+    /* The intermediate does not have the noCAv2 flag and is "probably not revoked,", so
+       kSecTrustRevocationChecked should not be in the results dictionary */
+    SecCertificateRef leaf = NULL, subCA = NULL;
+    NSNumber *revocationChecked = NULL;
+
+    require_action(leaf = SecCertificateCreateWithBytes(NULL, _leaf_serial_invalid_incomplete_ok1, sizeof(_leaf_serial_invalid_incomplete_ok1)), errOut,
+                   fail("unable to create cert"));
+    require_action(subCA = SecCertificateCreateWithBytes(NULL, _ca_serial_invalid_incomplete, sizeof(_ca_serial_invalid_incomplete)), errOut, fail("unable to create cert"));
+
+    revocationChecked = [self runRevocationCheckNoNetwork:leaf
+                                                    subCA:subCA];
+    XCTAssert(revocationChecked == NULL, "kSecTrustRevocationChecked is in the result dictionary");
+
+errOut:
+    CFReleaseNull(leaf);
+    CFReleaseNull(subCA);
+}
+#endif /* !TARGET_OS_BRIDGE */
 
 /* bridgeOS and watchOS do not support networked OCSP but do support stapling */
 - (void) test_stapled_revoked_response {
 
 /* bridgeOS and watchOS do not support networked OCSP but do support stapling */
 - (void) test_stapled_revoked_response {
index 90d878450e678935f1e97b73b3c51b212803d793..53a48d99b6ef130d52ed17961bce7a95a546378a 100644 (file)
@@ -1284,4 +1284,298 @@ uint8_t _devID_OCSPResponse[] = {
     0x44,0xc9,0x27,0x73,0x07,0xee,0x82,0xe5,0x4e,0xf5,0x70
 };
 
     0x44,0xc9,0x27,0x73,0x07,0xee,0x82,0xe5,0x4e,0xf5,0x70
 };
 
+uint8_t _leaf_sha256_valid_cav2_complete_ok1[] = {
+    0x30,0x82,0x04,0x7b,0x30,0x82,0x03,0x63,0xa0,0x03,0x02,0x01,0x02,0x02,0x05,0x00,
+    0xa4,0x37,0x10,0xc8,0x30,0x0d,0x06,0x09,0x2a,0x86,0x48,0x86,0xf7,0x0d,0x01,0x01,
+    0x0b,0x05,0x00,0x30,0x81,0x8f,0x31,0x26,0x30,0x24,0x06,0x03,0x55,0x04,0x03,0x0c,
+    0x1d,0x43,0x41,0x20,0x73,0x68,0x61,0x32,0x35,0x36,0x20,0x76,0x61,0x6c,0x69,0x64,
+    0x20,0x63,0x61,0x76,0x32,0x20,0x63,0x6f,0x6d,0x70,0x6c,0x65,0x74,0x65,0x31,0x22,
+    0x30,0x20,0x06,0x03,0x55,0x04,0x0b,0x0c,0x19,0x56,0x61,0x6c,0x69,0x64,0x20,0x50,
+    0x72,0x6f,0x6a,0x65,0x63,0x74,0x20,0x54,0x65,0x73,0x74,0x20,0x43,0x41,0x73,0x20,
+    0x56,0x32,0x31,0x13,0x30,0x11,0x06,0x03,0x55,0x04,0x0a,0x0c,0x0a,0x41,0x70,0x70,
+    0x6c,0x65,0x20,0x49,0x6e,0x63,0x2e,0x31,0x12,0x30,0x10,0x06,0x03,0x55,0x04,0x07,
+    0x0c,0x09,0x43,0x75,0x70,0x65,0x72,0x74,0x69,0x6e,0x6f,0x31,0x0b,0x30,0x09,0x06,
+    0x03,0x55,0x04,0x08,0x0c,0x02,0x43,0x41,0x31,0x0b,0x30,0x09,0x06,0x03,0x55,0x04,
+    0x06,0x13,0x02,0x55,0x53,0x30,0x20,0x17,0x0d,0x31,0x38,0x30,0x36,0x31,0x32,0x32,
+    0x33,0x32,0x38,0x32,0x32,0x5a,0x18,0x0f,0x32,0x31,0x31,0x38,0x30,0x35,0x31,0x39,
+    0x32,0x33,0x32,0x38,0x32,0x32,0x5a,0x30,0x81,0x95,0x31,0x2c,0x30,0x2a,0x06,0x03,
+    0x55,0x04,0x03,0x0c,0x23,0x4c,0x65,0x61,0x66,0x20,0x73,0x68,0x61,0x32,0x35,0x36,
+    0x20,0x76,0x61,0x6c,0x69,0x64,0x20,0x63,0x61,0x76,0x32,0x20,0x63,0x6f,0x6d,0x70,
+    0x6c,0x65,0x74,0x65,0x20,0x6f,0x6b,0x31,0x31,0x22,0x30,0x20,0x06,0x03,0x55,0x04,
+    0x0b,0x0c,0x19,0x56,0x61,0x6c,0x69,0x64,0x20,0x50,0x72,0x6f,0x6a,0x65,0x63,0x74,
+    0x20,0x54,0x65,0x73,0x74,0x20,0x43,0x41,0x73,0x20,0x56,0x32,0x31,0x13,0x30,0x11,
+    0x06,0x03,0x55,0x04,0x0a,0x0c,0x0a,0x41,0x70,0x70,0x6c,0x65,0x20,0x49,0x6e,0x63,
+    0x2e,0x31,0x12,0x30,0x10,0x06,0x03,0x55,0x04,0x07,0x0c,0x09,0x43,0x75,0x70,0x65,
+    0x72,0x74,0x69,0x6e,0x6f,0x31,0x0b,0x30,0x09,0x06,0x03,0x55,0x04,0x08,0x0c,0x02,
+    0x43,0x41,0x31,0x0b,0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x55,0x53,0x30,
+    0x82,0x01,0x22,0x30,0x0d,0x06,0x09,0x2a,0x86,0x48,0x86,0xf7,0x0d,0x01,0x01,0x01,
+    0x05,0x00,0x03,0x82,0x01,0x0f,0x00,0x30,0x82,0x01,0x0a,0x02,0x82,0x01,0x01,0x00,
+    0xc3,0x8b,0xfa,0x6c,0xa7,0x18,0xd3,0xbc,0x41,0x58,0x47,0x9c,0xa0,0x5a,0x21,0xb0,
+    0xaa,0x28,0xd3,0x2b,0xb3,0x57,0xdf,0x5b,0x0f,0x18,0xa8,0xa9,0x5a,0x01,0x9a,0x15,
+    0x0b,0x62,0x7f,0x51,0x7d,0x85,0x98,0x48,0x74,0xdf,0xa4,0xb7,0xf2,0x4c,0x88,0x10,
+    0xe0,0x41,0x31,0x77,0x64,0x2f,0xe2,0x47,0x3a,0x9b,0xf9,0xf4,0xf3,0xf9,0x3f,0x09,
+    0xbc,0x33,0xbb,0xbc,0xf9,0x75,0x7c,0xc8,0x4c,0x95,0xd2,0xa2,0xc3,0xc0,0x0a,0x5f,
+    0xf4,0x3b,0x50,0x95,0x53,0xf8,0xf2,0x77,0xce,0x02,0x06,0xbf,0x9e,0x74,0x2e,0x58,
+    0x6e,0xff,0x29,0xb6,0x80,0x6a,0xe4,0x07,0xaf,0x41,0x5b,0x91,0x49,0x30,0x3f,0x5b,
+    0x1b,0x2d,0xab,0x6a,0xf5,0x48,0x44,0x87,0xf0,0xaf,0xa4,0xb3,0x41,0x28,0x9f,0xf6,
+    0xae,0x35,0x0c,0xf6,0x09,0x17,0x34,0x55,0x35,0x50,0xf1,0x57,0xe7,0x6d,0x18,0x24,
+    0x35,0x40,0xc3,0xf5,0x41,0xe7,0x9d,0x76,0x33,0xcc,0xaa,0x85,0x3f,0x80,0x6e,0x7f,
+    0x55,0x62,0xf3,0x6d,0x61,0x26,0x90,0x4f,0x7d,0x73,0xc1,0x0b,0xae,0xf5,0xf8,0x04,
+    0x1a,0xbb,0x88,0x50,0x79,0xfd,0x59,0xe3,0xfc,0x58,0x4b,0xb6,0x20,0x5f,0x26,0xc6,
+    0x50,0x31,0x06,0x84,0x8a,0xa9,0xd4,0x1d,0x9b,0x7a,0x55,0x2e,0x52,0xfe,0x5b,0x95,
+    0xdf,0x0b,0x03,0xf3,0xcc,0xca,0x9b,0xb5,0x75,0x66,0x16,0x67,0x11,0x63,0x39,0xf9,
+    0x77,0x95,0x30,0x0a,0x74,0x9c,0xd4,0xc8,0x81,0x75,0xd3,0x3f,0x59,0x8a,0xea,0x17,
+    0x06,0xb3,0xc1,0x20,0x81,0xef,0xbe,0xde,0x91,0x44,0x7c,0x6a,0x49,0xae,0xf2,0x25,
+    0x02,0x03,0x01,0x00,0x01,0xa3,0x81,0xd3,0x30,0x81,0xd0,0x30,0x0e,0x06,0x03,0x55,
+    0x1d,0x0f,0x01,0x01,0xff,0x04,0x04,0x03,0x02,0x07,0x80,0x30,0x0c,0x06,0x03,0x55,
+    0x1d,0x13,0x01,0x01,0xff,0x04,0x02,0x30,0x00,0x30,0x1f,0x06,0x03,0x55,0x1d,0x23,
+    0x04,0x18,0x30,0x16,0x80,0x14,0x39,0xbb,0x91,0xb5,0x14,0x54,0x76,0x0c,0x1c,0x0c,
+    0xf4,0x4b,0xbc,0xd9,0xf6,0x51,0xc8,0xe2,0x03,0xfc,0x30,0x54,0x06,0x03,0x55,0x1d,
+    0x1f,0x04,0x4d,0x30,0x4b,0x30,0x49,0xa0,0x47,0xa0,0x45,0x86,0x43,0x68,0x74,0x74,
+    0x70,0x3a,0x2f,0x2f,0x76,0x61,0x6c,0x69,0x64,0x74,0x65,0x73,0x74,0x2e,0x61,0x70,
+    0x70,0x6c,0x65,0x2e,0x67,0x65,0x6f,0x66,0x66,0x6b,0x2e,0x6e,0x65,0x74,0x2f,0x76,
+    0x32,0x2d,0x73,0x68,0x61,0x32,0x35,0x36,0x2d,0x76,0x61,0x6c,0x69,0x64,0x2d,0x63,
+    0x61,0x76,0x32,0x2d,0x63,0x6f,0x6d,0x70,0x6c,0x65,0x74,0x65,0x2e,0x63,0x72,0x6c,
+    0x30,0x39,0x06,0x08,0x2b,0x06,0x01,0x05,0x05,0x07,0x01,0x01,0x04,0x2d,0x30,0x2b,
+    0x30,0x29,0x06,0x08,0x2b,0x06,0x01,0x05,0x05,0x07,0x30,0x01,0x86,0x1d,0x68,0x74,
+    0x74,0x70,0x3a,0x2f,0x2f,0x6b,0x6e,0x6f,0x77,0x6e,0x2d,0x61,0x6e,0x73,0x77,0x65,
+    0x72,0x2d,0x6f,0x63,0x73,0x70,0x2f,0x6f,0x63,0x73,0x70,0x30,0x0d,0x06,0x09,0x2a,
+    0x86,0x48,0x86,0xf7,0x0d,0x01,0x01,0x0b,0x05,0x00,0x03,0x82,0x01,0x01,0x00,0x02,
+    0xc7,0x2c,0xec,0x13,0xcc,0x7c,0x54,0xc6,0xe5,0x00,0x83,0x9b,0xc4,0xe5,0x4e,0xc3,
+    0xb1,0x3c,0x1d,0x95,0x2f,0x45,0xe7,0x75,0xaf,0x04,0x89,0xdb,0x05,0x21,0x13,0x12,
+    0x75,0x1a,0x52,0xc1,0x31,0x74,0x89,0x87,0xd4,0xfa,0xea,0x6b,0x6f,0x61,0xb1,0xb1,
+    0x21,0x49,0xd7,0x25,0xfd,0x31,0xc5,0xf7,0x31,0x1a,0xa5,0x9f,0x08,0xc3,0x16,0x84,
+    0x3e,0x93,0x7a,0xdd,0xc4,0x28,0xe2,0xec,0x90,0x70,0x6b,0x58,0x8f,0xdf,0x1f,0x1d,
+    0x46,0xf2,0x23,0xa2,0x2f,0x29,0xd4,0xe2,0xd1,0xa2,0x6e,0x20,0x94,0x91,0xa2,0x03,
+    0x81,0x04,0xf9,0x4e,0x36,0x4b,0x75,0x2e,0x3a,0x13,0x0a,0x94,0xf8,0x29,0x79,0x0d,
+    0xac,0x4a,0xff,0xb6,0x60,0x59,0xa4,0x10,0xea,0x39,0xdd,0x39,0x06,0x7c,0xbc,0x91,
+    0x5f,0xbd,0xa7,0x8c,0x9b,0xf5,0xbd,0x17,0x80,0xde,0x8f,0x71,0x22,0x6d,0xef,0xfe,
+    0x01,0x04,0xf2,0x7c,0xe1,0x35,0xd1,0x21,0x64,0x4d,0xd7,0xf0,0x43,0x26,0x0b,0x18,
+    0x78,0xc0,0xe8,0xad,0x0e,0xc5,0x38,0xb5,0xcc,0xaf,0x04,0x7a,0x78,0xbc,0x1b,0x89,
+    0x15,0x1f,0x1e,0x61,0x41,0xb6,0x46,0x8e,0x8b,0xd9,0xae,0x69,0x5c,0x9d,0x2b,0x83,
+    0x71,0x25,0xd4,0x48,0x8d,0xfc,0x48,0x51,0x60,0xb3,0xa9,0xf7,0x53,0xf2,0x9b,0xa7,
+    0x32,0x56,0x5f,0xea,0xd6,0x99,0xd0,0xef,0x2c,0x41,0x9d,0xe0,0x68,0x9f,0x8e,0xad,
+    0x86,0x90,0xaa,0x24,0x5e,0xef,0xb0,0xc7,0x15,0xf2,0xce,0x2d,0x54,0x0c,0x8e,0x5e,
+    0x3f,0x63,0xdc,0x74,0x35,0x0e,0x35,0x0d,0xfc,0x0b,0x45,0x56,0x5b,0xb1,0xe2
+};
+
+uint8_t _ca_sha256_valid_cav2_complete[] = {
+    0x30,0x82,0x04,0x41,0x30,0x82,0x03,0x29,0xa0,0x03,0x02,0x01,0x02,0x02,0x05,0x00,
+    0xd3,0x18,0x1d,0xb8,0x30,0x0d,0x06,0x09,0x2a,0x86,0x48,0x86,0xf7,0x0d,0x01,0x01,
+    0x0b,0x05,0x00,0x30,0x81,0x87,0x31,0x1e,0x30,0x1c,0x06,0x03,0x55,0x04,0x03,0x0c,
+    0x15,0x56,0x61,0x6c,0x69,0x64,0x20,0x54,0x65,0x73,0x74,0x20,0x43,0x41,0x20,0x52,
+    0x6f,0x6f,0x74,0x20,0x56,0x32,0x31,0x22,0x30,0x20,0x06,0x03,0x55,0x04,0x0b,0x0c,
+    0x19,0x56,0x61,0x6c,0x69,0x64,0x20,0x50,0x72,0x6f,0x6a,0x65,0x63,0x74,0x20,0x54,
+    0x65,0x73,0x74,0x20,0x43,0x41,0x73,0x20,0x56,0x32,0x31,0x13,0x30,0x11,0x06,0x03,
+    0x55,0x04,0x0a,0x0c,0x0a,0x41,0x70,0x70,0x6c,0x65,0x20,0x49,0x6e,0x63,0x2e,0x31,
+    0x12,0x30,0x10,0x06,0x03,0x55,0x04,0x07,0x0c,0x09,0x43,0x75,0x70,0x65,0x72,0x74,
+    0x69,0x6e,0x6f,0x31,0x0b,0x30,0x09,0x06,0x03,0x55,0x04,0x08,0x0c,0x02,0x43,0x41,
+    0x31,0x0b,0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x55,0x53,0x30,0x20,0x17,
+    0x0d,0x31,0x38,0x30,0x36,0x31,0x32,0x32,0x33,0x32,0x38,0x32,0x31,0x5a,0x18,0x0f,
+    0x32,0x31,0x31,0x38,0x30,0x35,0x31,0x39,0x32,0x33,0x32,0x38,0x32,0x31,0x5a,0x30,
+    0x81,0x8f,0x31,0x26,0x30,0x24,0x06,0x03,0x55,0x04,0x03,0x0c,0x1d,0x43,0x41,0x20,
+    0x73,0x68,0x61,0x32,0x35,0x36,0x20,0x76,0x61,0x6c,0x69,0x64,0x20,0x63,0x61,0x76,
+    0x32,0x20,0x63,0x6f,0x6d,0x70,0x6c,0x65,0x74,0x65,0x31,0x22,0x30,0x20,0x06,0x03,
+    0x55,0x04,0x0b,0x0c,0x19,0x56,0x61,0x6c,0x69,0x64,0x20,0x50,0x72,0x6f,0x6a,0x65,
+    0x63,0x74,0x20,0x54,0x65,0x73,0x74,0x20,0x43,0x41,0x73,0x20,0x56,0x32,0x31,0x13,
+    0x30,0x11,0x06,0x03,0x55,0x04,0x0a,0x0c,0x0a,0x41,0x70,0x70,0x6c,0x65,0x20,0x49,
+    0x6e,0x63,0x2e,0x31,0x12,0x30,0x10,0x06,0x03,0x55,0x04,0x07,0x0c,0x09,0x43,0x75,
+    0x70,0x65,0x72,0x74,0x69,0x6e,0x6f,0x31,0x0b,0x30,0x09,0x06,0x03,0x55,0x04,0x08,
+    0x0c,0x02,0x43,0x41,0x31,0x0b,0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x55,
+    0x53,0x30,0x82,0x01,0x22,0x30,0x0d,0x06,0x09,0x2a,0x86,0x48,0x86,0xf7,0x0d,0x01,
+    0x01,0x01,0x05,0x00,0x03,0x82,0x01,0x0f,0x00,0x30,0x82,0x01,0x0a,0x02,0x82,0x01,
+    0x01,0x00,0xb6,0xfb,0x13,0xd4,0x51,0x5a,0xe0,0x4e,0x35,0xb6,0xe0,0xc9,0xc3,0x78,
+    0x5f,0xec,0x24,0x23,0xf2,0x40,0x0e,0xf8,0x6c,0x7f,0x9a,0xc8,0x71,0xd8,0x0a,0x3e,
+    0xdb,0xf4,0x85,0xa5,0x11,0x0e,0x5e,0x89,0xd8,0xd9,0x86,0x5f,0xec,0x85,0xc1,0x36,
+    0x4c,0xa5,0x40,0x8b,0x2a,0x66,0xe2,0x41,0x07,0x08,0x74,0xb2,0xe0,0x75,0x5e,0x7b,
+    0xe0,0x10,0xf0,0x7f,0x69,0x44,0xa5,0xe5,0xfc,0xd4,0x00,0xc7,0x7e,0xe7,0xc5,0x50,
+    0xcf,0xc0,0xe1,0xac,0x84,0xe9,0x08,0x2a,0xba,0x36,0xc4,0x60,0x37,0x07,0x91,0xe0,
+    0xcd,0x55,0xef,0x3a,0xcf,0x73,0xb6,0xd6,0x60,0x46,0xa4,0x8a,0x65,0x6d,0xfc,0x77,
+    0xa1,0xa0,0x12,0xef,0x20,0xc3,0xc3,0xf5,0x82,0x88,0x36,0x42,0x61,0xec,0x3d,0x3a,
+    0xaf,0x73,0x55,0xda,0x4c,0xca,0x86,0x3d,0xbf,0x47,0x1d,0xdf,0x1f,0x41,0xbf,0x2a,
+    0x97,0x84,0x85,0x13,0x1e,0x63,0xc5,0x7e,0x2a,0xb4,0xd4,0x56,0x2b,0xa1,0x93,0xc3,
+    0xb8,0xb7,0x15,0x71,0xee,0xdc,0xe6,0x8b,0x08,0xe5,0xc9,0x04,0x9f,0xf6,0x87,0xd1,
+    0xbf,0x78,0xf8,0x91,0x38,0x43,0x57,0x4b,0x8a,0x12,0x66,0xd6,0xae,0xf2,0xf1,0xa8,
+    0x0b,0x1a,0x81,0xf6,0xfd,0x20,0x28,0x5f,0xaf,0xa8,0xe7,0xbc,0x90,0x09,0x85,0xd4,
+    0xae,0x21,0x02,0x52,0x41,0xa8,0x03,0x61,0x89,0x11,0x86,0xf1,0xe7,0x49,0x3d,0x14,
+    0x63,0x8a,0x69,0x5c,0x53,0x55,0xfe,0x25,0xbd,0xd0,0x2a,0xe8,0xe2,0x19,0xea,0x6f,
+    0x20,0x6e,0xb3,0x85,0xa7,0x49,0xe7,0xcc,0x96,0xd8,0x3f,0x4d,0xad,0xe9,0x22,0x03,
+    0x65,0x25,0x02,0x03,0x01,0x00,0x01,0xa3,0x81,0xa7,0x30,0x81,0xa4,0x30,0x0e,0x06,
+    0x03,0x55,0x1d,0x0f,0x01,0x01,0xff,0x04,0x04,0x03,0x02,0x01,0x06,0x30,0x12,0x06,
+    0x03,0x55,0x1d,0x13,0x01,0x01,0xff,0x04,0x08,0x30,0x06,0x01,0x01,0xff,0x02,0x01,
+    0x00,0x30,0x1d,0x06,0x03,0x55,0x1d,0x0e,0x04,0x16,0x04,0x14,0x39,0xbb,0x91,0xb5,
+    0x14,0x54,0x76,0x0c,0x1c,0x0c,0xf4,0x4b,0xbc,0xd9,0xf6,0x51,0xc8,0xe2,0x03,0xfc,
+    0x30,0x1f,0x06,0x03,0x55,0x1d,0x23,0x04,0x18,0x30,0x16,0x80,0x14,0xba,0x56,0xd8,
+    0x57,0x66,0xbd,0xfc,0x5b,0xe2,0x10,0xf2,0x39,0xb3,0xaf,0xb2,0x72,0xed,0x55,0x0f,
+    0x1c,0x30,0x3e,0x06,0x03,0x55,0x1d,0x1f,0x04,0x37,0x30,0x35,0x30,0x33,0xa0,0x31,
+    0xa0,0x2f,0x86,0x2d,0x68,0x74,0x74,0x70,0x3a,0x2f,0x2f,0x76,0x61,0x6c,0x69,0x64,
+    0x74,0x65,0x73,0x74,0x2e,0x61,0x70,0x70,0x6c,0x65,0x2e,0x67,0x65,0x6f,0x66,0x66,
+    0x6b,0x2e,0x6e,0x65,0x74,0x2f,0x76,0x32,0x2d,0x72,0x6f,0x6f,0x74,0x2e,0x63,0x72,
+    0x6c,0x30,0x0d,0x06,0x09,0x2a,0x86,0x48,0x86,0xf7,0x0d,0x01,0x01,0x0b,0x05,0x00,
+    0x03,0x82,0x01,0x01,0x00,0x58,0xc7,0x67,0x96,0xb5,0x7a,0x36,0x26,0x63,0xe8,0xc1,
+    0x7d,0xd9,0x0b,0x26,0xa4,0x2c,0x09,0xec,0x1e,0x67,0xfd,0x26,0xe4,0x5b,0x73,0x11,
+    0x1d,0xfc,0xaf,0xa5,0x70,0x05,0x19,0xdb,0xb7,0x42,0x1b,0xd0,0x36,0x6f,0x8e,0x01,
+    0xd3,0x19,0x72,0xff,0x36,0xa5,0x06,0x62,0x8b,0xd6,0x9b,0x40,0x24,0x54,0x48,0x4c,
+    0x3f,0xeb,0x8d,0x36,0x9b,0x56,0x2e,0x27,0xf6,0x19,0xbf,0xae,0x07,0x20,0x7c,0x64,
+    0x79,0xe3,0x4e,0x1d,0x44,0x26,0x79,0x4f,0x21,0x12,0x6e,0xe6,0x33,0xe3,0xd3,0x23,
+    0x55,0xfb,0x49,0x8b,0x7b,0x5d,0xce,0xf5,0x58,0x3b,0x3f,0xb5,0xc6,0x33,0xc2,0x6f,
+    0xf5,0xc5,0x0f,0x25,0xf9,0x65,0x79,0x67,0x5d,0xab,0xe8,0xe5,0x59,0x55,0xd3,0x62,
+    0xf4,0xfa,0xee,0x65,0xfa,0x55,0x4e,0xc7,0x0e,0x5c,0xa9,0x48,0xc1,0x10,0x30,0x73,
+    0xbd,0x5b,0xe3,0x75,0x2b,0xc3,0x20,0x10,0xc7,0x7d,0x3a,0x1d,0x8d,0x17,0x39,0xb5,
+    0x58,0xae,0x91,0x32,0xe1,0xd2,0x98,0xfe,0x9a,0xdf,0x4b,0x0e,0x79,0x7b,0x8a,0x7a,
+    0x32,0xc7,0x69,0xec,0xe4,0x8e,0xc5,0x4f,0x45,0x00,0x89,0xe6,0x53,0xa0,0x3d,0xee,
+    0x1c,0x5a,0xee,0x4d,0x49,0xf2,0x54,0x3b,0xc0,0x4d,0xb4,0xa1,0x47,0x65,0x65,0x7a,
+    0xbc,0x3d,0xda,0x54,0xee,0x56,0x74,0x22,0xa9,0x68,0x8e,0x70,0x48,0x2c,0x83,0x1e,
+    0x35,0x08,0xb0,0xf2,0x9a,0xb1,0x43,0x88,0x07,0x5e,0xd6,0x94,0x0b,0x4a,0xa5,0x5b,
+    0x17,0x47,0x8e,0xcc,0xfa,0xa0,0x24,0xd9,0x1a,0xad,0xad,0x23,0x4f,0xa3,0x59,0xf1,
+    0x58,0x45,0x12,0x48,0xdc
+};
+
+uint8_t _leaf_serial_invalid_incomplete_ok1[] = {
+    0x30,0x82,0x04,0x78,0x30,0x82,0x03,0x60,0xa0,0x03,0x02,0x01,0x02,0x02,0x05,0x00,
+    0xcd,0xfb,0x60,0x2b,0x30,0x0d,0x06,0x09,0x2a,0x86,0x48,0x86,0xf7,0x0d,0x01,0x01,
+    0x0b,0x05,0x00,0x30,0x81,0x8e,0x31,0x25,0x30,0x23,0x06,0x03,0x55,0x04,0x03,0x0c,
+    0x1c,0x43,0x41,0x20,0x73,0x65,0x72,0x69,0x61,0x6c,0x20,0x69,0x6e,0x76,0x61,0x6c,
+    0x69,0x64,0x20,0x69,0x6e,0x63,0x6f,0x6d,0x70,0x6c,0x65,0x74,0x65,0x31,0x22,0x30,
+    0x20,0x06,0x03,0x55,0x04,0x0b,0x0c,0x19,0x56,0x61,0x6c,0x69,0x64,0x20,0x50,0x72,
+    0x6f,0x6a,0x65,0x63,0x74,0x20,0x54,0x65,0x73,0x74,0x20,0x43,0x41,0x73,0x20,0x56,
+    0x32,0x31,0x13,0x30,0x11,0x06,0x03,0x55,0x04,0x0a,0x0c,0x0a,0x41,0x70,0x70,0x6c,
+    0x65,0x20,0x49,0x6e,0x63,0x2e,0x31,0x12,0x30,0x10,0x06,0x03,0x55,0x04,0x07,0x0c,
+    0x09,0x43,0x75,0x70,0x65,0x72,0x74,0x69,0x6e,0x6f,0x31,0x0b,0x30,0x09,0x06,0x03,
+    0x55,0x04,0x08,0x0c,0x02,0x43,0x41,0x31,0x0b,0x30,0x09,0x06,0x03,0x55,0x04,0x06,
+    0x13,0x02,0x55,0x53,0x30,0x20,0x17,0x0d,0x31,0x37,0x30,0x35,0x30,0x31,0x32,0x32,
+    0x34,0x30,0x30,0x37,0x5a,0x18,0x0f,0x32,0x31,0x31,0x37,0x30,0x34,0x30,0x37,0x32,
+    0x32,0x34,0x30,0x30,0x37,0x5a,0x30,0x81,0x94,0x31,0x2b,0x30,0x29,0x06,0x03,0x55,
+    0x04,0x03,0x0c,0x22,0x4c,0x65,0x61,0x66,0x20,0x73,0x65,0x72,0x69,0x61,0x6c,0x20,
+    0x69,0x6e,0x76,0x61,0x6c,0x69,0x64,0x20,0x69,0x6e,0x63,0x6f,0x6d,0x70,0x6c,0x65,
+    0x74,0x65,0x20,0x6f,0x6b,0x31,0x31,0x22,0x30,0x20,0x06,0x03,0x55,0x04,0x0b,0x0c,
+    0x19,0x56,0x61,0x6c,0x69,0x64,0x20,0x50,0x72,0x6f,0x6a,0x65,0x63,0x74,0x20,0x54,
+    0x65,0x73,0x74,0x20,0x43,0x41,0x73,0x20,0x56,0x32,0x31,0x13,0x30,0x11,0x06,0x03,
+    0x55,0x04,0x0a,0x0c,0x0a,0x41,0x70,0x70,0x6c,0x65,0x20,0x49,0x6e,0x63,0x2e,0x31,
+    0x12,0x30,0x10,0x06,0x03,0x55,0x04,0x07,0x0c,0x09,0x43,0x75,0x70,0x65,0x72,0x74,
+    0x69,0x6e,0x6f,0x31,0x0b,0x30,0x09,0x06,0x03,0x55,0x04,0x08,0x0c,0x02,0x43,0x41,
+    0x31,0x0b,0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x55,0x53,0x30,0x82,0x01,
+    0x22,0x30,0x0d,0x06,0x09,0x2a,0x86,0x48,0x86,0xf7,0x0d,0x01,0x01,0x01,0x05,0x00,
+    0x03,0x82,0x01,0x0f,0x00,0x30,0x82,0x01,0x0a,0x02,0x82,0x01,0x01,0x00,0xc0,0x12,
+    0xf8,0x4e,0x54,0xd5,0x0a,0xf6,0x43,0x16,0x4d,0xe2,0xe9,0x4c,0xb8,0x1c,0x05,0x6d,
+    0x1b,0x0c,0xd5,0x7c,0x29,0x1e,0x82,0x5e,0x6a,0xb6,0x85,0x03,0x72,0x13,0x60,0x94,
+    0xee,0x8d,0x56,0x03,0xd1,0x41,0x84,0x7c,0xf0,0xc1,0xd2,0x60,0xda,0x2e,0x56,0x8a,
+    0xca,0x01,0xf6,0xbe,0x89,0x7a,0x27,0x44,0x6d,0x28,0x2c,0xfa,0x02,0x36,0xf7,0xa7,
+    0x0d,0x4b,0x27,0x83,0x90,0x3c,0x14,0x1e,0x73,0xa5,0x61,0xc0,0xf4,0x05,0x43,0xed,
+    0x36,0x34,0x14,0x7a,0xe0,0x9b,0x4e,0xbb,0x0f,0x12,0x97,0xcb,0x1b,0x99,0x46,0x41,
+    0x3e,0xcb,0xa1,0xc3,0xab,0x43,0x27,0x45,0x37,0x61,0x94,0xe3,0xe8,0x60,0xd3,0x94,
+    0x21,0x6f,0xd8,0x61,0x28,0x81,0xb5,0xe3,0x0f,0xfd,0x2f,0x61,0xe4,0xa4,0x5c,0x25,
+    0x91,0x58,0xa0,0x55,0x5e,0xae,0x44,0x61,0x92,0x2b,0x99,0x13,0x1a,0xba,0x7c,0x1e,
+    0xa1,0x2d,0xd2,0x02,0x78,0x9d,0xf2,0xfb,0x1a,0xd8,0xc2,0x01,0x19,0x76,0x30,0xf1,
+    0x6a,0x65,0xd3,0x76,0xc7,0x67,0xb0,0xe8,0xa0,0x15,0xce,0x8d,0x90,0x45,0xe8,0x71,
+    0x68,0x9f,0x56,0x91,0x22,0xe4,0xf4,0x16,0x69,0x2d,0x87,0x23,0x52,0x51,0x27,0x13,
+    0x6e,0x7e,0xdf,0xf1,0x7e,0x67,0x5d,0x56,0xb8,0x8b,0x5d,0x75,0x09,0xb2,0x4a,0x3b,
+    0x5e,0xac,0x8e,0x42,0xf4,0xb6,0xc2,0xf6,0x5c,0xde,0x9d,0xeb,0xf5,0x82,0xed,0x71,
+    0xfa,0x23,0x94,0xd6,0x1c,0x19,0x65,0xa5,0xae,0x38,0xbd,0x57,0x27,0xfd,0x7c,0x2b,
+    0x30,0x71,0xb1,0xa0,0x4a,0xac,0xbe,0xcb,0xe7,0x17,0x81,0x20,0xe9,0x8f,0x02,0x03,
+    0x01,0x00,0x01,0xa3,0x81,0xd2,0x30,0x81,0xcf,0x30,0x0e,0x06,0x03,0x55,0x1d,0x0f,
+    0x01,0x01,0xff,0x04,0x04,0x03,0x02,0x07,0x80,0x30,0x0c,0x06,0x03,0x55,0x1d,0x13,
+    0x01,0x01,0xff,0x04,0x02,0x30,0x00,0x30,0x1f,0x06,0x03,0x55,0x1d,0x23,0x04,0x18,
+    0x30,0x16,0x80,0x14,0xa2,0xee,0xae,0x1f,0x41,0x28,0x69,0x40,0x55,0xdf,0xc5,0x33,
+    0x56,0xab,0xfc,0x23,0x68,0x17,0xcb,0x39,0x30,0x53,0x06,0x03,0x55,0x1d,0x1f,0x04,
+    0x4c,0x30,0x4a,0x30,0x48,0xa0,0x46,0xa0,0x44,0x86,0x42,0x68,0x74,0x74,0x70,0x3a,
+    0x2f,0x2f,0x76,0x61,0x6c,0x69,0x64,0x74,0x65,0x73,0x74,0x2e,0x61,0x70,0x70,0x6c,
+    0x65,0x2e,0x67,0x65,0x6f,0x66,0x66,0x6b,0x2e,0x6e,0x65,0x74,0x2f,0x76,0x32,0x2d,
+    0x73,0x65,0x72,0x69,0x61,0x6c,0x2d,0x69,0x6e,0x76,0x61,0x6c,0x69,0x64,0x2d,0x69,
+    0x6e,0x63,0x6f,0x6d,0x70,0x6c,0x65,0x74,0x65,0x2e,0x63,0x72,0x6c,0x30,0x39,0x06,
+    0x08,0x2b,0x06,0x01,0x05,0x05,0x07,0x01,0x01,0x04,0x2d,0x30,0x2b,0x30,0x29,0x06,
+    0x08,0x2b,0x06,0x01,0x05,0x05,0x07,0x30,0x01,0x86,0x1d,0x68,0x74,0x74,0x70,0x3a,
+    0x2f,0x2f,0x6b,0x6e,0x6f,0x77,0x6e,0x2d,0x61,0x6e,0x73,0x77,0x65,0x72,0x2d,0x6f,
+    0x63,0x73,0x70,0x2f,0x6f,0x63,0x73,0x70,0x30,0x0d,0x06,0x09,0x2a,0x86,0x48,0x86,
+    0xf7,0x0d,0x01,0x01,0x0b,0x05,0x00,0x03,0x82,0x01,0x01,0x00,0x6c,0x93,0x89,0x44,
+    0x96,0x48,0x63,0xa2,0x3c,0x68,0x26,0x3c,0xb5,0x36,0x4a,0x4c,0x3e,0xc4,0xd2,0x7c,
+    0x2a,0x62,0xed,0x48,0x61,0x60,0x7f,0xe0,0x90,0xe9,0x31,0xf0,0x12,0xa3,0xdd,0x51,
+    0x20,0xf2,0x4b,0xec,0x1c,0x0e,0x1c,0xd5,0xdc,0xbf,0x79,0x1a,0x4b,0xc4,0xd8,0x50,
+    0x9d,0x9d,0x17,0xcb,0x0b,0x13,0x12,0x5f,0x54,0x20,0x05,0x5a,0x36,0x27,0x6b,0xef,
+    0x89,0x34,0x96,0x73,0xae,0x9d,0xd2,0x1c,0x0a,0x98,0x39,0xec,0x83,0x2d,0xcd,0x47,
+    0x50,0x41,0x5e,0xac,0x03,0x02,0x43,0x05,0xdd,0x34,0xbe,0x64,0x97,0xe3,0x2f,0xa0,
+    0x70,0xc8,0xbd,0xc9,0x3e,0x6b,0x58,0x84,0x87,0xa5,0xea,0xbe,0xcc,0x7a,0xf8,0xe6,
+    0xe4,0xa7,0x57,0x32,0xc0,0x3e,0x5a,0x38,0xdb,0xf9,0x95,0xdb,0xb2,0x18,0x09,0xab,
+    0xbc,0x5c,0x22,0xe4,0x08,0xd7,0xd1,0xdf,0xd0,0xf6,0xef,0x02,0x0d,0xbc,0x9b,0xce,
+    0x0e,0xcd,0x13,0x60,0x48,0xcc,0x5f,0x0e,0xef,0x38,0x13,0x3b,0x88,0x51,0xca,0xea,
+    0x7e,0xb7,0xf9,0x53,0xa5,0xe2,0xe3,0x79,0xc5,0xe3,0x3f,0x3d,0x7b,0xfb,0x16,0xd8,
+    0xcf,0xed,0x8e,0x02,0x4a,0x60,0xa2,0x2e,0x00,0x16,0xd6,0x1b,0x8a,0xbe,0x0b,0xb3,
+    0x6b,0x91,0x10,0x97,0xf4,0xf0,0x39,0xa8,0x35,0xb8,0xc8,0x51,0x09,0x34,0x9e,0x0f,
+    0xc8,0x52,0xad,0x72,0xb1,0xe8,0x39,0x64,0xa4,0xa2,0x96,0xa2,0x85,0xff,0xc9,0x63,
+    0x45,0xe0,0xad,0xf0,0x15,0x91,0x66,0xd0,0x05,0x7b,0x39,0xbd,0x39,0x33,0xf8,0xd1,
+    0x46,0x21,0x02,0xe7,0xc3,0x73,0xda,0x65,0xe8,0xa7,0x64,0xbd
+};
+
+uint8_t _ca_serial_invalid_incomplete[] = {
+    0x30,0x82,0x04,0x40,0x30,0x82,0x03,0x28,0xa0,0x03,0x02,0x01,0x02,0x02,0x05,0x00,
+    0x80,0x00,0x46,0x45,0x30,0x0d,0x06,0x09,0x2a,0x86,0x48,0x86,0xf7,0x0d,0x01,0x01,
+    0x0b,0x05,0x00,0x30,0x81,0x87,0x31,0x1e,0x30,0x1c,0x06,0x03,0x55,0x04,0x03,0x0c,
+    0x15,0x56,0x61,0x6c,0x69,0x64,0x20,0x54,0x65,0x73,0x74,0x20,0x43,0x41,0x20,0x52,
+    0x6f,0x6f,0x74,0x20,0x56,0x32,0x31,0x22,0x30,0x20,0x06,0x03,0x55,0x04,0x0b,0x0c,
+    0x19,0x56,0x61,0x6c,0x69,0x64,0x20,0x50,0x72,0x6f,0x6a,0x65,0x63,0x74,0x20,0x54,
+    0x65,0x73,0x74,0x20,0x43,0x41,0x73,0x20,0x56,0x32,0x31,0x13,0x30,0x11,0x06,0x03,
+    0x55,0x04,0x0a,0x0c,0x0a,0x41,0x70,0x70,0x6c,0x65,0x20,0x49,0x6e,0x63,0x2e,0x31,
+    0x12,0x30,0x10,0x06,0x03,0x55,0x04,0x07,0x0c,0x09,0x43,0x75,0x70,0x65,0x72,0x74,
+    0x69,0x6e,0x6f,0x31,0x0b,0x30,0x09,0x06,0x03,0x55,0x04,0x08,0x0c,0x02,0x43,0x41,
+    0x31,0x0b,0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x55,0x53,0x30,0x20,0x17,
+    0x0d,0x31,0x37,0x30,0x35,0x30,0x31,0x32,0x32,0x34,0x30,0x30,0x36,0x5a,0x18,0x0f,
+    0x32,0x31,0x31,0x37,0x30,0x34,0x30,0x37,0x32,0x32,0x34,0x30,0x30,0x36,0x5a,0x30,
+    0x81,0x8e,0x31,0x25,0x30,0x23,0x06,0x03,0x55,0x04,0x03,0x0c,0x1c,0x43,0x41,0x20,
+    0x73,0x65,0x72,0x69,0x61,0x6c,0x20,0x69,0x6e,0x76,0x61,0x6c,0x69,0x64,0x20,0x69,
+    0x6e,0x63,0x6f,0x6d,0x70,0x6c,0x65,0x74,0x65,0x31,0x22,0x30,0x20,0x06,0x03,0x55,
+    0x04,0x0b,0x0c,0x19,0x56,0x61,0x6c,0x69,0x64,0x20,0x50,0x72,0x6f,0x6a,0x65,0x63,
+    0x74,0x20,0x54,0x65,0x73,0x74,0x20,0x43,0x41,0x73,0x20,0x56,0x32,0x31,0x13,0x30,
+    0x11,0x06,0x03,0x55,0x04,0x0a,0x0c,0x0a,0x41,0x70,0x70,0x6c,0x65,0x20,0x49,0x6e,
+    0x63,0x2e,0x31,0x12,0x30,0x10,0x06,0x03,0x55,0x04,0x07,0x0c,0x09,0x43,0x75,0x70,
+    0x65,0x72,0x74,0x69,0x6e,0x6f,0x31,0x0b,0x30,0x09,0x06,0x03,0x55,0x04,0x08,0x0c,
+    0x02,0x43,0x41,0x31,0x0b,0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x55,0x53,
+    0x30,0x82,0x01,0x22,0x30,0x0d,0x06,0x09,0x2a,0x86,0x48,0x86,0xf7,0x0d,0x01,0x01,
+    0x01,0x05,0x00,0x03,0x82,0x01,0x0f,0x00,0x30,0x82,0x01,0x0a,0x02,0x82,0x01,0x01,
+    0x00,0xcd,0x06,0x79,0xc3,0xeb,0x86,0x31,0x75,0xe7,0xb5,0x9d,0xe3,0x20,0xd3,0x13,
+    0x7c,0x90,0xfb,0xfe,0x51,0xd0,0x17,0x04,0x61,0x64,0x92,0xd2,0x63,0xe2,0x91,0x17,
+    0x90,0x2e,0xb0,0xa6,0x03,0x8f,0xb2,0xc2,0x8c,0x32,0xf2,0xbe,0x51,0xad,0xee,0x72,
+    0xf5,0x1f,0x15,0x94,0x72,0x8c,0x17,0xc1,0x47,0x3c,0x74,0xa2,0xb0,0x89,0x7d,0x96,
+    0xa3,0x01,0xf7,0x14,0x21,0x80,0x0d,0x6e,0x5d,0x56,0x2a,0xcc,0xdc,0x7b,0xfc,0x83,
+    0xe9,0xb4,0xbc,0x46,0x41,0x14,0xfb,0xb6,0xcc,0x4b,0x5a,0xa0,0x11,0x14,0x4c,0x92,
+    0x2f,0x47,0xd5,0x3c,0xc9,0xe9,0xfa,0xb5,0x66,0x6b,0x3a,0xdf,0x1b,0x62,0x1a,0x44,
+    0xa3,0x63,0xf3,0x1a,0x2f,0x46,0xdb,0xd0,0x9e,0x0d,0x9a,0xed,0xcd,0xf3,0xa0,0xb0,
+    0xd9,0xf0,0xb0,0x58,0xe8,0xfe,0xb7,0x34,0x6c,0x87,0xfa,0xf6,0x96,0xe3,0x2d,0xc1,
+    0xd5,0x54,0x56,0x88,0x61,0xa6,0xd6,0x58,0xbd,0x58,0x0d,0xc7,0x6f,0xe6,0x84,0xf8,
+    0x2a,0x24,0xf2,0x97,0xca,0x8c,0xae,0xf9,0xa5,0xe2,0x5e,0x4a,0x9f,0xbf,0xd2,0x4b,
+    0xe6,0x53,0x72,0xfc,0x05,0x93,0x72,0x40,0x0f,0xc5,0x26,0xda,0x00,0x35,0xe6,0x57,
+    0xef,0xc2,0x28,0xcf,0x62,0xe9,0x27,0x05,0x1c,0x47,0x33,0x92,0x14,0x87,0xcb,0x8a,
+    0x34,0xb3,0xb2,0xf8,0x88,0xe5,0x8c,0x44,0xdf,0x1b,0x62,0xbc,0xaa,0x9a,0x49,0x5b,
+    0xff,0x4c,0x37,0x33,0xad,0x6f,0x98,0x61,0xce,0xc5,0xb5,0x02,0xe0,0x12,0x24,0xc1,
+    0x59,0x28,0xd8,0x2b,0xea,0x94,0x82,0x2d,0x28,0x37,0xcc,0xf3,0x60,0x9f,0x08,0xe1,
+    0x3f,0x02,0x03,0x01,0x00,0x01,0xa3,0x81,0xa7,0x30,0x81,0xa4,0x30,0x0e,0x06,0x03,
+    0x55,0x1d,0x0f,0x01,0x01,0xff,0x04,0x04,0x03,0x02,0x01,0x06,0x30,0x12,0x06,0x03,
+    0x55,0x1d,0x13,0x01,0x01,0xff,0x04,0x08,0x30,0x06,0x01,0x01,0xff,0x02,0x01,0x00,
+    0x30,0x1d,0x06,0x03,0x55,0x1d,0x0e,0x04,0x16,0x04,0x14,0xa2,0xee,0xae,0x1f,0x41,
+    0x28,0x69,0x40,0x55,0xdf,0xc5,0x33,0x56,0xab,0xfc,0x23,0x68,0x17,0xcb,0x39,0x30,
+    0x1f,0x06,0x03,0x55,0x1d,0x23,0x04,0x18,0x30,0x16,0x80,0x14,0xba,0x56,0xd8,0x57,
+    0x66,0xbd,0xfc,0x5b,0xe2,0x10,0xf2,0x39,0xb3,0xaf,0xb2,0x72,0xed,0x55,0x0f,0x1c,
+    0x30,0x3e,0x06,0x03,0x55,0x1d,0x1f,0x04,0x37,0x30,0x35,0x30,0x33,0xa0,0x31,0xa0,
+    0x2f,0x86,0x2d,0x68,0x74,0x74,0x70,0x3a,0x2f,0x2f,0x76,0x61,0x6c,0x69,0x64,0x74,
+    0x65,0x73,0x74,0x2e,0x61,0x70,0x70,0x6c,0x65,0x2e,0x67,0x65,0x6f,0x66,0x66,0x6b,
+    0x2e,0x6e,0x65,0x74,0x2f,0x76,0x32,0x2d,0x72,0x6f,0x6f,0x74,0x2e,0x63,0x72,0x6c,
+    0x30,0x0d,0x06,0x09,0x2a,0x86,0x48,0x86,0xf7,0x0d,0x01,0x01,0x0b,0x05,0x00,0x03,
+    0x82,0x01,0x01,0x00,0x43,0xa4,0x08,0x47,0x8c,0x5c,0x79,0x0b,0x5f,0xe2,0xe7,0xec,
+    0x0e,0x70,0x69,0xee,0xd1,0xbb,0xda,0x02,0x06,0xcf,0x75,0xbe,0xe9,0xbf,0xfd,0xf7,
+    0xa0,0x3b,0x8e,0xf0,0x01,0x6c,0x1b,0x82,0x7d,0xeb,0x6f,0xaa,0x7a,0x3c,0xef,0x7b,
+    0xd9,0x9c,0x74,0x48,0x2a,0x2a,0xd1,0xa3,0x0d,0xee,0xe8,0xa3,0x55,0x83,0xd6,0xa4,
+    0x3a,0xb1,0x2b,0xf1,0x44,0x8e,0x3d,0xec,0xc7,0x74,0x73,0x1b,0xaf,0xf1,0xee,0xea,
+    0x69,0x8a,0xa2,0x88,0xf6,0x82,0x6a,0x2d,0x21,0xfd,0x0c,0xf2,0x50,0x6a,0x34,0x81,
+    0x43,0x8e,0x81,0x0a,0xfa,0x26,0xc4,0x4a,0x10,0x60,0x85,0xb7,0xf3,0xdc,0xd2,0xc7,
+    0xe2,0xe7,0x17,0x94,0x82,0x68,0x33,0xa3,0xd4,0x19,0xcb,0x10,0x16,0x65,0x4c,0x6b,
+    0xe7,0x3f,0x3a,0xbe,0x1b,0x3b,0x08,0x0c,0x16,0x0e,0xe0,0x7f,0x75,0x44,0xa9,0xfa,
+    0x6e,0x2f,0xd1,0x27,0x9b,0xc5,0x4e,0xec,0xac,0xb5,0xaf,0x2b,0xdf,0x10,0xbc,0x38,
+    0x85,0x4c,0x00,0x18,0x86,0x30,0x40,0x2d,0xab,0xf4,0xc3,0x1e,0xfb,0x3b,0xdf,0xa6,
+    0x72,0xb9,0x5e,0x24,0x81,0x39,0x92,0x02,0xf9,0xc9,0xc1,0xfc,0x35,0x1a,0x0a,0x0c,
+    0x2c,0xd4,0x4e,0xae,0x03,0x79,0x22,0xd5,0x97,0x29,0xf6,0x13,0x2f,0xce,0xb6,0x69,
+    0x5e,0x4f,0xc6,0x0c,0x06,0x00,0x28,0x7c,0x14,0x3e,0xab,0x32,0xaf,0xd6,0x02,0x04,
+    0x18,0xc8,0xdc,0x9c,0xaf,0x23,0x08,0xc3,0xca,0x3e,0xbc,0xf1,0x44,0xc5,0x43,0x91,
+    0x00,0x39,0x46,0x15,0x88,0xce,0xe9,0xe3,0xf5,0xae,0xdc,0x5b,0x63,0x77,0xd5,0xc8,
+    0xf9,0x35,0x0b,0xe1
+};
+
 #endif /* _TRUSTTESTS_REVOCATION_TESTS_H_ */
 #endif /* _TRUSTTESTS_REVOCATION_TESTS_H_ */
index 14836b9f45caeda6b8d3620b6bfffee034d8e470..021a4a1619f7aaeaf0cea52ec6fd55b801b629e0 100644 (file)
@@ -42,6 +42,7 @@ NS_ASSUME_NONNULL_BEGIN
 - (void)runCertificateTestForDirectory:(SecPolicyRef)policy subDirectory:(NSString *)resourceSubDirectory verifyDate:(NSDate*)date;
 
 - (id _Nullable) CF_RETURNS_RETAINED SecCertificateCreateFromResource:(NSString * )name subdirectory:(NSString *)dir;
 - (void)runCertificateTestForDirectory:(SecPolicyRef)policy subDirectory:(NSString *)resourceSubDirectory verifyDate:(NSDate*)date;
 
 - (id _Nullable) CF_RETURNS_RETAINED SecCertificateCreateFromResource:(NSString * )name subdirectory:(NSString *)dir;
+- (id _Nullable) CF_RETURNS_RETAINED SecCertificateCreateFromPEMResource:(NSString *)name subdirectory:(NSString *)dir;
 @end
 
 /* Use this interface to get a SecCertificateRef that has the same CFTypeID
 @end
 
 /* Use this interface to get a SecCertificateRef that has the same CFTypeID
index 7e8378c03c4c88a538b65f58d4bb1eadc5a2705d..99f8383fc341ba0ff0568a076097421a9d063901 100644 (file)
@@ -182,7 +182,24 @@ const CFStringRef kTestSystemRootKey = CFSTR("TestSystemRoot");
     NSURL *url = [[NSBundle bundleForClass:[self class]] URLForResource:name withExtension:@".cer"
                                                            subdirectory:dir];
     NSData *certData = [NSData dataWithContentsOfURL:url];
     NSURL *url = [[NSBundle bundleForClass:[self class]] URLForResource:name withExtension:@".cer"
                                                            subdirectory:dir];
     NSData *certData = [NSData dataWithContentsOfURL:url];
-    SecCertificateRef cert = SecCertificateCreateWithData(kCFAllocatorDefault, (CFDataRef)certData);
+    if (!certData) {
+        return nil;
+    }
+    SecCertificateRef cert = SecCertificateCreateWithData(kCFAllocatorDefault, (__bridge CFDataRef)certData);
+    return (__bridge id)cert;
+}
+
+- (id _Nullable) CF_RETURNS_RETAINED SecCertificateCreateFromPEMResource:(NSString *)name
+                                                            subdirectory:(NSString *)dir
+{
+    NSURL *url = [[NSBundle bundleForClass:[self class]] URLForResource:name withExtension:@".pem"
+                                                           subdirectory:dir];
+    NSData *certData = [NSData dataWithContentsOfURL:url];
+    if (!certData) {
+        return nil;
+    }
+
+    SecCertificateRef cert = SecCertificateCreateWithPEM(kCFAllocatorDefault, (__bridge CFDataRef)certData);
     return (__bridge id)cert;
 }
 
     return (__bridge id)cert;
 }
 
index c02a959cdb372b71181396d0ff9f877488d0a7f3..f4c276148e76848a2629f37187c9163d76f92912 100644 (file)
@@ -56,6 +56,7 @@ int ping_host(char *host_name);
 - (instancetype _Nullable) initWithTrustDictionary:(NSDictionary *)testDict;
 
 - (void)addAnchor:(SecCertificateRef)certificate;
 - (instancetype _Nullable) initWithTrustDictionary:(NSDictionary *)testDict;
 
 - (void)addAnchor:(SecCertificateRef)certificate;
+- (void)setNeedsEvaluation;
 
 - (bool)evaluate:(out NSError * _Nullable __autoreleasing * _Nullable)outError;
 - (bool)evaluateForExpectedResults:(out NSError * _Nullable __autoreleasing *)outError;
 
 - (bool)evaluate:(out NSError * _Nullable __autoreleasing * _Nullable)outError;
 - (bool)evaluateForExpectedResults:(out NSError * _Nullable __autoreleasing *)outError;
index b62f91126fb1a6dcd087adf51cab628765b834e4..a7ee45d9d96d092eaf43f50e50828e9930411b3e 100644 (file)
     }
 }
 
     }
 }
 
+- (void)setNeedsEvaluation {
+    SecTrustSetNeedsEvaluation(_trust);
+}
+
 - (bool)evaluate:(out NSError * _Nullable __autoreleasing *)outError {
     CFErrorRef localError = nil;
     _trustResult = kSecTrustResultInvalid;
 - (bool)evaluate:(out NSError * _Nullable __autoreleasing *)outError {
     CFErrorRef localError = nil;
     _trustResult = kSecTrustResultInvalid;
index d69762fe3be1ae37694e190dbadf44cfc9ac78e6..9a5280f3a5d453cccd28edef6f24da8caecce3f0 100644 (file)
@@ -31,6 +31,7 @@
 #include "OSX/sec/Security/SecItemShim.h"
 #import "server_security_helpers.h"
 #import "spi.h"
 #include "OSX/sec/Security/SecItemShim.h"
 #import "server_security_helpers.h"
 #import "spi.h"
+#import "utilities/SecAKSWrappers.h"
 #import <utilities/SecCFWrappers.h>
 #import <utilities/SecFileLocations.h>
 #import "utilities/der_plist.h"
 #import <utilities/SecCFWrappers.h>
 #import <utilities/SecFileLocations.h>
 #import "utilities/der_plist.h"
 
 - (void)testCreateSampleDatabase
 {
 
 - (void)testCreateSampleDatabase
 {
-#if USE_KEYSTORE
+    // The keychain code only does the right thing with generation count if TARGET_HAS_KEYSTORE
+#if TARGET_HAS_KEYSTORE
     id mock = OCMClassMock([SecMockAKS class]);
     OCMStub([mock useGenerationCount]).andReturn(true);
 #endif
     id mock = OCMClassMock([SecMockAKS class]);
     OCMStub([mock useGenerationCount]).andReturn(true);
 #endif
     */
 
     [self findManyItems:50];
     */
 
     [self findManyItems:50];
+
+#if TARGET_HAS_KEYSTORE
+    [mock stopMocking];
+#endif
 }
 
 - (void)testTestAKSGenerationCount
 {
 }
 
 - (void)testTestAKSGenerationCount
 {
-#if USE_KEYSTORE
+#if TARGET_HAS_KEYSTORE
     id mock = OCMClassMock([SecMockAKS class]);
     OCMStub([mock useGenerationCount]).andReturn(true);
 
     [self createManyItems];
     [self findManyItems:50];
     id mock = OCMClassMock([SecMockAKS class]);
     OCMStub([mock useGenerationCount]).andReturn(true);
 
     [self createManyItems];
     [self findManyItems:50];
+
+    [mock stopMocking];
 #endif
 }
 
 #endif
 }
 
index 781fb65ff663d1afed0d0ff8736dc2642fe5bd39..15019c8eb57a6fcfb852325735e42aabe29aca16 100644 (file)
@@ -187,6 +187,11 @@ extern const CFStringRef kSecPolicyAppleKeyTransparency
     API_AVAILABLE(macos(10.15), ios(13.0), watchos(6.0), tvos(13.0));
 extern const CFStringRef kSecPolicyAppleLegacySSL
     API_AVAILABLE(macos(10.15), ios(13.0), watchos(6.0), tvos(13.0));
     API_AVAILABLE(macos(10.15), ios(13.0), watchos(6.0), tvos(13.0));
 extern const CFStringRef kSecPolicyAppleLegacySSL
     API_AVAILABLE(macos(10.15), ios(13.0), watchos(6.0), tvos(13.0));
+extern const CFStringRef kSecPolicyAppleAlisha
+    API_AVAILABLE(macos(10.15.4), ios(13.4), watchos(6.2), tvos(13.4));
+extern const CFStringRef kSecPolicyAppleMeasuredBootPolicySigning
+    API_AVAILABLE(macos(10.15.4), ios(13.4), watchos(6.2), tvos(13.4));
+
 
 /*!
        @enum Policy Name Constants (Private)
 
 /*!
        @enum Policy Name Constants (Private)
@@ -209,6 +214,7 @@ extern const CFStringRef kSecPolicyAppleLegacySSL
     @constant kSecPolicyNameAppleAMPService
     @constant kSecPolicyNameAppleSiriService
     @constant kSecPolicyNameAppleHomeAppClipUploadService
     @constant kSecPolicyNameAppleAMPService
     @constant kSecPolicyNameAppleSiriService
     @constant kSecPolicyNameAppleHomeAppClipUploadService
+    @constant kSecPolicyNameAppleUpdatesService
  */
 extern const CFStringRef kSecPolicyNameAppleAST2Service
     __OSX_AVAILABLE(10.13) __IOS_AVAILABLE(11.0) __TVOS_AVAILABLE(11.0) __WATCHOS_AVAILABLE(4.0);
  */
 extern const CFStringRef kSecPolicyNameAppleAST2Service
     __OSX_AVAILABLE(10.13) __IOS_AVAILABLE(11.0) __TVOS_AVAILABLE(11.0) __WATCHOS_AVAILABLE(4.0);
@@ -244,6 +250,8 @@ extern const CFStringRef kSecPolicyNameAppleSiriService
     API_AVAILABLE(macos(10.15), ios(13.0), watchos(6.0), tvos(13.0));
 extern const CFStringRef kSecPolicyNameAppleHomeAppClipUploadService
     API_AVAILABLE(macos(10.15.1), ios(13.2), watchos(6.1), tvos(13.1));
     API_AVAILABLE(macos(10.15), ios(13.0), watchos(6.0), tvos(13.0));
 extern const CFStringRef kSecPolicyNameAppleHomeAppClipUploadService
     API_AVAILABLE(macos(10.15.1), ios(13.2), watchos(6.1), tvos(13.1));
+extern const CFStringRef kSecPolicyNameAppleUpdatesService
+    API_AVAILABLE(macos(10.15.4), ios(13.4), watchos(6.2), tvos(13.4));
 
 /*!
  @enum Policy Value Constants
 
 /*!
  @enum Policy Value Constants
@@ -1790,7 +1798,7 @@ SecPolicyRef SecPolicyCreateAppleComponentCertificate(CFDataRef __nullable testR
  pinning options:
      * The chain is anchored to any of the production Apple Root CAs.
      * There are exactly 3 certs in the chain.
  pinning options:
      * The chain is anchored to any of the production Apple Root CAs.
      * There are exactly 3 certs in the chain.
-     * The intermediate has a marker extension with OID TBD.
+     * The intermediate has a marker extension with OID 1.2.840.113635.100.6.2.3".
      * The leaf has a marker extension with OID 1.2.840.113635.100.6.69.1 and value
         matching the applicationId.
      * Revocation is checked via any available method.
      * The leaf has a marker extension with OID 1.2.840.113635.100.6.69.1 and value
         matching the applicationId.
      * Revocation is checked via any available method.
@@ -1821,6 +1829,37 @@ __nullable CF_RETURNS_RETAINED
 SecPolicyRef SecPolicyCreateLegacySSL(Boolean server, CFStringRef __nullable hostname)
     SPI_AVAILABLE(macos(10.15), ios(13.0), watchos(6.0), tvos(13.0));
 
 SecPolicyRef SecPolicyCreateLegacySSL(Boolean server, CFStringRef __nullable hostname)
     SPI_AVAILABLE(macos(10.15), ios(13.0), watchos(6.0), tvos(13.0));
 
+/*!
+ @function SecPolicyCreateAlisha
+ @abstract Returns a policy object for verifying Alisha certificates.
+ @discussion The resulting policy uses the Basic X.509 policy with no validity check and
+ pinning options:
+     * EC key sizes are P-256 or larger.
+ @result A policy object. The caller is responsible for calling CFRelease on this when
+ it is no longer needed.
+ */
+__nullable CF_RETURNS_RETAINED
+SecPolicyRef SecPolicyCreateAlisha(void)
+    API_AVAILABLE(macos(10.15.4), ios(13.4), watchos(6.2), tvos(13.4));
+
+/*!
+ @function SecPolicyCreateMeasuredBootPolicySigning
+ @abstract Returns a policy object for verifying Measured Boot Policy Signing certificates.
+ @discussion The resulting policy uses the Basic X.509 policy with no validity check and
+ pinning options:
+     * There are exactly 3 certs in the chain.
+     * The intermediate has a marker extension with OID 1.2.840.113635.100.6.24.17.
+     * The leaf has a marker extension with OID 1.2.840.113635.100.6.26.6.1
+     * RSA key sizes are 2048-bit or larger. EC key sizes are P-256 or larger.
+ Because this policy does not pin the anchors, the caller must use SecTrustSetAnchorCertificates with
+ the expected roots.
+ @result A policy object. The caller is responsible for calling CFRelease on this when
+ it is no longer needed.
+ */
+__nullable CF_RETURNS_RETAINED
+SecPolicyRef SecPolicyCreateMeasuredBootPolicySigning(void)
+    API_AVAILABLE(macos(10.15.4), ios(13.4), watchos(6.2), tvos(13.4));
+
 /*
  *  Legacy functions (OS X only)
  */
 /*
  *  Legacy functions (OS X only)
  */
index 6ddca031f069c808bb95eae0638667ecfc00702c..ebd9350f5aff080237b937af876bd63d049b5f10 100644 (file)
@@ -891,6 +891,25 @@ CFAbsoluteTime SecCertificatePathVCGetEarliestNextUpdate(SecCertificatePathVCRef
     return enu;
 }
 
     return enu;
 }
 
+bool SecCertificatePathVCRevocationCheckedAllCerts(SecCertificatePathVCRef path) {
+    CFIndex certIX, certCount = path->count;
+    if (certCount <= 1 || !path->rvcs) {
+        /* If there is only one certificate, it's the root, so revocation checking is irrelevant. */
+        return false;
+    }
+
+    for (certIX = 0; certIX < path->rvcCount - 1; ++certIX) {
+        SecRVCRef rvc = &((SecRVCRef)path->rvcs)[certIX];
+        if (!SecRVCRevocationChecked(rvc)) {
+            secdebug("rvc", "revocation has not been checked for all certs (not checked for cert %ld)", certIX);
+            return false;
+        }
+    }
+
+    secdebug("rvc", "revocation has been checked for all certs");
+    return true;
+}
+
 void SecCertificatePathVCSetRevocationReasonForCertificateAtIndex(SecCertificatePathVCRef certificatePath,
                                                                   CFIndex ix, CFNumberRef revocationReason) {
     if (ix > certificatePath->count - 1) { return; }
 void SecCertificatePathVCSetRevocationReasonForCertificateAtIndex(SecCertificatePathVCRef certificatePath,
                                                                   CFIndex ix, CFNumberRef revocationReason) {
     if (ix > certificatePath->count - 1) { return; }
index 91486de48e8bb455fbb7acb517c00eca828907a7..8a14434001d146800b55fb5a14129aa438b395c0 100644 (file)
@@ -129,6 +129,7 @@ bool SecCertificatePathVCIsRevocationDone(SecCertificatePathVCRef certificatePat
 void SecCertificatePathVCAllocateRVCs(SecCertificatePathVCRef certificatePath, CFIndex certCount);
 CFAbsoluteTime SecCertificatePathVCGetEarliestNextUpdate(SecCertificatePathVCRef path);
 void *SecCertificatePathVCGetRVCAtIndex(SecCertificatePathVCRef certificatePath, CFIndex ix); // Returns a SecRVCRef
 void SecCertificatePathVCAllocateRVCs(SecCertificatePathVCRef certificatePath, CFIndex certCount);
 CFAbsoluteTime SecCertificatePathVCGetEarliestNextUpdate(SecCertificatePathVCRef path);
 void *SecCertificatePathVCGetRVCAtIndex(SecCertificatePathVCRef certificatePath, CFIndex ix); // Returns a SecRVCRef
+bool SecCertificatePathVCRevocationCheckedAllCerts(SecCertificatePathVCRef path);
 bool SecCertificatePathVCIsRevocationRequiredForCertificateAtIndex(SecCertificatePathVCRef certificatePath,
                                                                    CFIndex ix);
 void SecCertificatePathVCSetRevocationRequiredForCertificateAtIndex(SecCertificatePathVCRef certificatePath,
 bool SecCertificatePathVCIsRevocationRequiredForCertificateAtIndex(SecCertificatePathVCRef certificatePath,
                                                                    CFIndex ix);
 void SecCertificatePathVCSetRevocationRequiredForCertificateAtIndex(SecCertificatePathVCRef certificatePath,
index 0363c6c3194e4c9cae52bff9646d40a157b18c66..2fc2a03fa5f94f2e7e9e03a39b3ddb890b2fc0e3 100644 (file)
@@ -246,7 +246,9 @@ static void SecOCSPCacheWith(void(^cacheJob)(SecOCSPCacheRef cache)) {
             CFRelease(dbPath);
         }
     }
             CFRelease(dbPath);
         }
     }
-    cacheJob(kSecOCSPCache);
+    if (kSecOCSPCache) {
+        cacheJob(kSecOCSPCache);
+    }
     os_unfair_lock_unlock(&cacheLock);
 }
 
     os_unfair_lock_unlock(&cacheLock);
 }
 
index d17d3bbde27fbbfaf3c6c88c57bd700f1ebfb70a..2cec6688655e3948622fc3e6817490b89a3a3e52 100644 (file)
@@ -2783,8 +2783,8 @@ if (__PC_TYPE_MEMBER_##TRUSTRESULT && CFEqual(key,CFSTR(#NAME))) { \
    policy->_options is a caller provided dictionary, only its cf type has
    been checked.
  */
    policy->_options is a caller provided dictionary, only its cf type has
    been checked.
  */
-bool SecPVCSetResultForced(SecPVCRef pvc,
-       CFStringRef key, CFIndex ix, CFTypeRef result, bool force) {
+bool SecPVCSetResultForcedWithTrustResult(SecPVCRef pvc, CFStringRef key, CFIndex ix, CFTypeRef result, bool force,
+                                          SecTrustResultType overrideDefaultTR) {
 
     /* If this is not something the current policy cares about ignore
        this error and return true so our caller continues evaluation. */
 
     /* If this is not something the current policy cares about ignore
        this error and return true so our caller continues evaluation. */
@@ -2797,17 +2797,27 @@ bool SecPVCSetResultForced(SecPVCRef pvc,
         }
     }
 
         }
     }
 
-       /* Check to see if the SecTrustSettings for the certificate in question
-          tell us to ignore this error. */
-       if (SecPVCIsAllowedError(pvc, ix, key)) {
-        secinfo("policy", "cert[%d]: skipped allowed error %@", (int) ix, key);
-               return true;
-       }
+    /* Get the default trust result for this key and override it if the caller needs to
+     * set a different trust result than the default. */
+    SecTrustResultType trustResult = trust_result_for_key(key);
+    if (overrideDefaultTR != kSecTrustResultInvalid) {
+        trustResult = overrideDefaultTR;
+    }
 
 
-    /* Check to see if exceptions tells us to ignore this error. */
-    if (SecPVCIsExceptedError(pvc, ix, key, result)) {
-        secinfo("policy", "cert[%d]: skipped exception error %@", (int) ix, key);
-        return true;
+    /* only recoverable errors can be allowed/excepted */
+    if (trustResult == kSecTrustResultRecoverableTrustFailure) {
+        /* Check to see if the SecTrustSettings for the certificate in question
+         tell us to ignore this error. */
+        if (SecPVCIsAllowedError(pvc, ix, key)) {
+            secinfo("policy", "cert[%d]: skipped allowed error %@", (int) ix, key);
+            return true;
+        }
+
+        /* Check to see if exceptions tells us to ignore this error. */
+        if (SecPVCIsExceptedError(pvc, ix, key, result)) {
+            secinfo("policy", "cert[%d]: skipped exception error %@", (int) ix, key);
+            return true;
+        }
     }
 
     secnotice("policy", "cert[%d]: %@ =(%s)[%s]> %@", (int) ix, key,
     }
 
     secnotice("policy", "cert[%d]: %@ =(%s)[%s]> %@", (int) ix, key,
@@ -2817,7 +2827,6 @@ bool SecPVCSetResultForced(SecPVCRef pvc,
               (force ? "force" : ""), result);
 
        /* Avoid resetting deny or fatal to recoverable */
               (force ? "force" : ""), result);
 
        /* Avoid resetting deny or fatal to recoverable */
-    SecTrustResultType trustResult = trust_result_for_key(key);
     if (SecPVCIsOkResult(pvc) || trustResult == kSecTrustResultFatalTrustFailure) {
         pvc->result = trustResult;
     } else if (trustResult == kSecTrustResultDeny &&
     if (SecPVCIsOkResult(pvc) || trustResult == kSecTrustResultFatalTrustFailure) {
         pvc->result = trustResult;
     } else if (trustResult == kSecTrustResultDeny &&
@@ -2843,6 +2852,10 @@ bool SecPVCSetResultForced(SecPVCRef pvc,
        return true;
 }
 
        return true;
 }
 
+bool SecPVCSetResultForced(SecPVCRef pvc, CFStringRef key, CFIndex ix, CFTypeRef result, bool force) {
+    return SecPVCSetResultForcedWithTrustResult(pvc, key, ix, result, force, kSecTrustResultInvalid);
+}
+
 bool SecPVCSetResult(SecPVCRef pvc,
        CFStringRef key, CFIndex ix, CFTypeRef result) {
     return SecPVCSetResultForced(pvc, key, ix, result, false);
 bool SecPVCSetResult(SecPVCRef pvc,
        CFStringRef key, CFIndex ix, CFTypeRef result) {
     return SecPVCSetResultForced(pvc, key, ix, result, false);
@@ -3649,12 +3662,12 @@ static void SecPVCCheckRequireCTConstraints(SecPVCRef pvc) {
     if (!SecOTAPKIKillSwitchEnabled(otaref, kOTAPKIKillSwitchCT) &&
         SecOTAPKIAssetStalenessLessThanSeconds(otaref, kSecOTAPKIAssetStalenessDisable)) {
         /* CT was required. Error is always set on leaf certificate. */
     if (!SecOTAPKIKillSwitchEnabled(otaref, kOTAPKIKillSwitchCT) &&
         SecOTAPKIAssetStalenessLessThanSeconds(otaref, kSecOTAPKIAssetStalenessDisable)) {
         /* CT was required. Error is always set on leaf certificate. */
-        SecPVCSetResultForced(pvc, kSecPolicyCheckCTRequired,
-                              0, kCFBooleanFalse, true);
         if (ctp != kSecPathCTRequiredOverridable) {
         if (ctp != kSecPathCTRequiredOverridable) {
-            /* Normally kSecPolicyCheckCTRequired is recoverable,
-             so need to manually change trust result here. */
-            pvc->result = kSecTrustResultFatalTrustFailure;
+            /* Normally kSecPolicyCheckCTRequired is recoverable */
+            SecPVCSetResultForcedWithTrustResult(pvc, kSecPolicyCheckCTRequired, 0, kCFBooleanFalse, true,
+                                                 kSecTrustResultFatalTrustFailure);
+        } else {
+            SecPVCSetResultForced(pvc, kSecPolicyCheckCTRequired, 0, kCFBooleanFalse, true);
         }
     }
     CFReleaseNull(otaref);
         }
     }
     CFReleaseNull(otaref);
index fbeebb822e1f58ba91bfd457d8bba48ff64ca6cf..44811a01d20cb67ce57f734a4d6a7f0be471a189 100644 (file)
@@ -49,11 +49,10 @@ SecPolicyRef SecPVCGetPolicy(SecPVCRef pv);
 /* Set the string result as the reason for the sub policy check key
    failing.  The policy check function should continue processing if
    this function returns true. */
 /* Set the string result as the reason for the sub policy check key
    failing.  The policy check function should continue processing if
    this function returns true. */
-bool SecPVCSetResult(SecPVCRef pv, CFStringRef key, CFIndex ix,
-       CFTypeRef result);
-bool SecPVCSetResultForced(SecPVCRef pvc,
-       CFStringRef key, CFIndex ix, CFTypeRef result, bool force);
-bool SecPVCIsOkResult(SecPVCRef pvc);
+bool SecPVCSetResult(SecPVCRef pv, CFStringRef key, CFIndex ix, CFTypeRef result);
+bool SecPVCSetResultForced(SecPVCRef pvc, CFStringRef key, CFIndex ix, CFTypeRef result, bool force);
+bool SecPVCSetResultForcedWithTrustResult(SecPVCRef pvc, CFStringRef key, CFIndex ix, CFTypeRef result, bool force,
+                                          SecTrustResultType overrideDefaultTR);
 
 /* Is the current result considered successful. */
 bool SecPVCIsOkResult(SecPVCRef pvc);
 
 /* Is the current result considered successful. */
 bool SecPVCIsOkResult(SecPVCRef pvc);
index 266bdba3214770c195355e510979db1f25d6b821..b0b5ac8699a183eb867508e481633198a3b172d1 100644 (file)
@@ -289,6 +289,9 @@ void SecORVCConsumeOCSPResponse(SecORVCRef rvc, SecOCSPResponseRef ocspResponse
     // but we haven't checked dates yet.
 
     bool sr_valid = SecOCSPSingleResponseCalculateValidity(sr, kSecDefaultOCSPResponseTTL, verifyTime);
     // but we haven't checked dates yet.
 
     bool sr_valid = SecOCSPSingleResponseCalculateValidity(sr, kSecDefaultOCSPResponseTTL, verifyTime);
+    if (sr_valid) {
+        rvc->rvc->revocation_checked = true;
+    }
     if (sr->certStatus == CS_Good) {
         // Side effect of SecOCSPResponseCalculateValidity sets ocspResponse->expireTime
         require_quiet(sr_valid && SecOCSPResponseCalculateValidity(ocspResponse, maxAge, kSecDefaultOCSPResponseTTL, verifyTime), errOut);
     if (sr->certStatus == CS_Good) {
         // Side effect of SecOCSPResponseCalculateValidity sets ocspResponse->expireTime
         require_quiet(sr_valid && SecOCSPResponseCalculateValidity(ocspResponse, maxAge, kSecDefaultOCSPResponseTTL, verifyTime), errOut);
@@ -479,12 +482,11 @@ static void SecRVCProcessValidPolicyConstraints(SecRVCRef rvc) {
             SecPolicyRef policy = (SecPolicyRef)CFArrayGetValueAtIndex(policies, policyIX);
             if (!SecRVCPolicyConstraintsPermitPolicy(constraints, count, policy)) {
                 policyDeniedByConstraints = true;
             SecPolicyRef policy = (SecPolicyRef)CFArrayGetValueAtIndex(policies, policyIX);
             if (!SecRVCPolicyConstraintsPermitPolicy(constraints, count, policy)) {
                 policyDeniedByConstraints = true;
-                SecPVCSetResultForced(pvc, kSecPolicyCheckIssuerPolicyConstraints, rvc->certIX,
-                                      kCFBooleanFalse, true);
-                pvc->result = kSecTrustResultRecoverableTrustFailure;
-                if (!rvc->valid_info->overridable) {
-                    /* error for this check should be non-recoverable */
-                    pvc->result = kSecTrustResultFatalTrustFailure;
+                if (rvc->valid_info->overridable) {
+                    SecPVCSetResultForcedWithTrustResult(pvc, kSecPolicyCheckIssuerPolicyConstraints, rvc->certIX,
+                                                         kCFBooleanFalse, true, kSecTrustResultRecoverableTrustFailure);
+                } else {
+                    SecPVCSetResultForced(pvc, kSecPolicyCheckIssuerPolicyConstraints, rvc->certIX, kCFBooleanFalse, true);
                 }
             }
         }
                 }
             }
         }
@@ -614,6 +616,10 @@ void SecRVCSetValidDeterminedErrorResult(SecRVCRef rvc) {
     CFReleaseNull(cfreason);
 }
 
     CFReleaseNull(cfreason);
 }
 
+bool SecRVCRevocationChecked(SecRVCRef rvc) {
+    return rvc->revocation_checked;
+}
+
 static void SecRVCProcessValidInfoResults(SecRVCRef rvc) {
     if (!rvc || !rvc->valid_info || !rvc->builder) {
         return;
 static void SecRVCProcessValidInfoResults(SecRVCRef rvc) {
     if (!rvc || !rvc->valid_info || !rvc->builder) {
         return;
@@ -645,6 +651,7 @@ static void SecRVCProcessValidInfoResults(SecRVCRef rvc) {
         } else if (allowed) {
             /* definitely not revoked (allowlisted) */
             SecCertificatePathVCSetIsAllowlisted(path, true);
         } else if (allowed) {
             /* definitely not revoked (allowlisted) */
             SecCertificatePathVCSetIsAllowlisted(path, true);
+            rvc->revocation_checked = true;
         }
         /* no-ca is definitive; no need to check further. */
         secdebug("validupdate", "rvc: definitely %s cert %" PRIdCFIndex,
         }
         /* no-ca is definitive; no need to check further. */
         secdebug("validupdate", "rvc: definitely %s cert %" PRIdCFIndex,
index b3f7a090d77aff6088aab90d1b0ce13b9230f655..ef41918f1f7b99b41cf4a7b305e0dd35c5d1eb70 100644 (file)
@@ -52,6 +52,8 @@ struct OpaqueSecRVC {
     SecValidInfoRef     valid_info;
 
     bool                done;
     SecValidInfoRef     valid_info;
 
     bool                done;
+
+    bool                revocation_checked;
 };
 typedef struct OpaqueSecRVC *SecRVCRef;
 
 };
 typedef struct OpaqueSecRVC *SecRVCRef;
 
@@ -91,6 +93,7 @@ void SecRVCDelete(SecRVCRef rvc);
 bool SecRVCHasDefinitiveValidInfo(SecRVCRef rvc);
 bool SecRVCHasRevokedValidInfo(SecRVCRef rvc);
 void SecRVCSetValidDeterminedErrorResult(SecRVCRef rvc);
 bool SecRVCHasDefinitiveValidInfo(SecRVCRef rvc);
 bool SecRVCHasRevokedValidInfo(SecRVCRef rvc);
 void SecRVCSetValidDeterminedErrorResult(SecRVCRef rvc);
+bool SecRVCRevocationChecked(SecRVCRef rvc);
 
 /* OCSP verification callbacks */
 void SecORVCConsumeOCSPResponse(SecORVCRef rvc, SecOCSPResponseRef ocspResponse /*CF_CONSUMED*/,
 
 /* OCSP verification callbacks */
 void SecORVCConsumeOCSPResponse(SecORVCRef rvc, SecOCSPResponseRef ocspResponse /*CF_CONSUMED*/,
index 06f56077ab55d47803fb714313f1cd4c335409de..8e1eeee02aab8a799f72ae90eb35d531e69aaed3 100644 (file)
@@ -1210,6 +1210,11 @@ static bool SecPathBuilderReportResult(SecPathBuilderRef builder) {
                                  kCFBooleanFalse); /* iOS key */
             CFDictionarySetValue(builder->info, kSecTrustRevocationChecked,
                                  kCFBooleanFalse); /* unified API key */
                                  kCFBooleanFalse); /* iOS key */
             CFDictionarySetValue(builder->info, kSecTrustRevocationChecked,
                                  kCFBooleanFalse); /* unified API key */
+        } else if (SecCertificatePathVCRevocationCheckedAllCerts(builder->bestPath)) {
+            CFDictionarySetValue(builder->info, kSecTrustInfoRevocationKey,
+                                 kCFBooleanTrue); /* iOS key */
+            CFDictionarySetValue(builder->info, kSecTrustRevocationChecked,
+                                 kCFBooleanTrue); /* unified API key */
         }
     }
 
         }
     }