origin_crypto_sdk/seed/
gen.rs1use crate::internal::getrandom;
30use crate::internal::zeroize::Zeroize;
31use crate::primitives::sha3::{sha3_256, Shake256};
32use sha3::digest::{ExtendableOutput, Update, XofReader};
33
34#[derive(Debug, Clone, Copy, PartialEq, Eq)]
40pub enum SeedVariant {
41 Blake2bShake256,
43 Blake2bSha3_256,
45 Blake2bSha512,
47 Shake256Sha3_256,
49}
50
51#[derive(Debug, Clone)]
53pub struct GeneratedSeed {
54 pub seed: Vec<u8>,
56 pub salt: Vec<u8>,
58 pub variant: SeedVariant,
60}
61
62impl Drop for GeneratedSeed {
63 fn drop(&mut self) {
64 self.seed.zeroize();
65 }
66}
67
68pub fn generate(variant: SeedVariant) -> GeneratedSeed {
77 generate_with_entropy(variant, None)
78}
79
80pub fn generate_with_entropy(variant: SeedVariant, entropy: Option<&[u8]>) -> GeneratedSeed {
84 let key_bytes = entropy.map(|e| e.to_vec()).unwrap_or_else(|| {
86 let mut buf = vec![0u8; 32];
87 getrandom::fill(&mut buf).expect("OS RNG failed");
88 buf
89 });
90
91 let msg_bytes = if let Some(e) = entropy {
93 let mut derived = [0u8; 32];
95 derived.copy_from_slice(&sha3_256(e));
96 derived.to_vec()
97 } else {
98 let mut buf = vec![0u8; 32];
99 getrandom::fill(&mut buf).expect("OS RNG failed");
100 buf
101 };
102
103 let mut salt = [0u8; 16];
105 if let Some(e) = entropy {
106 let salt_input = [0xDEu8; 16]; let hash = sha3_256(&[e, &salt_input[..]].concat());
109 salt.copy_from_slice(&hash[..16]);
110 } else {
111 getrandom::fill(&mut salt).expect("OS RNG failed");
112 }
113
114 let seed = match variant {
115 SeedVariant::Blake2bShake256 => {
116 let blake_key: [u8; 32] = key_bytes.as_slice().try_into().unwrap_or([0u8; 32]);
118 let blake_hash = blake3::keyed_hash(&blake_key, &msg_bytes);
119
120 let mut hasher = Shake256::default();
122 hasher.update(blake_hash.as_bytes());
123 hasher.update(&salt);
124 let mut reader = hasher.finalize_xof();
125 let mut out = [0u8; 32];
126 reader.read(&mut out);
127 out.to_vec()
128 }
129
130 SeedVariant::Blake2bSha3_256 => {
131 let blake_key: [u8; 32] = key_bytes.as_slice().try_into().unwrap_or([0u8; 32]);
133 let blake_hash = blake3::keyed_hash(&blake_key, &msg_bytes);
134
135 let mut input = Vec::with_capacity(32 + 16);
137 input.extend_from_slice(blake_hash.as_bytes());
138 input.extend_from_slice(&salt);
139 sha3_256(&input).to_vec()
140 }
141
142 SeedVariant::Blake2bSha512 => {
143 let blake_key: [u8; 32] = key_bytes.as_slice().try_into().unwrap_or([0u8; 32]);
145 let blake_hash = blake3::keyed_hash(&blake_key, &msg_bytes);
146
147 use sha2::{Digest, Sha512};
149 let mut hasher = Sha512::new();
150 hasher.update(blake_hash.as_bytes());
151 hasher.update(&salt);
152 let hash = hasher.finalize();
153 hash[..32].to_vec()
154 }
155
156 SeedVariant::Shake256Sha3_256 => {
157 let mut hasher = Shake256::default();
159 hasher.update(&key_bytes);
160 hasher.update(&msg_bytes);
161 let mut reader = hasher.finalize_xof();
162 let mut shake_out = [0u8; 32];
163 reader.read(&mut shake_out);
164
165 let mut input = Vec::with_capacity(32 + 16);
167 input.extend_from_slice(&shake_out);
168 input.extend_from_slice(&salt);
169 sha3_256(&input).to_vec()
170 }
171 };
172
173 drop(key_bytes);
175 drop(msg_bytes);
176
177 GeneratedSeed {
178 seed,
179 salt: salt.to_vec(),
180 variant,
181 }
182}
183
184#[cfg(test)]
189mod tests {
190 use super::*;
191
192 #[test]
193 fn determinism() {
194 let entropy = [0xABu8; 64];
195 for &variant in &[
196 SeedVariant::Blake2bShake256,
197 SeedVariant::Blake2bSha3_256,
198 SeedVariant::Blake2bSha512,
199 SeedVariant::Shake256Sha3_256,
200 ] {
201 let a = generate_with_entropy(variant, Some(&entropy));
202 let b = generate_with_entropy(variant, Some(&entropy));
203 assert_eq!(a.seed, b.seed, "determinism failed for {variant:?}");
204 }
205 }
206
207 #[test]
208 fn all_variants_produce_32_bytes() {
209 for &variant in &[
210 SeedVariant::Blake2bShake256,
211 SeedVariant::Blake2bSha3_256,
212 SeedVariant::Blake2bSha512,
213 SeedVariant::Shake256Sha3_256,
214 ] {
215 let gen = generate(variant);
216 assert_eq!(gen.seed.len(), 32, "seed length for {variant:?}");
217 assert_eq!(gen.salt.len(), 16, "salt length for {variant:?}");
218 }
219 }
220
221 #[test]
222 fn different_variants_produce_different_seeds() {
223 let entropy = [0x42u8; 64];
224 let variants = [
225 SeedVariant::Blake2bShake256,
226 SeedVariant::Blake2bSha3_256,
227 SeedVariant::Blake2bSha512,
228 SeedVariant::Shake256Sha3_256,
229 ];
230 let seeds: Vec<Vec<u8>> = variants
231 .iter()
232 .map(|v| generate_with_entropy(*v, Some(&entropy)).seed.clone())
233 .collect();
234
235 for i in 0..seeds.len() {
236 for j in (i + 1)..seeds.len() {
237 assert_ne!(
238 seeds[i], seeds[j],
239 "variants {i} and {j} produced same seed"
240 );
241 }
242 }
243 }
244
245 #[test]
246 fn randomness() {
247 let a = generate(SeedVariant::Blake2bShake256);
249 let b = generate(SeedVariant::Blake2bShake256);
250 assert_ne!(a.seed, b.seed);
251 }
252}