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}