matter_commissioning/lib.rs
1//! Matter commissioning state machine.
2//!
3//! This is Milestone 6 of the `matter-rust` roadmap. The crate is currently
4//! shipping in phases:
5//!
6//! - **M6.1:** setup payload codec — see [`setup`].
7//! - **M6.2.1:** typed attestation cert wrappers ([`Dac`], [`Pai`],
8//! [`Paa`]) and [`PaaTrustStore`] — see [`attestation`].
9//! - **M6.2.2 (current):** [`verify_chain`] — `rustls-webpki` path
10//! validation with `KeyUsage::client_auth()`, plus a Matter
11//! VID/PID equality overlay. Six new [`AttestationError`] variants.
12//! - **M6.2.3:** `verify_attestation_response` + matter.js
13//! byte-parity capture.
14//! - **M6.2.4–M6.2.6:** see [`attestation`].
15//! - **M6.3:** Node Operational Certificate issuance — see [`noc`].
16//! - **M6.4.2:** attestation on-wire flow + off-wire
17//! `AttestationVerification`. State machine drives
18//! `SendPaiCertRequest` → `SendDacCertRequest` →
19//! `SendAttestationRequest` → `AttestationVerification`, chaining
20//! M6.2's `verify_chain` + `verify_attestation_response` + the
21//! `extract_attestation_elements_fields` helper.
22//! - **M6.4.3:** CD verification wired into
23//! `AttestationVerification`. The state machine now calls
24//! `verify_certification_declaration` against
25//! [`attestation::CdSigningRoots`] and advances past attestation on
26//! a valid CD; `CommissionerConfig` gains a `cd_signing_roots`
27//! reference.
28//! - **M6.4.4:** CSR + NOC issuance flow. State machine
29//! drives `SendOpCertSigningRequest` → `ValidateCsr` →
30//! `GenerateNocChain` → `SendTrustedRootCert` → `SendNoc`, then
31//! advances to `Stage::ReadNetworkCommissioningInfo` (M6.5.2 expands
32//! the network subgraph). Integrates M6.3's
33//! `verify_csr_response` + `issue_noc` + the `OpCreds`
34//! `AddTrustedRoot` / `AddNOC` encoders.
35//! - **M6.4.5:** PASE→CASE handoff + `CommissioningComplete`.
36//! The state machine drives end-to-end from `SecurePairing` through
37//! `Action::Done(CommissionedFabric)` on canned responses plus a
38//! mock `on_case_established()` callback. New public API
39//! `Commissioner::on_case_established` for the M6.6 driver's CASE
40//! handshake success signal; `Expectation::CaseFailed` for the
41//! failure path.
42//! - **M6.4 (complete):** commissioning state machine. End-to-end
43//! cursor from `SecurePairing` through
44//! `Action::Done(CommissionedFabric)` on canned responses + a
45//! mock CASE-established callback. matter.js byte-parity gate
46//! infrastructure shipped — see [`state_machine`] for the API.
47//! - **M6.5 (current):** Wi-Fi network commissioning. Expands the
48//! `NetworkCommissioning` no-op slot into the real Wi-Fi sub-cursor
49//! (`ReadNetworkCommissioningInfo` → `NetworkSetup` →
50//! `FailsafeBeforeNetworkEnable` → `NetworkEnable`; the generic stages
51//! also carry the M9-C2 Thread provisioning path). Ethernet-only
52//! devices skip the Wi-Fi sub-cursor entirely; Thread-only devices
53//! fail fast with a typed `NetworkFeatureUnsupported` error. New
54//! `RemediationHint` enum surfaces actionable categories for
55//! `NetworkRejected`. Failsafe-expiry now derives from
56//! `BasicCommissioningInfo` (was hardcoded 60s in M6.4). Optional
57//! `tracing` feature instruments every dispatch arm.
58//! - **M6.6.1 (current):** Interaction Model framing — see [`im`].
59//! `build_invoke_request` / `parse_invoke_response`,
60//! `build_read_request` / `parse_report_data`. Pure codec over
61//! `matter-codec`; the wire-I/O driver follows in M6.6.2+.
62//! - **M6.6 (next-next):** Tokio driver + first real-device
63//! commission. Wires the M6.4 state machine into `matter-transport`'s
64//! session layer + drives `matter-crypto`'s SIGMA-I CASE handshake.
65//!
66//! ## Quick-start (M6.1 only)
67//!
68//! ```
69//! use matter_commissioning::setup::{parse_qr, parse_manual_code};
70//! # fn run() -> Result<(), matter_commissioning::setup::Error> {
71//! let from_qr = parse_qr("MT:Y.K90AFN00KA0648G00")?;
72//! let from_manual = parse_manual_code("11693312331")?;
73//! assert_eq!(from_qr.vendor_id, Some(0xFFF1));
74//! assert_eq!(from_manual.passcode.as_u32(), 20_202_021);
75//! # Ok(())
76//! # }
77//! # let _ = run;
78//! ```
79//!
80//! Replace the QR string + manual code above with values captured for
81//! your own devices via `cargo xtask capture-setup` if you change the
82//! fixture set.
83//!
84//! ## Optional `tracing` feature
85//!
86//! Enable the `tracing` crate feature to get per-method spans on
87//! `Commissioner::poll`, `Commissioner::on_response`, and
88//! `Commissioner::on_case_established`. Span fields (`stage`,
89//! `expectation`) align best-effort with matter.js's log-event format
90//! so operators can grep across both implementations. Compatibility
91//! is not guaranteed across matter.js minor versions.
92
93#![forbid(unsafe_code)]
94
95pub mod attestation;
96pub mod clusters;
97#[cfg(feature = "driver")]
98pub mod driver;
99pub mod error;
100/// Lowercase-hex rendering for `tracing` debug dumps of wire bytes.
101#[cfg(feature = "tracing")]
102pub(crate) mod hexdump {
103 use std::fmt::Write;
104
105 /// Render `bytes` as a contiguous lowercase-hex string.
106 pub(crate) fn hex(bytes: &[u8]) -> String {
107 bytes
108 .iter()
109 .fold(String::with_capacity(bytes.len() * 2), |mut out, b| {
110 // Vec-backed String writes are infallible.
111 let _ = write!(out, "{b:02x}");
112 out
113 })
114 }
115}
116/// Interaction Model message framing — re-exported from [`matter_interaction`]
117/// (lifted out of this crate in M7.1; all `im::` paths are unchanged).
118pub use matter_interaction as im;
119pub mod noc;
120pub mod setup;
121pub mod state_machine;
122#[cfg(feature = "test-support")]
123pub mod test_support;
124pub mod thread_dataset;
125#[cfg(feature = "wiretrace")]
126pub mod wiretrace;
127
128pub use setup::{
129 encode_manual_code, encode_qr, parse_manual_code, parse_qr, CommissioningFlow,
130 DiscoveryCapabilities, Discriminator, Error as SetupError, Passcode, SetupPayload,
131};
132
133pub use attestation::{
134 extract_attestation_elements_fields, verify_attestation_response,
135 verify_certification_declaration, verify_certification_declaration_with_paa, verify_chain,
136 verify_dac_signed_elements, AttestationElementsFields, AttestationError, AttestationResponse,
137 CdSigningRoots, ChainVerification, Dac, Paa, PaaTrustStore, Pai, ProductId, VendorId,
138};
139
140pub use noc::{
141 decode_attestation_response, decode_certificate_chain_response, decode_csr_response,
142 decode_noc_response, encode_add_noc, encode_add_trusted_root, encode_attestation_request,
143 encode_certificate_chain_request, encode_csr_request, encode_update_noc, issue_icac, issue_noc,
144 parse_and_verify_csr, parse_nocsr, verify_csr_response, CertChainType,
145 CertificateChainResponse, CsrResponse, FabricRecord, NocError, NocResponse, NocRng,
146 NocsrElements, ParsedCsr, SystemNocRng, VerifiedCsr,
147};
148
149pub use clusters::network_commissioning::{
150 decode_connect_network_response, decode_feature_map, decode_network_config_response,
151 encode_add_or_update_wifi_network, encode_connect_network, remediation_for,
152 ConnectNetworkResponse, NetworkCommissioningFeature, NetworkConfigResponse,
153};
154
155pub use im::{
156 build_invoke_request, build_read_request, parse_invoke_response, parse_report_data,
157 AttributePath, CommandPath, ImError, ImStatus, InvokeResponse, ReportData, IM_REVISION,
158};
159
160#[cfg(feature = "__test_shortcuts")]
161pub use state_machine::TestStateSeeds;
162pub use state_machine::{
163 Action, CommissionedFabric, Commissioner, CommissionerConfig, CommissioningError, Expectation,
164 NetworkCredentials, NetworkKind, RemediationHint, SessionContext, Stage, WiFiCredentials,
165};
166
167pub use thread_dataset::{ThreadDataset, ThreadDatasetError};