1use super::bits;
2use super::index::*;
3use super::mnemonics::*;
4use cryptoxide::hashing::sha2::Sha256;
5
6#[cfg(not(feature = "std"))]
7use core::fmt;
8#[cfg(feature = "std")]
9use {std::error::Error, std::fmt};
10
11#[derive(PartialEq, Eq, PartialOrd, Ord, Clone)]
16pub struct Entropy<const N: usize>(pub [u8; N]);
17
18#[derive(Debug, Clone)]
20pub enum EntropyError {
21 InvalidParameters {
23 checksum_bits: usize,
25 total_bits: usize,
27 words: usize,
29 },
30 ChecksumInvalid,
32}
33
34impl fmt::Display for EntropyError {
35 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
36 match self {
37 Self::InvalidParameters {
38 checksum_bits,
39 total_bits,
40 words,
41 } => {
42 write!(
43 f,
44 "Invalid Parameters checksum-bits={}, total-bits={}, words={}",
45 checksum_bits, total_bits, words
46 )
47 }
48 Self::ChecksumInvalid => write!(f, "Invalid Checksum"),
49 }
50 }
51}
52
53#[cfg(feature = "std")]
54impl Error for EntropyError {}
55
56impl<const N: usize> Entropy<N> {
57 pub fn generate<G>(gen: G) -> Self
59 where
60 G: Fn() -> u8,
61 {
62 let mut bytes = [0u8; N];
63 for e in bytes.iter_mut() {
64 *e = gen();
65 }
66 Self(bytes)
67 }
68
69 fn full_checksum_data(&self) -> [u8; 32] {
70 Sha256::new().update(&self.0).finalize()
71 }
72
73 pub fn from_slice(slice: &[u8]) -> Option<Self> {
77 if slice.len() == N {
78 let mut out = [0u8; N];
79 out.copy_from_slice(slice);
80 Some(Self(out))
81 } else {
82 None
83 }
84 }
85
86 pub fn from_mnemonics<const W: usize, const CS: usize>(
108 mnemonics: &Mnemonics<W>,
109 ) -> Result<Self, EntropyError> {
110 assert!(CS <= 256);
111
112 let total_bits = N * 8 + CS;
113 if total_bits != Mnemonics::<W>::BITS {
114 return Err(EntropyError::InvalidParameters {
115 checksum_bits: CS,
116 total_bits,
117 words: W,
118 });
119 }
120 use bits::BitWriterBy11;
121
122 let mut entropy = [0u8; N];
123 let mut entropy_writer_pos = 0;
124 let mut checksum_data = [0u8; 256];
125
126 let emit = |b: u8| {
128 if entropy_writer_pos >= N {
129 checksum_data[entropy_writer_pos - N] = b;
130 } else {
131 entropy[entropy_writer_pos] = b;
132 }
133 entropy_writer_pos += 1;
134 };
135 let mut to_validate = BitWriterBy11::new(emit);
136 for mnemonic in mnemonics.indices() {
137 to_validate.write(mnemonic.0);
138 }
139 to_validate.finalize();
140
141 let ret = Self(entropy);
142
143 let expected_checksum = ret.full_checksum_data();
146 if CS > 0 {
147 let checksum_data = &checksum_data[0..(entropy_writer_pos - N)];
148 let mut rem = CS;
149 let mut ofs = 0;
150 while rem > 0 {
151 if rem >= 8 {
152 if checksum_data[ofs] != expected_checksum[ofs] {
153 return Err(EntropyError::ChecksumInvalid);
154 }
155 rem -= 8;
156 } else {
157 let mask = ((1 << rem) - 1) << (8 - rem);
159 if (checksum_data[ofs] & mask) != (expected_checksum[ofs] & mask) {
160 return Err(EntropyError::ChecksumInvalid);
161 }
162 rem = 0;
163 }
164 ofs += 1;
165 }
166 }
167
168 Ok(ret)
169 }
170
171 pub fn to_mnemonics<const W: usize, const CS: usize>(
188 &self,
189 ) -> Result<Mnemonics<W>, EntropyError> {
190 assert!(CS <= 256);
191 let total_bits = N * 8 + CS;
192 if total_bits != Mnemonics::<W>::BITS {
193 return Err(EntropyError::InvalidParameters {
194 checksum_bits: CS,
195 total_bits,
196 words: W,
197 });
198 }
199 use bits::{NextRead, ReadState};
200
201 let checksum = self.full_checksum_data();
202
203 let mut state = ReadState::default();
204 let mut read_pos = 0;
205 let mut write_pos = 0;
206
207 let mut words = [MnemonicIndex(0); W];
208 while write_pos < W {
209 let next_byte = if read_pos >= N {
210 checksum[read_pos - N]
211 } else {
212 self.0[read_pos]
213 };
214 read_pos += 1;
215 match state.read8(next_byte) {
216 NextRead::Zero(next_state) => {
217 state = next_state;
218 }
219 NextRead::One(n, next_state) => {
220 words[write_pos] = MnemonicIndex::new(n).unwrap();
221 write_pos += 1;
222 state = next_state;
223 }
224 }
225 }
226
227 Ok(Mnemonics::<W>::from(words))
228 }
229}
230
231impl<const N: usize> AsRef<[u8]> for Entropy<N> {
232 fn as_ref(&self) -> &[u8] {
233 &self.0
234 }
235}