bip39/mnemonic.rs
1use util::{checksum, IterExt, BitWriter};
2use crypto::{gen_random_bytes, sha256_first_byte};
3use error::ErrorKind;
4use failure::Error;
5use mnemonic_type::MnemonicType;
6use language::Language;
7use std::fmt;
8
9/// The primary type in this crate, most tasks require creating or using one.
10///
11/// To create a *new* [`Mnemonic`][Mnemonic] from a randomly generated key, call [`Mnemonic::new()`][Mnemonic::new()].
12///
13/// To get a [`Mnemonic`][Mnemonic] instance for an existing mnemonic phrase, including
14/// those generated by other software or hardware wallets, use [`Mnemonic::from_phrase()`][Mnemonic::from_phrase()].
15///
16/// You can get the HD wallet [`Seed`][Seed] from a [`Mnemonic`][Mnemonic] by calling [`Seed::new()`][Seed::new()].
17/// From there you can either get the raw byte value with [`Seed::as_bytes()`][Seed::as_bytes()], or the hex
18/// representation using Rust formatting: `format!("{:X}", seed)`.
19///
20/// You can also get the original entropy value back from a [`Mnemonic`][Mnemonic] with [`Mnemonic::entropy()`][Mnemonic::entropy()],
21/// but beware that the entropy value is **not the same thing** as an HD wallet seed, and should
22/// *never* be used that way.
23///
24/// [Mnemonic]: ./mnemonic/struct.Mnemonic.html
25/// [Mnemonic::new()]: ./mnemonic/struct.Mnemonic.html#method.new
26/// [Mnemonic::from_phrase()]: ./mnemonic/struct.Mnemonic.html#method.from_phrase
27/// [Mnemonic::entropy()]: ./mnemonic/struct.Mnemonic.html#method.entropy
28/// [Seed]: ./seed/struct.Seed.html
29/// [Seed::new()]: ./seed/struct.Seed.html#method.new
30/// [Seed::as_bytes()]: ./seed/struct.Seed.html#method.as_bytes
31///
32#[derive(Clone)]
33pub struct Mnemonic {
34 phrase: String,
35 lang: Language,
36 entropy: Vec<u8>,
37}
38
39impl Mnemonic {
40 /// Generates a new [`Mnemonic`][Mnemonic]
41 ///
42 /// Use [`Mnemonic::phrase()`][Mnemonic::phrase()] to get an `str` slice of the generated phrase.
43 ///
44 /// # Example
45 ///
46 /// ```
47 /// use bip39::{Mnemonic, MnemonicType, Language};
48 ///
49 /// let mnemonic = Mnemonic::new(MnemonicType::Words12, Language::English);
50 /// let phrase = mnemonic.phrase();
51 ///
52 /// println!("phrase: {}", phrase);
53 ///
54 /// assert_eq!(phrase.split(" ").count(), 12);
55 /// ```
56 ///
57 /// [Mnemonic]: ./mnemonic/struct.Mnemonic.html
58 /// [Mnemonic::phrase()]: ./mnemonic/struct.Mnemonic.html#method.phrase
59 pub fn new(mtype: MnemonicType, lang: Language) -> Mnemonic {
60 let entropy = gen_random_bytes(mtype.entropy_bits() / 8);
61
62 Mnemonic::from_entropy_unchecked(entropy, lang)
63 }
64
65 /// Create a [`Mnemonic`][Mnemonic] from pre-generated entropy
66 ///
67 /// # Example
68 ///
69 /// ```
70 /// use bip39::{Mnemonic, MnemonicType, Language};
71 ///
72 /// let entropy = &[0x33, 0xE4, 0x6B, 0xB1, 0x3A, 0x74, 0x6E, 0xA4, 0x1C, 0xDD, 0xE4, 0x5C, 0x90, 0x84, 0x6A, 0x79];
73 /// let mnemonic = Mnemonic::from_entropy(entropy, Language::English).unwrap();
74 ///
75 /// assert_eq!("crop cash unable insane eight faith inflict route frame loud box vibrant", mnemonic.phrase());
76 /// assert_eq!("33E46BB13A746EA41CDDE45C90846A79", format!("{:X}", mnemonic));
77 /// ```
78 ///
79 /// [Mnemonic]: ../mnemonic/struct.Mnemonic.html
80 pub fn from_entropy(entropy: &[u8], lang: Language) -> Result<Mnemonic, Error> {
81 // Validate entropy size
82 MnemonicType::for_key_size(entropy.len() * 8)?;
83
84 Ok(Self::from_entropy_unchecked(entropy, lang))
85 }
86
87 fn from_entropy_unchecked<E>(entropy: E, lang: Language) -> Mnemonic
88 where
89 E: Into<Vec<u8>>
90 {
91 let entropy = entropy.into();
92 let wordlist = lang.wordlist();
93
94 let checksum_byte = sha256_first_byte(&entropy);
95
96 // First, create a byte iterator for the given entropy and the first byte of the
97 // hash of the entropy that will serve as the checksum (up to 8 bits for biggest
98 // entropy source).
99 //
100 // Then we transform that into a bits iterator that returns 11 bits at a
101 // time (as u16), which we can map to the words on the `wordlist`.
102 //
103 // Given the entropy is of correct size, this ought to give us the correct word
104 // count.
105 let phrase = entropy.iter()
106 .chain(Some(&checksum_byte))
107 .bits()
108 .map(|bits| wordlist.get_word(bits))
109 .join(" ");
110
111 Mnemonic {
112 phrase,
113 lang,
114 entropy
115 }
116 }
117
118 /// Create a [`Mnemonic`][Mnemonic] from an existing mnemonic phrase
119 ///
120 /// The phrase supplied will be checked for word length and validated according to the checksum
121 /// specified in BIP0039
122 ///
123 /// # Example
124 ///
125 /// ```
126 /// use bip39::{Mnemonic, Language};
127 ///
128 /// let phrase = "park remain person kitchen mule spell knee armed position rail grid ankle";
129 /// let mnemonic = Mnemonic::from_phrase(phrase, Language::English).unwrap();
130 ///
131 /// assert_eq!(phrase, mnemonic.phrase());
132 /// ```
133 ///
134 /// [Mnemonic]: ../mnemonic/struct.Mnemonic.html
135 pub fn from_phrase<S>(phrase: S, lang: Language) -> Result<Mnemonic, Error>
136 where
137 S: Into<String>,
138 {
139 let phrase = phrase.into();
140
141 // this also validates the checksum and phrase length before returning the entropy so we
142 // can store it. We don't use the validate function here to avoid having a public API that
143 // takes a phrase string and returns the entropy directly.
144 let entropy = Mnemonic::phrase_to_entropy(&phrase, lang)?;
145
146 let mnemonic = Mnemonic {
147 phrase,
148 lang,
149 entropy,
150 };
151
152 Ok(mnemonic)
153 }
154
155 /// Validate a mnemonic phrase
156 ///
157 /// The phrase supplied will be checked for word length and validated according to the checksum
158 /// specified in BIP0039.
159 ///
160 /// # Example
161 ///
162 /// ```
163 /// use bip39::{Mnemonic, Language};
164 ///
165 /// let test_mnemonic = "park remain person kitchen mule spell knee armed position rail grid ankle";
166 ///
167 /// assert!(Mnemonic::validate(test_mnemonic, Language::English).is_ok());
168 /// ```
169 pub fn validate(phrase: &str, lang: Language) -> Result<(), Error> {
170 Mnemonic::phrase_to_entropy(phrase, lang)?;
171
172 Ok(())
173 }
174
175 /// Calculate the checksum, verify it and return the entropy
176 ///
177 /// Only intended for internal use, as returning a `Vec<u8>` that looks a bit like it could be
178 /// used as the seed is likely to cause problems for someone eventually. All the other functions
179 /// that return something like that are explicit about what it is and what to use it for.
180 fn phrase_to_entropy(phrase: &str, lang: Language) -> Result<Vec<u8>, Error> {
181 let wordmap = lang.wordmap();
182
183 // Preallocate enough space for the longest possible word list
184 let mut bits = BitWriter::with_capacity(264);
185
186 for word in phrase.split(" ") {
187 bits.push(wordmap.get_bits(&word)?);
188 }
189
190 let mtype = MnemonicType::for_word_count(bits.len() / 11)?;
191
192 debug_assert!(bits.len() == mtype.total_bits(), "Insufficient amount of bits to validate");
193
194 let mut entropy = bits.into_bytes();
195 let entropy_bytes = mtype.entropy_bits() / 8;
196
197 let actual_checksum = checksum(entropy[entropy_bytes], mtype.checksum_bits());
198
199 // Truncate to get rid of the byte containing the checksum
200 entropy.truncate(entropy_bytes);
201
202 let checksum_byte = sha256_first_byte(&entropy);
203 let expected_checksum = checksum(checksum_byte, mtype.checksum_bits());
204
205 if actual_checksum != expected_checksum {
206 Err(ErrorKind::InvalidChecksum)?;
207 }
208
209 Ok(entropy)
210 }
211
212 /// Get the mnemonic phrase as a string reference.
213 pub fn phrase(&self) -> &str {
214 &self.phrase
215 }
216
217 /// Consume the `Mnemonic` and return the phrase as a `String`.
218 ///
219 /// This operation doesn't perform any allocations.
220 pub fn into_phrase(self) -> String {
221 self.phrase
222 }
223
224 /// Get the original entropy value of the mnemonic phrase as a slice.
225 ///
226 /// # Example
227 ///
228 /// ```
229 /// use bip39::{Mnemonic, Language};
230 ///
231 /// let phrase = "park remain person kitchen mule spell knee armed position rail grid ankle";
232 ///
233 /// let mnemonic = Mnemonic::from_phrase(phrase, Language::English).unwrap();
234 ///
235 /// let entropy: &[u8] = mnemonic.entropy();
236 /// ```
237 ///
238 /// **Note:** You shouldn't use the generated entropy as secrets, for that generate a new
239 /// `Seed` from the `Mnemonic`.
240 pub fn entropy(&self) -> &[u8] {
241 &self.entropy
242 }
243
244 /// Get the [`Language`][Language]
245 ///
246 /// [Language]: ../language/struct.Language.html
247 pub fn language(&self) -> Language {
248 self.lang
249 }
250}
251
252impl AsRef<str> for Mnemonic {
253 fn as_ref(&self) -> &str {
254 self.phrase()
255 }
256}
257
258impl fmt::Display for Mnemonic {
259 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
260 fmt::Display::fmt(self.phrase(), f)
261 }
262}
263
264impl fmt::Debug for Mnemonic {
265 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
266 fmt::Debug::fmt(self.phrase(), f)
267 }
268}
269
270impl fmt::LowerHex for Mnemonic {
271 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
272 if f.alternate() {
273 f.write_str("0x")?;
274 }
275
276 for byte in self.entropy() {
277 write!(f, "{:x}", byte)?;
278 }
279
280 Ok(())
281 }
282}
283
284impl fmt::UpperHex for Mnemonic {
285 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
286 if f.alternate() {
287 f.write_str("0x")?;
288 }
289
290 for byte in self.entropy() {
291 write!(f, "{:X}", byte)?;
292 }
293
294 Ok(())
295 }
296}
297
298impl From<Mnemonic> for String {
299 fn from(val: Mnemonic) -> String {
300 val.into_phrase()
301 }
302}
303
304#[cfg(test)]
305mod test {
306 use super::*;
307
308 #[test]
309 fn back_to_back() {
310 let m1 = Mnemonic::new(MnemonicType::Words12, Language::English);
311 let m2 = Mnemonic::from_phrase(m1.phrase(), Language::English).unwrap();
312 let m3 = Mnemonic::from_entropy(m1.entropy(), Language::English).unwrap();
313
314 assert_eq!(m1.entropy(), m2.entropy(), "Entropy must be the same");
315 assert_eq!(m1.entropy(), m3.entropy(), "Entropy must be the same");
316 assert_eq!(m1.phrase(), m2.phrase(), "Phrase must be the same");
317 assert_eq!(m1.phrase(), m3.phrase(), "Phrase must be the same");
318 }
319
320 #[test]
321 fn mnemonic_from_entropy() {
322 let entropy = &[0x33, 0xE4, 0x6B, 0xB1, 0x3A, 0x74, 0x6E, 0xA4, 0x1C, 0xDD, 0xE4, 0x5C, 0x90, 0x84, 0x6A, 0x79];
323 let phrase = "crop cash unable insane eight faith inflict route frame loud box vibrant";
324
325 let mnemonic = Mnemonic::from_entropy(entropy, Language::English).unwrap();
326
327 assert_eq!(phrase, mnemonic.phrase());
328 }
329
330 #[test]
331 fn mnemonic_from_phrase() {
332 let entropy = &[0x33, 0xE4, 0x6B, 0xB1, 0x3A, 0x74, 0x6E, 0xA4, 0x1C, 0xDD, 0xE4, 0x5C, 0x90, 0x84, 0x6A, 0x79];
333 let phrase = "crop cash unable insane eight faith inflict route frame loud box vibrant";
334
335 let mnemonic = Mnemonic::from_phrase(phrase, Language::English).unwrap();
336
337 assert_eq!(entropy, mnemonic.entropy());
338 }
339
340 #[test]
341 fn mnemonic_format() {
342 let mnemonic = Mnemonic::new(MnemonicType::Words15, Language::English);
343
344 assert_eq!(mnemonic.phrase(), format!("{}", mnemonic));
345 }
346
347 #[test]
348 fn mnemonic_hex_format() {
349 let entropy = &[0x33, 0xE4, 0x6B, 0xB1, 0x3A, 0x74, 0x6E, 0xA4, 0x1C, 0xDD, 0xE4, 0x5C, 0x90, 0x84, 0x6A, 0x79];
350
351 let mnemonic = Mnemonic::from_entropy(entropy, Language::English).unwrap();
352
353 assert_eq!(format!("{:x}", mnemonic), "33e46bb13a746ea41cdde45c90846a79");
354 assert_eq!(format!("{:X}", mnemonic), "33E46BB13A746EA41CDDE45C90846A79");
355 assert_eq!(format!("{:#x}", mnemonic), "0x33e46bb13a746ea41cdde45c90846a79");
356 assert_eq!(format!("{:#X}", mnemonic), "0x33E46BB13A746EA41CDDE45C90846A79");
357 }
358}