Integrate secure pairing with example app and device controller (#2230)

* Fixes and cleanup in Secure Pairing class

* Integrate secure pairing with example app and device controller

* Fix override warning

* Remove manual keys from Android wrapper

* Remove pairing logs

* Restyled by clang-format

* Fix LGTM warning

* Fix controller logs

* const nodeID

Co-authored-by: Restyled.io <commits@restyled.io>
diff --git a/examples/chip-tool/main.cpp b/examples/chip-tool/main.cpp
index 164ad14..7031e6b 100644
--- a/examples/chip-tool/main.cpp
+++ b/examples/chip-tool/main.cpp
@@ -60,16 +60,6 @@
 constexpr NodeId kRemoteDeviceId = 12344321;
 constexpr std::chrono::seconds kWaitingForResponseTimeout(1);
 
-static const unsigned char local_private_key[] = { 0x00, 0xd1, 0x90, 0xd9, 0xb3, 0x95, 0x1c, 0x5f, 0xa4, 0xe7, 0x47,
-                                                   0x92, 0x5b, 0x0a, 0xa9, 0xa7, 0xc1, 0x1c, 0xe7, 0x06, 0x10, 0xe2,
-                                                   0xdd, 0x16, 0x41, 0x52, 0x55, 0xb7, 0xb8, 0x80, 0x8d, 0x87, 0xa1 };
-
-static const unsigned char remote_public_key[] = { 0x04, 0xe2, 0x07, 0x64, 0xff, 0x6f, 0x6a, 0x91, 0xd9, 0xc2, 0xc3, 0x0a, 0xc4,
-                                                   0x3c, 0x56, 0x4b, 0x42, 0x8a, 0xf3, 0xb4, 0x49, 0x29, 0x39, 0x95, 0xa2, 0xf7,
-                                                   0x02, 0x8c, 0xa5, 0xce, 0xf3, 0xc9, 0xca, 0x24, 0xc5, 0xd4, 0x5c, 0x60, 0x79,
-                                                   0x48, 0x30, 0x3c, 0x53, 0x86, 0xd9, 0x23, 0xe6, 0x61, 0x1f, 0x5a, 0x3d, 0xdf,
-                                                   0x9f, 0xdc, 0x35, 0xea, 0xd0, 0xde, 0x16, 0x7e, 0x64, 0xde, 0x7f, 0x3c, 0xa6 };
-
 static const char * PAYLOAD    = "Message from Standalone CHIP echo client!";
 bool isDeviceConnected         = false;
 static bool waitingForResponse = true;
@@ -79,17 +69,6 @@
                       void * appReqState)
 {
     isDeviceConnected = true;
-
-    if (state != NULL)
-    {
-        CHIP_ERROR err = controller->ManualKeyExchange(state, remote_public_key, sizeof(remote_public_key), local_private_key,
-                                                       sizeof(local_private_key));
-
-        if (err != CHIP_NO_ERROR)
-        {
-            fprintf(stderr, "Failed to exchange keys\n");
-        }
-    }
 }
 
 static bool ContentMayBeADataModelMessage(System::PacketBuffer * buffer)
diff --git a/examples/lock-app/nrfconnect/main/Server.cpp b/examples/lock-app/nrfconnect/main/Server.cpp
index 5086630..7912829 100644
--- a/examples/lock-app/nrfconnect/main/Server.cpp
+++ b/examples/lock-app/nrfconnect/main/Server.cpp
@@ -50,16 +50,6 @@
 #define EXAMPLE_SERVER_NODEID 0x3546526e
 #endif // EXAMPLE_SERVER_NODEID
 
-const uint8_t local_private_key[] = { 0xc6, 0x1a, 0x2f, 0x89, 0x36, 0x67, 0x2b, 0x26, 0x12, 0x47, 0x4f,
-                                      0x11, 0x0e, 0x34, 0x15, 0x81, 0x81, 0x12, 0xfc, 0x36, 0xeb, 0x65,
-                                      0x61, 0x07, 0xaa, 0x63, 0xe8, 0xc5, 0x22, 0xac, 0x52, 0xa1 };
-
-const uint8_t remote_public_key[] = { 0x04, 0x30, 0x77, 0x2c, 0xe7, 0xd4, 0x0a, 0xf2, 0xf3, 0x19, 0xbd, 0xfb, 0x1f,
-                                      0xcc, 0x88, 0xd9, 0x83, 0x25, 0x89, 0xf2, 0x09, 0xf3, 0xab, 0xe4, 0x33, 0xb6,
-                                      0x7a, 0xff, 0x73, 0x3b, 0x01, 0x35, 0x34, 0x92, 0x73, 0x14, 0x59, 0x0b, 0xbd,
-                                      0x44, 0x72, 0x1b, 0xcd, 0xb9, 0x02, 0x53, 0xd9, 0xaf, 0xcc, 0x1a, 0xcd, 0xae,
-                                      0xe8, 0x87, 0x2e, 0x52, 0x3b, 0x98, 0xf0, 0xa1, 0x88, 0x4a, 0xe3, 0x03, 0x75 };
-
 class ServerCallback : public SecureSessionMgrCallback
 {
 public:
@@ -93,15 +83,7 @@
 
     void OnNewConnection(Transport::PeerConnectionState * state, SecureSessionMgrBase * mgr) override
     {
-        CHIP_ERROR err;
-
         LOG_INF("Received a new connection.");
-
-        err = state->GetSecureSession().TemporaryManualKeyExchange(remote_public_key, sizeof(remote_public_key), local_private_key,
-                                                                   sizeof(local_private_key));
-
-        if (err != CHIP_NO_ERROR)
-            LOG_INF("Failed to setup encryption");
     }
 
 private:
diff --git a/examples/platform/nrf528xx/app/Server.cpp b/examples/platform/nrf528xx/app/Server.cpp
index 6ba9df9..4099e1b 100644
--- a/examples/platform/nrf528xx/app/Server.cpp
+++ b/examples/platform/nrf528xx/app/Server.cpp
@@ -68,16 +68,6 @@
 char deviceName[128];
 constexpr uint16_t kUDPBroadcastPort = 23367;
 
-const uint8_t local_private_key[] = { 0xc6, 0x1a, 0x2f, 0x89, 0x36, 0x67, 0x2b, 0x26, 0x12, 0x47, 0x4f,
-                                      0x11, 0x0e, 0x34, 0x15, 0x81, 0x81, 0x12, 0xfc, 0x36, 0xeb, 0x65,
-                                      0x61, 0x07, 0xaa, 0x63, 0xe8, 0xc5, 0x22, 0xac, 0x52, 0xa1 };
-
-const uint8_t remote_public_key[] = { 0x04, 0x30, 0x77, 0x2c, 0xe7, 0xd4, 0x0a, 0xf2, 0xf3, 0x19, 0xbd, 0xfb, 0x1f,
-                                      0xcc, 0x88, 0xd9, 0x83, 0x25, 0x89, 0xf2, 0x09, 0xf3, 0xab, 0xe4, 0x33, 0xb6,
-                                      0x7a, 0xff, 0x73, 0x3b, 0x01, 0x35, 0x34, 0x92, 0x73, 0x14, 0x59, 0x0b, 0xbd,
-                                      0x44, 0x72, 0x1b, 0xcd, 0xb9, 0x02, 0x53, 0xd9, 0xaf, 0xcc, 0x1a, 0xcd, 0xae,
-                                      0xe8, 0x87, 0x2e, 0x52, 0x3b, 0x98, 0xf0, 0xa1, 0x88, 0x4a, 0xe3, 0x03, 0x75 };
-
 class ServerCallback : public SecureSessionMgrCallback
 {
 public:
@@ -110,16 +100,7 @@
 
     virtual void OnNewConnection(Transport::PeerConnectionState * state, SecureSessionMgrBase * mgr)
     {
-        CHIP_ERROR err;
-
         NRF_LOG_INFO("Received a new connection.");
-
-        err = state->GetSecureSession().TemporaryManualKeyExchange(remote_public_key, sizeof(remote_public_key), local_private_key,
-                                                                   sizeof(local_private_key));
-        VerifyOrExit(err == CHIP_NO_ERROR, NRF_LOG_INFO("Failed to setup encryption"));
-
-    exit:
-        return;
     }
 
 private:
diff --git a/examples/wifi-echo/server/esp32/main/EchoServer.cpp b/examples/wifi-echo/server/esp32/main/EchoServer.cpp
index bb83a63..c1c4ae4 100644
--- a/examples/wifi-echo/server/esp32/main/EchoServer.cpp
+++ b/examples/wifi-echo/server/esp32/main/EchoServer.cpp
@@ -55,7 +55,7 @@
 using namespace ::chip::Inet;
 using namespace ::chip::Transport;
 
-constexpr NodeId kLocalNodeId = 12344321;
+extern const NodeId kLocalNodeId = 12344321;
 extern LEDWidget statusLED; // In wifi-echo.cpp
 
 namespace {
@@ -200,16 +200,7 @@
 
     void OnNewConnection(Transport::PeerConnectionState * state, SecureSessionMgrBase * mgr) override
     {
-        CHIP_ERROR err;
-
         ESP_LOGI(TAG, "Received a new connection.");
-
-        err = state->GetSecureSession().TemporaryManualKeyExchange(remote_public_key, sizeof(remote_public_key), local_private_key,
-                                                                   sizeof(local_private_key));
-        VerifyOrExit(err == CHIP_NO_ERROR, ESP_LOGE(TAG, "Failed to setup encryption"));
-
-    exit:
-        return;
     }
 
 private:
@@ -247,6 +238,12 @@
 
 } // namespace
 
+void PairingComplete(Optional<NodeId> peerNodeId, uint16_t peerKeyId, uint16_t localKeyId, SecurePairingSession * pairing)
+{
+    Optional<Transport::PeerAddress> peer(Transport::Type::kUndefined);
+    sessions.NewPairing(peerNodeId, peer, peerKeyId, localKeyId, pairing);
+}
+
 // The echo server assumes the platform's networking has been setup already
 void startServer()
 {
diff --git a/examples/wifi-echo/server/esp32/main/RendezvousSession.cpp b/examples/wifi-echo/server/esp32/main/RendezvousSession.cpp
index e06364c..4458d27 100644
--- a/examples/wifi-echo/server/esp32/main/RendezvousSession.cpp
+++ b/examples/wifi-echo/server/esp32/main/RendezvousSession.cpp
@@ -29,11 +29,55 @@
 
 Ble::BLEEndPoint * RendezvousSession::mEndPoint = nullptr;
 
-RendezvousSession::RendezvousSession(BluetoothWidget * virtualLed)
+bool RendezvousSession::mPairingInProgress = false;
+SecurePairingSession RendezvousSession::mPairing;
+
+static constexpr uint32_t kSpake2p_Iteration_Count = 50000;
+static const char * kSpake2pKeyExchangeSalt        = "SPAKE2P Key Exchange Salt";
+
+extern void PairingComplete(Optional<NodeId> peerNodeId, uint16_t peerKeyId, uint16_t localKeyId, SecurePairingSession * pairing);
+
+RendezvousSession::RendezvousSession(BluetoothWidget * virtualLed, uint32_t setUpPINCode, NodeId myNodeId)
 {
     mVirtualLed = virtualLed;
 
     DeviceLayer::ConnectivityMgr().AddCHIPoBLEConnectionHandler(HandleConnectionOpened);
+
+    RendezvousSession::mPairing.WaitForPairing(setUpPINCode, kSpake2p_Iteration_Count,
+                                               (const unsigned char *) kSpake2pKeyExchangeSalt, strlen(kSpake2pKeyExchangeSalt),
+                                               Optional<NodeId>::Value(myNodeId), 0, this);
+    RendezvousSession::mPairingInProgress = true;
+    mSetUpPINCode                         = setUpPINCode;
+    mNodeId                               = myNodeId;
+}
+
+CHIP_ERROR RendezvousSession::OnNewMessageForPeer(System::PacketBuffer * buffer)
+{
+    CHIP_ERROR err = CHIP_NO_ERROR;
+
+    VerifyOrExit(mEndPoint, err = CHIP_ERROR_INCORRECT_STATE);
+    err = mEndPoint->Send(buffer);
+
+exit:
+    return err;
+}
+
+void RendezvousSession::OnPairingError(CHIP_ERROR error)
+{
+    ChipLogError(Ble, "RendezvousSession: failed in pairing");
+    mPaired = false;
+    RendezvousSession::mPairing.WaitForPairing(mSetUpPINCode, kSpake2p_Iteration_Count,
+                                               (const unsigned char *) kSpake2pKeyExchangeSalt, strlen(kSpake2pKeyExchangeSalt),
+                                               Optional<NodeId>::Value(mNodeId), 0, this);
+    RendezvousSession::mPairingInProgress = true;
+}
+
+void RendezvousSession::OnPairingComplete(Optional<NodeId> peerNodeId, uint16_t peerKeyId, uint16_t localKeyId)
+{
+    ChipLogProgress(Ble, "RendezvousSession: pairing complete");
+    mPaired                               = true;
+    RendezvousSession::mPairingInProgress = false;
+    PairingComplete(peerNodeId, peerKeyId, localKeyId, &RendezvousSession::mPairing);
 }
 
 CHIP_ERROR RendezvousSession::Send(const char * msg)
@@ -74,32 +118,49 @@
 
 void RendezvousSession::HandleMessageReceived(Ble::BLEEndPoint * endPoint, PacketBuffer * buffer)
 {
-    const size_t bufferLen = buffer->DataLength();
-    char msg[bufferLen];
-    msg[bufferLen] = 0;
-    memcpy(msg, buffer->Start(), bufferLen);
-
-    ChipLogProgress(Ble, "RendezvousSession: Receive message: %s", msg);
-
-    if ((bufferLen > 3) && (msg[0] == msg[1]) && (msg[0] == msg[bufferLen - 1]))
+    if (RendezvousSession::mPairingInProgress)
     {
-        // WiFi credentials, of the form ‘::SSID:password:’, where ‘:’ can be any single ASCII character.
-        msg[1]      = 0;
-        char * ssid = strtok(&msg[2], msg);
-        char * key  = strtok(NULL, msg);
-        if (ssid && key)
-        {
-            ChipLogProgress(Ble, "RendezvousSession: SSID: %s, key: %s", ssid, key);
-            SetWiFiStationProvisioning(ssid, key);
-        }
-        else
-        {
-            ChipLogError(Ble, "RendezvousSession: SSID: %p, key: %p", ssid, key);
-        }
+        MessageHeader header;
+        size_t headerSize = 0;
+
+        CHIP_ERROR err = header.Decode(buffer->Start(), buffer->DataLength(), &headerSize);
+        SuccessOrExit(err);
+
+        buffer->ConsumeHead(headerSize);
+        RendezvousSession::mPairing.HandlePeerMessage(header, buffer);
     }
     else
     {
-        // Echo.
-        mEndPoint->Send(buffer);
+        const size_t bufferLen = buffer->DataLength();
+        char msg[bufferLen];
+        msg[bufferLen] = 0;
+        memcpy(msg, buffer->Start(), bufferLen);
+
+        ChipLogProgress(Ble, "RendezvousSession: Receive message: %s", msg);
+
+        if ((bufferLen > 3) && (msg[0] == msg[1]) && (msg[0] == msg[bufferLen - 1]))
+        {
+            // WiFi credentials, of the form ‘::SSID:password:’, where ‘:’ can be any single ASCII character.
+            msg[1]      = 0;
+            char * ssid = strtok(&msg[2], msg);
+            char * key  = strtok(NULL, msg);
+            if (ssid && key)
+            {
+                ChipLogProgress(Ble, "RendezvousSession: SSID: %s, key: %s", ssid, key);
+                SetWiFiStationProvisioning(ssid, key);
+            }
+            else
+            {
+                ChipLogError(Ble, "RendezvousSession: SSID: %p, key: %p", ssid, key);
+            }
+        }
+        else
+        {
+            // Echo.
+            mEndPoint->Send(buffer);
+        }
     }
+
+exit:
+    return;
 }
diff --git a/examples/wifi-echo/server/esp32/main/include/RendezvousSession.h b/examples/wifi-echo/server/esp32/main/include/RendezvousSession.h
index fa56bf8..536d3ec 100644
--- a/examples/wifi-echo/server/esp32/main/include/RendezvousSession.h
+++ b/examples/wifi-echo/server/esp32/main/include/RendezvousSession.h
@@ -18,13 +18,14 @@
 #include "BluetoothWidget.h"
 
 #include <platform/CHIPDeviceLayer.h>
+#include <transport/SecurePairingSession.h>
 
 using namespace ::chip;
 
-class RendezvousSession
+class RendezvousSession : public SecurePairingSessionDelegate
 {
 public:
-    RendezvousSession(BluetoothWidget * virtualLed);
+    RendezvousSession(BluetoothWidget * virtualLed, uint32_t setUpPINCode, NodeId myNodeId);
     CHIP_ERROR Send(const char * msg);
 
 private:
@@ -32,6 +33,17 @@
     static void HandleConnectionClosed(Ble::BLEEndPoint * endPoint, BLE_ERROR err);
     static void HandleMessageReceived(Ble::BLEEndPoint * endPoint, System::PacketBuffer * buffer);
 
+    virtual CHIP_ERROR OnNewMessageForPeer(System::PacketBuffer * msgBuf);
+    virtual void OnPairingError(CHIP_ERROR error);
+    virtual void OnPairingComplete(Optional<NodeId> peerNodeId, uint16_t peerKeyId, uint16_t localKeyId);
+
     static BluetoothWidget * mVirtualLed;
     static Ble::BLEEndPoint * mEndPoint;
+
+    static SecurePairingSession mPairing;
+    static bool mPairingInProgress;
+
+    bool mPaired           = false;
+    uint32_t mSetUpPINCode = 0;
+    NodeId mNodeId;
 };
diff --git a/examples/wifi-echo/server/esp32/main/wifi-echo.cpp b/examples/wifi-echo/server/esp32/main/wifi-echo.cpp
index 07312d7..9c9ab5c 100644
--- a/examples/wifi-echo/server/esp32/main/wifi-echo.cpp
+++ b/examples/wifi-echo/server/esp32/main/wifi-echo.cpp
@@ -102,6 +102,8 @@
 BluetoothWidget bluetoothLED;
 WiFiWidget wifiLED;
 
+extern NodeId kLocalNodeId;
+
 const char * TAG = "wifi-echo-demo";
 
 static EchoDeviceCallbacks EchoCallbacks;
@@ -463,7 +465,14 @@
 
     if (isRendezvousBLE())
     {
-        rendezvousSession = new RendezvousSession(&bluetoothLED);
+        uint32_t setupPINCode;
+        err = ConfigurationMgr().GetSetupPinCode(setupPINCode);
+        if (err != CHIP_NO_ERROR)
+        {
+            ESP_LOGE(TAG, "GetSetupPinCode() failed: %s", ErrorStr(err));
+            return;
+        }
+        rendezvousSession = new RendezvousSession(&bluetoothLED, setupPINCode, kLocalNodeId);
     }
 
 #if CONFIG_USE_ECHO_CLIENT
diff --git a/src/controller/CHIPDeviceController.cpp b/src/controller/CHIPDeviceController.cpp
index fbe6240..1ff36d8 100644
--- a/src/controller/CHIPDeviceController.cpp
+++ b/src/controller/CHIPDeviceController.cpp
@@ -53,6 +53,9 @@
 
 using namespace chip::Encoding;
 
+static constexpr uint32_t kSpake2p_Iteration_Count = 50000;
+static const char * kSpake2pKeyExchangeSalt        = "SPAKE2P Key Exchange Salt";
+
 ChipDeviceController::ChipDeviceController()
 {
     mState           = kState_NotInitialized;
@@ -146,6 +149,72 @@
     return err;
 }
 
+CHIP_ERROR ChipDeviceController::OnNewMessageForPeer(System::PacketBuffer * msgBuf)
+{
+    return SendMessage(mAppReqState, msgBuf);
+}
+
+void ChipDeviceController::OnPairingError(CHIP_ERROR error)
+{
+    ChipLogError(Controller, "Failed to pair with accessory. Error %d", error);
+    mPairingInProgress = false;
+
+    if (mOnError != nullptr)
+    {
+        mOnError(this, mAppReqState, error, nullptr);
+    }
+}
+
+void ChipDeviceController::OnPairingComplete(Optional<NodeId> peerNodeId, uint16_t peerKeyId, uint16_t localKeyId)
+{
+    ChipLogProgress(Controller, "Successfully paired with accessory. Key Id %d", peerKeyId);
+    mPairingInProgress = false;
+
+    if (mPairingComplete != nullptr)
+    {
+        mPeerKeyId        = peerKeyId;
+        mLocalPairedKeyId = localKeyId;
+        ChipLogProgress(Controller, "Calling mPairingComplete");
+        mPairingComplete(this, nullptr, mAppReqState);
+    }
+}
+
+void ChipDeviceController::PairingMessageHandler(ChipDeviceController * controller, void * appReqState,
+                                                 System::PacketBuffer * payload)
+{
+    if (controller->mPairingInProgress)
+    {
+        MessageHeader header;
+        size_t headerSize = 0;
+        CHIP_ERROR err    = header.Decode(payload->Start(), payload->DataLength(), &headerSize);
+        SuccessOrExit(err);
+
+        payload->ConsumeHead(headerSize);
+        controller->mPairingSession.HandlePeerMessage(header, payload);
+    }
+    else if (controller->mAppMsgHandler != nullptr)
+    {
+        controller->mAppMsgHandler(controller, appReqState, payload);
+    }
+
+exit:
+    return;
+}
+
+void ChipDeviceController::BLEConnectionHandler(ChipDeviceController * controller, Transport::PeerConnectionState * state,
+                                                void * appReqState)
+{
+    ChipLogProgress(Controller, "Starting pairing session");
+    controller->mPairingInProgress = true;
+    CHIP_ERROR err                 = controller->mPairingSession.Pair(
+        controller->mSetupPINCode, kSpake2p_Iteration_Count, (const unsigned char *) kSpake2pKeyExchangeSalt,
+        strlen(kSpake2pKeyExchangeSalt), Optional<NodeId>::Value(controller->mLocalDeviceId), controller->mNextKeyId++, controller);
+    SuccessOrExit(err);
+
+exit:
+    return;
+}
+
 CHIP_ERROR ChipDeviceController::ConnectDevice(NodeId remoteDeviceId, const uint16_t discriminator, const uint32_t setupPINCode,
                                                void * appReqState, NewConnectionHandler onConnected,
                                                MessageReceiveHandler onMessageReceived, ErrorHandler onError)
@@ -155,11 +224,22 @@
 #if CONFIG_DEVICE_LAYER && CONFIG_NETWORK_LAYER_BLE
     Transport::BLE * transport;
 
+    ChipLogProgress(Controller, "Received new pairing request");
+    ChipLogProgress(Controller, "mState %d. mConState %d", mState, mConState);
     VerifyOrExit(mState == kState_Initialized && mConState == kConnectionState_NotConnected, err = CHIP_ERROR_INCORRECT_STATE);
 
+    if (mPairingInProgress)
+    {
+        ChipLogError(Controller, "Pairing was already is progress. This will restart pairing.");
+    }
+
     mRemoteDeviceId  = Optional<NodeId>::Value(remoteDeviceId);
     mAppReqState     = appReqState;
-    mOnNewConnection = onConnected;
+    mPairingComplete = onConnected;
+    mOnNewConnection = BLEConnectionHandler;
+    mAppMsgHandler   = onMessageReceived;
+
+    mSetupPINCode = setupPINCode;
 
     transport = new Transport::BLE();
     err       = transport->Init(Transport::BleConnectionParameters(this, DeviceLayer::ConnectivityMgr().GetBleLayer())
@@ -172,7 +252,7 @@
     // connected state before 'OnConnect'
     mConState = kConnectionState_Connected;
 
-    mOnComplete.Response = onMessageReceived;
+    mOnComplete.Response = PairingMessageHandler;
     mOnError             = onError;
 
     if (err != CHIP_NO_ERROR)
@@ -214,15 +294,16 @@
 
     mSessionManager->SetDelegate(this);
 
-    // connected state before 'OnConnect' so that key exchange is accepted
-    mConState = kConnectionState_Connected;
-
-    err = mSessionManager->Connect(remoteDeviceId, Transport::PeerAddress::UDP(deviceAddr, devicePort));
-    SuccessOrExit(err);
+    mConState = kConnectionState_SecureConnected;
 
     mOnComplete.Response = onMessageReceived;
     mOnError             = onError;
 
+    err = mSessionManager->NewPairing(mRemoteDeviceId,
+                                      Optional<Transport::PeerAddress>::Value(Transport::PeerAddress::UDP(deviceAddr, devicePort)),
+                                      mPeerKeyId, mLocalPairedKeyId, &mPairingSession);
+    SuccessOrExit(err);
+
     mMessageNumber = 1;
 
 exit:
@@ -239,26 +320,6 @@
     return err;
 }
 
-CHIP_ERROR ChipDeviceController::ManualKeyExchange(Transport::PeerConnectionState * state, const unsigned char * remote_public_key,
-                                                   const size_t public_key_length, const unsigned char * local_private_key,
-                                                   const size_t private_key_length)
-{
-    CHIP_ERROR err = CHIP_NO_ERROR;
-
-    if (!IsConnected() || mSessionManager == NULL)
-    {
-        return CHIP_ERROR_INCORRECT_STATE;
-    }
-
-    err = state->GetSecureSession().TemporaryManualKeyExchange(remote_public_key, public_key_length, local_private_key,
-                                                               private_key_length);
-    SuccessOrExit(err);
-    mConState = kConnectionState_SecureConnected;
-
-exit:
-    return err;
-}
-
 CHIP_ERROR ChipDeviceController::PopulatePeerAddress(Transport::PeerAddress & peerAddress)
 {
     CHIP_ERROR err = CHIP_NO_ERROR;
@@ -376,10 +437,7 @@
     }
 }
 
-void ChipDeviceController::OnNewConnection(Transport::PeerConnectionState * state, SecureSessionMgrBase * mgr)
-{
-    mOnNewConnection(this, state, mAppReqState);
-}
+void ChipDeviceController::OnNewConnection(Transport::PeerConnectionState * state, SecureSessionMgrBase * mgr) {}
 
 void ChipDeviceController::OnMessageReceived(const MessageHeader & header, Transport::PeerConnectionState * state,
                                              System::PacketBuffer * msgBuf, SecureSessionMgrBase * mgr)
diff --git a/src/controller/CHIPDeviceController.h b/src/controller/CHIPDeviceController.h
index 2d290bf..624aa72 100644
--- a/src/controller/CHIPDeviceController.h
+++ b/src/controller/CHIPDeviceController.h
@@ -33,6 +33,7 @@
 #include <core/CHIPTLV.h>
 #include <support/DLLUtil.h>
 #include <transport/BLE.h>
+#include <transport/SecurePairingSession.h>
 #include <transport/SecureSessionMgr.h>
 #include <transport/UDP.h>
 
@@ -50,7 +51,9 @@
 typedef void (*MessageReceiveHandler)(ChipDeviceController * deviceController, void * appReqState, System::PacketBuffer * payload);
 };
 
-class DLL_EXPORT ChipDeviceController : public SecureSessionMgrCallback, public Transport::BLECallbackHandler
+class DLL_EXPORT ChipDeviceController : public SecureSessionMgrCallback,
+                                        public SecurePairingSessionDelegate,
+                                        public Transport::BLECallbackHandler
 {
     friend class ChipDeviceControllerCallback;
 
@@ -105,20 +108,26 @@
 
     /**
      * @brief
-     *   The keypair for the secure channel. This is a utility function that will be used
-     *   until we have automatic key exchange in place. The function is useful only for
-     *   example applications for now. It will eventually be removed.
+     *   Called when pairing session generates a new message that should be sent to peer.
      *
-     * @param state  Peer connection for which to establish the key
-     * @param remote_public_key  A pointer to peer's public key
-     * @param public_key_length  Length of remote_public_key
-     * @param local_private_key  A pointer to local private key
-     * @param private_key_length Length of local_private_key
-     * @return CHIP_ERROR        The result of key derivation
+     * @param msgBuf the new message that should be sent to the peer
      */
-    CHIP_ERROR ManualKeyExchange(Transport::PeerConnectionState * state, const unsigned char * remote_public_key,
-                                 const size_t public_key_length, const unsigned char * local_private_key,
-                                 const size_t private_key_length);
+    virtual CHIP_ERROR OnNewMessageForPeer(System::PacketBuffer * msgBuf) override;
+
+    /**
+     * @brief
+     *   Called when pairing fails with an error
+     *
+     * @param error error code
+     */
+    virtual void OnPairingError(CHIP_ERROR error) override;
+
+    /**
+     * @brief
+     *   Called when the pairing is complete and the new secure session has been established
+     *
+     */
+    virtual void OnPairingComplete(Optional<NodeId> peerNodeId, uint16_t peerKeyId, uint16_t localKeyId) override;
 
     /**
      * @brief
@@ -226,6 +235,8 @@
 
     ErrorHandler mOnError;
     NewConnectionHandler mOnNewConnection;
+    NewConnectionHandler mPairingComplete = nullptr;
+    MessageReceiveHandler mAppMsgHandler  = nullptr;
     System::PacketBuffer * mCurReqMsg;
 
     NodeId mLocalDeviceId;
@@ -234,8 +245,21 @@
     Optional<NodeId> mRemoteDeviceId;
     uint32_t mMessageNumber = 0;
 
+    SecurePairingSession mPairingSession;
+    uint16_t mNextKeyId     = 0;
+    bool mPairingInProgress = false;
+
+    uint32_t mSetupPINCode     = 0;
+    uint16_t mPeerKeyId        = 0;
+    uint16_t mLocalPairedKeyId = 0;
+
     void ClearRequestState();
     void ClearOpState();
+
+    static void PairingMessageHandler(ChipDeviceController * deviceController, void * appReqState, System::PacketBuffer * payload);
+
+    static void BLEConnectionHandler(ChipDeviceController * deviceController, Transport::PeerConnectionState * state,
+                                     void * appReqState);
 };
 
 } // namespace DeviceController
diff --git a/src/controller/java/CHIPDeviceController-JNI.cpp b/src/controller/java/CHIPDeviceController-JNI.cpp
index 67fbc26..b8b912f 100644
--- a/src/controller/java/CHIPDeviceController-JNI.cpp
+++ b/src/controller/java/CHIPDeviceController-JNI.cpp
@@ -80,16 +80,6 @@
 chip::NodeId kLocalDeviceId  = 112233;
 chip::NodeId kRemoteDeviceId = 12344321;
 
-static const unsigned char local_private_key[] = { 0x00, 0xd1, 0x90, 0xd9, 0xb3, 0x95, 0x1c, 0x5f, 0xa4, 0xe7, 0x47,
-                                                   0x92, 0x5b, 0x0a, 0xa9, 0xa7, 0xc1, 0x1c, 0xe7, 0x06, 0x10, 0xe2,
-                                                   0xdd, 0x16, 0x41, 0x52, 0x55, 0xb7, 0xb8, 0x80, 0x8d, 0x87, 0xa1 };
-
-static const unsigned char remote_public_key[] = { 0x04, 0xe2, 0x07, 0x64, 0xff, 0x6f, 0x6a, 0x91, 0xd9, 0xc2, 0xc3, 0x0a, 0xc4,
-                                                   0x3c, 0x56, 0x4b, 0x42, 0x8a, 0xf3, 0xb4, 0x49, 0x29, 0x39, 0x95, 0xa2, 0xf7,
-                                                   0x02, 0x8c, 0xa5, 0xce, 0xf3, 0xc9, 0xca, 0x24, 0xc5, 0xd4, 0x5c, 0x60, 0x79,
-                                                   0x48, 0x30, 0x3c, 0x53, 0x86, 0xd9, 0x23, 0xe6, 0x61, 0x1f, 0x5a, 0x3d, 0xdf,
-                                                   0x9f, 0xdc, 0x35, 0xea, 0xd0, 0xde, 0x16, 0x7e, 0x64, 0xde, 0x7f, 0x3c, 0xa6 };
-
 jint JNI_OnLoad(JavaVM * jvm, void * reserved)
 {
     CHIP_ERROR err = CHIP_NO_ERROR;
@@ -412,25 +402,7 @@
     env->ExceptionClear();
 }
 
-void HandleKeyExchange(ChipDeviceController * deviceController, Transport::PeerConnectionState * state, void * appReqState)
-{
-    JNIEnv * env;
-
-    sJVM->GetEnv((void **) &env, JNI_VERSION_1_6);
-
-    CHIP_ERROR err = deviceController->ManualKeyExchange(state, remote_public_key, sizeof(remote_public_key), local_private_key,
-                                                         sizeof(local_private_key));
-
-    if (err != CHIP_NO_ERROR)
-    {
-        ChipLogError(Controller, "Failed to exchange keys");
-        ThrowError(env, err);
-    }
-    else
-    {
-        HandleSimpleOperationComplete(deviceController, appReqState);
-    }
-}
+void HandleKeyExchange(ChipDeviceController * deviceController, Transport::PeerConnectionState * state, void * appReqState) {}
 
 void HandleEchoResponse(ChipDeviceController * deviceController, void * appReqState, System::PacketBuffer * payload)
 {
diff --git a/src/darwin/Framework/CHIP/CHIPDeviceController.mm b/src/darwin/Framework/CHIP/CHIPDeviceController.mm
index be79df3..0c84c14 100644
--- a/src/darwin/Framework/CHIP/CHIPDeviceController.mm
+++ b/src/darwin/Framework/CHIP/CHIPDeviceController.mm
@@ -115,13 +115,6 @@
     [controller _dispatchAsyncConnectBlock];
 }
 
-static void doKeyExchange(
-    chip::DeviceController::ChipDeviceController * cppController, chip::Transport::PeerConnectionState * state, void * appReqState)
-{
-    CHIPDeviceController * controller = (__bridge CHIPDeviceController *) appReqState;
-    [controller _manualKeyExchange:state];
-}
-
 static void onMessageReceived(
     chip::DeviceController::ChipDeviceController * deviceController, void * appReqState, chip::System::PacketBuffer * buffer)
 {
@@ -188,22 +181,6 @@
     }
 }
 
-- (void)_manualKeyExchange:(chip::Transport::PeerConnectionState *)state
-{
-    [self.lock lock];
-    const unsigned char * local_key_bytes = (const unsigned char *) [self.localKey bytes];
-    const unsigned char * peer_key_bytes = (const unsigned char *) [self.peerKey bytes];
-
-    CHIP_ERROR err
-        = self.cppController->ManualKeyExchange(state, peer_key_bytes, self.peerKey.length, local_key_bytes, self.localKey.length);
-    [self.lock unlock];
-
-    if (err != CHIP_NO_ERROR) {
-        CHIP_LOG_ERROR("Failed to exchange keys");
-        [self _dispatchAsyncErrorBlock:[CHIPError errorForCHIPErrorCode:err]];
-    }
-}
-
 - (BOOL)connect:(NSString *)ipAddress
       local_key:(NSData *)local_key
        peer_key:(NSData *)peer_key
@@ -219,7 +196,7 @@
     chip::Inet::IPAddress addr;
     chip::Inet::IPAddress::FromString([ipAddress UTF8String], addr);
     err = self.cppController->ConnectDevice(
-        kRemoteDeviceId, addr, (__bridge void *) self, doKeyExchange, onMessageReceived, onInternalError, CHIP_PORT);
+        kRemoteDeviceId, addr, (__bridge void *) self, nil, onMessageReceived, onInternalError, CHIP_PORT);
     [self.lock unlock];
 
     if (err != CHIP_NO_ERROR) {
diff --git a/src/transport/SecurePairingSession.cpp b/src/transport/SecurePairingSession.cpp
index de14e41..d8528a0 100644
--- a/src/transport/SecurePairingSession.cpp
+++ b/src/transport/SecurePairingSession.cpp
@@ -237,8 +237,8 @@
 
     {
         BufBound bbuf(resp->Start(), Y_len + verifier_len);
-        VerifyOrExit(bbuf.Put(&Y[0], Y_len) == Y_len, err = CHIP_ERROR_NO_MEMORY);
-        VerifyOrExit(bbuf.Put(verifier, verifier_len) == Y_len + verifier_len, err = CHIP_ERROR_NO_MEMORY);
+        bbuf.Put(&Y[0], Y_len);
+        bbuf.Put(verifier, verifier_len);
         VerifyOrExit(bbuf.Fit(), err = CHIP_ERROR_NO_MEMORY);
     }
 
@@ -289,7 +289,7 @@
 
     {
         BufBound bbuf(resp->Start(), verifier_len);
-        VerifyOrExit(bbuf.Put(verifier, verifier_len) == verifier_len, err = CHIP_ERROR_NO_MEMORY);
+        bbuf.Put(verifier, verifier_len);
         VerifyOrExit(bbuf.Fit(), err = CHIP_ERROR_NO_MEMORY);
     }
 
@@ -313,7 +313,7 @@
     mPairingComplete = true;
 
     // Call delegate to indicate pairing completion
-    mDelegate->OnPairingComplete(mPeerNodeId, mPeerKeyId);
+    mDelegate->OnPairingComplete(mPeerNodeId, mPeerKeyId, mKeyId);
 
 exit:
 
@@ -347,7 +347,7 @@
     mPairingComplete = true;
 
     // Call delegate to indicate pairing completion
-    mDelegate->OnPairingComplete(mPeerNodeId, mPeerKeyId);
+    mDelegate->OnPairingComplete(mPeerNodeId, mPeerKeyId, mKeyId);
 
 exit:
 
diff --git a/src/transport/SecurePairingSession.h b/src/transport/SecurePairingSession.h
index e8c0758..32391a1 100644
--- a/src/transport/SecurePairingSession.h
+++ b/src/transport/SecurePairingSession.h
@@ -64,8 +64,9 @@
      *
      * @param peerNodeId  Node ID of peer
      * @param peerKeyId   Encrytion key ID assigned by the peer node for this connection
+     * @param localKeyId  Encrytion key ID assigned by the local node for this connection
      */
-    virtual void OnPairingComplete(Optional<NodeId> peerNodeId, uint16_t peerKeyId) {}
+    virtual void OnPairingComplete(Optional<NodeId> peerNodeId, uint16_t peerKeyId, uint16_t localKeyId) {}
 
     virtual ~SecurePairingSessionDelegate() {}
 };
@@ -79,7 +80,7 @@
     SecurePairingSession & operator=(const SecurePairingSession &) = delete;
     SecurePairingSession & operator=(SecurePairingSession &&) = default;
 
-    ~SecurePairingSession(void);
+    virtual ~SecurePairingSession(void);
 
     /**
      * @brief
diff --git a/src/transport/SecureSession.cpp b/src/transport/SecureSession.cpp
index b4ce2ee..1142df6 100644
--- a/src/transport/SecureSession.cpp
+++ b/src/transport/SecureSession.cpp
@@ -35,8 +35,6 @@
 
 namespace {
 
-const char * kManualKeyExchangeChannelInfo = "Manual Key Exchanged Channel";
-
 constexpr size_t kAESCCMIVLen = 12;
 constexpr size_t kMaxAADLen   = 128;
 
@@ -110,9 +108,8 @@
 
 CHIP_ERROR SecureSession::GetIV(const MessageHeader & header, uint8_t * iv, size_t len)
 {
-    CHIP_ERROR err     = CHIP_NO_ERROR;
-    uint64_t nodeID    = 0;
-    uint32_t messageID = 0;
+    CHIP_ERROR err  = CHIP_NO_ERROR;
+    uint64_t nodeID = 0;
 
     BufBound bbuf(iv, len);
 
@@ -123,10 +120,8 @@
         nodeID = header.GetSourceNodeId().Value();
     }
 
-    VerifyOrExit(bbuf.Put(&nodeID, sizeof(nodeID)) == sizeof(nodeID), err = CHIP_ERROR_NO_MEMORY);
-
-    messageID = header.GetMessageId();
-    VerifyOrExit(bbuf.Put(&messageID, sizeof(messageID)) == sizeof(nodeID) + sizeof(messageID), err = CHIP_ERROR_NO_MEMORY);
+    bbuf.PutLE64(nodeID);
+    bbuf.PutLE32(header.GetMessageId());
     VerifyOrExit(bbuf.Fit(), err = CHIP_ERROR_NO_MEMORY);
 
 exit:
@@ -208,18 +203,4 @@
     return error;
 }
 
-CHIP_ERROR SecureSession::TemporaryManualKeyExchange(const unsigned char * remote_public_key, const size_t public_key_length,
-                                                     const unsigned char * local_private_key, const size_t private_key_length)
-{
-    CHIP_ERROR err  = CHIP_NO_ERROR;
-    size_t info_len = strlen(kManualKeyExchangeChannelInfo);
-
-    err = Init(remote_public_key, public_key_length, local_private_key, private_key_length, NULL, 0,
-               (const unsigned char *) kManualKeyExchangeChannelInfo, info_len);
-    SuccessOrExit(err);
-
-exit:
-    return err;
-}
-
 } // namespace chip
diff --git a/src/transport/SecureSession.h b/src/transport/SecureSession.h
index a76ba3f..38b3e91 100644
--- a/src/transport/SecureSession.h
+++ b/src/transport/SecureSession.h
@@ -107,22 +107,6 @@
      */
     void Reset(void);
 
-    /**
-     * @brief
-     *   The keypair for the secure channel. This is a utility function that will be used
-     *   until we have automatic key exchange in place. The function is useful only for
-     *   example applications for now. It will eventually be removed.
-     *
-     * @param remote_public_key  A pointer to peer's public key
-     * @param public_key_length  Length of remote_public_key
-     * @param local_private_key  A pointer to local private key
-     * @param private_key_length Length of local_private_key
-     * @return CHIP_ERROR        The result of key derivation
-     */
-    [[deprecated("Available until actual key exchange is implemented")]] CHIP_ERROR
-    TemporaryManualKeyExchange(const unsigned char * remote_public_key, const size_t public_key_length,
-                               const unsigned char * local_private_key, const size_t private_key_length);
-
 private:
     static constexpr size_t kAES_CCM128_Key_Length = 16;
 
diff --git a/src/transport/SecureSessionMgr.cpp b/src/transport/SecureSessionMgr.cpp
index ec3bff1..a57a650 100644
--- a/src/transport/SecureSessionMgr.cpp
+++ b/src/transport/SecureSessionMgr.cpp
@@ -27,6 +27,7 @@
 #include <string.h>
 #include <support/CodeUtils.h>
 #include <support/logging/CHIPLogging.h>
+#include <transport/SecurePairingSession.h>
 #include <transport/SecureSessionMgr.h>
 
 #include <inttypes.h>
@@ -85,27 +86,6 @@
     return err;
 }
 
-CHIP_ERROR SecureSessionMgrBase::Connect(NodeId peerNodeId, const Transport::PeerAddress & peerAddress)
-{
-    CHIP_ERROR err              = CHIP_NO_ERROR;
-    PeerConnectionState * state = nullptr;
-
-    VerifyOrExit(mState == State::kInitialized, err = CHIP_ERROR_INCORRECT_STATE);
-
-    err = mPeerConnections.CreateNewPeerConnectionState(peerAddress, &state);
-    SuccessOrExit(err);
-
-    state->SetPeerNodeId(peerNodeId);
-
-    if (mCB != nullptr)
-    {
-        mCB->OnNewConnection(state, this);
-    }
-
-exit:
-    return err;
-}
-
 CHIP_ERROR SecureSessionMgrBase::SendMessage(NodeId peerNodeId, System::PacketBuffer * msgBuf)
 {
     CHIP_ERROR err              = CHIP_NO_ERROR;
@@ -135,6 +115,7 @@
             .SetSourceNodeId(mLocalNodeId)              //
             .SetDestinationNodeId(peerNodeId)           //
             .SetMessageId(state->GetSendMessageIndex()) //
+            .SetEncryptionKeyID(state->GetLocalKeyID()) //
             .SetPayloadLength(headerSize + msgBuf->TotalLength());
 
         VerifyOrExit(msgBuf->EnsureReservedSize(headerSize), err = CHIP_ERROR_NO_MEMORY);
@@ -181,22 +162,33 @@
     return err;
 }
 
-CHIP_ERROR SecureSessionMgrBase::AllocateNewConnection(const MessageHeader & header, const PeerAddress & address,
-                                                       Transport::PeerConnectionState ** state)
+CHIP_ERROR SecureSessionMgrBase::NewPairing(Optional<NodeId> peerNodeId, const Optional<Transport::PeerAddress> & peerAddr,
+                                            uint16_t peerKeyId, uint16_t localKeyId, SecurePairingSession * pairing)
 {
     CHIP_ERROR err = CHIP_NO_ERROR;
 
-    err = mPeerConnections.CreateNewPeerConnectionState(address, state);
-    SuccessOrExit(err);
+    PeerConnectionState * state = nullptr;
 
-    if (header.GetSourceNodeId().HasValue())
+    // Find any existing connection with the same node and key ID
+    if (mPeerConnections.FindPeerConnectionState(peerNodeId, peerKeyId, &state))
     {
-        (*state)->SetPeerNodeId(header.GetSourceNodeId().Value());
+        mPeerConnections.MarkConnectionExpired(state);
     }
 
-    if (mCB != nullptr)
+    ChipLogProgress(Inet, "New pairing for key %d!!", peerKeyId);
+    state = nullptr;
+    err   = mPeerConnections.CreateNewPeerConnectionState(peerNodeId, peerKeyId, localKeyId, &state);
+    SuccessOrExit(err);
+
+    if (peerAddr.HasValue())
     {
-        mCB->OnNewConnection(*state, this);
+        state->SetPeerAddress(peerAddr.Value());
+    }
+
+    if (state != nullptr)
+    {
+        err = pairing->DeriveSecureSession((const unsigned char *) kSpake2pI2RSessionInfo, strlen(kSpake2pI2RSessionInfo),
+                                           state->GetSecureSession());
     }
 
 exit:
@@ -229,30 +221,19 @@
 
     VerifyOrExit(msg != nullptr, ChipLogError(Inet, "Secure transport received NULL packet, discarding"));
 
+    if (!connection->mPeerConnections.FindPeerConnectionState(header.GetSourceNodeId(), header.GetEncryptionKeyID(), &state))
     {
-        if (!connection->mPeerConnections.FindPeerConnectionState(peerAddress, &state))
-        {
-            if (header.GetSourceNodeId().HasValue())
-            {
-                // If the data is from a new address BUT the node id is the same as a previous
-                // connection, mark the previous connection invalid in order to not have duplicate node ids.
-                if (connection->mPeerConnections.FindPeerConnectionState(header.GetSourceNodeId().Value(), &state))
-                {
-                    connection->mPeerConnections.MarkConnectionExpired(state);
-                }
-            }
-
-            ChipLogProgress(Inet, "New peer connection received.");
-
-            err = connection->AllocateNewConnection(header, peerAddress, &state);
-            SuccessOrExit(err);
-        }
-        else
-        {
-            connection->mPeerConnections.MarkConnectionActive(state);
-        }
+        ChipLogProgress(Inet, "Data received on an unknown connection (%d). Dropping it!!", header.GetEncryptionKeyID());
+        ExitNow(err = CHIP_ERROR_KEY_NOT_FOUND_FROM_PEER);
     }
 
+    if (!state->GetPeerAddress().IsInitialized())
+    {
+        state->SetPeerAddress(peerAddress);
+    }
+
+    connection->mPeerConnections.MarkConnectionActive(state);
+
     // TODO this is where messages should be decoded
     {
         uint8_t * data          = msg->Start();
diff --git a/src/transport/SecureSessionMgr.h b/src/transport/SecureSessionMgr.h
index 91995f8..b447e51 100644
--- a/src/transport/SecureSessionMgr.h
+++ b/src/transport/SecureSessionMgr.h
@@ -36,6 +36,7 @@
 #include <support/DLLUtil.h>
 #include <transport/Base.h>
 #include <transport/PeerConnections.h>
+#include <transport/SecurePairingSession.h>
 #include <transport/SecureSession.h>
 #include <transport/Tuple.h>
 
@@ -92,13 +93,6 @@
 {
 public:
     /**
-     * Establishes a connection to the given peer node.
-     *
-     * A connection needs to be established before SendMessage can be called.
-     */
-    CHIP_ERROR Connect(NodeId peerNodeId, const Transport::PeerAddress & peerAddress);
-
-    /**
      * @brief
      *   Send a message to a currently connected peer
      *
@@ -127,6 +121,18 @@
         mCB = cb->Retain();
     }
 
+    /**
+     * @brief
+     *   Establish a new pairing with a peer node
+     *
+     * @details
+     *   This method sets up a new pairing with the peer node. It also
+     *   establishes the security keys for secure communication with the
+     *   peer node.
+     */
+    CHIP_ERROR NewPairing(Optional<NodeId> peerNodeId, const Optional<Transport::PeerAddress> & peerAddr, uint16_t peerKeyId,
+                          uint16_t localKeyId, SecurePairingSession * pairing);
+
 protected:
     /**
      * @brief
diff --git a/src/transport/tests/TestSecurePairingSession.cpp b/src/transport/tests/TestSecurePairingSession.cpp
index c99c0b9..d36db69 100644
--- a/src/transport/tests/TestSecurePairingSession.cpp
+++ b/src/transport/tests/TestSecurePairingSession.cpp
@@ -56,7 +56,7 @@
 
     virtual void OnPairingError(CHIP_ERROR error) { mNumPairingErrors++; }
 
-    virtual void OnPairingComplete(Optional<NodeId> peerNodeId, uint16_t peerKeyId) { mNumPairingComplete++; }
+    virtual void OnPairingComplete(Optional<NodeId> peerNodeId, uint16_t peerKeyId, uint16_t localKeyId) { mNumPairingComplete++; }
 
     uint32_t mNumMessageSend     = 0;
     uint32_t mNumPairingErrors   = 0;
diff --git a/src/transport/tests/TestSecureSessionMgr.cpp b/src/transport/tests/TestSecureSessionMgr.cpp
index 8d7434a..e8d653b 100644
--- a/src/transport/tests/TestSecureSessionMgr.cpp
+++ b/src/transport/tests/TestSecureSessionMgr.cpp
@@ -43,16 +43,6 @@
 
 TestContext sContext;
 
-static const unsigned char local_private_key[] = { 0x00, 0xd1, 0x90, 0xd9, 0xb3, 0x95, 0x1c, 0x5f, 0xa4, 0xe7, 0x47,
-                                                   0x92, 0x5b, 0x0a, 0xa9, 0xa7, 0xc1, 0x1c, 0xe7, 0x06, 0x10, 0xe2,
-                                                   0xdd, 0x16, 0x41, 0x52, 0x55, 0xb7, 0xb8, 0x80, 0x8d, 0x87, 0xa1 };
-
-static const unsigned char remote_public_key[] = { 0x04, 0xe2, 0x07, 0x64, 0xff, 0x6f, 0x6a, 0x91, 0xd9, 0xc2, 0xc3, 0x0a, 0xc4,
-                                                   0x3c, 0x56, 0x4b, 0x42, 0x8a, 0xf3, 0xb4, 0x49, 0x29, 0x39, 0x95, 0xa2, 0xf7,
-                                                   0x02, 0x8c, 0xa5, 0xce, 0xf3, 0xc9, 0xca, 0x24, 0xc5, 0xd4, 0x5c, 0x60, 0x79,
-                                                   0x48, 0x30, 0x3c, 0x53, 0x86, 0xd9, 0x23, 0xe6, 0x61, 0x1f, 0x5a, 0x3d, 0xdf,
-                                                   0x9f, 0xdc, 0x35, 0xea, 0xd0, 0xde, 0x16, 0x7e, 0x64, 0xde, 0x7f, 0x3c, 0xa6 };
-
 static const char PAYLOAD[]         = "Hello!";
 constexpr NodeId kSourceNodeId      = 123654;
 constexpr NodeId kDestinationNodeId = 111222333;
@@ -80,7 +70,7 @@
     {
         NL_TEST_ASSERT(mSuite, header.GetSourceNodeId() == Optional<NodeId>::Value(kSourceNodeId));
         NL_TEST_ASSERT(mSuite, header.GetDestinationNodeId() == Optional<NodeId>::Value(kDestinationNodeId));
-        NL_TEST_ASSERT(mSuite, state->GetPeerNodeId() == kDestinationNodeId);
+        NL_TEST_ASSERT(mSuite, state->GetPeerNodeId() == kSourceNodeId);
 
         size_t data_len = msgBuf->DataLength();
 
@@ -90,22 +80,43 @@
         ReceiveHandlerCallCount++;
     }
 
-    virtual void OnNewConnection(PeerConnectionState * state, SecureSessionMgrBase * mgr)
-    {
-        CHIP_ERROR err;
-
-        NewConnectionHandlerCallCount++;
-
-        err = state->GetSecureSession().TemporaryManualKeyExchange(remote_public_key, sizeof(remote_public_key), local_private_key,
-                                                                   sizeof(local_private_key));
-        NL_TEST_ASSERT(mSuite, err == CHIP_NO_ERROR);
-    }
+    virtual void OnNewConnection(PeerConnectionState * state, SecureSessionMgrBase * mgr) { NewConnectionHandlerCallCount++; }
 
     nlTestSuite * mSuite              = nullptr;
     int ReceiveHandlerCallCount       = 0;
     int NewConnectionHandlerCallCount = 0;
 };
 
+class TestSecurePairing : public SecurePairingSession
+{
+public:
+    TestSecurePairing() : SecurePairingSession() {}
+
+    ~TestSecurePairing(void) {}
+
+    CHIP_ERROR WaitForPairing(uint32_t mySetUpPINCode, uint32_t pbkdf2IterCount, const unsigned char * salt, size_t saltLen,
+                              Optional<NodeId> myNodeId, uint16_t myKeyId, SecurePairingSessionDelegate * delegate)
+    {
+        return CHIP_NO_ERROR;
+    }
+
+    CHIP_ERROR Pair(uint32_t peerSetUpPINCode, uint32_t pbkdf2IterCount, const unsigned char * salt, size_t saltLen,
+                    Optional<NodeId> myNodeId, uint16_t myKeyId, SecurePairingSessionDelegate * delegate)
+    {
+        return CHIP_NO_ERROR;
+    }
+
+    CHIP_ERROR DeriveSecureSession(const unsigned char * info, size_t info_len, SecureSession & session)
+    {
+        const char * secret = "Secure Session Mgr Test Secret";
+        size_t secretLen    = strlen(secret);
+        return session.InitFromSecret((const unsigned char *) secret, secretLen, (const unsigned char *) "", 0,
+                                      (const unsigned char *) secret, secretLen);
+    }
+
+    CHIP_ERROR HandlePeerMessage(const MessageHeader & header, System::PacketBuffer * msg) { return CHIP_NO_ERROR; }
+};
+
 TestSessMgrCallback callback;
 
 void CheckSimpleInitTest(nlTestSuite * inSuite, void * inContext)
@@ -146,11 +157,14 @@
 
     conn.SetDelegate(&callback);
 
-    callback.NewConnectionHandlerCallCount = 0;
+    TestSecurePairing pairing1, pairing2;
+    Optional<Transport::PeerAddress> peer(Transport::PeerAddress::UDP(addr, CHIP_PORT));
 
-    err = conn.Connect(kDestinationNodeId, PeerAddress::UDP(addr));
+    err = conn.NewPairing(Optional<NodeId>::Value(kSourceNodeId), peer, 1, 2, &pairing1);
     NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR);
-    NL_TEST_ASSERT(inSuite, callback.NewConnectionHandlerCallCount == 1);
+
+    err = conn.NewPairing(Optional<NodeId>::Value(kDestinationNodeId), peer, 2, 1, &pairing2);
+    NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR);
 
     // Should be able to send a message to itself by just calling send.
     callback.ReceiveHandlerCallCount = 0;