diff --git a/examples/chip-tool/commands/pairing/OpenCommissioningWindowCommand.cpp b/examples/chip-tool/commands/pairing/OpenCommissioningWindowCommand.cpp
index 3d4c296e7864fb..bc80e568b2bd7f 100644
--- a/examples/chip-tool/commands/pairing/OpenCommissioningWindowCommand.cpp
+++ b/examples/chip-tool/commands/pairing/OpenCommissioningWindowCommand.cpp
@@ -34,10 +34,14 @@ CHIP_ERROR OpenCommissioningWindowCommand::RunCommand()
     if (mCommissioningWindowOption == Controller::CommissioningWindowOpener::CommissioningWindowOption::kTokenWithRandomPIN)
     {
         SetupPayload ignored;
-        return mWindowOpener->OpenCommissioningWindow(mNodeId, System::Clock::Seconds16(mCommissioningWindowTimeout), mIteration,
-                                                      mDiscriminator, NullOptional, NullOptional,
-                                                      &mOnOpenCommissioningWindowCallback, ignored,
-                                                      /* readVIDPIDAttributes */ true);
+        return mWindowOpener->OpenCommissioningWindow(Controller::CommissioningWindowPasscodeParams()
+                                                          .SetNodeId(mNodeId)
+                                                          .SetTimeout(mCommissioningWindowTimeout)
+                                                          .SetIteration(mIteration)
+                                                          .SetDiscriminator(mDiscriminator)
+                                                          .SetReadVIDPIDAttributes(true)
+                                                          .SetCallback(&mOnOpenCommissioningWindowCallback),
+                                                      ignored);
     }
 
     ChipLogError(chipTool, "Unknown commissioning window option: %d", to_underlying(mCommissioningWindowOption));
diff --git a/examples/common/pigweed/BUILD.gn b/examples/common/pigweed/BUILD.gn
index e523bea380c03f..6252f86e637890 100644
--- a/examples/common/pigweed/BUILD.gn
+++ b/examples/common/pigweed/BUILD.gn
@@ -82,6 +82,7 @@ pw_proto_library("button_service") {
 
 pw_proto_library("fabric_admin_service") {
   sources = [ "protos/fabric_admin_service.proto" ]
+  inputs = [ "protos/fabric_admin_service.options" ]
   deps = [ "$dir_pw_protobuf:common_protos" ]
   strip_prefix = "protos"
   prefix = "fabric_admin_service"
diff --git a/examples/common/pigweed/protos/fabric_admin_service.options b/examples/common/pigweed/protos/fabric_admin_service.options
new file mode 100644
index 00000000000000..9a65ae8a2b61d1
--- /dev/null
+++ b/examples/common/pigweed/protos/fabric_admin_service.options
@@ -0,0 +1,2 @@
+chip.rpc.DeviceCommissioningWindowInfo.verifier max_size:97  // kSpake2p_VerifierSerialized_Length
+chip.rpc.DeviceCommissioningWindowInfo.salt max_size:32      // kSpake2p_Max_PBKDF_Salt_Length
diff --git a/examples/common/pigweed/protos/fabric_admin_service.proto b/examples/common/pigweed/protos/fabric_admin_service.proto
index e52fd2951ac0d7..7f6ec4f4995b12 100644
--- a/examples/common/pigweed/protos/fabric_admin_service.proto
+++ b/examples/common/pigweed/protos/fabric_admin_service.proto
@@ -5,8 +5,13 @@ import 'pw_protobuf_protos/common.proto';
 package chip.rpc;
 
 // Define the message for a synchronized end device with necessary fields
-message DeviceInfo {
+message DeviceCommissioningWindowInfo {
   uint64 node_id = 1;
+  uint32 commissioning_timeout = 2;
+  uint32 discriminator = 3;
+  uint32 iterations = 4;
+  bytes salt = 5;
+  bytes verifier = 6;
 }
 
 // Define the response message to convey the status of the operation
@@ -15,6 +20,5 @@ message OperationStatus {
 }
 
 service FabricAdmin {
-  rpc OpenCommissioningWindow(DeviceInfo) returns (OperationStatus){}
+  rpc OpenCommissioningWindow(DeviceCommissioningWindowInfo) returns (OperationStatus){}
 }
-
diff --git a/examples/common/pigweed/rpc_services/FabricAdmin.h b/examples/common/pigweed/rpc_services/FabricAdmin.h
index 5254b9e9054a0c..14de9d50f60673 100644
--- a/examples/common/pigweed/rpc_services/FabricAdmin.h
+++ b/examples/common/pigweed/rpc_services/FabricAdmin.h
@@ -34,7 +34,8 @@ class FabricAdmin : public pw_rpc::nanopb::FabricAdmin::Service<FabricAdmin>
 public:
     virtual ~FabricAdmin() = default;
 
-    virtual pw::Status OpenCommissioningWindow(const chip_rpc_DeviceInfo & request, chip_rpc_OperationStatus & response)
+    virtual pw::Status OpenCommissioningWindow(const chip_rpc_DeviceCommissioningWindowInfo & request,
+                                               chip_rpc_OperationStatus & response)
     {
         return pw::Status::Unimplemented();
     }
diff --git a/examples/fabric-admin/commands/pairing/OpenCommissioningWindowCommand.cpp b/examples/fabric-admin/commands/pairing/OpenCommissioningWindowCommand.cpp
index 93d1f6f51002f8..480cfbca35a20b 100644
--- a/examples/fabric-admin/commands/pairing/OpenCommissioningWindowCommand.cpp
+++ b/examples/fabric-admin/commands/pairing/OpenCommissioningWindowCommand.cpp
@@ -33,11 +33,31 @@ CHIP_ERROR OpenCommissioningWindowCommand::RunCommand()
 
     if (mCommissioningWindowOption == Controller::CommissioningWindowOpener::CommissioningWindowOption::kTokenWithRandomPIN)
     {
-        SetupPayload ignored;
-        return mWindowOpener->OpenCommissioningWindow(mNodeId, System::Clock::Seconds16(mCommissioningWindowTimeout), mIteration,
-                                                      mDiscriminator, NullOptional, NullOptional,
-                                                      &mOnOpenCommissioningWindowCallback, ignored,
-                                                      /* readVIDPIDAttributes */ true);
+        if (mVerifier.HasValue())
+        {
+            VerifyOrReturnError(mSalt.HasValue(), CHIP_ERROR_INVALID_ARGUMENT);
+            return mWindowOpener->OpenCommissioningWindow(Controller::CommissioningWindowVerifierParams()
+                                                              .SetNodeId(mNodeId)
+                                                              .SetTimeout(mCommissioningWindowTimeout)
+                                                              .SetIteration(mIteration)
+                                                              .SetDiscriminator(mDiscriminator)
+                                                              .SetVerifier(mVerifier.Value())
+                                                              .SetSalt(mSalt.Value())
+                                                              .SetCallback(&mOnOpenCommissioningWindowVerifierCallback));
+        }
+        else
+        {
+            SetupPayload ignored;
+            return mWindowOpener->OpenCommissioningWindow(Controller::CommissioningWindowPasscodeParams()
+                                                              .SetNodeId(mNodeId)
+                                                              .SetTimeout(mCommissioningWindowTimeout)
+                                                              .SetIteration(mIteration)
+                                                              .SetDiscriminator(mDiscriminator)
+                                                              .SetSalt(mSalt)
+                                                              .SetReadVIDPIDAttributes(true)
+                                                              .SetCallback(&mOnOpenCommissioningWindowCallback),
+                                                          ignored);
+        }
     }
 
     ChipLogError(NotSpecified, "Unknown commissioning window option: %d", to_underlying(mCommissioningWindowOption));
@@ -58,6 +78,11 @@ void OpenCommissioningWindowCommand::OnOpenCommissioningWindowResponse(void * co
     OnOpenBasicCommissioningWindowResponse(context, remoteId, err);
 }
 
+void OpenCommissioningWindowCommand::OnOpenCommissioningWindowVerifierResponse(void * context, NodeId remoteId, CHIP_ERROR err)
+{
+    OnOpenBasicCommissioningWindowResponse(context, remoteId, err);
+}
+
 void OpenCommissioningWindowCommand::OnOpenBasicCommissioningWindowResponse(void * context, NodeId remoteId, CHIP_ERROR err)
 {
     LogErrorOnFailure(err);
diff --git a/examples/fabric-admin/commands/pairing/OpenCommissioningWindowCommand.h b/examples/fabric-admin/commands/pairing/OpenCommissioningWindowCommand.h
index 86e1b68c3d5f3f..09788507210aaf 100644
--- a/examples/fabric-admin/commands/pairing/OpenCommissioningWindowCommand.h
+++ b/examples/fabric-admin/commands/pairing/OpenCommissioningWindowCommand.h
@@ -35,6 +35,7 @@ class OpenCommissioningWindowCommand : public CHIPCommand
     OpenCommissioningWindowCommand(CredentialIssuerCommands * credIssuerCommands) :
         CHIPCommand("open-commissioning-window", credIssuerCommands),
         mOnOpenCommissioningWindowCallback(OnOpenCommissioningWindowResponse, this),
+        mOnOpenCommissioningWindowVerifierCallback(OnOpenCommissioningWindowVerifierResponse, this),
         mOnOpenBasicCommissioningWindowCallback(OnOpenBasicCommissioningWindowResponse, this)
     {
         AddArgument("node-id", 0, UINT64_MAX, &mNodeId, "Node to send command to.");
@@ -47,6 +48,12 @@ class OpenCommissioningWindowCommand : public CHIPCommand
                     &mIteration, "Number of PBKDF iterations to use to derive the verifier.  Ignored if 'option' is 0.");
         AddArgument("discriminator", 0, 4096, &mDiscriminator, "Discriminator to use for advertising.  Ignored if 'option' is 0.");
         AddArgument("timeout", 0, UINT16_MAX, &mTimeout, "Time, in seconds, before this command is considered to have timed out.");
+        AddArgument("salt", &mSalt,
+                    "Salt payload encoded in hexadecimal. Random salt will be generated if absent. "
+                    "This needs to be present if verifier is provided, corresponding to salt used for generating verifier");
+        AddArgument("verifier", &mVerifier,
+                    "PAKE Passcode verifier encoded in hexadecimal format. Will be generated from random setup pin and other "
+                    "params if absent");
     }
 
     void RegisterDelegate(CommissioningWindowDelegate * delegate) { mDelegate = delegate; }
@@ -69,12 +76,16 @@ class OpenCommissioningWindowCommand : public CHIPCommand
     uint16_t mDiscriminator;
 
     chip::Optional<uint16_t> mTimeout;
+    chip::Optional<chip::ByteSpan> mSalt;
+    chip::Optional<chip::ByteSpan> mVerifier;
 
     chip::Platform::UniquePtr<chip::Controller::CommissioningWindowOpener> mWindowOpener;
 
     static void OnOpenCommissioningWindowResponse(void * context, NodeId deviceId, CHIP_ERROR status, chip::SetupPayload payload);
+    static void OnOpenCommissioningWindowVerifierResponse(void * context, NodeId deviceId, CHIP_ERROR status);
     static void OnOpenBasicCommissioningWindowResponse(void * context, NodeId deviceId, CHIP_ERROR status);
 
     chip::Callback::Callback<chip::Controller::OnOpenCommissioningWindow> mOnOpenCommissioningWindowCallback;
+    chip::Callback::Callback<chip::Controller::OnOpenCommissioningWindowWithVerifier> mOnOpenCommissioningWindowVerifierCallback;
     chip::Callback::Callback<chip::Controller::OnOpenBasicCommissioningWindow> mOnOpenBasicCommissioningWindowCallback;
 };
diff --git a/examples/fabric-admin/rpc/RpcServer.cpp b/examples/fabric-admin/rpc/RpcServer.cpp
index 51ecb1ff6c13c5..d4979e5a27c427 100644
--- a/examples/fabric-admin/rpc/RpcServer.cpp
+++ b/examples/fabric-admin/rpc/RpcServer.cpp
@@ -37,14 +37,27 @@ namespace {
 class FabricAdmin final : public rpc::FabricAdmin
 {
 public:
-    pw::Status OpenCommissioningWindow(const chip_rpc_DeviceInfo & request, chip_rpc_OperationStatus & response) override
+    pw::Status OpenCommissioningWindow(const chip_rpc_DeviceCommissioningWindowInfo & request,
+                                       chip_rpc_OperationStatus & response) override
     {
-        NodeId nodeId = request.node_id;
+        NodeId nodeId                 = request.node_id;
+        uint32_t commissioningTimeout = request.commissioning_timeout;
+        uint32_t iterations           = request.iterations;
+        uint32_t discriminator        = request.discriminator;
+
+        char saltHex[Crypto::kSpake2p_Max_PBKDF_Salt_Length * 2 + 1];
+        Encoding::BytesToHex(request.salt.bytes, request.salt.size, saltHex, sizeof(saltHex), Encoding::HexFlags::kNullTerminate);
+
+        char verifierHex[Crypto::kSpake2p_VerifierSerialized_Length * 2 + 1];
+        Encoding::BytesToHex(request.verifier.bytes, request.verifier.size, verifierHex, sizeof(verifierHex),
+                             Encoding::HexFlags::kNullTerminate);
+
         ChipLogProgress(NotSpecified, "Received OpenCommissioningWindow request: 0x%lx", nodeId);
 
-        char command[64];
-        snprintf(command, sizeof(command), "pairing open-commissioning-window %ld %d %d %d %d %d", nodeId, kRootEndpointId,
-                 kEnhancedCommissioningMethod, kWindowTimeout, kIteration, kDiscriminator);
+        char command[512];
+        snprintf(command, sizeof(command), "pairing open-commissioning-window %ld %d %d %d %d %d --salt hex:%s --verifier hex:%s",
+                 nodeId, kRootEndpointId, kEnhancedCommissioningMethod, commissioningTimeout, iterations, discriminator, saltHex,
+                 verifierHex);
 
         PushCommand(command);
 
diff --git a/examples/fabric-bridge-app/linux/RpcClient.cpp b/examples/fabric-bridge-app/linux/RpcClient.cpp
index 02916b87aaa06a..d2aef5d1d82e5e 100644
--- a/examples/fabric-bridge-app/linux/RpcClient.cpp
+++ b/examples/fabric-bridge-app/linux/RpcClient.cpp
@@ -95,12 +95,9 @@ CHIP_ERROR InitRpcClient(uint16_t rpcServerPort)
     return rpc::client::StartPacketProcessing();
 }
 
-CHIP_ERROR OpenCommissioningWindow(NodeId nodeId)
+CHIP_ERROR OpenCommissioningWindow(chip_rpc_DeviceCommissioningWindowInfo device)
 {
-    ChipLogProgress(NotSpecified, "OpenCommissioningWindow with Node Id 0x:" ChipLogFormatX64, ChipLogValueX64(nodeId));
-
-    chip_rpc_DeviceInfo device;
-    device.node_id = nodeId;
+    ChipLogProgress(NotSpecified, "OpenCommissioningWindow with Node Id 0x" ChipLogFormatX64, ChipLogValueX64(device.node_id));
 
     // The RPC call is kept alive until it completes. When a response is received, it will be logged by the handler
     // function and the call will complete.
@@ -114,3 +111,35 @@ CHIP_ERROR OpenCommissioningWindow(NodeId nodeId)
 
     return WaitForResponse(call);
 }
+
+CHIP_ERROR
+OpenCommissioningWindow(chip::Controller::CommissioningWindowPasscodeParams params)
+{
+    chip_rpc_DeviceCommissioningWindowInfo device;
+    device.node_id               = params.GetNodeId();
+    device.commissioning_timeout = params.GetTimeout().count();
+    device.discriminator         = params.GetDiscriminator();
+    device.iterations            = params.GetIteration();
+
+    return OpenCommissioningWindow(device);
+}
+
+CHIP_ERROR
+OpenCommissioningWindow(chip::Controller::CommissioningWindowVerifierParams params)
+{
+    chip_rpc_DeviceCommissioningWindowInfo device;
+    device.node_id               = params.GetNodeId();
+    device.commissioning_timeout = params.GetTimeout().count();
+    device.discriminator         = params.GetDiscriminator();
+    device.iterations            = params.GetIteration();
+
+    VerifyOrReturnError(params.GetSalt().size() <= sizeof(device.salt.bytes), CHIP_ERROR_BUFFER_TOO_SMALL);
+    memcpy(device.salt.bytes, params.GetSalt().data(), params.GetSalt().size());
+    device.salt.size = static_cast<size_t>(params.GetSalt().size());
+
+    VerifyOrReturnError(params.GetVerifier().size() <= sizeof(device.verifier.bytes), CHIP_ERROR_BUFFER_TOO_SMALL);
+    memcpy(device.verifier.bytes, params.GetVerifier().data(), params.GetVerifier().size());
+    device.verifier.size = static_cast<size_t>(params.GetVerifier().size());
+
+    return OpenCommissioningWindow(device);
+}
diff --git a/examples/fabric-bridge-app/linux/include/RpcClient.h b/examples/fabric-bridge-app/linux/include/RpcClient.h
index 34fa5c19de9349..e7e2cc5b48505c 100644
--- a/examples/fabric-bridge-app/linux/include/RpcClient.h
+++ b/examples/fabric-bridge-app/linux/include/RpcClient.h
@@ -18,6 +18,7 @@
 
 #pragma once
 
+#include <controller/CommissioningWindowParams.h>
 #include <platform/CHIPDeviceLayer.h>
 
 constexpr uint16_t kFabricAdminServerPort = 33001;
@@ -33,12 +34,25 @@ constexpr uint16_t kFabricAdminServerPort = 33001;
 CHIP_ERROR InitRpcClient(uint16_t rpcServerPort);
 
 /**
- * Opens a commissioning window for a specified node.
+ * Opens a commissioning window for a specified node using setup PIN (passcode).
  *
- * @param nodeId The identifier of the node for which the commissioning window should be opened.
+ * @param params    Params for opening the commissioning window using passcode.
  * @return CHIP_ERROR An error code indicating the success or failure of the operation.
  * - CHIP_NO_ERROR: The RPC command was successfully processed.
  * - CHIP_ERROR_BUSY: Another commissioning window is currently in progress.
  * - CHIP_ERROR_INTERNAL: An internal error occurred.
  */
-CHIP_ERROR OpenCommissioningWindow(chip::NodeId nodeId);
+CHIP_ERROR
+OpenCommissioningWindow(chip::Controller::CommissioningWindowPasscodeParams params);
+
+/**
+ * Opens a commissioning window for a specified node using pre-computed PAKE passcode verifier.
+ *
+ * @param params    Params for opening the commissioning window using verifier.
+ * @return CHIP_ERROR An error code indicating the success or failure of the operation.
+ * - CHIP_NO_ERROR: The RPC command was successfully sent.
+ * - CHIP_ERROR_BUSY: Another commissioning window is currently in progress.
+ * - CHIP_ERROR_INTERNAL: An internal error occurred.
+ */
+CHIP_ERROR
+OpenCommissioningWindow(chip::Controller::CommissioningWindowVerifierParams params);
diff --git a/examples/fabric-bridge-app/linux/main.cpp b/examples/fabric-bridge-app/linux/main.cpp
index 0aa22b8445ee56..7e0a70bc82b18b 100644
--- a/examples/fabric-bridge-app/linux/main.cpp
+++ b/examples/fabric-bridge-app/linux/main.cpp
@@ -69,7 +69,11 @@ void BridgePollingThread()
 #if defined(PW_RPC_FABRIC_BRIDGE_SERVICE) && PW_RPC_FABRIC_BRIDGE_SERVICE
             else if (ch == 'o')
             {
-                CHIP_ERROR err = OpenCommissioningWindow(0x1234);
+                CHIP_ERROR err = OpenCommissioningWindow(chip::Controller::CommissioningWindowPasscodeParams()
+                                                             .SetNodeId(0x1234)
+                                                             .SetTimeout(300)
+                                                             .SetDiscriminator(3840)
+                                                             .SetIteration(1000));
                 if (err != CHIP_NO_ERROR)
                 {
                     ChipLogError(NotSpecified, "Failed to call OpenCommissioningWindow RPC: %" CHIP_ERROR_FORMAT, err.Format());
@@ -115,7 +119,7 @@ void AdministratorCommissioningCommandHandler::InvokeCommand(HandlerContext & ha
     using Protocols::InteractionModel::Status;
 
     EndpointId endpointId = handlerContext.mRequestPath.mEndpointId;
-    ChipLogProgress(NotSpecified, "Received command to open commissioning window on Endpoind: %d", endpointId);
+    ChipLogProgress(NotSpecified, "Received command to open commissioning window on Endpoint: %d", endpointId);
 
     if (handlerContext.mRequestPath.mCommandId != Commands::OpenCommissioningWindow::Id || endpointId == kRootEndpointId)
     {
@@ -124,23 +128,37 @@ void AdministratorCommissioningCommandHandler::InvokeCommand(HandlerContext & ha
     }
 
     handlerContext.SetCommandHandled();
-    Status status = Status::Success;
+
+    Commands::OpenCommissioningWindow::DecodableType commandData;
+    if (DataModel::Decode(handlerContext.mPayload, commandData) != CHIP_NO_ERROR)
+    {
+        handlerContext.mCommandHandler.AddStatus(handlerContext.mRequestPath, Status::InvalidCommand);
+        return;
+    }
+
+    Status status = Status::Failure;
 
 #if defined(PW_RPC_FABRIC_BRIDGE_SERVICE) && PW_RPC_FABRIC_BRIDGE_SERVICE
     Device * device = DeviceMgr().GetDevice(endpointId);
 
     // TODO: issues:#33784, need to make OpenCommissioningWindow synchronous
-    if (device != nullptr && OpenCommissioningWindow(device->GetNodeId()) == CHIP_NO_ERROR)
+    if (device != nullptr &&
+        OpenCommissioningWindow(chip::Controller::CommissioningWindowVerifierParams()
+                                    .SetNodeId(device->GetNodeId())
+                                    .SetTimeout(commandData.commissioningTimeout)
+                                    .SetDiscriminator(commandData.discriminator)
+                                    .SetIteration(commandData.iterations)
+                                    .SetSalt(commandData.salt)
+                                    .SetVerifier(commandData.PAKEPasscodeVerifier)) == CHIP_NO_ERROR)
     {
         ChipLogProgress(NotSpecified, "Commissioning window is now open");
+        status = Status::Success;
     }
     else
     {
-        status = Status::Failure;
         ChipLogProgress(NotSpecified, "Commissioning window is failed to open");
     }
 #else
-    status = Status::Failure;
     ChipLogProgress(NotSpecified, "Commissioning window failed to open: PW_RPC_FABRIC_BRIDGE_SERVICE not defined");
 #endif // defined(PW_RPC_FABRIC_BRIDGE_SERVICE) && PW_RPC_FABRIC_BRIDGE_SERVICE
 
diff --git a/src/controller/BUILD.gn b/src/controller/BUILD.gn
index b06b2defecb8f2..b4e81158d497e8 100644
--- a/src/controller/BUILD.gn
+++ b/src/controller/BUILD.gn
@@ -74,6 +74,7 @@ static_library("controller") {
     "CommandSenderAllocator.h",
     "CommissioneeDeviceProxy.h",
     "CommissioningDelegate.h",
+    "CommissioningWindowParams.h",
     "DeviceDiscoveryDelegate.h",
     "DevicePairingDelegate.h",
     "InvokeInteraction.h",
diff --git a/src/controller/CHIPDeviceController.h b/src/controller/CHIPDeviceController.h
index 1ab85ca0dc182b..1c4b490faa891a 100644
--- a/src/controller/CHIPDeviceController.h
+++ b/src/controller/CHIPDeviceController.h
@@ -243,9 +243,10 @@ class DLL_EXPORT DeviceController : public AbstractDnssdDiscoveryController
      * An error return from this function means that neither callback has been
      * called yet, and neither callback will be called in the future.
      */
-    CHIP_ERROR GetConnectedDevice(NodeId peerNodeId, Callback::Callback<OnDeviceConnected> * onConnection,
-                                  chip::Callback::Callback<OnDeviceConnectionFailure> * onFailure,
-                                  TransportPayloadCapability transportPayloadCapability = TransportPayloadCapability::kMRPPayload)
+    virtual CHIP_ERROR
+    GetConnectedDevice(NodeId peerNodeId, Callback::Callback<OnDeviceConnected> * onConnection,
+                       Callback::Callback<OnDeviceConnectionFailure> * onFailure,
+                       TransportPayloadCapability transportPayloadCapability = TransportPayloadCapability::kMRPPayload)
     {
         VerifyOrReturnError(mState == State::Initialized, CHIP_ERROR_INCORRECT_STATE);
         mSystemState->CASESessionMgr()->FindOrEstablishSession(ScopedNodeId(peerNodeId, GetFabricIndex()), onConnection, onFailure,
diff --git a/src/controller/CommissioningWindowOpener.cpp b/src/controller/CommissioningWindowOpener.cpp
index 47666972137bcc..35011e69565c4a 100644
--- a/src/controller/CommissioningWindowOpener.cpp
+++ b/src/controller/CommissioningWindowOpener.cpp
@@ -43,11 +43,12 @@ CHIP_ERROR CommissioningWindowOpener::OpenBasicCommissioningWindow(NodeId device
 
     // Basic commissioning does not use the setup payload.
 
-    mCommissioningWindowOption        = CommissioningWindowOption::kOriginalSetupCode;
-    mBasicCommissioningWindowCallback = callback;
-    mCommissioningWindowCallback      = nullptr;
-    mNodeId                           = deviceId;
-    mCommissioningWindowTimeout       = timeout;
+    mCommissioningWindowOption           = CommissioningWindowOption::kOriginalSetupCode;
+    mBasicCommissioningWindowCallback    = callback;
+    mCommissioningWindowCallback         = nullptr;
+    mCommissioningWindowVerifierCallback = nullptr;
+    mNodeId                              = deviceId;
+    mCommissioningWindowTimeout          = timeout;
 
     mNextStep = Step::kOpenCommissioningWindow;
     return mController->GetConnectedDevice(mNodeId, &mDeviceConnected, &mDeviceConnectionFailure);
@@ -59,60 +60,74 @@ CHIP_ERROR CommissioningWindowOpener::OpenCommissioningWindow(NodeId deviceId, S
                                                               Callback::Callback<OnOpenCommissioningWindow> * callback,
                                                               SetupPayload & payload, bool readVIDPIDAttributes)
 {
-    VerifyOrReturnError(mNextStep == Step::kAcceptCommissioningStart, CHIP_ERROR_INCORRECT_STATE);
+    return OpenCommissioningWindow(CommissioningWindowPasscodeParams()
+                                       .SetNodeId(deviceId)
+                                       .SetTimeout(timeout)
+                                       .SetIteration(iteration)
+                                       .SetDiscriminator(discriminator)
+                                       .SetSetupPIN(setupPIN)
+                                       .SetSalt(salt)
+                                       .SetReadVIDPIDAttributes(readVIDPIDAttributes)
+                                       .SetCallback(callback),
+                                   payload);
+}
 
-    VerifyOrReturnError(kSpake2p_Min_PBKDF_Iterations <= iteration && iteration <= kSpake2p_Max_PBKDF_Iterations,
+CHIP_ERROR CommissioningWindowOpener::OpenCommissioningWindow(const CommissioningWindowPasscodeParams & params,
+                                                              SetupPayload & payload)
+{
+    VerifyOrReturnError(mNextStep == Step::kAcceptCommissioningStart, CHIP_ERROR_INCORRECT_STATE);
+    VerifyOrReturnError(params.HasNodeId(), CHIP_ERROR_INVALID_ARGUMENT);
+    VerifyOrReturnError(params.HasDiscriminator(), CHIP_ERROR_INVALID_ARGUMENT);
+    VerifyOrReturnError(kSpake2p_Min_PBKDF_Iterations <= params.GetIteration() &&
+                            params.GetIteration() <= kSpake2p_Max_PBKDF_Iterations,
+                        CHIP_ERROR_INVALID_ARGUMENT);
+    VerifyOrReturnError(!params.HasSalt() ||
+                            (params.GetSalt().size() >= kSpake2p_Min_PBKDF_Salt_Length &&
+                             params.GetSalt().size() <= kSpake2p_Max_PBKDF_Salt_Length),
                         CHIP_ERROR_INVALID_ARGUMENT);
-    VerifyOrReturnError(
-        !salt.HasValue() ||
-            (salt.Value().size() >= kSpake2p_Min_PBKDF_Salt_Length && salt.Value().size() <= kSpake2p_Max_PBKDF_Salt_Length),
-        CHIP_ERROR_INVALID_ARGUMENT);
 
     mSetupPayload = SetupPayload();
 
-    if (setupPIN.HasValue())
+    if (params.HasSetupPIN())
     {
-        if (!SetupPayload::IsValidSetupPIN(setupPIN.Value()))
-        {
-            return CHIP_ERROR_INVALID_ARGUMENT;
-        }
-
+        VerifyOrReturnError(SetupPayload::IsValidSetupPIN(params.GetSetupPIN()), CHIP_ERROR_INVALID_ARGUMENT);
         mCommissioningWindowOption = CommissioningWindowOption::kTokenWithProvidedPIN;
-        mSetupPayload.setUpPINCode = setupPIN.Value();
+        mSetupPayload.setUpPINCode = params.GetSetupPIN();
     }
     else
     {
         mCommissioningWindowOption = CommissioningWindowOption::kTokenWithRandomPIN;
     }
 
-    if (salt.HasValue())
+    mSetupPayload.version = 0;
+    mDiscriminator.SetLongValue(params.GetDiscriminator());
+    mSetupPayload.discriminator = mDiscriminator;
+    mSetupPayload.rendezvousInformation.SetValue(RendezvousInformationFlag::kOnNetwork);
+
+    if (params.HasSalt())
     {
-        memcpy(mPBKDFSaltBuffer, salt.Value().data(), salt.Value().size());
-        mPBKDFSalt = ByteSpan(mPBKDFSaltBuffer, salt.Value().size());
+        memcpy(mPBKDFSaltBuffer, params.GetSalt().data(), params.GetSalt().size());
+        mPBKDFSalt = ByteSpan(mPBKDFSaltBuffer, params.GetSalt().size());
     }
     else
     {
         ReturnErrorOnFailure(DRBG_get_bytes(mPBKDFSaltBuffer, sizeof(mPBKDFSaltBuffer)));
         mPBKDFSalt = ByteSpan(mPBKDFSaltBuffer);
     }
+    mPBKDFIterations = params.GetIteration();
 
-    mSetupPayload.version = 0;
-    mSetupPayload.discriminator.SetLongValue(discriminator);
-    mSetupPayload.rendezvousInformation.SetValue(RendezvousInformationFlag::kOnNetwork);
-
-    mCommissioningWindowCallback      = callback;
-    mBasicCommissioningWindowCallback = nullptr;
-    mNodeId                           = deviceId;
-    mCommissioningWindowTimeout       = timeout;
-    mPBKDFIterations                  = iteration;
-
-    bool randomSetupPIN = !setupPIN.HasValue();
+    bool randomSetupPIN = !params.HasSetupPIN();
     ReturnErrorOnFailure(
         PASESession::GeneratePASEVerifier(mVerifier, mPBKDFIterations, mPBKDFSalt, randomSetupPIN, mSetupPayload.setUpPINCode));
 
-    payload = mSetupPayload;
+    payload                              = mSetupPayload;
+    mCommissioningWindowCallback         = params.GetCallback();
+    mBasicCommissioningWindowCallback    = nullptr;
+    mCommissioningWindowVerifierCallback = nullptr;
+    mNodeId                              = params.GetNodeId();
+    mCommissioningWindowTimeout          = params.GetTimeout();
 
-    if (readVIDPIDAttributes)
+    if (params.GetReadVIDPIDAttributes())
     {
         mNextStep = Step::kReadVID;
     }
@@ -124,6 +139,35 @@ CHIP_ERROR CommissioningWindowOpener::OpenCommissioningWindow(NodeId deviceId, S
     return mController->GetConnectedDevice(mNodeId, &mDeviceConnected, &mDeviceConnectionFailure);
 }
 
+CHIP_ERROR CommissioningWindowOpener::OpenCommissioningWindow(const CommissioningWindowVerifierParams & params)
+{
+    VerifyOrReturnError(mNextStep == Step::kAcceptCommissioningStart, CHIP_ERROR_INCORRECT_STATE);
+    VerifyOrReturnError(params.HasNodeId(), CHIP_ERROR_INVALID_ARGUMENT);
+    VerifyOrReturnError(params.HasDiscriminator(), CHIP_ERROR_INVALID_ARGUMENT);
+    VerifyOrReturnError(kSpake2p_Min_PBKDF_Iterations <= params.GetIteration() &&
+                            params.GetIteration() <= kSpake2p_Max_PBKDF_Iterations,
+                        CHIP_ERROR_INVALID_ARGUMENT);
+    VerifyOrReturnError(params.GetSalt().size() >= kSpake2p_Min_PBKDF_Salt_Length &&
+                            params.GetSalt().size() <= kSpake2p_Max_PBKDF_Salt_Length,
+                        CHIP_ERROR_INVALID_ARGUMENT);
+    memcpy(mPBKDFSaltBuffer, params.GetSalt().data(), params.GetSalt().size());
+    mPBKDFSalt = ByteSpan(mPBKDFSaltBuffer, params.GetSalt().size());
+
+    ReturnErrorOnFailure(mVerifier.Deserialize(params.GetVerifier()));
+    mCommissioningWindowVerifierCallback = params.GetCallback();
+    mBasicCommissioningWindowCallback    = nullptr;
+    mCommissioningWindowCallback         = nullptr;
+    mNodeId                              = params.GetNodeId();
+    mCommissioningWindowTimeout          = params.GetTimeout();
+    mPBKDFIterations                     = params.GetIteration();
+    mCommissioningWindowOption           = CommissioningWindowOption::kTokenWithProvidedPIN;
+    mDiscriminator.SetLongValue(params.GetDiscriminator());
+
+    mNextStep = Step::kOpenCommissioningWindow;
+
+    return mController->GetConnectedDevice(mNodeId, &mDeviceConnected, &mDeviceConnectionFailure);
+}
+
 CHIP_ERROR CommissioningWindowOpener::OpenCommissioningWindowInternal(Messaging::ExchangeManager & exchangeMgr,
                                                                       const SessionHandle & sessionHandle)
 {
@@ -142,7 +186,7 @@ CHIP_ERROR CommissioningWindowOpener::OpenCommissioningWindowInternal(Messaging:
         AdministratorCommissioning::Commands::OpenCommissioningWindow::Type request;
         request.commissioningTimeout = mCommissioningWindowTimeout.count();
         request.PAKEPasscodeVerifier = serializedVerifierSpan;
-        request.discriminator        = mSetupPayload.discriminator.GetLongValue();
+        request.discriminator        = mDiscriminator.GetLongValue();
         request.iterations           = mPBKDFIterations;
         request.salt                 = mPBKDFSalt;
 
@@ -230,15 +274,19 @@ void CommissioningWindowOpener::OnOpenCommissioningWindowSuccess(void * context,
 
         self->mCommissioningWindowCallback->mCall(self->mCommissioningWindowCallback->mContext, self->mNodeId, CHIP_NO_ERROR,
                                                   self->mSetupPayload);
-        // Don't touch `self` anymore; it might have been destroyed by the
-        // callee.
+        // Don't touch `self` anymore; it might have been destroyed by the callee.
+    }
+    else if (self->mCommissioningWindowVerifierCallback != nullptr)
+    {
+        self->mCommissioningWindowVerifierCallback->mCall(self->mCommissioningWindowVerifierCallback->mContext, self->mNodeId,
+                                                          CHIP_NO_ERROR);
+        // Don't touch `self` anymore; it might have been destroyed by the callee.
     }
     else if (self->mBasicCommissioningWindowCallback != nullptr)
     {
         self->mBasicCommissioningWindowCallback->mCall(self->mBasicCommissioningWindowCallback->mContext, self->mNodeId,
                                                        CHIP_NO_ERROR);
-        // Don't touch `self` anymore; it might have been destroyed by the
-        // callee.
+        // Don't touch `self` anymore; it might have been destroyed by the callee.
     }
 }
 
@@ -252,6 +300,11 @@ void CommissioningWindowOpener::OnOpenCommissioningWindowFailure(void * context,
         self->mCommissioningWindowCallback->mCall(self->mCommissioningWindowCallback->mContext, self->mNodeId, error,
                                                   SetupPayload());
     }
+    else if (self->mCommissioningWindowVerifierCallback != nullptr)
+    {
+        self->mCommissioningWindowVerifierCallback->mCall(self->mCommissioningWindowVerifierCallback->mContext, self->mNodeId,
+                                                          error);
+    }
     else if (self->mBasicCommissioningWindowCallback != nullptr)
     {
         self->mBasicCommissioningWindowCallback->mCall(self->mBasicCommissioningWindowCallback->mContext, self->mNodeId, error);
diff --git a/src/controller/CommissioningWindowOpener.h b/src/controller/CommissioningWindowOpener.h
index 10547dce3a662d..b657345ca53219 100644
--- a/src/controller/CommissioningWindowOpener.h
+++ b/src/controller/CommissioningWindowOpener.h
@@ -20,22 +20,17 @@
 #include <app/OperationalSessionSetup.h>
 #include <app/data-model/NullObject.h>
 #include <controller/CHIPDeviceController.h>
+#include <controller/CommissioningWindowParams.h>
 #include <crypto/CHIPCryptoPAL.h>
 #include <lib/core/CHIPCallback.h>
 #include <lib/core/CHIPError.h>
 #include <lib/core/NodeId.h>
 #include <lib/core/Optional.h>
 #include <setup_payload/SetupPayload.h>
-#include <system/SystemClock.h>
 
 namespace chip {
 namespace Controller {
 
-// Passing SetupPayload by value on purpose, in case a consumer decides to reuse
-// this object from inside the callback.
-typedef void (*OnOpenCommissioningWindow)(void * context, NodeId deviceId, CHIP_ERROR status, SetupPayload payload);
-typedef void (*OnOpenBasicCommissioningWindow)(void * context, NodeId deviceId, CHIP_ERROR status);
-
 /**
  * A helper class to open a commissioning window given some parameters.
  */
@@ -107,6 +102,38 @@ class CommissioningWindowOpener
                                        Callback::Callback<OnOpenCommissioningWindow> * callback, SetupPayload & payload,
                                        bool readVIDPIDAttributes = false);
 
+    /**
+     * @brief
+     *   Try to look up the device attached to our controller with the given
+     *   node id and ask it to re-enter commissioning mode with a PASE verifier
+     *   derived from the given information and the given discriminator. The
+     *   device will exit commissioning mode after a successful commissioning,
+     *   or after the given `timeout` time.
+     *
+     * @param[in] params        The parameters required to open an enhanced commissioning window
+     *                          with the provided or generated passcode.
+     * @param[out] payload      The setup payload, not including the VID/PID bits,
+     *                          even if those were asked for, that is generated
+     *                          based on the passed-in information.  The payload
+     *                          provided to the callback function, unlike this
+     *                          out parameter, will include the VID/PID bits if
+     *                          readVIDPIDAttributes is true.
+     */
+    CHIP_ERROR OpenCommissioningWindow(const CommissioningWindowPasscodeParams & params, SetupPayload & payload);
+
+    /**
+     * @brief
+     *   Try to look up the device attached to our controller with the given
+     *   node id and ask it to re-enter commissioning mode with a PASE verifier
+     *   derived from the given information and the given discriminator. The
+     *   device will exit commissioning mode after a successful commissioning,
+     *   or after the given `timeout` time.
+     *
+     * @param[in] params    The parameters required to open an enhanced commissioning window
+     *                      with the provided PAKE passcode verifier.
+     */
+    CHIP_ERROR OpenCommissioningWindow(const CommissioningWindowVerifierParams & params);
+
 private:
     enum class Step : uint8_t
     {
@@ -133,9 +160,11 @@ class CommissioningWindowOpener
     DeviceController * const mController = nullptr;
     Step mNextStep                       = Step::kAcceptCommissioningStart;
 
-    Callback::Callback<OnOpenCommissioningWindow> * mCommissioningWindowCallback           = nullptr;
-    Callback::Callback<OnOpenBasicCommissioningWindow> * mBasicCommissioningWindowCallback = nullptr;
+    Callback::Callback<OnOpenCommissioningWindow> * mCommissioningWindowCallback                     = nullptr;
+    Callback::Callback<OnOpenCommissioningWindowWithVerifier> * mCommissioningWindowVerifierCallback = nullptr;
+    Callback::Callback<OnOpenBasicCommissioningWindow> * mBasicCommissioningWindowCallback           = nullptr;
     SetupPayload mSetupPayload;
+    SetupDiscriminator mDiscriminator{};
     NodeId mNodeId                                       = kUndefinedNodeId;
     System::Clock::Seconds16 mCommissioningWindowTimeout = System::Clock::kZero;
     CommissioningWindowOption mCommissioningWindowOption = CommissioningWindowOption::kOriginalSetupCode;
diff --git a/src/controller/CommissioningWindowParams.h b/src/controller/CommissioningWindowParams.h
new file mode 100644
index 00000000000000..a4f0e43fa096c2
--- /dev/null
+++ b/src/controller/CommissioningWindowParams.h
@@ -0,0 +1,190 @@
+/*
+ *    Copyright (c) 2024 Project CHIP Authors
+ *    All rights reserved.
+ *
+ *    Licensed under the Apache License, Version 2.0 (the "License");
+ *    you may not use this file except in compliance with the License.
+ *    You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing, software
+ *    distributed under the License is distributed on an "AS IS" BASIS,
+ *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *    See the License for the specific language governing permissions and
+ *    limitations under the License.
+ */
+
+#pragma once
+
+#include <lib/core/CHIPCallback.h>
+#include <lib/core/CHIPError.h>
+#include <lib/core/NodeId.h>
+#include <lib/core/Optional.h>
+#include <lib/support/Span.h>
+#include <setup_payload/SetupPayload.h>
+#include <system/SystemClock.h>
+
+namespace chip {
+namespace Controller {
+
+// Passing SetupPayload by value on purpose, in case a consumer decides to reuse
+// this object from inside the callback.
+typedef void (*OnOpenCommissioningWindow)(void * context, NodeId deviceId, CHIP_ERROR status, SetupPayload payload);
+typedef void (*OnOpenCommissioningWindowWithVerifier)(void * context, NodeId deviceId, CHIP_ERROR status);
+typedef void (*OnOpenBasicCommissioningWindow)(void * context, NodeId deviceId, CHIP_ERROR status);
+
+template <typename Derived>
+class CommissioningWindowCommonParams
+{
+public:
+    CommissioningWindowCommonParams() = default;
+
+    bool HasNodeId() const { return mNodeId != kUndefinedNodeId; }
+    NodeId GetNodeId() const
+    {
+        VerifyOrDie(HasNodeId());
+        return mNodeId;
+    }
+    // The node identifier of device
+    Derived & SetNodeId(NodeId nodeId)
+    {
+        mNodeId = nodeId;
+        return static_cast<Derived &>(*this);
+    }
+
+    System::Clock::Seconds16 GetTimeout() const { return mTimeout; }
+    // The duration for which the commissioning window should remain open.
+    Derived & SetTimeout(System::Clock::Seconds16 timeout)
+    {
+        mTimeout = timeout;
+        return static_cast<Derived &>(*this);
+    }
+    Derived & SetTimeout(uint16_t timeoutSeconds) { return SetTimeout(System::Clock::Seconds16(timeoutSeconds)); }
+
+    // The PAKE iteration count associated with the PAKE Passcode ID and
+    // ephemeral PAKE passcode verifier to be used for this commissioning.
+    uint32_t GetIteration() const { return mIteration; }
+    Derived & SetIteration(uint32_t iteration)
+    {
+        mIteration = iteration;
+        return static_cast<Derived &>(*this);
+    }
+
+    // The long discriminator for the DNS-SD advertisement.
+    uint16_t GetDiscriminator() const { return mDiscriminator.Value(); }
+    bool HasDiscriminator() const { return mDiscriminator.HasValue(); }
+    Derived & SetDiscriminator(uint16_t discriminator)
+    {
+        mDiscriminator = MakeOptional(discriminator);
+        return static_cast<Derived &>(*this);
+    }
+
+private:
+    NodeId mNodeId                    = kUndefinedNodeId;
+    System::Clock::Seconds16 mTimeout = System::Clock::Seconds16(300); // Defaulting
+    uint32_t mIteration               = 1000;                          // Defaulting
+    Optional<uint16_t> mDiscriminator = NullOptional; // Using optional type to avoid picking a sentinnel in valid range
+};
+
+class CommissioningWindowPasscodeParams : public CommissioningWindowCommonParams<CommissioningWindowPasscodeParams>
+{
+public:
+    CommissioningWindowPasscodeParams() = default;
+
+    bool HasSetupPIN() const { return mSetupPIN.HasValue(); }
+    // Get the value of setup PIN (Passcode) if present, crashes otherwise.
+    uint32_t GetSetupPIN() const { return mSetupPIN.Value(); }
+    // The setup PIN (Passcode) to use. A random one will be generated if not provided.
+    CommissioningWindowPasscodeParams & SetSetupPIN(uint32_t setupPIN) { return SetSetupPIN(MakeOptional(setupPIN)); }
+    // The setup PIN (Passcode) to use. A random one will be generated if NullOptional is used.
+    CommissioningWindowPasscodeParams & SetSetupPIN(Optional<uint32_t> setupPIN)
+    {
+        mSetupPIN = setupPIN;
+        return *this;
+    }
+
+    bool HasSalt() const { return mSalt.HasValue(); }
+    // Get the value of salt if present.
+    // Dies if absent! Make sure to check HasSalt()
+    ByteSpan GetSalt() const { return mSalt.Value(); }
+    // The salt to use. A random one will be generated if not provided.
+    // If provided, must be at least kSpake2p_Min_PBKDF_Salt_Length bytes
+    // and at most kSpake2p_Max_PBKDF_Salt_Length bytes in length.
+    CommissioningWindowPasscodeParams & SetSalt(ByteSpan salt) { return SetSalt(MakeOptional(salt)); }
+    // The salt to use. A random one will be generated if NullOptional is used.
+    // If provided, must be at least kSpake2p_Min_PBKDF_Salt_Length bytes
+    // and at most kSpake2p_Max_PBKDF_Salt_Length bytes in length.
+    // Note that this an overloaded optional arg function to support existing APIs.
+    CommissioningWindowPasscodeParams & SetSalt(Optional<ByteSpan> salt)
+    {
+        mSalt = salt;
+        return *this;
+    }
+
+    bool GetReadVIDPIDAttributes() const { return mReadVIDPIDAttributes; }
+    // Should the API internally read VID and PID from the device while opening the
+    // commissioning window.  If this argument is `true`, the API will read VID and PID
+    // from the device and include them in the setup payload passed to the callback.
+    CommissioningWindowPasscodeParams & SetReadVIDPIDAttributes(bool readVIDPIDAttributes)
+    {
+        mReadVIDPIDAttributes = readVIDPIDAttributes;
+        return *this;
+    }
+
+    Callback::Callback<OnOpenCommissioningWindow> * GetCallback() const { return mCallback; }
+    // The function to be called on success or failure of opening the commissioning window.
+    // This will include the SetupPayload generated from provided parameters.
+    CommissioningWindowPasscodeParams & SetCallback(Callback::Callback<OnOpenCommissioningWindow> * callback)
+    {
+        mCallback = callback;
+        return *this;
+    }
+
+private:
+    Optional<uint32_t> mSetupPIN                              = NullOptional;
+    Optional<ByteSpan> mSalt                                  = NullOptional;
+    bool mReadVIDPIDAttributes                                = false;
+    Callback::Callback<OnOpenCommissioningWindow> * mCallback = nullptr;
+};
+
+class CommissioningWindowVerifierParams : public CommissioningWindowCommonParams<CommissioningWindowVerifierParams>
+{
+public:
+    CommissioningWindowVerifierParams() = default;
+
+    ByteSpan GetVerifier() const { return mVerifier.Value(); }
+    // The PAKE passcode verifier generated with enclosed iterations, salt and not-enclosed passcode.
+    CommissioningWindowVerifierParams & SetVerifier(ByteSpan verifier)
+    {
+        mVerifier = MakeOptional(verifier);
+        return *this;
+    }
+
+    ByteSpan GetSalt() const { return mSalt.Value(); }
+    // The salt that was used to generate the verifier.
+    // It must be at least kSpake2p_Min_PBKDF_Salt_Length bytes.
+    // Note: This is REQUIRED when verifier is used
+    CommissioningWindowVerifierParams & SetSalt(ByteSpan salt)
+    {
+        mSalt = MakeOptional(salt);
+        return *this;
+    }
+
+    Callback::Callback<OnOpenCommissioningWindowWithVerifier> * GetCallback() const { return mCallback; }
+    // The function to be called on success or failure of opening the
+    // commissioning window. This will NOT include the SetupPayload.
+    CommissioningWindowVerifierParams & SetCallback(Callback::Callback<OnOpenCommissioningWindowWithVerifier> * callback)
+    {
+        mCallback = callback;
+        return *this;
+    }
+
+private:
+    Optional<ByteSpan> mSalt;
+    Optional<ByteSpan> mVerifier;
+    Callback::Callback<OnOpenCommissioningWindowWithVerifier> * mCallback = nullptr;
+};
+
+} // namespace Controller
+} // namespace chip
diff --git a/src/controller/python/ChipDeviceController-ScriptBinding.cpp b/src/controller/python/ChipDeviceController-ScriptBinding.cpp
index a98c8082d45380..556ae2c6943325 100644
--- a/src/controller/python/ChipDeviceController-ScriptBinding.cpp
+++ b/src/controller/python/ChipDeviceController-ScriptBinding.cpp
@@ -714,9 +714,14 @@ PyChipError pychip_DeviceController_OpenCommissioningWindow(chip::Controller::De
         SetupPayload payload;
         auto opener =
             Platform::New<Controller::CommissioningWindowOpener>(static_cast<chip::Controller::DeviceController *>(devCtrl));
-        PyChipError err = ToPyChipError(opener->OpenCommissioningWindow(nodeid, System::Clock::Seconds16(timeout), iteration,
-                                                                        discriminator, NullOptional, NullOptional,
-                                                                        pairingDelegate->GetOpenWindowCallback(opener), payload));
+        PyChipError err =
+            ToPyChipError(opener->OpenCommissioningWindow(Controller::CommissioningWindowPasscodeParams()
+                                                              .SetNodeId(nodeid)
+                                                              .SetTimeout(timeout)
+                                                              .SetIteration(iteration)
+                                                              .SetDiscriminator(discriminator)
+                                                              .SetCallback(pairingDelegate->GetOpenWindowCallback(opener)),
+                                                          payload));
         return err;
     }
 
diff --git a/src/controller/tests/BUILD.gn b/src/controller/tests/BUILD.gn
index 7b3dff0e27d441..a1d7b9433075b7 100644
--- a/src/controller/tests/BUILD.gn
+++ b/src/controller/tests/BUILD.gn
@@ -31,6 +31,7 @@ chip_test_suite("tests") {
     test_sources += [ "TestReadChunking.cpp" ]
     test_sources += [ "TestWriteChunking.cpp" ]
     test_sources += [ "TestEventNumberCaching.cpp" ]
+    test_sources += [ "TestCommissioningWindowOpener.cpp" ]
   }
 
   cflags = [ "-Wconversion" ]
diff --git a/src/controller/tests/TestCommissioningWindowOpener.cpp b/src/controller/tests/TestCommissioningWindowOpener.cpp
new file mode 100644
index 00000000000000..01aa42fbe8b17a
--- /dev/null
+++ b/src/controller/tests/TestCommissioningWindowOpener.cpp
@@ -0,0 +1,192 @@
+/*
+ *
+ *    Copyright (c) 2024 Project CHIP Authors
+ *    All rights reserved.
+ *
+ *    Licensed under the Apache License, Version 2.0 (the "License");
+ *    you may not use this file except in compliance with the License.
+ *    You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing, software
+ *    distributed under the License is distributed on an "AS IS" BASIS,
+ *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *    See the License for the specific language governing permissions and
+ *    limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+
+#include <controller/CommissioningWindowOpener.h>
+#include <lib/core/CHIPError.h>
+#include <lib/core/ErrorStr.h>
+#include <lib/support/logging/CHIPLogging.h>
+
+using namespace chip;
+
+namespace {
+
+class MockDeviceController : public Controller::DeviceController
+{
+public:
+    CHIP_ERROR
+    GetConnectedDevice(NodeId peerNodeId, Callback::Callback<OnDeviceConnected> * onConnection,
+                       Callback::Callback<OnDeviceConnectionFailure> * onFailure,
+                       TransportPayloadCapability transportPayloadCapability = TransportPayloadCapability::kMRPPayload) override
+    {
+        return CHIP_NO_ERROR;
+    }
+};
+
+// Valid crypto values from src/protocols/secure_channel/tests/TestPASESession.cpp
+constexpr uint32_t sTestSpake2p01_PinCode                                     = 20202021;
+constexpr uint32_t sTestSpake2p01_IterationCount                              = 1000;
+constexpr uint8_t sTestSpake2p01_Salt[]                                       = { 0x53, 0x50, 0x41, 0x4B, 0x45, 0x32, 0x50, 0x20,
+                                                                                  0x4B, 0x65, 0x79, 0x20, 0x53, 0x61, 0x6C, 0x74 };
+constexpr Crypto::Spake2pVerifierSerialized sTestSpake2p01_SerializedVerifier = {
+    0xB9, 0x61, 0x70, 0xAA, 0xE8, 0x03, 0x34, 0x68, 0x84, 0x72, 0x4F, 0xE9, 0xA3, 0xB2, 0x87, 0xC3, 0x03, 0x30, 0xC2, 0xA6,
+    0x60, 0x37, 0x5D, 0x17, 0xBB, 0x20, 0x5A, 0x8C, 0xF1, 0xAE, 0xCB, 0x35, 0x04, 0x57, 0xF8, 0xAB, 0x79, 0xEE, 0x25, 0x3A,
+    0xB6, 0xA8, 0xE4, 0x6B, 0xB0, 0x9E, 0x54, 0x3A, 0xE4, 0x22, 0x73, 0x6D, 0xE5, 0x01, 0xE3, 0xDB, 0x37, 0xD4, 0x41, 0xFE,
+    0x34, 0x49, 0x20, 0xD0, 0x95, 0x48, 0xE4, 0xC1, 0x82, 0x40, 0x63, 0x0C, 0x4F, 0xF4, 0x91, 0x3C, 0x53, 0x51, 0x38, 0x39,
+    0xB7, 0xC0, 0x7F, 0xCC, 0x06, 0x27, 0xA1, 0xB8, 0x57, 0x3A, 0x14, 0x9F, 0xCD, 0x1F, 0xA4, 0x66, 0xCF
+};
+
+static void OCWPasscodeCallback(void * context, NodeId deviceId, CHIP_ERROR status, SetupPayload payload) {}
+static void OCWVerifierCallback(void * context, NodeId deviceId, CHIP_ERROR status) {}
+
+class TestCommissioningWindowOpener : public ::testing::Test
+{
+public:
+    static void SetUpTestSuite() { ASSERT_EQ(Platform::MemoryInit(), CHIP_NO_ERROR); }
+    static void TearDownTestSuite() { Platform::MemoryShutdown(); }
+
+protected:
+    // Initialize with a null pointer for now, replace with a valid controller pointer if available
+    MockDeviceController mockController;
+    Controller::CommissioningWindowOpener opener = Controller::CommissioningWindowOpener(&mockController);
+};
+
+TEST_F(TestCommissioningWindowOpener, OpenCommissioningWindowVerifier_Success)
+{
+    Callback::Callback<Controller::OnOpenCommissioningWindowWithVerifier> callback(OCWVerifierCallback, this);
+
+    CHIP_ERROR err = opener.OpenCommissioningWindow(Controller::CommissioningWindowVerifierParams()
+                                                        .SetNodeId(0x1234)
+                                                        .SetTimeout(300)
+                                                        .SetIteration(sTestSpake2p01_IterationCount)
+                                                        .SetDiscriminator(3840)
+                                                        .SetSalt(ByteSpan(sTestSpake2p01_Salt))
+                                                        .SetVerifier(ByteSpan(sTestSpake2p01_SerializedVerifier))
+                                                        .SetCallback(&callback));
+    EXPECT_EQ(err, CHIP_NO_ERROR);
+}
+
+TEST_F(TestCommissioningWindowOpener, OpenCommissioningWindowVerifier_Failure_InvalidSalt)
+{
+    Callback::Callback<Controller::OnOpenCommissioningWindowWithVerifier> callback(OCWVerifierCallback, this);
+
+    CHIP_ERROR err = opener.OpenCommissioningWindow(Controller::CommissioningWindowVerifierParams()
+                                                        .SetNodeId(0x1234)
+                                                        .SetTimeout(300)
+                                                        .SetIteration(sTestSpake2p01_IterationCount)
+                                                        .SetDiscriminator(3840)
+                                                        .SetSalt(ByteSpan())
+                                                        .SetVerifier(ByteSpan(sTestSpake2p01_SerializedVerifier))
+                                                        .SetCallback(&callback));
+    EXPECT_EQ(err, CHIP_ERROR_INVALID_ARGUMENT);
+}
+
+TEST_F(TestCommissioningWindowOpener, OpenCommissioningWindowVerifier_Failure_InvalidVerifier)
+{
+    Callback::Callback<Controller::OnOpenCommissioningWindowWithVerifier> callback(OCWVerifierCallback, this);
+
+    CHIP_ERROR err = opener.OpenCommissioningWindow(Controller::CommissioningWindowVerifierParams()
+                                                        .SetNodeId(0x1234)
+                                                        .SetTimeout(300)
+                                                        .SetIteration(sTestSpake2p01_IterationCount)
+                                                        .SetDiscriminator(3840)
+                                                        .SetSalt(ByteSpan(sTestSpake2p01_Salt))
+                                                        .SetVerifier(ByteSpan())
+                                                        .SetCallback(&callback));
+    EXPECT_EQ(err, CHIP_ERROR_INVALID_ARGUMENT);
+}
+
+TEST_F(TestCommissioningWindowOpener, OpenCommissioningWindowVerifier_Failure_InvalidIteration)
+{
+    Callback::Callback<Controller::OnOpenCommissioningWindowWithVerifier> callback(OCWVerifierCallback, this);
+
+    CHIP_ERROR err = opener.OpenCommissioningWindow(Controller::CommissioningWindowVerifierParams()
+                                                        .SetNodeId(0x1234)
+                                                        .SetTimeout(300)
+                                                        .SetIteration(0)
+                                                        .SetDiscriminator(3840)
+                                                        .SetSalt(ByteSpan(sTestSpake2p01_Salt))
+                                                        .SetVerifier(ByteSpan(sTestSpake2p01_SerializedVerifier))
+                                                        .SetCallback(&callback));
+    EXPECT_EQ(err, CHIP_ERROR_INVALID_ARGUMENT);
+}
+
+TEST_F(TestCommissioningWindowOpener, OpenCommissioningWindowPasscode_Success)
+{
+    SetupPayload ignored;
+    Callback::Callback<Controller::OnOpenCommissioningWindow> callback(OCWPasscodeCallback, this);
+    CHIP_ERROR err = opener.OpenCommissioningWindow(Controller::CommissioningWindowPasscodeParams()
+                                                        .SetNodeId(0x1234)
+                                                        .SetTimeout(300)
+                                                        .SetIteration(sTestSpake2p01_IterationCount)
+                                                        .SetDiscriminator(3840)
+                                                        .SetSetupPIN(sTestSpake2p01_PinCode)
+                                                        .SetReadVIDPIDAttributes(true)
+                                                        .SetSalt(ByteSpan(sTestSpake2p01_Salt))
+                                                        .SetCallback(&callback),
+                                                    ignored);
+    EXPECT_EQ(err, CHIP_NO_ERROR);
+}
+
+TEST_F(TestCommissioningWindowOpener, OpenCommissioningWindowPasscode_Success_NoPin)
+{
+    SetupPayload ignored;
+    Callback::Callback<Controller::OnOpenCommissioningWindow> callback(OCWPasscodeCallback, this);
+    CHIP_ERROR err = opener.OpenCommissioningWindow(Controller::CommissioningWindowPasscodeParams()
+                                                        .SetNodeId(0x1234)
+                                                        .SetTimeout(300)
+                                                        .SetIteration(sTestSpake2p01_IterationCount)
+                                                        .SetDiscriminator(3840)
+                                                        .SetSalt(ByteSpan(sTestSpake2p01_Salt))
+                                                        .SetCallback(&callback),
+                                                    ignored);
+    EXPECT_EQ(err, CHIP_NO_ERROR);
+}
+
+TEST_F(TestCommissioningWindowOpener, OpenCommissioningWindowPasscode_Success_NoSalt)
+{
+    SetupPayload ignored;
+    Callback::Callback<Controller::OnOpenCommissioningWindow> callback(OCWPasscodeCallback, this);
+    CHIP_ERROR err = opener.OpenCommissioningWindow(Controller::CommissioningWindowPasscodeParams()
+                                                        .SetNodeId(0x1234)
+                                                        .SetTimeout(300)
+                                                        .SetIteration(sTestSpake2p01_IterationCount)
+                                                        .SetDiscriminator(3840)
+                                                        .SetSetupPIN(sTestSpake2p01_PinCode)
+                                                        .SetCallback(&callback),
+                                                    ignored);
+    EXPECT_EQ(err, CHIP_NO_ERROR);
+}
+
+TEST_F(TestCommissioningWindowOpener, OpenCommissioningWindowPasscode_Failure_InvalidIteration)
+{
+    SetupPayload ignored;
+    Callback::Callback<Controller::OnOpenCommissioningWindow> callback(OCWPasscodeCallback, this);
+    CHIP_ERROR err = opener.OpenCommissioningWindow(Controller::CommissioningWindowPasscodeParams()
+                                                        .SetNodeId(0x1234)
+                                                        .SetTimeout(300)
+                                                        .SetIteration(0)
+                                                        .SetDiscriminator(3840)
+                                                        .SetCallback(&callback),
+                                                    ignored);
+    EXPECT_EQ(err, CHIP_ERROR_INVALID_ARGUMENT);
+}
+
+// Add more test cases as needed to cover different scenarios
+} // namespace