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//! - [`aead`]: AES-128-CCM-128 AEAD helpers. Prefer [`SessionAead`] over
14//! the free functions on any path that encrypts/decrypts more than once
15//! per key, to avoid repeating AES key expansion per call.
16//! - [`error`]: the crate error type.
17//!
18//! # Cryptographic discipline
19//!
20//! This crate never implements primitives. AES, ECDH, ECDSA, SHA, HKDF, and
21//! HMAC come from `ring`. EC scalar/point arithmetic (which ring deliberately
22//! doesn't expose) comes from `p256`. We implement only the Matter-defined
23//! protocols on top of those primitives.
24
25#![forbid(unsafe_code)]
26
27pub mod aead;
28pub mod case;
29pub mod checkin;
30pub mod error;
31pub mod operational;
32pub mod pase;
33
34#[cfg(feature = "test-support")]
35pub mod test_support;
36
37pub use aead::SessionAead;
38pub use case::initiator::CaseInitiator;
39pub use case::responder::CaseResponder;
40pub use case::signer::{CaseSigner, RingSigner, SignerError};
41
42/// Canonical name for the ECDSA-P256-SHA256 signer trait outside CASE.
43///
44/// `CaseSigner` is the original name (introduced in M4.1). Outside the
45/// CASE handshake, callers should import this re-export — the trait
46/// itself is identical.
47pub use case::signer::CaseSigner as Signer;
48pub use case::{
49 CaseCredentials, CaseMessageKind, CaseSessionKeys, CaseSessionOutput, LocalInfo, PeerInfo,
50 ResumptionId, ResumptionRecord, Sigma1Outcome,
51};
52pub use error::{Error, Result};
53pub use operational::{
54 derive_compressed_fabric_id, derive_group_privacy_key, derive_group_session_id,
55 derive_operational_ipk, group_multicast_ipv6,
56};
57pub use pase::{
58 pake_passcode_verifier, PaseMessageKind, PasePbkdfParams, PaseProver, PaseSessionKeys,
59 PaseVerifier,
60};
61
62/// Fill `buf` with cryptographically secure random bytes (ring `SystemRandom`).
63///
64/// # Errors
65/// Returns [`Error::Rng`] if the system RNG fails.
66pub fn random_bytes(buf: &mut [u8]) -> Result<()> {
67 use ring::rand::SecureRandom;
68 ring::rand::SystemRandom::new()
69 .fill(buf)
70 .map_err(|_| Error::Rng)
71}
72
73#[cfg(test)]
74#[allow(clippy::unwrap_used)] // Test-code carve-out: see CLAUDE.md.
75mod tests {
76 #[test]
77 fn random_bytes_fills_and_varies() {
78 let mut a = [0u8; 32];
79 let mut b = [0u8; 32];
80 crate::random_bytes(&mut a).unwrap();
81 crate::random_bytes(&mut b).unwrap();
82 assert_ne!(a, [0u8; 32]);
83 assert_ne!(a, b); // collision probability ~2^-256
84 }
85}