#if OCTAGON func GenerateFullECKey(keySize: Int) -> (SecKey) { let keyPair = _SFECKeyPair.init(randomKeyPairWith: _SFECKeySpecifier.init(curve: SFEllipticCurve.nistp384))! var keyAttributes: Dictionary = [:] 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 = [:] 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 = [] 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, 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 = Array() secretArray.append(secret) return KCJoiningAcceptTestDelegate(withSecrets: secretArray, retries: 3, code: code) } init(withSecrets secrets: Array, 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