Skip to content

Commit

Permalink
Include VID/PID in ECM commissioning Setup Payload (#11894)
Browse files Browse the repository at this point in the history
* Include VID/PID in ECM commissioning Setup Payload

* address some review comments

* use templated ReadAttribute API

* address review comments

* read VID PID only if asked by the application
  • Loading branch information
pan-apple authored and pull[bot] committed Feb 25, 2022
1 parent a55d9fb commit a919966
Show file tree
Hide file tree
Showing 4 changed files with 127 additions and 39 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -53,5 +53,6 @@ void OpenCommissioningWindowCommand::OnOpenCommissioningWindowResponse(void * co
CHIP_ERROR OpenCommissioningWindowCommand::OpenCommissioningWindow()
{
return CurrentCommissioner().OpenCommissioningWindowWithCallback(
mNodeId, mTimeout, mIteration, mDiscriminator, mCommissioningWindowOption, &mOnOpenCommissioningWindowCallback);
mNodeId, mTimeout, mIteration, mDiscriminator, mCommissioningWindowOption, &mOnOpenCommissioningWindowCallback,
/* readVIDPIDAttributes */ true);
}
141 changes: 104 additions & 37 deletions src/controller/CHIPDeviceController.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -353,10 +353,57 @@ CHIP_ERROR DeviceController::GetPeerAddressAndPort(PeerId peerId, Inet::IPAddres
return CHIP_NO_ERROR;
}

void DeviceController::OnPIDReadResponse(void * context, uint16_t value)
{
ChipLogProgress(Controller, "Received PID for the device. Value %d", value);
DeviceController * controller = static_cast<DeviceController *>(context);
controller->mSetupPayload.productID = value;

if (controller->OpenCommissioningWindowInternal() != CHIP_NO_ERROR)
{
OnOpenPairingWindowFailureResponse(context, 0);
}
}

void DeviceController::OnVIDReadResponse(void * context, uint16_t value)
{
ChipLogProgress(Controller, "Received VID for the device. Value %d", value);

DeviceController * controller = static_cast<DeviceController *>(context);

controller->mSetupPayload.vendorID = value;

OperationalDeviceProxy * device =
controller->mCASESessionManager->FindExistingSession(controller->mDeviceWithCommissioningWindowOpen);
if (device == nullptr)
{
ChipLogError(Controller, "Could not find device for opening commissioning window");
OnOpenPairingWindowFailureResponse(context, 0);
return;
}

constexpr EndpointId kBasicClusterEndpoint = 0;
chip::Controller::BasicCluster cluster;
cluster.Associate(device, kBasicClusterEndpoint);

if (cluster.ReadAttribute<app::Clusters::Basic::Attributes::ProductID::TypeInfo>(context, OnPIDReadResponse,
OnVIDPIDReadFailureResponse) != CHIP_NO_ERROR)
{
ChipLogError(Controller, "Could not read PID for opening commissioning window");
OnOpenPairingWindowFailureResponse(context, 0);
}
}

void DeviceController::OnVIDPIDReadFailureResponse(void * context, EmberAfStatus status)
{
ChipLogProgress(Controller, "Failed to read VID/PID for the device. status %d", status);
OnOpenPairingWindowFailureResponse(context, status);
}

void DeviceController::OnOpenPairingWindowSuccessResponse(void * context)
{
ChipLogProgress(Controller, "Successfully opened pairing window on the device");
DeviceController * controller = reinterpret_cast<DeviceController *>(context);
DeviceController * controller = static_cast<DeviceController *>(context);
if (controller->mCommissioningWindowCallback != nullptr)
{
controller->mCommissioningWindowCallback->mCall(controller->mCommissioningWindowCallback->mContext,
Expand All @@ -368,12 +415,11 @@ void DeviceController::OnOpenPairingWindowSuccessResponse(void * context)
void DeviceController::OnOpenPairingWindowFailureResponse(void * context, uint8_t status)
{
ChipLogError(Controller, "Failed to open pairing window on the device. Status %d", status);
DeviceController * controller = reinterpret_cast<DeviceController *>(context);
DeviceController * controller = static_cast<DeviceController *>(context);
if (controller->mCommissioningWindowCallback != nullptr)
{
CHIP_ERROR error = CHIP_ERROR_INVALID_PASE_PARAMETER;
// TODO - Use cluster enum chip::app::Clusters::AdministratorCommissioning::StatusCode::kBusy
if (status == 1)
if (status == EmberAfStatusCode::EMBER_ZCL_STATUS_CODE_BUSY)
{
error = CHIP_ERROR_ANOTHER_COMMISSIONING_IN_PROGRESS;
}
Expand All @@ -393,38 +439,60 @@ CHIP_ERROR DeviceController::ComputePASEVerifier(uint32_t iterations, uint32_t s

CHIP_ERROR DeviceController::OpenCommissioningWindowWithCallback(NodeId deviceId, uint16_t timeout, uint16_t iteration,
uint16_t discriminator, uint8_t option,
Callback::Callback<OnOpenCommissioningWindow> * callback)
Callback::Callback<OnOpenCommissioningWindow> * callback,
bool readVIDPIDAttributes)
{
ChipLogProgress(Controller, "OpenCommissioningWindow for device ID %" PRIu64, deviceId);
VerifyOrReturnError(mState == State::Initialized, CHIP_ERROR_INCORRECT_STATE);

OperationalDeviceProxy * device = mCASESessionManager->FindExistingSession(deviceId);
VerifyOrReturnError(device != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
mSetupPayload = SetupPayload();

std::string QRCode;
std::string manualPairingCode;
SetupPayload payload;
CommissioningWindowOption commissioningWindowOption;
ByteSpan salt(reinterpret_cast<const uint8_t *>(kSpake2pKeyExchangeSalt), strlen(kSpake2pKeyExchangeSalt));
mSetupPayload.version = 0;
mSetupPayload.discriminator = discriminator;
mSetupPayload.rendezvousInformation = RendezvousInformationFlags(RendezvousInformationFlag::kOnNetwork);

payload.discriminator = discriminator;
mCommissioningWindowCallback = callback;
mDeviceWithCommissioningWindowOpen = deviceId;
mCommissioningWindowTimeout = timeout;
mCommissioningWindowIteration = iteration;

switch (option)
{
case 0:
commissioningWindowOption = CommissioningWindowOption::kOriginalSetupCode;
mCommissioningWindowOption = CommissioningWindowOption::kOriginalSetupCode;
break;
case 1:
commissioningWindowOption = CommissioningWindowOption::kTokenWithRandomPIN;
mCommissioningWindowOption = CommissioningWindowOption::kTokenWithRandomPIN;
break;
case 2:
commissioningWindowOption = CommissioningWindowOption::kTokenWithProvidedPIN;
mCommissioningWindowOption = CommissioningWindowOption::kTokenWithProvidedPIN;
break;
default:
ChipLogError(Controller, "Invalid Pairing Window option");
return CHIP_ERROR_INVALID_ARGUMENT;
}

if (callback != nullptr && mCommissioningWindowOption != CommissioningWindowOption::kOriginalSetupCode && readVIDPIDAttributes)
{
OperationalDeviceProxy * device = mCASESessionManager->FindExistingSession(mDeviceWithCommissioningWindowOpen);
VerifyOrReturnError(device != nullptr, CHIP_ERROR_INVALID_ARGUMENT);

constexpr EndpointId kBasicClusterEndpoint = 0;
chip::Controller::BasicCluster cluster;
cluster.Associate(device, kBasicClusterEndpoint);

return cluster.ReadAttribute<app::Clusters::Basic::Attributes::VendorID::TypeInfo>(this, OnVIDReadResponse,
OnVIDPIDReadFailureResponse);
}

return OpenCommissioningWindowInternal();
}

CHIP_ERROR DeviceController::OpenCommissioningWindowInternal()
{
ChipLogProgress(Controller, "OpenCommissioningWindow for device ID %" PRIu64, mDeviceWithCommissioningWindowOpen);
VerifyOrReturnError(mState == State::Initialized, CHIP_ERROR_INCORRECT_STATE);

OperationalDeviceProxy * device = mCASESessionManager->FindExistingSession(mDeviceWithCommissioningWindowOpen);
VerifyOrReturnError(device != nullptr, CHIP_ERROR_INVALID_ARGUMENT);

constexpr EndpointId kAdministratorCommissioningClusterEndpoint = 0;

chip::Controller::AdministratorCommissioningCluster cluster;
Expand All @@ -433,41 +501,40 @@ CHIP_ERROR DeviceController::OpenCommissioningWindowWithCallback(NodeId deviceId
Callback::Cancelable * successCallback = mOpenPairingSuccessCallback.Cancel();
Callback::Cancelable * failureCallback = mOpenPairingFailureCallback.Cancel();

payload.version = 0;
payload.rendezvousInformation = RendezvousInformationFlags(RendezvousInformationFlag::kOnNetwork);

mCommissioningWindowCallback = callback;
if (commissioningWindowOption != CommissioningWindowOption::kOriginalSetupCode)
if (mCommissioningWindowOption != CommissioningWindowOption::kOriginalSetupCode)
{
bool randomSetupPIN = (commissioningWindowOption == CommissioningWindowOption::kTokenWithRandomPIN);
ByteSpan salt(Uint8::from_const_char(kSpake2pKeyExchangeSalt), strlen(kSpake2pKeyExchangeSalt));
bool randomSetupPIN = (mCommissioningWindowOption == CommissioningWindowOption::kTokenWithRandomPIN);
PASEVerifier verifier;

ReturnErrorOnFailure(PASESession::GeneratePASEVerifier(verifier, iteration, salt, randomSetupPIN, payload.setUpPINCode));
ReturnErrorOnFailure(PASESession::GeneratePASEVerifier(verifier, mCommissioningWindowIteration, salt, randomSetupPIN,
mSetupPayload.setUpPINCode));

uint8_t serializedVerifier[2 * kSpake2p_WS_Length];
VerifyOrReturnError(sizeof(serializedVerifier) == sizeof(verifier), CHIP_ERROR_INTERNAL);

memcpy(serializedVerifier, verifier.mW0, kSpake2p_WS_Length);
memcpy(&serializedVerifier[kSpake2p_WS_Length], verifier.mL, kSpake2p_WS_Length);

ReturnErrorOnFailure(cluster.OpenCommissioningWindow(successCallback, failureCallback, timeout,
ByteSpan(serializedVerifier, sizeof(serializedVerifier)),
payload.discriminator, iteration, salt, mPAKEVerifierID++));
ReturnErrorOnFailure(cluster.OpenCommissioningWindow(
successCallback, failureCallback, mCommissioningWindowTimeout, ByteSpan(serializedVerifier, sizeof(serializedVerifier)),
mSetupPayload.discriminator, mCommissioningWindowIteration, salt, mPAKEVerifierID++));

char payloadBuffer[QRCodeBasicSetupPayloadGenerator::kMaxQRCodeBase38RepresentationLength];

ReturnErrorOnFailure(ManualSetupPayloadGenerator(payload).payloadDecimalStringRepresentation(manualPairingCode));
ChipLogProgress(Controller, "Manual pairing code: [%s]", manualPairingCode.c_str());
MutableCharSpan manualCode(payloadBuffer);
ReturnErrorOnFailure(ManualSetupPayloadGenerator(mSetupPayload).payloadDecimalStringRepresentation(manualCode));
ChipLogProgress(Controller, "Manual pairing code: [%s]", payloadBuffer);

ReturnErrorOnFailure(QRCodeSetupPayloadGenerator(payload).payloadBase38Representation(QRCode));
ChipLogProgress(Controller, "SetupQRCode: [%s]", QRCode.c_str());
MutableCharSpan QRCode(payloadBuffer);
ReturnErrorOnFailure(QRCodeBasicSetupPayloadGenerator(mSetupPayload).payloadBase38Representation(QRCode));
ChipLogProgress(Controller, "SetupQRCode: [%s]", payloadBuffer);
}
else
{
ReturnErrorOnFailure(cluster.OpenBasicCommissioningWindow(successCallback, failureCallback, timeout));
ReturnErrorOnFailure(cluster.OpenBasicCommissioningWindow(successCallback, failureCallback, mCommissioningWindowTimeout));
}

mSetupPayload = payload;
mDeviceWithCommissioningWindowOpen = deviceId;

return CHIP_NO_ERROR;
}

Expand Down
19 changes: 18 additions & 1 deletion src/controller/CHIPDeviceController.h
Original file line number Diff line number Diff line change
Expand Up @@ -311,10 +311,16 @@ class DLL_EXPORT DeviceController : public SessionReleaseDelegate,
* the PIN code provied in the setupPayload).
* @param[in] callback The function to be called on success or failure of opening of commissioning window.
*
* @param[in] readVIDPIDAttributes Should the API internally read VID and PID from the device while opening the
* commissioning window. VID and PID is only needed for enchanced commissioning mode.
* If this argument is `true`, and enhanced commissioning mode is used, the API will
* read VID and PID from the device.
*
* @return CHIP_ERROR CHIP_NO_ERROR on success, or corresponding error
*/
CHIP_ERROR OpenCommissioningWindowWithCallback(NodeId deviceId, uint16_t timeout, uint16_t iteration, uint16_t discriminator,
uint8_t option, Callback::Callback<OnOpenCommissioningWindow> * callback);
uint8_t option, Callback::Callback<OnOpenCommissioningWindow> * callback,
bool readVIDPIDAttributes = false);

#if CHIP_DEVICE_CONFIG_ENABLE_DNSSD
void RegisterDeviceAddressUpdateDelegate(DeviceAddressUpdateDelegate * delegate) { mDeviceAddressUpdateDelegate = delegate; }
Expand Down Expand Up @@ -396,11 +402,22 @@ class DLL_EXPORT DeviceController : public SessionReleaseDelegate,
Callback::Callback<DefaultSuccessCallback> mOpenPairingSuccessCallback;
Callback::Callback<DefaultFailureCallback> mOpenPairingFailureCallback;

static void OnPIDReadResponse(void * context, uint16_t value);
static void OnVIDReadResponse(void * context, uint16_t value);
static void OnVIDPIDReadFailureResponse(void * context, EmberAfStatus status);

CHIP_ERROR OpenCommissioningWindowInternal();

// TODO - Support opening commissioning window simultaneously on multiple devices
Callback::Callback<OnOpenCommissioningWindow> * mCommissioningWindowCallback = nullptr;
SetupPayload mSetupPayload;
NodeId mDeviceWithCommissioningWindowOpen;

uint16_t mCommissioningWindowTimeout;
uint16_t mCommissioningWindowIteration;

CommissioningWindowOption mCommissioningWindowOption;

static void OnOpenPairingWindowSuccessResponse(void * context);
static void OnOpenPairingWindowFailureResponse(void * context, uint8_t status);

Expand Down
3 changes: 3 additions & 0 deletions src/setup_payload/QRCodeSetupPayloadGenerator.h
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,9 @@ class QRCodeBasicSetupPayloadGenerator
* producing the requested string.
*/
CHIP_ERROR payloadBase38Representation(MutableCharSpan & outBuffer);

// TODO - Find the optimal value for maximum length of QR Code Base38 string
static constexpr uint16_t kMaxQRCodeBase38RepresentationLength = 128;
};

} // namespace chip

0 comments on commit a919966

Please sign in to comment.