Skip to content

Commit

Permalink
Separate PASE and commissioning. (#12060)
Browse files Browse the repository at this point in the history
* Separate PASE and commissioning.

Current "PairDevice" commands will continue to work as previously,
but separating out the commissioning part from the PASE connection
so vendors can have an easier time implemeting their own commissioning
flows.

* Update src/controller/CHIPDeviceController.cpp

Co-authored-by: Martin Turon <mturon@google.com>

* Fix android.

* Restyled by autopep8

* Add comments on appropriate use of functions.

Co-authored-by: Martin Turon <mturon@google.com>
Co-authored-by: Restyled.io <commits@restyled.io>
  • Loading branch information
3 people authored and pull[bot] committed Nov 20, 2023
1 parent 30f8cb4 commit 1688804
Show file tree
Hide file tree
Showing 7 changed files with 273 additions and 70 deletions.
121 changes: 85 additions & 36 deletions src/controller/CHIPDeviceController.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -761,6 +761,20 @@ CHIP_ERROR DeviceCommissioner::PairDevice(NodeId remoteDeviceId, const char * se

CHIP_ERROR DeviceCommissioner::PairDevice(NodeId remoteDeviceId, RendezvousParameters & params)
{
CommissioningParameters commissioningParams;
return PairDevice(remoteDeviceId, params, commissioningParams);
}

CHIP_ERROR DeviceCommissioner::PairDevice(NodeId remoteDeviceId, RendezvousParameters & rendezvousParams,
CommissioningParameters & commissioningParams)
{
ReturnErrorOnFailure(EstablishPASEConnection(remoteDeviceId, rendezvousParams));
return Commission(remoteDeviceId, commissioningParams);
}

CHIP_ERROR DeviceCommissioner::EstablishPASEConnection(NodeId remoteDeviceId, RendezvousParameters & params)
{

CHIP_ERROR err = CHIP_NO_ERROR;
CommissioneeDeviceProxy * device = nullptr;
Transport::PeerAddress peerAddress = Transport::PeerAddress::UDP(Inet::IPAddress::Any);
Expand Down Expand Up @@ -804,39 +818,13 @@ CHIP_ERROR DeviceCommissioner::PairDevice(NodeId remoteDeviceId, RendezvousParam

mDeviceBeingCommissioned = device;

// If the CSRNonce is passed in, using that else using a random one..
if (params.HasCSRNonce())
{
ReturnErrorOnFailure(device->SetCSRNonce(params.GetCSRNonce().Value()));
}
else
{
uint8_t mCSRNonce[kOpCSRNonceLength];
Crypto::DRBG_get_bytes(mCSRNonce, sizeof(mCSRNonce));
ReturnErrorOnFailure(device->SetCSRNonce(ByteSpan(mCSRNonce)));
}

// If the AttestationNonce is passed in, using that else using a random one..
if (params.HasAttestationNonce())
{
ReturnErrorOnFailure(device->SetAttestationNonce(params.GetAttestationNonce().Value()));
}
else
{
uint8_t mAttestationNonce[kAttestationNonceLength];
Crypto::DRBG_get_bytes(mAttestationNonce, sizeof(mAttestationNonce));
ReturnErrorOnFailure(device->SetAttestationNonce(ByteSpan(mAttestationNonce)));
}

mIsIPRendezvous = (params.GetPeerAddress().GetTransportType() != Transport::Type::kBle);

device->Init(GetControllerDeviceInitParams(), remoteDeviceId, peerAddress, fabric->GetFabricIndex());

err = device->GetPairing().MessageDispatch().Init(mSystemState->SessionMgr());
SuccessOrExit(err);

mSystemState->SystemLayer()->StartTimer(chip::System::Clock::Milliseconds32(kSessionEstablishmentTimeout),
OnSessionEstablishmentTimeoutCallback, this);
if (params.GetPeerAddress().GetTransportType() != Transport::Type::kBle)
{
device->SetAddress(params.GetPeerAddress().GetIPAddress());
Expand Down Expand Up @@ -874,9 +862,10 @@ CHIP_ERROR DeviceCommissioner::PairDevice(NodeId remoteDeviceId, RendezvousParam
err = device->GetPairing().Pair(params.GetPeerAddress(), params.GetSetupPINCode(), keyID, exchangeCtxt, this);
SuccessOrExit(err);

// Immediately persist the updted mNextKeyID value
// Immediately persist the updated mNextKeyID value
// TODO maybe remove FreeRendezvousSession() since mNextKeyID is always persisted immediately
PersistNextKeyId();
mCommissioningStage = kSecurePairing;

exit:
if (err != CHIP_NO_ERROR)
Expand All @@ -897,6 +886,58 @@ CHIP_ERROR DeviceCommissioner::PairDevice(NodeId remoteDeviceId, RendezvousParam
return err;
}

CHIP_ERROR DeviceCommissioner::Commission(NodeId remoteDeviceId, CommissioningParameters & params)
{
// TODO(cecille): Can we get rid of mDeviceBeingCommissioned and use the remote id instead? Would require storing the
// commissioning stage in the device.
CommissioneeDeviceProxy * device = mDeviceBeingCommissioned;
if (device->GetDeviceId() != remoteDeviceId || (!device->IsSecureConnected() && !device->IsSessionSetupInProgress()))
{
ChipLogError(Controller, "Invalid device for commissioning" ChipLogFormatX64, ChipLogValueX64(remoteDeviceId));
return CHIP_ERROR_INCORRECT_STATE;
}
if (mCommissioningStage != CommissioningStage::kSecurePairing)
{
ChipLogError(Controller, "Commissioning already in progress - not restarting");
return CHIP_ERROR_INCORRECT_STATE;
}
// If the CSRNonce is passed in, using that else using a random one..
if (params.HasCSRNonce())
{
ReturnErrorOnFailure(device->SetCSRNonce(params.GetCSRNonce().Value()));
}
else
{
uint8_t mCSRNonce[kOpCSRNonceLength];
Crypto::DRBG_get_bytes(mCSRNonce, sizeof(mCSRNonce));
ReturnErrorOnFailure(device->SetCSRNonce(ByteSpan(mCSRNonce)));
}

// If the AttestationNonce is passed in, using that else using a random one..
if (params.HasAttestationNonce())
{
ReturnErrorOnFailure(device->SetAttestationNonce(params.GetAttestationNonce().Value()));
}
else
{
uint8_t mAttestationNonce[kAttestationNonceLength];
Crypto::DRBG_get_bytes(mAttestationNonce, sizeof(mAttestationNonce));
ReturnErrorOnFailure(device->SetAttestationNonce(ByteSpan(mAttestationNonce)));
}

mSystemState->SystemLayer()->StartTimer(chip::System::Clock::Milliseconds32(kSessionEstablishmentTimeout),
OnSessionEstablishmentTimeoutCallback, this);
if (device->IsSecureConnected())
{
AdvanceCommissioningStage(CHIP_NO_ERROR);
}
else
{
mRunCommissioningAfterConnection = true;
}
return CHIP_NO_ERROR;
}

CHIP_ERROR DeviceCommissioner::StopPairing(NodeId remoteDeviceId)
{
VerifyOrReturnError(mState == State::Initialized, CHIP_ERROR_INCORRECT_STATE);
Expand Down Expand Up @@ -981,21 +1022,29 @@ void DeviceCommissioner::OnSessionEstablished()

// TODO: Add code to receive OpCSR from the device, and process the signing request
// For IP rendezvous, this is sent as part of the state machine.
bool usingLegacyFlowWithImmediateStart = !mIsIPRendezvous;

if (usingLegacyFlowWithImmediateStart)
if (mRunCommissioningAfterConnection)
{
err = SendCertificateChainRequestCommand(mDeviceBeingCommissioned, CertificateType::kPAI);
if (err != CHIP_NO_ERROR)
mRunCommissioningAfterConnection = false;
bool usingLegacyFlowWithImmediateStart = !mIsIPRendezvous;
if (usingLegacyFlowWithImmediateStart)
{
ChipLogError(Ble, "Failed in sending 'Certificate Chain request' command to the device: err %s", ErrorStr(err));
OnSessionEstablishmentError(err);
return;
err = SendCertificateChainRequestCommand(mDeviceBeingCommissioned, CertificateType::kPAI);
if (err != CHIP_NO_ERROR)
{
ChipLogError(Ble, "Failed in sending 'Certificate Chain request' command to the device: err %s", ErrorStr(err));
OnSessionEstablishmentError(err);
return;
}
}
else
{
AdvanceCommissioningStage(CHIP_NO_ERROR);
}
}
else
{
AdvanceCommissioningStage(CHIP_NO_ERROR);
ChipLogProgress(Controller, "OnPairingComplete");
mPairingDelegate->OnPairingComplete(CHIP_NO_ERROR);
}
}

Expand Down
43 changes: 41 additions & 2 deletions src/controller/CHIPDeviceController.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
#include <lib/support/Pool.h>
#include <lib/support/SerializableIntegerSet.h>
#include <lib/support/Span.h>
#include <lib/support/ThreadOperationalDataset.h>
#include <messaging/ExchangeMgr.h>
#include <protocols/secure_channel/MessageCounterManager.h>
#include <protocols/secure_channel/RendezvousParameters.h>
Expand Down Expand Up @@ -490,9 +491,46 @@ class DLL_EXPORT DeviceCommissioner : public DeviceController,
* in the Init() call.
*
* @param[in] remoteDeviceId The remote device Id.
* @param[in] params The Rendezvous connection parameters
* @param[in] rendezvousParams The Rendezvous connection parameters
* @param[in] commssioningParams The commissioning parameters (uses defualt if not supplied)
*/
CHIP_ERROR PairDevice(NodeId remoteDeviceId, RendezvousParameters & params);
CHIP_ERROR PairDevice(NodeId remoteDeviceId, RendezvousParameters & rendezvousParams);
CHIP_ERROR PairDevice(NodeId remoteDeviceId, RendezvousParameters & rendezvousParams,
CommissioningParameters & commissioningParams);

/**
* @brief
* Start establishing a PASE connection with a node for the purposes of commissioning.
* Commissioners that wish to use the auto-commissioning functions should use the
* supplied "PairDevice" functions above to automatically establish a connection then
* perform commissioning. This function is intended to be use by commissioners that
* are not using the supplied auto-commissioner.
*
* This function is non-blocking. PASE is established once the DevicePairingDelegate
* receives the OnPairingComplete call.
*
* PASE connections can only be established with nodes that have their commissioning
* window open. The PASE connection will fail if this window is not open and the
* OnPairingComplete will be called with an error.
*
* @param[in] remoteDeviceId The remote device Id.
* @param[in] rendezvousParams The Rendezvous connection parameters
*/
CHIP_ERROR EstablishPASEConnection(NodeId remoteDeviceId, RendezvousParameters & params);

/**
* @brief
* Start the auto-commissioning process on a node after establishing a PASE connection.
* This function is intended to be used in conjunction with the EstablishPASEConnection
* function. It can be called either before or after the DevicePairingDelegate receives
* the OnPairingComplete call. Commissioners that want to perform simple auto-commissioning
* should use the supplied "PairDevice" functions above, which will establish the PASE
* connection and commission automatically.
*
* @param[in] remoteDeviceId The remote device Id.
* @param[in] params The commissioning parameters
*/
CHIP_ERROR Commission(NodeId remoteDeviceId, CommissioningParameters & params);

CHIP_ERROR GetDeviceBeingCommissioned(NodeId deviceId, CommissioneeDeviceProxy ** device);

Expand Down Expand Up @@ -628,6 +666,7 @@ class DLL_EXPORT DeviceCommissioner : public DeviceController,
bool mPairedDevicesUpdated;

CommissioningStage mCommissioningStage = CommissioningStage::kSecurePairing;
bool mRunCommissioningAfterConnection = false;

BitMapObjectPool<CommissioneeDeviceProxy, kNumMaxActiveDevices> mCommissioneeDevicePool;

Expand Down
26 changes: 14 additions & 12 deletions src/controller/java/CHIPDeviceController-JNI.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -185,18 +185,19 @@ JNI_METHOD(void, pairDevice)

ChipLogProgress(Controller, "pairDevice() called with device ID, connection object, and pincode");

RendezvousParameters params = RendezvousParameters()
.SetSetupPINCode(pinCode)
RendezvousParameters rendezvousParams = RendezvousParameters()
.SetSetupPINCode(pinCode)
#if CONFIG_NETWORK_LAYER_BLE
.SetConnectionObject(reinterpret_cast<BLE_CONNECTION_OBJECT>(connObj))
.SetConnectionObject(reinterpret_cast<BLE_CONNECTION_OBJECT>(connObj))
#endif
.SetPeerAddress(Transport::PeerAddress::BLE());
.SetPeerAddress(Transport::PeerAddress::BLE());
CommissioningParameters commissioningParams = CommissioningParameters();
if (csrNonce != nullptr)
{
JniByteArray jniCsrNonce(env, csrNonce);
params.SetCSRNonce(jniCsrNonce.byteSpan());
commissioningParams.SetCSRNonce(jniCsrNonce.byteSpan());
}
err = wrapper->Controller()->PairDevice(deviceId, params);
err = wrapper->Controller()->PairDevice(deviceId, rendezvousParams, commissioningParams);

if (err != CHIP_NO_ERROR)
{
Expand All @@ -221,16 +222,17 @@ JNI_METHOD(void, pairDeviceWithAddress)
ChipLogError(Controller, "Failed to parse IP address."),
JniReferences::GetInstance().ThrowError(env, sChipDeviceControllerExceptionCls, CHIP_ERROR_INVALID_ARGUMENT));

RendezvousParameters params = RendezvousParameters()
.SetDiscriminator(discriminator)
.SetSetupPINCode(pinCode)
.SetPeerAddress(Transport::PeerAddress::UDP(addr, port));
RendezvousParameters rendezvousParams = RendezvousParameters()
.SetDiscriminator(discriminator)
.SetSetupPINCode(pinCode)
.SetPeerAddress(Transport::PeerAddress::UDP(addr, port));
CommissioningParameters commissioningParams = CommissioningParameters();
if (csrNonce != nullptr)
{
JniByteArray jniCsrNonce(env, csrNonce);
params.SetCSRNonce(jniCsrNonce.byteSpan());
commissioningParams.SetCSRNonce(jniCsrNonce.byteSpan());
}
err = wrapper->Controller()->PairDevice(deviceId, params);
err = wrapper->Controller()->PairDevice(deviceId, rendezvousParams, commissioningParams);

if (err != CHIP_NO_ERROR)
{
Expand Down
21 changes: 21 additions & 0 deletions src/controller/python/ChipDeviceController-ScriptBinding.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,10 @@ ChipError::StorageType pychip_DeviceController_ConnectBLE(chip::Controller::Devi
ChipError::StorageType pychip_DeviceController_ConnectIP(chip::Controller::DeviceCommissioner * devCtrl, const char * peerAddrStr,
uint32_t setupPINCode, chip::NodeId nodeid);
ChipError::StorageType pychip_DeviceController_CloseSession(chip::Controller::DeviceCommissioner * devCtrl, chip::NodeId nodeid);
ChipError::StorageType pychip_DeviceController_EstablishPASESessionIP(chip::Controller::DeviceCommissioner * devCtrl,
const char * peerAddrStr, uint32_t setupPINCode,
chip::NodeId nodeid);
ChipError::StorageType pychip_DeviceController_Commission(chip::Controller::DeviceCommissioner * devCtrl, chip::NodeId nodeid);

ChipError::StorageType
pychip_DeviceController_DiscoverCommissionableNodesLongDiscriminator(chip::Controller::DeviceCommissioner * devCtrl,
Expand Down Expand Up @@ -346,6 +350,23 @@ ChipError::StorageType pychip_DeviceController_CloseSession(chip::Controller::De
{
return pychip_GetConnectedDeviceByNodeId(devCtrl, nodeid, CloseSessionCallback);
}
ChipError::StorageType pychip_DeviceController_EstablishPASESessionIP(chip::Controller::DeviceCommissioner * devCtrl,
const char * peerAddrStr, uint32_t setupPINCode,
chip::NodeId nodeid)
{
chip::Inet::IPAddress peerAddr;
chip::Transport::PeerAddress addr;
RendezvousParameters params = chip::RendezvousParameters().SetSetupPINCode(setupPINCode);
VerifyOrReturnError(chip::Inet::IPAddress::FromString(peerAddrStr, peerAddr), CHIP_ERROR_INVALID_ARGUMENT.AsInteger());
addr.SetTransportType(chip::Transport::Type::kUdp).SetIPAddress(peerAddr);
params.SetPeerAddress(addr).SetDiscriminator(0);
return devCtrl->EstablishPASEConnection(nodeid, params).AsInteger();
}
ChipError::StorageType pychip_DeviceController_Commission(chip::Controller::DeviceCommissioner * devCtrl, chip::NodeId nodeid)
{
CommissioningParameters params;
return devCtrl->Commission(nodeid, params).AsInteger();
}

ChipError::StorageType pychip_DeviceController_DiscoverAllCommissionableNodes(chip::Controller::DeviceCommissioner * devCtrl)
{
Expand Down
54 changes: 54 additions & 0 deletions src/controller/python/chip-device-ctrl.py
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,8 @@ def __init__(self, rendezvousAddr=None, controllerNodeId=0, bluetoothAdapter=Non
"close-ble",
"close-session",
"resolve",
"paseonly",
"commission",
"zcl",
"zclread",
"zclsubscribe",
Expand Down Expand Up @@ -491,6 +493,58 @@ def ConnectFromSetupPayload(self, setupPayload, nodeid):
print(f"Unable to connect: {ex}")
return -1

def do_paseonly(self, line):
"""
paseonly -ip <ip address> <setup pin code> [<nodeid>]
TODO: Add more methods to connect to device (like cert for auth, and IP
for connection)
"""

try:
args = shlex.split(line)
if len(args) <= 1:
print("Usage:")
self.do_help("paseonly")
return

nodeid = random.randint(1, 1000000) # Just a random number
if len(args) == 4:
nodeid = int(args[3])
print("Device is assigned with nodeid = {}".format(nodeid))

if args[0] == "-ip" and len(args) >= 3:
self.devCtrl.EstablishPASESessionIP(args[1].encode(
"utf-8"), int(args[2]), nodeid)
else:
print("Usage:")
self.do_help("paseonly")
return
print(
"Device temporary node id (**this does not match spec**): {}".format(nodeid))
except Exception as ex:
print(str(ex))
return

def do_commission(self, line):
"""
commission nodeid
Runs commissioning on a device that has been connected with paseonly
"""
try:
args = shlex.split(line)
if len(args) != 1:
print("Usage:")
self.do_help("commission")
return

nodeid = int(args[0])
self.devCtrl.Commission(nodeid)
except Exception as ex:
print(str(ex))
return

def do_connect(self, line):
"""
connect -ip <ip address> <setup pin code> [<nodeid>]
Expand Down
Loading

0 comments on commit 1688804

Please sign in to comment.