OctagonTests+SOS.swift [plain text]
#if OCTAGON
class OctagonSOSTests: OctagonTestsBase {
func testSOSOctagonKeyConsistency() throws {
self.putFakeKeyHierarchy(inCloudKit: self.manateeZoneID)
self.putSelfTLKShares(inCloudKit: self.manateeZoneID)
self.saveTLKMaterial(toKeychain: self.manateeZoneID)
self.startCKAccountStatusMock()
self.mockSOSAdapter.circleStatus = SOSCCStatus(kSOSCCInCircle)
XCTAssertTrue(OctagonPerformSOSUpgrade(), "SOS upgrade should be on")
self.cuttlefishContext.startOctagonStateMachine()
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.waitForCKModifications()
self.assertSelfTLKSharesInCloudKit(context: self.cuttlefishContext)
let peerID = try self.cuttlefishContext.accountMetadataStore.getEgoPeerID()
XCTAssertNotNil(peerID, "Should have a peer ID")
// CKKS will upload new TLKShares
self.assertAllCKKSViewsUpload(tlkShares: 2)
let newSOSPeer = createSOSPeer(peerID: peerID)
self.mockSOSAdapter.selfPeer = newSOSPeer
self.mockSOSAdapter.trustedPeers.add(newSOSPeer)
// Now restart the context
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)
self.assertConsidersSelfTrustedCachedAccountStatus(context: self.cuttlefishContext)
let restartedPeerID = try self.cuttlefishContext.accountMetadataStore.getEgoPeerID()
XCTAssertNotNil(restartedPeerID, "Should have a peer ID after restarting")
XCTAssertEqual(peerID, restartedPeerID, "Should have the same peer ID after restarting")
assertAllCKKSViews(enter: SecCKKSZoneKeyStateReady, within: 10 * NSEC_PER_SEC)
self.verifyDatabaseMocks()
}
func testSOSOctagonKeyConsistencyLocked() throws {
self.putFakeKeyHierarchy(inCloudKit: self.manateeZoneID)
self.putSelfTLKShares(inCloudKit: self.manateeZoneID)
self.saveTLKMaterial(toKeychain: self.manateeZoneID)
self.startCKAccountStatusMock()
self.mockSOSAdapter.circleStatus = SOSCCStatus(kSOSCCInCircle)
XCTAssertTrue(OctagonPerformSOSUpgrade(), "SOS upgrade should be on")
self.cuttlefishContext.startOctagonStateMachine()
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.waitForCKModifications()
self.assertSelfTLKSharesInCloudKit(context: self.cuttlefishContext)
let peerID = try self.cuttlefishContext.accountMetadataStore.getEgoPeerID()
XCTAssertNotNil(peerID, "Should have a peer ID")
let newSOSPeer = createSOSPeer(peerID: peerID)
self.mockSOSAdapter.selfPeer = newSOSPeer
self.mockSOSAdapter.trustedPeers.add(newSOSPeer)
self.aksLockState = true
self.lockStateTracker.recheck()
assertAllCKKSViews(enter: SecCKKSZoneKeyStateReady, within: 10 * NSEC_PER_SEC)
// Now restart the context
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: OctagonStateWaitForUnlock, within: 10 * NSEC_PER_SEC)
assertAllCKKSViews(enter: SecCKKSZoneKeyStateWaitForTrust, within: 10 * NSEC_PER_SEC)
self.assertAllCKKSViewsUpload(tlkShares: 2)
self.aksLockState = false
self.lockStateTracker.recheck()
self.assertEnters(context: self.cuttlefishContext, state: OctagonStateReady, within: 10 * NSEC_PER_SEC)
self.assertConsidersSelfTrustedCachedAccountStatus(context: self.cuttlefishContext)
let restartedPeerID = try self.cuttlefishContext.accountMetadataStore.getEgoPeerID()
XCTAssertNotNil(restartedPeerID, "Should have a peer ID after restarting")
XCTAssertEqual(peerID, restartedPeerID, "Should have the same peer ID after restarting")
self.verifyDatabaseMocks()
self.waitForCKModifications()
assertAllCKKSViews(enter: SecCKKSZoneKeyStateReady, within: 10 * NSEC_PER_SEC)
}
func testSOSOctagonKeyConsistencySucceedsAfterUpdatingSOS() throws {
self.putFakeKeyHierarchy(inCloudKit: self.manateeZoneID!)
self.putSelfTLKShares(inCloudKit: self.manateeZoneID!)
self.saveTLKMaterial(toKeychain: self.manateeZoneID!)
self.startCKAccountStatusMock()
self.mockSOSAdapter.circleStatus = SOSCCStatus(kSOSCCInCircle)
XCTAssertTrue(OctagonPerformSOSUpgrade(), "SOS upgrade should be on")
self.cuttlefishContext.startOctagonStateMachine()
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.waitForCKModifications()
self.assertSelfTLKSharesInCloudKit(context: self.cuttlefishContext)
let peerID = try self.cuttlefishContext.accountMetadataStore.getEgoPeerID()
XCTAssertNotNil(peerID, "Should have a peer ID")
let newSOSPeer = createSOSPeer(peerID: peerID)
self.mockSOSAdapter.selfPeer = newSOSPeer
self.mockSOSAdapter.trustedPeers.add(newSOSPeer)
self.assertEnters(context: self.cuttlefishContext, state: OctagonStateReady, within: 10 * NSEC_PER_SEC)
self.assertSelfTLKSharesInCloudKit(context: self.cuttlefishContext)
self.assertConsidersSelfTrustedCachedAccountStatus(context: self.cuttlefishContext)
assertAllCKKSViews(enter: SecCKKSZoneKeyStateReady, within: 10 * NSEC_PER_SEC)
// Now restart the context
self.manager.removeContext(forContainerName: OTCKContainerName, contextID: OTDefaultContext)
self.restartCKKSViews()
self.cuttlefishContext = self.manager.context(forContainerName: OTCKContainerName, contextID: OTDefaultContext)
self.cuttlefishContext.startOctagonStateMachine()
self.aksLockState = true
self.lockStateTracker.recheck()
self.assertEnters(context: self.cuttlefishContext, state: OctagonStateWaitForUnlock, within: 10 * NSEC_PER_SEC)
assertAllCKKSViews(enter: SecCKKSZoneKeyStateWaitForTrust, within: 10 * NSEC_PER_SEC)
self.assertAllCKKSViewsUpload(tlkShares: 2)
self.aksLockState = false
self.lockStateTracker.recheck()
self.assertEnters(context: self.cuttlefishContext, state: OctagonStateReady, within: 10 * NSEC_PER_SEC)
self.assertConsidersSelfTrustedCachedAccountStatus(context: self.cuttlefishContext)
let restartedPeerID = try self.cuttlefishContext.accountMetadataStore.getEgoPeerID()
XCTAssertNotNil(restartedPeerID, "Should have a peer ID after restarting")
XCTAssertEqual(peerID, restartedPeerID, "Should have the same peer ID after restarting")
self.verifyDatabaseMocks()
self.waitForCKModifications()
assertAllCKKSViews(enter: SecCKKSZoneKeyStateReady, within: 10 * NSEC_PER_SEC)
}
func testSOSPerformOctagonKeyConsistencyOnCircleChange() throws {
self.startCKAccountStatusMock()
// Octagon establishes its identity before SOS joins
self.mockSOSAdapter.circleStatus = SOSCCStatus(kSOSCCNotInCircle)
self.assertResetAndBecomeTrustedInDefaultContext()
let peerID = try self.cuttlefishContext.accountMetadataStore.getEgoPeerID()
XCTAssertNotNil(peerID, "Should have a peer ID")
// Now, SOS arrives
let updateExpectation = self.expectation(description: "Octagon should inform SOS of its keys")
self.mockSOSAdapter.updateOctagonKeySetListener = { _ in
// Don't currently check the key set at all here
updateExpectation.fulfill()
}
// CKKS will upload itself new shares (for the newly trusted SOS self peer)
self.assertAllCKKSViewsUpload(tlkShares: 1)
self.mockSOSAdapter.circleStatus = SOSCCStatus(kSOSCCInCircle)
// Note: this should probably be sendSelfPeerChangedUpdate, but we don't have great fidelity around which peer
// actually changed. So, just use this channel for now
self.mockSOSAdapter.sendTrustedPeerSetChangedUpdate()
self.wait(for: [updateExpectation], timeout: 10)
self.assertEnters(context: self.cuttlefishContext, state: OctagonStateReady, within: 10 * NSEC_PER_SEC)
self.assertAllCKKSViews(enter: SecCKKSZoneKeyStateReady, within: 10 * NSEC_PER_SEC)
}
func testDisablingSOSFeatureFlag() throws {
self.startCKAccountStatusMock()
OctagonSetSOSFeatureEnabled(false)
let recoverykeyotcliqueContext = OTConfigurationContext()
recoverykeyotcliqueContext.context = "recoveryContext"
recoverykeyotcliqueContext.dsid = "1234"
recoverykeyotcliqueContext.altDSID = self.mockAuthKit.altDSID!
recoverykeyotcliqueContext.otControl = self.otControl
var clique: OTClique
do {
clique = try OTClique.newFriends(withContextData: recoverykeyotcliqueContext)
XCTAssertNotNil(clique, "Clique should not be nil")
} catch {
XCTFail("Shouldn't have errored making new friends: \(error)")
throw error
}
do{
try clique.joinAfterRestore()
} catch {
XCTAssertNotNil(error, "error should not be nil")
}
do{
try clique.isLastFriend()
} catch {
XCTAssertNotNil(error, "error should not be nil")
}
do{
try clique.safariPasswordSyncingEnabled()
} catch {
XCTAssertNotNil(error, "error should not be nil")
}
do{
try clique.waitForInitialSync()
} catch {
XCTAssertNotNil(error, "error should not be nil")
}
clique.viewSet(Set(), disabledViews: Set())
do{
try clique.setUserCredentialsAndDSID("", password: Data())
} catch {
XCTAssertNotNil(error, "error should not be nil")
}
do{
try clique.tryUserCredentialsAndDSID("", password: Data())
} catch {
XCTAssertNotNil(error, "error should not be nil")
}
do{
try clique.peersHaveViewsEnabled([""])
} catch {
XCTAssertNotNil(error, "error should not be nil")
}
do{
try clique.requestToJoinCircle()
} catch {
XCTAssertNotNil(error, "error should not be nil")
}
clique.accountUserKeyAvailable()
do{
_ = try clique.copyViewUnawarePeerInfo()
}catch{
XCTAssertNotNil(error, "error should not be nil")
}
do {
_ = try clique.copyPeerPeerInfo()
}catch{
XCTAssertNotNil(error, "error should not be nil")
}
}
}
#endif