1use rand::rngs::OsRng;
2use rand::RngCore;
3use sha256::digest;
4
5mod language;
6mod types;
7mod utils;
8
9pub use language::Language;
10pub use crate::types::MnemonicType;
11
12const MIN_WORDS: usize = 12;
13const MAX_WORDS: usize = 24;
14const DEFAULT_MNEMONIC_TYPE: MnemonicType = MnemonicType::Bits256; #[derive(Debug)]
17pub enum MnemonicError {
18 InvalidChecksum,
19 InvalidEntropy,
20 GeneratorError,
21}
22
23impl std::fmt::Display for MnemonicError {
24 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
25 match self {
26 MnemonicError::InvalidChecksum => write!(f, "Invalid checksum."),
27 MnemonicError::InvalidEntropy => write!(f, "Invalid entropy."),
28 MnemonicError::GeneratorError => write!(f, "Error when creating Mnemonic instance!")
29 }
30 }
31}
32
33pub struct EntropyInfo {
34 pub bytes: usize,
35 pub bits: usize,
36}
37
38pub struct Mnemonic {
39 lang: Language,
40 mnemonic_type: MnemonicType,
41 entropy: Vec<u8>,
42 checksum: u8,
43 mnemonic_phrase: Vec<String>,
44}
45
46
47impl Mnemonic {
48
49 pub fn new(lang: Language, mnemonic_type: MnemonicType) -> Mnemonic {
51 match Self::generator(lang, mnemonic_type) {
52 Ok(mut mnemonic) => {
53 mnemonic.mnemonic_phrase_generation();
54 mnemonic
55 }
56 Err(e) => {
57 eprintln!("Error creating mnemonic: {}, using default fallback", e);
58 Mnemonic::default()
60 }
61 }
62 }
63
64 fn generator(lang: Language, mnemonic_type: MnemonicType) -> Result<Mnemonic, MnemonicError> {
66 let (mut raw_entropy, checksum_decimal) = utils::prepare_data_for_mnemonic_struct_initialization(mnemonic_type); raw_entropy.push(checksum_decimal);
71
72 Ok(Mnemonic {
73 lang,
74 mnemonic_type,
75 entropy: raw_entropy, checksum: checksum_decimal,
77 mnemonic_phrase: Vec::new(),
78 })
79 }
80
81 fn default() -> Mnemonic {
83 let (mut raw_entropy, checksum_decimal) = utils::prepare_data_for_mnemonic_struct_initialization(DEFAULT_MNEMONIC_TYPE);
84 raw_entropy.push(checksum_decimal);
85
86 let mut mnemonic = Mnemonic {
87 lang: Language::English,
88 mnemonic_type: DEFAULT_MNEMONIC_TYPE,
89 entropy: raw_entropy,
90 checksum: checksum_decimal,
91 mnemonic_phrase: Vec::new(),
92 };
93
94 mnemonic.mnemonic_phrase_generation();
95 mnemonic
96 }
97
98 pub fn validate_checksum(&self) -> Result<bool, MnemonicError> {
99 let binary_entropy = self.convert_entropy_to_binary();
105 let checksum_bits = self.mnemonic_type.bits() / 32;
106
107 if binary_entropy.len() < checksum_bits {
108 return Err(MnemonicError::InvalidChecksum);
109 }
110
111 let checksum_binary = &binary_entropy[binary_entropy.len() - checksum_bits..];
112 let checksum_decimal = u8::from_str_radix(&checksum_binary, 2)
113 .map_err(|_| MnemonicError::InvalidChecksum)?;
114
115 Ok(checksum_decimal == self.checksum)
116 }
117
118 pub fn mnemonic_phrase(&self) -> &Vec<String> {
120 &self.mnemonic_phrase
121 }
122
123 fn generate_entropy(mnemonic_type: MnemonicType) -> Vec<u8> {
125 let mut rng = OsRng {};
126 let entropy_bytes_count = mnemonic_type.bytes();
127
128 let mut entropy = vec![0u8; entropy_bytes_count]; rng.fill_bytes(&mut entropy); entropy
133 }
134
135 fn generate_checksum(entropy: &Vec<u8>, mnemonic_type: MnemonicType) -> u8 {
136 let hash = digest(entropy); if hash.len() < 2 {
139 panic!("Hash must be at least 2 characters.");
140 }
141
142 let checksum_bits = mnemonic_type.bits() / 32;
143 let checksum_index = if checksum_bits == 4 {1} else if checksum_bits == 8 {2} else {0}; let checksum = &hash[..checksum_index]; u8::from_str_radix(&checksum, 16).expect("Failed to parse checksum as u8") }
148
149 fn convert_entropy_to_binary(&self) -> String {
150 let mut binary_entropy = String::new();
152
153 for el in &self.entropy {
154 let binary_repr = format!("{:08b}", el);
156 binary_entropy += &binary_repr
157 }
158 binary_entropy }
160
161 fn mnemonic_phrase_generation(&mut self) {
162 let binary_entropy = self.convert_entropy_to_binary(); let mut start_idx = 0;
166 let mut chunks = Vec::new(); while start_idx + 11 <= binary_entropy.len() {
170 chunks.push(binary_entropy.get(start_idx..start_idx + 11).unwrap());
172 start_idx += 11; }
174
175 let wordlist = Language::get_predefined_word_list(&self.lang); for chunk in chunks {
178 let decimal = usize::from_str_radix(chunk, 2).unwrap(); let phrase = wordlist[decimal];
182 self.add_mnemonic_phrase(String::from(phrase));
183 }
184 }
185
186 fn add_mnemonic_phrase(&mut self, word: String) {
187 self.mnemonic_phrase.push(word);
189 }
190}