OctagonPairingTests.swift [plain text]
#if OCTAGON
func GenerateFullECKey(keySize: Int) -> (SecKey) {
let keyPair = _SFECKeyPair.init(randomKeyPairWith: _SFECKeySpecifier.init(curve: SFEllipticCurve.nistp384))!
var keyAttributes: Dictionary<String, String> = [:]
keyAttributes[kSecAttrKeyClass as String] = kSecAttrKeyClassPrivate as String
keyAttributes[kSecAttrKeyType as String] = kSecAttrKeyTypeEC as String
let key = SecKeyCreateWithData(keyPair.keyData as CFData, keyAttributes as CFDictionary, nil)!
return key
}
class KCJoiningRequestTestDelegate: NSObject, KCJoiningRequestSecretDelegate, KCJoiningRequestCircleDelegate {
var sharedSecret: String = ""
var accountCode: String = ""
var circleJoinData = Data()
var peerInfo: SOSPeerInfoRef?
var incorrectSecret: String = ""
var incorrectTries: Int = 0
class func requestDelegate(withSecret secret: String) -> KCJoiningRequestTestDelegate {
return self.requestDelegateWithSecret(secret: secret, wrongSecret: "", retries: 0)
}
class func requestDelegateWithSecret(secret: String, wrongSecret: String, retries: Int) -> KCJoiningRequestTestDelegate {
return KCJoiningRequestTestDelegate(withSecret: secret, incorrectSecret: wrongSecret, retries: retries)
}
init(withSecret secret: String, incorrectSecret: String, retries: Int) {
let signingKey = GenerateFullECKey(keySize: 256)
let octagonSigningKey = GenerateFullECKey(keySize: 384)
let octagonEncryptionKey = GenerateFullECKey(keySize: 384)
XCTAssertNotNil(octagonSigningKey, "signing key should not be nil")
XCTAssertNotNil(octagonEncryptionKey, "encryption key should not be nil")
var gestalt: Dictionary<String, String> = [:]
gestalt[kPIUserDefinedDeviceNameKey as String] = "Fakey"
let newPeerInfo = SOSPeerInfoCreate(nil, gestalt as CFDictionary, nil, signingKey, octagonSigningKey, octagonEncryptionKey, nil)
self.peerInfo = newPeerInfo
self.sharedSecret = secret
self.incorrectSecret = incorrectSecret
self.incorrectTries = retries
}
func nextSecret() -> String {
if (self.incorrectTries > 0) {
self.incorrectTries -= 1
return self.incorrectSecret
}
return self.sharedSecret
}
func secret() -> String {
return self.nextSecret()
}
func verificationFailed(_ codeChanged: Bool) -> String {
return self.nextSecret()
}
func processAccountCode(_ accountCode: String, error: NSErrorPointer) -> Bool {
self.accountCode = accountCode
return true
}
func copyPeerInfoError(_ error: NSErrorPointer) -> SOSPeerInfoRef {
return self.peerInfo!
}
func processCircleJoin(_ circleJoinData: Data, version: PiggyBackProtocolVersion, error: NSErrorPointer) -> Bool {
self.circleJoinData = circleJoinData
return true
}
}
class KCJoiningAcceptTestDelegate: NSObject, KCJoiningAcceptSecretDelegate, KCJoiningAcceptCircleDelegate {
var secrets: Array<String> = []
var currentSecret: Int = 0
var retriesLeft: Int = 0
var retriesPerSecret: Int = 0
var codeToUse: String = ""
var circleJoinData = Data()
var peerInfo: SOSPeerInfoRef?
class func acceptDelegateWithSecrets(secrets: Array<String>, retries: Int, code: String) -> KCJoiningAcceptTestDelegate {
return KCJoiningAcceptTestDelegate(withSecrets: secrets, retries: retries, code: code)
}
class func acceptDelegateWithSecret(secret: String, code: String) -> KCJoiningAcceptTestDelegate {
return KCJoiningAcceptTestDelegate.initWithSecret(secret: secret, code: code)
}
class func initWithSecret(secret: String, code: String) -> KCJoiningAcceptTestDelegate {
var secretArray: Array<String> = Array()
secretArray.append(secret)
return KCJoiningAcceptTestDelegate(withSecrets: secretArray, retries: 3, code: code)
}
init(withSecrets secrets: Array<String>, retries: Int, code: String) {
self.secrets = secrets
self.currentSecret = 0
self.retriesPerSecret = retries
self.retriesLeft = self.retriesPerSecret
self.codeToUse = code
let joinDataBuffer = [10, 9, 8, 7, 6, 5, 4, 3, 2, 1]
do {
self.circleJoinData = try NSKeyedArchiver.archivedData(withRootObject: joinDataBuffer, requiringSecureCoding: false)
} catch {
XCTFail("error loading account state: \(error)")
}
}
func advanceSecret() -> KCRetryOrNot {
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
}
}
func secret() -> String {
return self.secrets[self.currentSecret]
}
func accountCode() -> String {
return self.codeToUse
}
func circleGetInitialSyncViews(error: Error) -> Data {
return Data()
}
func verificationFailed(_ error: NSErrorPointer) -> KCRetryOrNot {
return self.advanceSecret()
}
func circleJoinData(for peer: SOSPeerInfoRef, error: NSErrorPointer) -> Data {
let joinDataBuffer = [ 10, 9, 8, 7, 6, 5, 4, 3, 2, 1 ]
self.peerInfo = peer
do {
return try NSKeyedArchiver.archivedData(withRootObject: joinDataBuffer, requiringSecureCoding: false)
} catch {
XCTFail("error loading account state: \(error)")
return Data()
}
}
func circleGetInitialSyncViews(_ flags: SOSInitialSyncFlags, error: NSErrorPointer) -> Data {
return Data()
}
}
@objcMembers class OctagonPairingTests: OctagonTestsBase {
var sosAdapterForAcceptor: CKKSMockSOSPresentAdapter!
var cuttlefishContextForAcceptor: OTCuttlefishContext!
var contextForAcceptor = "defaultContextForAcceptor"
var initiatorPiggybackingConfig: OTJoiningConfiguration!
var acceptorPiggybackingConfig: OTJoiningConfiguration!
var initiatorPairingConfig: OTJoiningConfiguration!
var acceptorPairingConfig: OTJoiningConfiguration!
var circle: SOSCircleRef!
var sosControl: NSXPCConnection!
var initiatorName = "uniqueInitiatorID"
var fcInitiator: FCPairingFakeSOSControl!
var fcAcceptor: FCPairingFakeSOSControl!
override func setUp() {
super.setUp()
// The acceptor should have its own SOS state
self.sosAdapterForAcceptor = CKKSMockSOSPresentAdapter(selfPeer: self.createSOSPeer(peerID: "sos-acceptor"),
trustedPeers: Set(),
essential: false)
self.sosAdapterForAcceptor.circleStatus = SOSCCStatus(kSOSCCInCircle)
self.cuttlefishContextForAcceptor = self.manager.context(forContainerName: OTCKContainerName,
contextID: self.contextForAcceptor,
sosAdapter: self.sosAdapterForAcceptor,
authKitAdapter: self.mockAuthKit3,
lockStateTracker: self.lockStateTracker,
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)
}
func getAcceptorInCircle() {
let resetAndEstablishExpectation = self.expectation(description: "resetAndEstablish callback occurs")
self.cuttlefishContextForAcceptor.startOctagonStateMachine()
self.cuttlefishContextForAcceptor.rpcResetAndEstablish(.testGenerated) { resetError in
XCTAssertNil(resetError, "Should be no error calling resetAndEstablish")
resetAndEstablishExpectation.fulfill()
}
self.wait(for: [resetAndEstablishExpectation], timeout: 10)
self.assertEnters(context: self.cuttlefishContextForAcceptor, state: OctagonStateReady, within: 10 * NSEC_PER_SEC)
self.assertConsidersSelfTrusted(context: self.cuttlefishContextForAcceptor)
XCTAssertEqual(self.fakeCuttlefishServer.state.bottles.count, 1, "should be 1 bottles")
}
func setupPairingEndpoints(withPairNumber pairNumber: String, initiatorContextID: String, acceptorContextID: String, initiatorUniqueID: String, acceptorUniqueID: String) -> (KCPairingChannel, KCPairingChannel) {
let (acceptorClique, initiatorClique) = self.setupOTCliquePair(withNumber: pairNumber)
XCTAssertNotNil(acceptorClique, "acceptorClique should not be nil")
XCTAssertNotNil(initiatorClique, "initiatorClique should not be nil")
let acceptorContext = KCPairingChannelContext()
acceptorContext.model = "AcceptorModel"
acceptorContext.osVersion = "AcceptorOsVersion"
acceptorContext.modelClass = "AcceptorModelClass"
acceptorContext.uniqueDeviceID = acceptorUniqueID
acceptorContext.uniqueClientID = initiatorUniqueID
let initiatorContext = KCPairingChannelContext()
initiatorContext.model = "InitiatorModel"
initiatorContext.osVersion = "InitiatorOsVersion"
initiatorContext.modelClass = "InitiatorModelClass"
initiatorContext.uniqueDeviceID = initiatorUniqueID
initiatorContext.uniqueDeviceID = initiatorUniqueID
let acceptor = acceptorClique!.setupPairingChannel(asAcceptor: acceptorContext)
let initiator = initiatorClique!.setupPairingChannel(asInitiator: initiatorContext)
XCTAssertNotNil(acceptor, "acceptor should not be nil")
XCTAssertNotNil(initiator, "initiator should not be nil")
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)
acceptor.setConfiguration(acceptorPairingConfig)
initiator.setConfiguration(initiatorPairingConfig)
self.circle = SOSCircleCreate(kCFAllocatorDefault, "TEST DOMAIN" as CFString, nil) as SOSCircleRef
self.fcInitiator = FCPairingFakeSOSControl(randomAccountKey: true, circle: circle)
self.fcAcceptor = FCPairingFakeSOSControl(randomAccountKey: true, circle: circle)
let sosConnectionInitiator = FakeNSXPCConnectionSOS(withSOSControl: self.fcInitiator)
let sosConnectionAcceptor = FakeNSXPCConnectionSOS(withSOSControl: self.fcAcceptor)
acceptor.setXPCConnectionObject(sosConnectionAcceptor)
initiator.setXPCConnectionObject(sosConnectionInitiator)
return (acceptor, initiator)
}
func setupOTCliquePair(withNumber count: String) -> (OTClique?, OTClique?) {
let secondAcceptorData = OTConfigurationContext()
secondAcceptorData.context = "secondAcceptor"
secondAcceptorData.dsid = "a-"+count
secondAcceptorData.altDSID = "alt-a-"+count
let acceptorAnalytics = SFSignInAnalytics(signInUUID: "uuid", category: "com.apple.cdp", eventName: "signed in")
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 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)
return (acceptor, initiator)
} catch {
XCTFail("error creating test clique: \(error)")
}
return(nil, nil)
}
func setupKCJoiningSessionObjects() -> (KCJoiningRequestTestDelegate?, KCJoiningAcceptTestDelegate?, KCJoiningAcceptSession?, KCJoiningRequestSecretSession?) {
let secret = "123456"
let code = "987654"
let dsid: UInt64 = 0x1234567887654321
let requestDelegate = KCJoiningRequestTestDelegate.requestDelegate(withSecret: secret)
let acceptDelegate = KCJoiningAcceptTestDelegate.acceptDelegateWithSecret(secret: secret, code: code)
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)
} catch {
XCTFail("error creating test clique: \(error)")
return (nil, nil, nil, nil)
}
}
}
#endif