matter-crypto 0.3.0

Matter protocol session establishment: PASE (SPAKE2+) and CASE (SIGMA).
Documentation
//! Matter session-establishment protocols.
//!
//! Milestones 3 (PASE / SPAKE2+) and 4 (CASE / SIGMA) of the `matter-rust`
//! roadmap.
//!
//! # Scope
//!
//! - [`pase`]: Password Authenticated Session Establishment (SPAKE2+).
//!   M3.1 (current): math + KDF primitives. M3.2: state machines.
//!   M3.3: matter.js byte-parity verification.
//! - [`case`]: Certificate Authenticated Session Establishment (SIGMA-I).
//!   Placeholder; M4 territory.
//! - [`aead`]: AES-128-CCM-128 AEAD helpers. Prefer [`SessionAead`] over
//!   the free functions on any path that encrypts/decrypts more than once
//!   per key, to avoid repeating AES key expansion per call.
//! - [`error`]: the crate error type.
//!
//! # Cryptographic discipline
//!
//! This crate never implements primitives. AES, ECDH, ECDSA, SHA, HKDF, and
//! HMAC come from `ring`. EC scalar/point arithmetic (which ring deliberately
//! doesn't expose) comes from `p256`. We implement only the Matter-defined
//! protocols on top of those primitives.

#![forbid(unsafe_code)]

pub mod aead;
pub mod case;
pub mod checkin;
pub mod error;
pub mod operational;
pub mod pase;

#[cfg(feature = "test-support")]
pub mod test_support;

pub use aead::SessionAead;
pub use case::initiator::CaseInitiator;
pub use case::responder::CaseResponder;
pub use case::signer::{CaseSigner, RingSigner, SignerError};

/// Canonical name for the ECDSA-P256-SHA256 signer trait outside CASE.
///
/// `CaseSigner` is the original name (introduced in M4.1). Outside the
/// CASE handshake, callers should import this re-export — the trait
/// itself is identical.
pub use case::signer::CaseSigner as Signer;
pub use case::{
    CaseCredentials, CaseMessageKind, CaseSessionKeys, CaseSessionOutput, LocalInfo, PeerInfo,
    ResumptionId, ResumptionRecord, Sigma1Outcome,
};
pub use error::{Error, Result};
pub use operational::{
    derive_compressed_fabric_id, derive_group_privacy_key, derive_group_session_id,
    derive_operational_ipk, group_multicast_ipv6,
};
pub use pase::{
    pake_passcode_verifier, PaseMessageKind, PasePbkdfParams, PaseProver, PaseSessionKeys,
    PaseVerifier,
};

/// Fill `buf` with cryptographically secure random bytes (ring `SystemRandom`).
///
/// # Errors
/// Returns [`Error::Rng`] if the system RNG fails.
pub fn random_bytes(buf: &mut [u8]) -> Result<()> {
    use ring::rand::SecureRandom;
    ring::rand::SystemRandom::new()
        .fill(buf)
        .map_err(|_| Error::Rng)
}

#[cfg(test)]
#[allow(clippy::unwrap_used)] // Test-code carve-out: see CLAUDE.md.
mod tests {
    #[test]
    fn random_bytes_fills_and_varies() {
        let mut a = [0u8; 32];
        let mut b = [0u8; 32];
        crate::random_bytes(&mut a).unwrap();
        crate::random_bytes(&mut b).unwrap();
        assert_ne!(a, [0u8; 32]);
        assert_ne!(a, b); // collision probability ~2^-256
    }
}