//! Salted Challenge Response Authentication Mechanism. //! //! RFC: . //! //! Reference implementation: //! * //! * mod exchange; mod key; mod messages; mod secret; mod signature; #[cfg(test)] mod password; pub use exchange::Exchange; pub use key::ScramKey; pub use secret::ServerSecret; pub use secret::*; use hmac::{Hmac, Mac}; use sha2::{Digest, Sha256}; // TODO: add SCRAM-SHA-256-PLUS /// A list of supported SCRAM methods. pub const METHODS: &[&str] = &["SCRAM-SHA-256"]; /// Decode base64 into array without any heap allocations fn base64_decode_array(input: impl AsRef<[u8]>) -> Option<[u8; N]> { let mut bytes = [0u8; N]; let size = base64::decode_config_slice(input, base64::STANDARD, &mut bytes).ok()?; if size != N { return None; } Some(bytes) } /// This function essentially is `Hmac(sha256, key, input)`. /// Further reading: . fn hmac_sha256<'a>(key: &[u8], parts: impl IntoIterator) -> [u8; 32] { let mut mac = Hmac::::new_from_slice(key).expect("bad key size"); parts.into_iter().for_each(|s| mac.update(s)); // TODO: maybe newer `hmac` et al already migrated to regular arrays? let mut result = [0u8; 32]; result.copy_from_slice(mac.finalize().into_bytes().as_slice()); result } fn sha256<'a>(parts: impl IntoIterator) -> [u8; 32] { let mut hasher = Sha256::new(); parts.into_iter().for_each(|s| hasher.update(s)); let mut result = [0u8; 32]; result.copy_from_slice(hasher.finalize().as_slice()); result }