1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
//! Device-attestation trust material: the PAA roots that anchor DAC/PAI chain
//! validation and the CD signing roots that anchor Certification-Declaration
//! signatures. Configured once on the controller (attestation is a fabric-wide
//! security policy — chip holds it on the commissioner the same way).
//!
//! This is a concrete value type for v1.0. When ledger-backed sourcing (DCL)
//! lands post-1.0, a `trait AttestationVerifier` can emerge here without an
//! API break to the commissioning entry point.
use std::path::Path;
use matter_commissioning::{CdSigningRoots, PaaTrustStore};
use crate::error::Error;
/// The trust anchors used to verify a device during commissioning.
#[derive(Debug)]
pub struct AttestationTrust {
pub(crate) paa: PaaTrustStore,
pub(crate) cd: CdSigningRoots,
}
impl AttestationTrust {
/// Construct from the bundled CSA **test / example** roots: the PAA
/// test roots plus the CD signing roots that verify chip's example
/// devices — the synthetic loopback root, chip's test CD signing
/// authority, and CSA production "CD Signing Key 001" (which signs the
/// VID=0xFFF1 CD served by every example-DAC device, including the
/// esp-matter ESP32-C6). Suitable for CSA-test / dev / example devices
/// and the hermetic loopback.
///
/// This is **not** the full CSA production trust set: an arbitrary
/// certified product may present a DAC chained to a PAA, or a CD signed
/// by a CSA production key, that is not bundled here. Commissioners for
/// arbitrary certified devices use [`Self::from_dirs`] pointed at the
/// production roots.
#[must_use]
pub fn example_device_roots() -> Self {
Self {
paa: PaaTrustStore::with_example_device_roots(),
cd: CdSigningRoots::with_example_device_roots(),
}
}
/// Load PAA roots from a directory of `.der` certificates and CD signing
/// roots from a directory (or single file) of `.der` certificates — the
/// production path (e.g. connectedhomeip's `credentials/production/...`).
///
/// # Errors
///
/// Returns [`Error::Trust`] if a directory cannot be read or a certificate
/// fails to parse.
pub fn from_dirs(paa_dir: &Path, cd_dir: &Path) -> Result<Self, Error> {
let mut paa = PaaTrustStore::empty();
for entry in
std::fs::read_dir(paa_dir).map_err(|e| Error::Trust(format!("paa dir: {e}")))?
{
let path = entry
.map_err(|e| Error::Trust(format!("paa entry: {e}")))?
.path();
if path.extension().and_then(|x| x.to_str()) != Some("der") {
continue;
}
let der = std::fs::read(&path).map_err(|e| Error::Trust(format!("paa read: {e}")))?;
let cert = matter_commissioning::Paa::from_der(&der)
.map_err(|e| Error::Trust(format!("paa parse {}: {e:?}", path.display())))?;
paa.add(cert);
}
let mut cd_ders: Vec<Vec<u8>> = Vec::new();
if cd_dir.is_dir() {
for entry in
std::fs::read_dir(cd_dir).map_err(|e| Error::Trust(format!("cd dir: {e}")))?
{
let path = entry
.map_err(|e| Error::Trust(format!("cd entry: {e}")))?
.path();
if path.extension().and_then(|x| x.to_str()) != Some("der") {
continue;
}
cd_ders
.push(std::fs::read(&path).map_err(|e| Error::Trust(format!("cd read: {e}")))?);
}
} else {
cd_ders.push(std::fs::read(cd_dir).map_err(|e| Error::Trust(format!("cd read: {e}")))?);
}
let refs: Vec<&[u8]> = cd_ders.iter().map(Vec::as_slice).collect();
let cd = CdSigningRoots::from_cert_der(&refs)
.map_err(|e| Error::Trust(format!("cd parse: {e:?}")))?;
Ok(Self { paa, cd })
}
}
#[cfg(test)]
#[allow(clippy::unwrap_used, clippy::expect_used)] // Test code: CLAUDE.md allows unwrap/expect with justification.
mod tests {
use super::*;
#[test]
fn example_device_roots_constructs() {
let _trust = AttestationTrust::example_device_roots();
// Construction succeeds and yields usable PAA + CD stores; deeper
// verification is covered by matter-commissioning's attestation tests.
}
#[test]
fn from_dirs_errors_on_missing_dir() {
let err = AttestationTrust::from_dirs(
Path::new("/nonexistent/paa"),
Path::new("/nonexistent/cd"),
)
.expect_err("missing dir must error");
assert!(matches!(err, Error::Trust(_)));
}
/// `from_dirs` must skip non-`.der` files (`.pem`, `.txt`, etc.) in
/// both the PAA and CD directories. The connectedhomeip
/// `credentials/development/{paa-root-certs,cd-certs}` directories
/// contain `.pem` files alongside each `.der`; without the extension
/// filter, `from_dirs` errors trying to parse PEM as DER.
///
/// Test strategy:
/// - Write a real PAA root DER into a temp PAA dir alongside a junk
/// `.pem` and a `.txt`.
/// - Write a real X.509 P-256 cert DER into a temp CD dir alongside
/// the same junk files.
/// - Assert that `from_dirs` succeeds (junk files were skipped) and
/// that each store contains exactly 1 entry.
///
/// Temp dirs are created under `target/` so they stay out of the
/// source tree and survive interrupted runs gracefully (the directory
/// is cleaned up at the end of the test).
///
/// Cert fixtures are read from the in-repo `test-vectors/` tree via
/// `CARGO_MANIFEST_DIR` so no extra crate dependency is required.
#[test]
fn from_dirs_skips_non_der_files() {
use std::fs;
// ── locate in-repo fixtures ────────────────────────────────────────
// CARGO_MANIFEST_DIR points to `crates/matter-controller/`.
let manifest_dir = Path::new(env!("CARGO_MANIFEST_DIR"));
let repo_root = manifest_dir
.parent() // crates/
.unwrap()
.parent() // matter-rust/
.unwrap();
// PAA cert: a real Matter PAA (no-VID variant) bundled in the
// commissioning crate's CSA test-root collection.
let paa_der_src = repo_root
.join("crates/matter-commissioning/src/attestation/csa_test_roots")
.join("Chip-Test-PAA-NoVID-Cert.der");
let paa_bytes = fs::read(&paa_der_src).expect("bundled PAA NoVID DER must be readable");
// CD signing cert: reuse the same PAA cert (any X.509 P-256 cert
// satisfies `CdSigningRoots::from_cert_der`; we only need the
// extension filter to run, not a real attestation verification).
let cd_bytes = paa_bytes.clone();
// ── build temp directories under target/ ──────────────────────────
let target_dir = repo_root.join("target").join("from-dirs-test");
let paa_dir = target_dir.join("paa");
let cd_dir = target_dir.join("cd");
fs::create_dir_all(&paa_dir).expect("create temp PAA dir");
fs::create_dir_all(&cd_dir).expect("create temp CD dir");
// Write the real DER cert into each dir.
fs::write(paa_dir.join("test-paa.der"), &paa_bytes).expect("write PAA DER");
fs::write(cd_dir.join("test-cd.der"), &cd_bytes).expect("write CD DER");
// Write junk files alongside — these must be silently skipped.
fs::write(paa_dir.join("test-paa.pem"), b"not der at all")
.expect("write junk pem in PAA dir");
fs::write(paa_dir.join("README.txt"), b"also junk").expect("write junk txt in PAA dir");
fs::write(cd_dir.join("test-cd.pem"), b"not der at all").expect("write junk pem in CD dir");
fs::write(cd_dir.join("notes.txt"), b"also junk").expect("write junk txt in CD dir");
// ── exercise `from_dirs` ──────────────────────────────────────────
let trust = AttestationTrust::from_dirs(&paa_dir, &cd_dir)
.expect("from_dirs must succeed when non-.der files are present");
// Each dir contained exactly one .der file.
assert_eq!(trust.paa.len(), 1, "exactly one PAA loaded");
assert_eq!(trust.cd.len(), 1, "exactly one CD signing root loaded");
// ── clean up ──────────────────────────────────────────────────────
// Best-effort: a failure here does not invalidate the test result.
let _ = fs::remove_dir_all(&target_dir);
}
}