dup_crypto/mnemonic/
mnemonic_type.rs

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