Skip to content

Implement commissioner-side support for Per-Device Credentials - #74032

Open
ksperling-apple wants to merge 5 commits into
project-chip:masterfrom
ksperling-apple:pdc-commissioning
Open

Implement commissioner-side support for Per-Device Credentials#74032
ksperling-apple wants to merge 5 commits into
project-chip:masterfrom
ksperling-apple:pdc-commissioning

Conversation

@ksperling-apple

Copy link
Copy Markdown
Contributor

Summary

  • PDC commissioning in DeviceCommissioner / AutoCommissioner

    Implement support for PDC commissioning steps in the DeviceCommissioner
    and AutoCommissioner. The new abstraction introduced to handle the
    interaction with the network infrastructure (i.e. with the NETIM cluster
    on a NIM device) is the NetworkIdentityRegistrar. The registrar provides
    the low-level operations for retrieval of the Network Identity and
    registration and unregistration of the Network Client Identity during
    commissioning.

    The DeviceCommissioner gains two new steps (kPDCGetNetworkIdentity and
    kPDCRegisterClientIdentity) that encapsulate those interactions and
    handles roll-back of the client identity if commissioning does not
    succeed. The kWiFiNetworkSetup step is updated to use the PDC variant of
    AddOrUpdateWiFiNetwork as necessary. The commissioner validates that
    both the network and client identities are valid, and that the
    commissionee holds the private key for the client identity.

    Note the two new steps are inserted in the appropriate places in the
    enum (kPDCRegisterClientIdentity inside the kWiFiNetworkSetup ..
    kICDSendStayActive range, and kPDCGetNetworkIdentity before kCleanup),
    so some existing enum values are being renumbered.

  • Implement NetworkIdentityManagementRegistrar

    This class implements the NetworkIdentityRegistrar interface expected by
    the DeviceCommissioner by interacting with a Network Identity Management
    cluster server (generally on a NIM device).

  • Implement PDC commissioning support in chip-tool

    This adds --pdc-netim-node-id and --pdc-netim-endpoint-id endpoint
    arguments to existing WiFi commissioning commands. If they are
    specified, a PDC-capable commissionee will be commissioned with PDC
    credentials obtained from the pointed-to Network Infrastructure
    Management cluster server (generally on a NIM device). If the
    commissionee is not PDC-capable, the password parameter will be used as
    before. (The special password value of "-", which is not a valid WiFi
    password, can be used to disable that fallback.)

Testing

New unit tests for all low-level aspects, particular the AutoCommissioner / DeviceCommissioner aspects.
Manual end-to-end testing with the NIM example app (still requires some manual intervention on the AP side due to integration code that is still missing.)

Alongside the definition of CertificateKeyId.
Implement support for PDC commissioning steps in the DeviceCommissioner
and AutoCommissioner. The new abstraction introduced to handle the
interaction with the network infrastructure (i.e. with the NETIM cluster
on a NIM device) is the NetworkIdentityRegistrar. The registrar provides
the low-level operations for retrieval of the Network Identity and
registration and unregistration of the Network Client Identity during
commissioning.

The DeviceCommissioner gains two new steps (kPDCGetNetworkIdentity and
kPDCRegisterClientIdentity) that encapsulate those interactions and
handles roll-back of the client identity if commissioning does not
succeed. The kWiFiNetworkSetup step is updated to use the PDC variant of
AddOrUpdateWiFiNetwork as necessary. The commissioner validates that
both the network and client identities are valid, and that the
commissionee holds the private key for the client identity.

Note the two new steps are inserted in the appropriate places in the
enum (kPDCRegisterClientIdentity inside the kWiFiNetworkSetup ..
kICDSendStayActive range, and kPDCGetNetworkIdentity before kCleanup),
so some existing enum values are being renumbered.

The AutoCommissioner (or other commissioning delegate) is responsible
for orchestration and propagating the step outputs from
CommissioningReports into CommissioningParameters for subsequent steps.

Note that PDC commissioning support is not guarded behind an ifdef,
since commissioners that support Wi-Fi SHOULD support PDC, and there is
currently no supported way to build e.g. a Thread-only commissioner in
any case.
This class implements the NetworkIdentityRegistrar interface expected by
the DeviceCommissioner by interacting with a Network Identity Management
cluster server (generally on a NIM device).
This adds --pdc-netim-node-id and --pdc-netim-endpoint-id endpoint
arguments to existing WiFi commissioning commands. If they are
specified, a PDC-capable commissionee will be commissioned with PDC
credentials obtained from the pointed-to Network Infrastructure
Management cluster server (generally on a NIM device). If the
commissionee is not PDC-capable, the password parameter will be used as
before. (The special password value of "-", which is not a valid WiFi
password, can be used to disable that fallback.)
@coderabbitai

coderabbitai Bot commented Sep 9, 2026

Copy link
Copy Markdown
Contributor

Review Change StackReview Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Advanced

Run ID: bdc622e8-549d-4c6a-8643-71cf30c53e98

📥 Commits

Reviewing files that changed from the base of the PR and between fc18ade and e5b0141.

📒 Files selected for processing (26)
  • examples/chip-tool/commands/pairing/PairingCommand.cpp
  • examples/chip-tool/commands/pairing/PairingCommand.h
  • src/app/clusters/network-identity-management-server/BUILD.gn
  • src/app/clusters/network-identity-management-server/Logging.h
  • src/app/clusters/network-identity-management-server/NetworkIdentityManagementCluster.cpp
  • src/controller/AutoCommissioner.cpp
  • src/controller/AutoCommissioner.h
  • src/controller/BUILD.gn
  • src/controller/CHIPDeviceController.cpp
  • src/controller/CHIPDeviceController.h
  • src/controller/CommissioningDelegate.cpp
  • src/controller/CommissioningDelegate.h
  • src/controller/ControllerOperation.cpp
  • src/controller/ControllerOperation.h
  • src/controller/NetworkIdentityManagementRegistrar.cpp
  • src/controller/NetworkIdentityManagementRegistrar.h
  • src/controller/NetworkIdentityRegistrar.h
  • src/controller/tests/AutoCommissionerTestAccess.h
  • src/controller/tests/BUILD.gn
  • src/controller/tests/DeviceCommissionerTestAccess.h
  • src/controller/tests/TestAutoCommissioner.cpp
  • src/controller/tests/TestCommissioningDelegate.cpp
  • src/controller/tests/TestNetworkIdentityManagementRegistrar.cpp
  • src/controller/tests/TestPDCCommissioning.cpp
  • src/credentials/CHIPCert.h
  • src/python_testing/TC_CGEN_2_2.py
💤 Files with no reviewable changes (3)
  • src/app/clusters/network-identity-management-server/BUILD.gn
  • src/app/clusters/network-identity-management-server/Logging.h
  • src/app/clusters/network-identity-management-server/NetworkIdentityManagementCluster.cpp

Included review availability: Your plan provides up to 8 included reviews per hour; 5 remain after this review.


📝 Walkthrough

Walkthrough

This change adds Per-Device Credentials commissioning for Wi-Fi networks. It defines network identity registrar interfaces and NIM operations, adds PDC commissioning stages and report data, verifies possession signatures, and registers client identities. DeviceCommissioner now rolls back registrations during failures, cancellation, and shutdown. chip-tool pairing accepts registrar node and endpoint arguments and defers exit during revocation. Tests cover registrar lifecycle, commissioning, validation, retries, and rollback.

Sequence Diagram(s)

sequenceDiagram
  participant chip-tool
  participant DeviceCommissioner
  participant NetworkIdentityManagementRegistrar
  participant Commissionee
  chip-tool->>DeviceCommissioner: Start pairing with PDC credentials
  DeviceCommissioner->>NetworkIdentityManagementRegistrar: GetNetworkIdentity
  NetworkIdentityManagementRegistrar-->>DeviceCommissioner: Return network identity
  DeviceCommissioner->>Commissionee: Configure Wi-Fi with PDC data
  Commissionee-->>DeviceCommissioner: Return client identity and signature
  DeviceCommissioner->>NetworkIdentityManagementRegistrar: RegisterClient
  DeviceCommissioner-->>chip-tool: Complete pairing or defer for rollback
Loading

Priority: ➖ Normal

Merge Risk: ⚪ Minimal · up to e5b01

No concrete current-head issue remains that should block merging.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 36.13% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 238 functions across 21 files. (2 skipped… Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely summarizes the primary change: commissioner-side support for Per-Device Credentials.
Description check ✅ Passed The description directly explains the PDC commissioning changes, NetworkIdentityRegistrar implementation, chip-tool support, rollback behavior, and testing.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Full details: Docstring Coverage

Explanation

Docstring coverage is 36.13% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 238 functions across 21 files. (2 skipped: 2 unsupported.)

  • Fix all pre-merge checks with AI
✨ Finishing Touches 💡 1
🛠️ Fix failing CI checks 💡
  • Create stacked PR
  • Commit on current branch

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

A missing required include for PRIX32 in src/credentials/CHIPCert.h can cause build failures (and there is also a small doc typo to fix).

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Pull request overview

Implements commissioner-side commissioning support for Wi‑Fi Per-Device Credentials (PDC) by introducing a NetworkIdentityRegistrar abstraction, integrating new commissioning stages into AutoCommissioner/DeviceCommissioner, and wiring chip-tool CLI support for NETIM-driven PDC flows.

Changes:

  • Add two new commissioning stages (kPDCGetNetworkIdentity, kPDCRegisterClientIdentity) and associated parameter/report plumbing (network identity, client identity, possession nonce/signature, rollback policy).
  • Implement NetworkIdentityManagementRegistrar to drive the Network Identity Management cluster and provide lifecycle/idle-wait semantics.
  • Extend chip-tool pairing commands with --pdc-netim-node-id/--pdc-netim-endpoint-id, plus substantial new unit test coverage for commissioner and registrar behavior.
File summaries
File Description
src/python_testing/TC_CGEN_2_2.py Updates Python test’s hardcoded commissioning stage number to reflect enum renumbering.
src/credentials/CHIPCert.h Adds common logging macros for CertificateKeyId rendering.
src/controller/tests/TestPDCCommissioning.cpp Adds comprehensive unit tests for PDC flows across AutoCommissioner/DeviceCommissioner, including rollback and retry behavior.
src/controller/tests/TestNetworkIdentityManagementRegistrar.cpp Adds lifecycle/idle-wait unit tests for NetworkIdentityManagementRegistrar.
src/controller/tests/TestCommissioningDelegate.cpp Extends commissioning parameter setter/getter tests for new PDC-related fields.
src/controller/tests/TestAutoCommissioner.cpp Adds tests ensuring PDCPossessionNonce buffer constraints and span relocation behavior.
src/controller/tests/DeviceCommissionerTestAccess.h Exposes PDC-specific internal hooks for tests (verification and registration state).
src/controller/tests/BUILD.gn Registers new controller test sources.
src/controller/tests/AutoCommissionerTestAccess.h Exposes additional test accessors used by new PDC tests.
src/controller/NetworkIdentityRegistrar.h Introduces registrar interface used by commissioner for PDC identity query/register/revoke.
src/controller/NetworkIdentityManagementRegistrar.h Adds concrete registrar implementation interface + idle-wait/shutdown semantics.
src/controller/NetworkIdentityManagementRegistrar.cpp Implements NETIM-backed registrar operations, cancellation, and idle-wait notification ordering.
src/controller/ControllerOperation.h Introduces reusable controller operation base for CASE-connect + optional invoke lifecycle.
src/controller/ControllerOperation.cpp Implements controller operation connection/invoke cancellation plumbing.
src/controller/CommissioningDelegate.h Adds new stages, WiFiCredentials/CommissioningParameters extensions, and new report variants.
src/controller/CommissioningDelegate.cpp Adds StageToString/MetricKey support for the new PDC stages.
src/controller/CHIPDeviceController.h Adds PDC callbacks, verification, and rollback tracking fields to DeviceCommissioner.
src/controller/CHIPDeviceController.cpp Implements PDC stage behavior, response validation, registrar interactions, and rollback/wait logic.
src/controller/BUILD.gn Wires new controller sources into the build.
src/controller/AutoCommissioner.h Adds PDC buffers and ClearPDCParameters() API.
src/controller/AutoCommissioner.cpp Implements PDC stage selection, parameter capture/clearing, nonce generation, and retry/cleanup behavior.
src/app/clusters/network-identity-management-server/NetworkIdentityManagementCluster.cpp Removes now-redundant include of the deleted logging header.
src/app/clusters/network-identity-management-server/Logging.h Deletes cluster-local KeyId logging macros (moved to shared credentials header).
src/app/clusters/network-identity-management-server/BUILD.gn Removes Logging.h from the build sources list.
examples/chip-tool/commands/pairing/PairingCommand.h Adds CLI args and state for NETIM-backed PDC registrar lifecycle management.
examples/chip-tool/commands/pairing/PairingCommand.cpp Implements NETIM registrar construction, Wi‑Fi credential selection, and deferred exit until revocation completes.
Review details
  • Files reviewed: 26/26 changed files
  • Comments generated: 2
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines 30 to 38
#include <cstdint>
#include <string.h>

#include <crypto/CHIPCryptoPAL.h>
#include <lib/asn1/ASN1.h>
#include <lib/core/CASEAuthTag.h>
#include <lib/core/CHIPConfig.h>
#include <lib/core/CHIPEncoding.h>
#include <lib/core/DataModelTypes.h>
Comment on lines +55 to +58
* Asynchronous methods do not return a status to their caller: they must invoke their callback
* exactly once. Completing the callback synchronously, i.e. before returning from the method, is
* allowed, and is the expected way signal errors that prevent the operation from starting.
*
Copilot AI review requested due to automatic review settings September 9, 2026 12:59

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

src/credentials/CHIPCert.h introduces PRIX32 usage in new logging macros without ensuring the required PRI*/PRIX* format macros header is included, which can cause build failures depending on include order/toolchain.

Review details

Suppressed comments (1)

src/credentials/CHIPCert.h:38

  • ChipLogFormatKeyId uses PRIX32, but this header does not include a header that guarantees the PRI*/PRIX* format macros are defined. This can break compilation depending on include order/toolchain. Include <inttypes.h> (or <cinttypes>) in this header.
#include <lib/asn1/ASN1.h>
#include <lib/core/CASEAuthTag.h>
#include <lib/core/CHIPConfig.h>
#include <lib/core/CHIPEncoding.h>
#include <lib/core/DataModelTypes.h>
  • Files reviewed: 26/26 changed files
  • Comments generated: 0 new
  • Review effort level: Lite

Comment thread src/controller/BUILD.gn
"ExampleOperationalCredentialsIssuer.cpp",
"NetworkIdentityManagementRegistrar.cpp",
"NetworkIdentityManagementRegistrar.h",
"NetworkIdentityRegistrar.h",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NetworkIdentityRegistrar.h is already in the unconditional sources list above (with the other header-only entries), so this second copy inside the conditional block is redundant.

Comment on lines +3398 to +3400
// Nothing was registered, so we owe the registrar no rollback.
ChipLogFailure(status, Controller, "Failed to register Network Client Identity");
commissioner->mNetworkClientRegistration.Clear();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A cancelled registration keeps the rollback obligation, because the AddClient may still have taken effect. A failure that arrives after the command reached the network is the same situation. A lost response or a dropped session leaves an entry on the network with nothing recording it.

RemoveClient already maps NOT_FOUND to success, so revoking either way costs one round trip on an attempt that is failing anyway. How about, removing the clear():

Suggested change
// Nothing was registered, so we owe the registrar no rollback.
ChipLogFailure(status, Controller, "Failed to register Network Client Identity");
commissioner->mNetworkClientRegistration.Clear();
// Note we keep the rollback obligation. A failure that arrives after the command reached
// the network may still have taken effect, in the same way a cancelled one may have.
ChipLogFailure(status, Controller, "Failed to register Network Client Identity");

// If present, kWiFiNetworkSetup will configure the commissionee for PDC using this identity.
// The AutoCommissioner populates this from the PDCNetworkIdentityInfo report returned
// by kPDCGetNetworkIdentity, but a CommissioningDelegate is free to obtain the Network Identity
// in some other way and bypass that step entirely.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The spec says clients should check the PDC feature flag before using PDC. A server on cluster revision 2 or below ignores the NetworkIdentity field and configures an open network instead of rejecting the command.

The AutoCommissioner does check the flag. A delegate that supplies the Network Identity itself, as this comment invites, has nothing in kWiFiNetworkSetup to stop it. Is that meant to be the delegate's responsibility?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is the delegate's job. The DeviceCommissioner just executes the steps it's told to execute, e.g. executing kWiFiNetworkSetup doesn't check that the Network Commissioning cluster on the commissionee actually supports WiFi. I'll add a sentence to that comment.

void Fail(CHIP_ERROR error) override { Complete(error); }

uint8_t mClientIdentity[Credentials::kMaxCHIPCompactNetworkIdentityLength];
uint8_t mClientIdentityLength = 0;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The guard in Start() bounds the identity by sizeof(mClientIdentity), and the length is then narrowed to uint8_t. Nothing couples the two, so a future identity type larger than 255 bytes would pass the guard and truncate silently. The spec anticipates one, in the note under QueryIdentityResponse.

Suggested change
uint8_t mClientIdentityLength = 0;
uint8_t mClientIdentityLength = 0;
static_assert(Credentials::kMaxCHIPCompactNetworkIdentityLength <= UINT8_MAX);

void DeviceCommissioner::ReportUnrevokedNetworkClientIdentity(Credentials::CertificateKeyId clientIdentifier, const char * reason,
CHIP_ERROR error)
{
bool haveError = (error != CHIP_NO_ERROR);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
bool haveError = (error != CHIP_NO_ERROR);
[[maybe_unused]] bool haveError = (error != CHIP_NO_ERROR);

std::unique_ptr<NetworkIdentityManagementRegistrar> & owner;
std::optional<CHIP_ERROR> status;

Callback::Callback<OnClientUnregisteredFunct> callback{ [](void * context, CHIP_ERROR status) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The status param is causing a variable shadowing error with the status above

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants