matter_crypto/lib.rs
1//! Matter session-establishment protocols.
2//!
3//! Milestones 3 (PASE / SPAKE2+) and 4 (CASE / SIGMA) of the `matter-rust`
4//! roadmap.
5//!
6//! # Scope
7//!
8//! - [`pase`]: Password Authenticated Session Establishment (SPAKE2+).
9//! M3.1 (current): math + KDF primitives. M3.2: state machines.
10//! M3.3: matter.js byte-parity verification.
11//! - [`case`]: Certificate Authenticated Session Establishment (SIGMA-I).
12//! Placeholder; M4 territory.
13//! - [`error`]: the crate error type.
14//!
15//! # Cryptographic discipline
16//!
17//! This crate never implements primitives. AES, ECDH, ECDSA, SHA, HKDF, and
18//! HMAC come from `ring`. EC scalar/point arithmetic (which ring deliberately
19//! doesn't expose) comes from `p256`. We implement only the Matter-defined
20//! protocols on top of those primitives.
21
22#![forbid(unsafe_code)]
23
24pub mod aead;
25pub mod case;
26pub mod checkin;
27pub mod error;
28pub mod operational;
29pub mod pase;
30
31#[cfg(feature = "test-support")]
32pub mod test_support;
33
34pub use case::initiator::CaseInitiator;
35pub use case::responder::CaseResponder;
36pub use case::signer::{CaseSigner, RingSigner, SignerError};
37
38/// Canonical name for the ECDSA-P256-SHA256 signer trait outside CASE.
39///
40/// `CaseSigner` is the original name (introduced in M4.1). Outside the
41/// CASE handshake, callers should import this re-export — the trait
42/// itself is identical.
43pub use case::signer::CaseSigner as Signer;
44pub use case::{
45 CaseCredentials, CaseMessageKind, CaseSessionKeys, CaseSessionOutput, LocalInfo, PeerInfo,
46 ResumptionId, ResumptionRecord, Sigma1Outcome,
47};
48pub use error::{Error, Result};
49pub use operational::{
50 derive_compressed_fabric_id, derive_group_privacy_key, derive_group_session_id,
51 derive_operational_ipk, group_multicast_ipv6,
52};
53pub use pase::{
54 pake_passcode_verifier, PaseMessageKind, PasePbkdfParams, PaseProver, PaseSessionKeys,
55 PaseVerifier,
56};
57
58/// Fill `buf` with cryptographically secure random bytes (ring `SystemRandom`).
59///
60/// # Errors
61/// Returns [`Error::Rng`] if the system RNG fails.
62pub fn random_bytes(buf: &mut [u8]) -> Result<()> {
63 use ring::rand::SecureRandom;
64 ring::rand::SystemRandom::new()
65 .fill(buf)
66 .map_err(|_| Error::Rng)
67}
68
69#[cfg(test)]
70#[allow(clippy::unwrap_used)] // Test-code carve-out: see CLAUDE.md.
71mod tests {
72 #[test]
73 fn random_bytes_fills_and_varies() {
74 let mut a = [0u8; 32];
75 let mut b = [0u8; 32];
76 crate::random_bytes(&mut a).unwrap();
77 crate::random_bytes(&mut b).unwrap();
78 assert_ne!(a, [0u8; 32]);
79 assert_ne!(a, b); // collision probability ~2^-256
80 }
81}