1use kdk_zeroize::prelude::*;
2use sha2::{Digest, Sha256};
3
4use crate::error::EntropyError;
5use crate::utils::check_length;
6use crate::CoinEntropy;
7
8pub const fn min_flips<const N: usize>() -> Result<usize, EntropyError> {
11 match N {
12 16 => Ok(128),
13 20 => Ok(160),
14 24 => Ok(192),
15 28 => Ok(224),
16 32 => Ok(256),
17 _ => Err(EntropyError::UnsupportedConfig(2, N)),
18 }
19}
20
21pub const fn max_flips<const N: usize>() -> Result<usize, EntropyError> {
23 match min_flips::<N>() {
24 Ok(m) => Ok(m * 2),
25 Err(e) => Err(e),
26 }
27}
28
29pub fn coin_to_entropy<const N: usize>(flips: &[u8]) -> Result<CoinEntropy<N>, EntropyError> {
53 let required = min_flips::<N>()?;
54 let allowed = max_flips::<N>()?;
55 check_length(flips.len(), required, allowed)?;
56
57 for (i, &flip) in flips.iter().enumerate() {
58 if flip > 1 {
59 return Err(EntropyError::RollOutOfRange(flip, i));
60 }
61 }
62
63 let mut hasher = Sha256::new();
64 for &flip in flips {
65 hasher.update([b'0' + flip]);
66 }
67 let digest = hasher.finalize();
68
69 let mut acc: CoinEntropy<N> = CoinEntropy::new([0u8; N]);
70 acc.expose_secret_mut().copy_from_slice(&digest[..N]);
71 Ok(acc)
72}