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}