matter-commissioning
Matter commissioning: setup payloads, the ten-stage state machine, device attestation, NOC issuance, and network commissioning.
Part of matter-rust. Milestone 6.
Status: pre-release (
0.0.0).Milestone 6.4 (Commissioning State Machine): complete — the state machine drives end-to-end from
SecurePairingthroughAction::Done(CommissionedFabric)on canned responses + a mockon_case_establishedcallback. matter.js byte-parity gate infrastructure shipped (operator-touch wiring deferred — seeTODO-1.0.md).Phases available:
- M6.1: the setup-payload codec (QR + manual pairing code).
- M6.2.1: typed attestation cert wrappers (
Dac/Pai/Paa),PaaTrustStorewith bundled CSA test roots,VendorId/ProductIdnewtypes. Parsing only.- M6.2.2:
verify_chain—rustls-webpki0.103 path validation withKeyUsage::client_auth()plus a Matter VID/PID equality overlay. Six granularAttestationErrorvariants with a documentedwebpki::Errormapping. 8-row negative-fixture matrix.- M6.2.3 (M6.2 feature-complete):
verify_attestation_response— pure ECDSA P-256/SHA-256 verification viaringoverattestation_elements || attestation_challenge. Single coarseBadResponseSignatureerror variant; matter.js byte-parity for happy-path + four single-byte mutations.- M6.3 (feature-complete): NOC issuance —
FabricRecord,verify_csr_response,issue_noc, OpCreds command codecs with matter.js byte-parity.- M6.4 (complete): commissioning state machine — all six sub-phases (M6.4.1 skeleton → M6.4.6 byte-parity gate infrastructure) shipped.
Next: M6.5 (Wi-Fi network commissioning subgraph) and M6.6 (Tokio driver + first real-device commission). With M6.6 lands the first public demo of the library commissioning a real Matter device.
Example: parse a QR code
use parse_qr;
let payload = parse_qr?;
assert_eq!;
assert_eq!;
(Replace the QR string with the actual captured value from
test-vectors/commissioning/setup/qr-spec-example.json.)
Example: parse a manual pairing code
use parse_manual_code;
let payload = parse_manual_code?;
assert_eq!;
Example: parse a DAC and reach for a trusted root (M6.2.1)
use ;
#
Chain validation against the trust store is M6.2.2.
Example: validate an attestation chain (M6.2.2)
use MatterTime;
use ;
# #
Production callers build their own PaaTrustStore from CSA-published
production roots (M8 deliverable). The bundled with_csa_test_roots()
is for examples and integration tests only.
Example: verify an attestation response (M6.2.3)
use ;
#
The dac_public_key is exactly what Dac::public_key() returns
(raw SEC1 uncompressed P-256, 65 bytes). The attestation_challenge
is the 16-byte session value at [32..48] of the PASE/CASE session
key blob (exposed as CaseSessionKeys::attestation_challenge or
PaseSessionKeys::attestation_key). Any verification failure folds
into the single coarse AttestationError::BadResponseSignature.
Example: drive the early commissioning stages (M6.4.1)
use Arc;
use MatterTime;
use CdSigningRoots;
use ;
use ;
use ;
#
M6.4.1 only drives SecurePairing → ReadCommissioningInfo →
ArmFailsafe → ConfigRegulatory. M6.4.2 extends the flow through
the attestation request/response stages, and M6.4.3 wires the
CSA-signed Certification Declaration check into the off-wire
AttestationVerification step so the cursor can advance past
attestation into the (M6.4.4) CSR + NOC issuance stages.
Example: attestation flow through CD verification (M6.4.3)
The same driver loop from the M6.4.1 example works unchanged — after
ConfigRegulatory the state machine emits four more Action::Invoke
calls (PAI cert, DAC cert, AttestationRequest) and one off-wire
AttestationVerification step. M6.4.3 wires the CD-verify step in,
so on a valid CD the cursor advances past attestation:
use ;
#
M6.4.4 will land the CSR / NOC issuance stages that consume the advanced cursor.
Example: verify a Certification Declaration standalone (M6.4.3)
verify_certification_declaration can be called directly without
involving the state machine — useful for offline analysis of captured
CD blobs:
use ;
#
Production callers replace with_csa_test_roots() with
CdSigningRoots::from_pem(&[my_root_pem]) loading the CSA-published
signing root(s) supplied by deployment.
The verifier performs five checks in order:
- Parse the CMS/PKCS#7 SignedData via the
cmscrate. - Validate the CMS envelope shape (single signer, attached content,
ecdsa-with-SHA256). - Verify the ECDSA-P256/SHA-256 signature against each trusted root; accept on first match.
- Decode the inner Matter-TLV CD body to extract
vendor_id+product_id_array. - Cross-check the declared VID/PID against the
expected_vid/expected_pidarguments.
Any failure surfaces as a specific
AttestationError::CertificationDeclaration* variant.
Example: full commissioning driver loop reaching Action::Done (M6.4.5)
The complete cursor walks from SecurePairing through
Action::Done(CommissionedFabric). The caller (M6.6's Tokio driver in
the next major milestone) frames Invoke envelopes + routes via
matter-transport, then performs mDNS find-operational + the SIGMA
handshake when the state machine signals Action::EstablishCase:
use ;
#
The returned CommissionedFabric carries the long-lived fabric record
(RCAC + IPK + fabric ID), the peer's operational node ID, the device's
NOC public key, and the terminal stage cursor (always
Stage::Cleanup).
Wi-Fi commissioning configuration (M6.5+)
use ;
let config = CommissionerConfig ;
let mut sm = new?;
For Ethernet-only devices (or devices already on their operational
network), set network: NetworkCredentials::AlreadyOnNetwork — the state
machine detects the network shape at Stage::ReadNetworkCommissioningInfo
and skips the Wi-Fi sub-cursor.
Thread commissioning is supported: set
network: NetworkCredentials::Thread(dataset) with a
ThreadDataset built from an operational dataset
(e.g. ot-ctl dataset active -x, hex-decoded). If the supplied credential
type doesn't match what the device actually offers — e.g. Thread
credentials against a device whose NetworkCommissioning::FeatureMap lacks
the Thread bit — commissioning fails fast with
CommissioningError::NetworkFeatureUnsupported { needed }, naming the
network type the device is missing.
Optional tracing feature
Enable per-method spans for observability:
= { = "...", = ["tracing"] }
Span field names (stage, expectation) align best-effort with
matter.js's log-event format so operators can grep across both
implementations.
Byte parity
Every fixture in test-vectors/commissioning/setup/ is captured from
matter.js by cargo xtask capture-setup. The integration test in
tests/setup_byte_parity.rs asserts that encode_qr / encode_manual_code
produce byte-identical output and that parse_qr / parse_manual_code
recover the same SetupPayload.
For attestation-response verification, test-vectors/attestation/response/
is captured by cargo xtask capture-attestation. The integration test
in tests/attestation_response_byte_parity.rs asserts that Rust and
matter.js's NodeJsStyleCrypto.verifyEcdsa produce the same
accept/reject verdict for a happy-path tuple plus four single-byte
mutations. (Byte-parity is on verdicts, not raw bytes — ECDSA's k
is randomized per signing call, so the captured signature varies
across script runs while the test assertions remain stable.)