use serde::Serialize;
use sha2::{Digest, Sha256};
#[allow(
clippy::expect_used,
reason = "Lonca types have string keys and no floats, so serde_json can not fail on them"
)]
pub fn canonical_bytes<T: Serialize + ?Sized>(value: &T) -> Vec<u8> {
serde_json::to_vec(value).expect("lonca types always serialize")
}
pub fn sha256_hex(bytes: &[u8]) -> String {
hex::encode(Sha256::digest(bytes))
}
pub fn digest_of<T: Serialize + ?Sized>(value: &T) -> String {
sha256_hex(&canonical_bytes(value))
}
#[allow(
clippy::expect_used,
reason = "without the OS random source no token or id can be made safely: stop"
)]
pub fn random_bytes<const N: usize>() -> [u8; N] {
let mut buf = [0u8; N];
getrandom::fill(&mut buf).expect("operating system random source");
buf
}
pub fn random_id(prefix: &str) -> String {
format!("{prefix}_{}", hex::encode(random_bytes::<8>()))
}
pub fn nonce() -> String {
hex::encode(random_bytes::<16>())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn nonces_are_fresh_and_sized_for_signatures() {
let a = nonce();
let b = nonce();
assert_eq!(a.len(), 32);
assert!(a.bytes().all(|c| c.is_ascii_hexdigit()));
assert_ne!(a, b);
}
use std::collections::BTreeMap;
#[test]
fn map_order_does_not_depend_on_insertion_order() {
let mut a = BTreeMap::new();
a.insert("b", 1);
a.insert("a", 2);
let mut b = BTreeMap::new();
b.insert("a", 2);
b.insert("b", 1);
assert_eq!(canonical_bytes(&a), canonical_bytes(&b));
assert_eq!(digest_of(&a), digest_of(&b));
}
#[test]
fn sha256_known_vector() {
assert_eq!(
sha256_hex(b"abc"),
"ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad"
);
}
}