Skip to main content

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}