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}