ergo_lib/wallet/
mnemonic.rs

1//! Mnemonic operations according to BIP32/BIP39
2
3use hmac::Hmac;
4use pbkdf2::pbkdf2;
5use sha2::Sha512;
6extern crate unicode_normalization;
7use unicode_normalization::UnicodeNormalization;
8
9/// Length of mnemonic seed in bytes
10const SHA512_OUTPUT_LEN: usize = 512 / 8;
11
12/// Mnemonic seed
13pub type MnemonicSeed = [u8; SHA512_OUTPUT_LEN];
14
15/// Mnemonic type
16#[derive(PartialEq, Eq, Debug, Clone)]
17pub struct Mnemonic();
18
19impl Mnemonic {
20    const PBKDF2_ITERATIONS: u32 = 2048;
21
22    /// Convert a mnemonic phrase into a mnemonic seed
23    /// mnemonic_pass is optional and is used to salt the seed
24    pub fn to_seed(mnemonic_phrase: &str, mnemonic_pass: &str) -> MnemonicSeed {
25        let mut seed: MnemonicSeed = [0u8; SHA512_OUTPUT_LEN];
26        let normalized_phrase = mnemonic_phrase.nfkd().collect::<String>();
27        let normalized_pass = mnemonic_pass.nfkd().collect::<String>();
28        pbkdf2::<Hmac<Sha512>>(
29            normalized_phrase.as_bytes(),
30            format!("mnemonic{}", normalized_pass).as_bytes(),
31            Mnemonic::PBKDF2_ITERATIONS,
32            &mut seed,
33        );
34
35        seed
36    }
37}
38
39#[cfg(test)]
40#[allow(clippy::unwrap_used)]
41mod tests {
42    use super::*;
43
44    #[test]
45    fn test_mnemonic_to_seed() {
46        let mnemonic = "change me do not use me change me do not use me";
47        let seed = Mnemonic::to_seed(mnemonic, "");
48        let encoded_seed = base16::encode_lower(&seed);
49        let expected = "c5b2537b52b27b903b34c423783ced17c489e4385ec6d49d6a19a7f892ecd3917db36675de36bcbe3b8dbc6f803877f4155bdf83482ca5f0fc4282a61ac842a3";
50
51        assert_eq!(encoded_seed, expected);
52    }
53
54    #[test]
55    fn test_mnemonic_to_seed_with_pass() {
56        let mnemonic = "change me do not use me change me do not use me";
57        let seed = Mnemonic::to_seed(mnemonic, "password123");
58        let encoded_seed = base16::encode_lower(&seed);
59        let expected = "dfe3088b88e2eb8588482e8c56d9cde497c4e1f63fd29b480cbb0ed0227331d51301cfc2d461acce642868ecb618a37b4fd75d48dc6189674c55fbafd807d69c";
60
61        assert_eq!(encoded_seed, expected);
62    }
63}