bip39/mnemonic_type.rs
1use crate::error::ErrorKind;
2use std::fmt;
3
4const ENTROPY_OFFSET: usize = 8;
5
6/// Determines the number of words that will be present in a [`Mnemonic`][Mnemonic] phrase
7///
8/// Also directly affects the amount of entropy that will be used to create a [`Mnemonic`][Mnemonic],
9/// and therefore the cryptographic strength of the HD wallet keys/addresses that can be derived from
10/// it using the [`Seed`][Seed].
11///
12/// For example, a 12 word mnemonic phrase is essentially a friendly representation of a 128-bit key,
13/// while a 24 word mnemonic phrase is essentially a 256-bit key.
14///
15/// If you know you want a specific phrase length, you can use the enum variant directly, for example
16/// `MnemonicType::Words12`.
17///
18/// You can also get a `MnemonicType` that corresponds to one of the standard BIP39 key sizes by
19/// passing arbitrary `usize` values:
20///
21/// ```
22/// use bip39::{MnemonicType};
23///
24/// let mnemonic_type = MnemonicType::for_key_size(128).unwrap();
25/// ```
26///
27/// [MnemonicType]: ../mnemonic_type/struct.MnemonicType.html
28/// [Mnemonic]: ../mnemonic/struct.Mnemonic.html
29/// [Seed]: ../seed/struct.Seed.html
30///
31#[derive(Debug, Copy, Clone, Default)]
32pub enum MnemonicType {
33 // ... = (entropy_bits << ...) | checksum_bits
34 #[default]
35 Words12 = (128 << ENTROPY_OFFSET) | 4,
36 Words15 = (160 << ENTROPY_OFFSET) | 5,
37 Words18 = (192 << ENTROPY_OFFSET) | 6,
38 Words21 = (224 << ENTROPY_OFFSET) | 7,
39 Words24 = (256 << ENTROPY_OFFSET) | 8,
40}
41
42impl MnemonicType {
43 /// Get a `MnemonicType` for a mnemonic phrase with a specific number of words
44 ///
45 /// Specifying a word count not provided for by the BIP39 standard will return an `Error`
46 /// of kind `ErrorKind::InvalidWordLength`.
47 ///
48 /// # Example
49 /// ```
50 /// use bip39::{MnemonicType};
51 ///
52 /// let mnemonic_type = MnemonicType::for_word_count(12).unwrap();
53 /// ```
54 pub fn for_word_count(size: usize) -> Result<MnemonicType, ErrorKind> {
55 let mnemonic_type = match size {
56 12 => MnemonicType::Words12,
57 15 => MnemonicType::Words15,
58 18 => MnemonicType::Words18,
59 21 => MnemonicType::Words21,
60 24 => MnemonicType::Words24,
61 _ => Err(ErrorKind::InvalidWordLength(size))?,
62 };
63
64 Ok(mnemonic_type)
65 }
66
67 /// Get a `MnemonicType` for a mnemonic phrase representing the given key size as bits
68 ///
69 /// Specifying a key size not provided for by the BIP39 standard will return an `Error`
70 /// of kind `ErrorKind::InvalidKeysize`.
71 ///
72 /// # Example
73 /// ```
74 /// use bip39::{MnemonicType};
75 ///
76 /// let mnemonic_type = MnemonicType::for_key_size(128).unwrap();
77 /// ```
78 pub fn for_key_size(size: usize) -> Result<MnemonicType, ErrorKind> {
79 let mnemonic_type = match size {
80 128 => MnemonicType::Words12,
81 160 => MnemonicType::Words15,
82 192 => MnemonicType::Words18,
83 224 => MnemonicType::Words21,
84 256 => MnemonicType::Words24,
85 _ => Err(ErrorKind::InvalidKeysize(size))?,
86 };
87
88 Ok(mnemonic_type)
89 }
90
91 /// Get a `MnemonicType` for an existing mnemonic phrase
92 ///
93 /// This can be used when you need information about a mnemonic phrase based on the number of
94 /// words, for example you can get the entropy value using [`MnemonicType::entropy_bits`][MnemonicType::entropy_bits()].
95 ///
96 /// Specifying a phrase that does not match one of the standard BIP39 phrase lengths will return
97 /// an `Error` of kind `ErrorKind::InvalidWordLength`. The phrase will not be validated in any
98 /// other way.
99 ///
100 /// # Example
101 /// ```
102 /// use bip39::{MnemonicType};
103 ///
104 /// let test_mnemonic = "park remain person kitchen mule spell knee armed position rail grid ankle";
105 ///
106 /// let mnemonic_type = MnemonicType::for_phrase(test_mnemonic).unwrap();
107 ///
108 /// let entropy_bits = mnemonic_type.entropy_bits();
109 /// ```
110 ///
111 /// [MnemonicType::entropy_bits()]: ./enum.MnemonicType.html#method.entropy_bits
112 pub fn for_phrase(phrase: &str) -> Result<MnemonicType, ErrorKind> {
113 let word_count = phrase.split(' ').count();
114
115 Self::for_word_count(word_count)
116 }
117
118 /// Return the number of entropy+checksum bits
119 ///
120 ///
121 /// # Example
122 /// ```
123 /// use bip39::{MnemonicType};
124 ///
125 /// let test_mnemonic = "park remain person kitchen mule spell knee armed position rail grid ankle";
126 ///
127 /// let mnemonic_type = MnemonicType::for_phrase(test_mnemonic).unwrap();
128 ///
129 /// let total_bits = mnemonic_type.total_bits();
130 /// ```
131 pub fn total_bits(&self) -> usize {
132 self.entropy_bits() + self.checksum_bits() as usize
133 }
134
135 /// Return the number of entropy bits
136 ///
137 ///
138 /// # Example
139 /// ```
140 /// use bip39::{MnemonicType};
141 ///
142 /// let test_mnemonic = "park remain person kitchen mule spell knee armed position rail grid ankle";
143 ///
144 /// let mnemonic_type = MnemonicType::for_phrase(test_mnemonic).unwrap();
145 ///
146 /// let entropy_bits = mnemonic_type.entropy_bits();
147 /// ```
148 pub fn entropy_bits(&self) -> usize {
149 (*self as usize) >> ENTROPY_OFFSET
150 }
151
152 /// Return the number of checksum bits
153 ///
154 ///
155 /// # Example
156 /// ```
157 /// use bip39::{MnemonicType};
158 ///
159 /// let test_mnemonic = "park remain person kitchen mule spell knee armed position rail grid ankle";
160 ///
161 /// let mnemonic_type = MnemonicType::for_phrase(test_mnemonic).unwrap();
162 ///
163 /// let checksum_bits = mnemonic_type.checksum_bits();
164 /// ```
165 pub fn checksum_bits(&self) -> u8 {
166 (*self as usize) as u8
167 }
168
169 /// Return the number of words
170 ///
171 ///
172 /// # Example
173 /// ```
174 /// use bip39::{MnemonicType};
175 ///
176 /// let mnemonic_type = MnemonicType::Words12;
177 ///
178 /// let word_count = mnemonic_type.word_count();
179 /// ```
180 pub fn word_count(&self) -> usize {
181 self.total_bits() / 11
182 }
183}
184
185impl fmt::Display for MnemonicType {
186 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
187 write!(
188 f,
189 "{} words ({}bits)",
190 self.word_count(),
191 self.entropy_bits()
192 )
193 }
194}
195
196#[cfg(test)]
197mod test {
198 use super::*;
199 #[cfg(target_arch = "wasm32")]
200 use wasm_bindgen_test::*;
201
202 #[cfg_attr(all(target_arch = "wasm32"), wasm_bindgen_test)]
203 #[cfg_attr(not(target_arch = "wasm32"), test)]
204 fn word_count() {
205 assert_eq!(MnemonicType::Words12.word_count(), 12);
206 assert_eq!(MnemonicType::Words15.word_count(), 15);
207 assert_eq!(MnemonicType::Words18.word_count(), 18);
208 assert_eq!(MnemonicType::Words21.word_count(), 21);
209 assert_eq!(MnemonicType::Words24.word_count(), 24);
210 }
211
212 #[cfg_attr(all(target_arch = "wasm32"), wasm_bindgen_test)]
213 #[cfg_attr(not(target_arch = "wasm32"), test)]
214 fn entropy_bits() {
215 assert_eq!(MnemonicType::Words12.entropy_bits(), 128);
216 assert_eq!(MnemonicType::Words15.entropy_bits(), 160);
217 assert_eq!(MnemonicType::Words18.entropy_bits(), 192);
218 assert_eq!(MnemonicType::Words21.entropy_bits(), 224);
219 assert_eq!(MnemonicType::Words24.entropy_bits(), 256);
220 }
221
222 #[cfg_attr(all(target_arch = "wasm32"), wasm_bindgen_test)]
223 #[cfg_attr(not(target_arch = "wasm32"), test)]
224 fn checksum_bits() {
225 assert_eq!(MnemonicType::Words12.checksum_bits(), 4);
226 assert_eq!(MnemonicType::Words15.checksum_bits(), 5);
227 assert_eq!(MnemonicType::Words18.checksum_bits(), 6);
228 assert_eq!(MnemonicType::Words21.checksum_bits(), 7);
229 assert_eq!(MnemonicType::Words24.checksum_bits(), 8);
230 }
231}