OctagonPairingTests.swift   [plain text]


#if OCTAGON

func GenerateFullECKey(keySize: Int) -> (SecKey) {

    let keyPair = _SFECKeyPair.init(randomKeyPairWith: _SFECKeySpecifier.init(curve: SFEllipticCurve.nistp384))!

    var keyAttributes: [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: [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: [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: [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: [String] = Array()
        secretArray.append(secret)
        return KCJoiningAcceptTestDelegate(withSecrets: secretArray, retries: 3, code: code)
    }

    init(withSecrets secrets: [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()
    }
}

// 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 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,
                                                                 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() {
        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

        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,
                                                           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)

        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

        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 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)
    }

    func setupKCJoiningSessionObjects() -> (KCJoiningRequestTestDelegate?, KCJoiningAcceptTestDelegate?, KCJoiningAcceptSession?, KCJoiningRequestSecretSession?) {
        let dsid: UInt64 = 0x1234567887654321

        do {
            return try self.setupKCJoiningSessionObjects(dsid: dsid, sponsorConfiguration: self.acceptorPiggybackingConfig)
        } catch {
            XCTFail("error creating test clique: \(error)")
            return (nil, nil, nil, nil)
        }
    }
}

#endif