use hmac::Hmac;
use pbkdf2::pbkdf2;
use sha2::Sha512;
extern crate unicode_normalization;
use unicode_normalization::UnicodeNormalization;
const SHA512_OUTPUT_LEN: usize = 512 / 8;
pub type MnemonicSeed = [u8; SHA512_OUTPUT_LEN];
#[derive(PartialEq, Eq, Debug, Clone)]
pub struct Mnemonic();
impl Mnemonic {
const PBKDF2_ITERATIONS: u32 = 2048;
pub fn to_seed(mnemonic_phrase: &str, mnemonic_pass: &str) -> MnemonicSeed {
let mut seed: MnemonicSeed = [0u8; SHA512_OUTPUT_LEN];
let normalized_phrase = mnemonic_phrase.nfkd().collect::<String>();
let normalized_pass = mnemonic_pass.nfkd().collect::<String>();
pbkdf2::<Hmac<Sha512>>(
normalized_phrase.as_bytes(),
format!("mnemonic{}", normalized_pass).as_bytes(),
Mnemonic::PBKDF2_ITERATIONS,
&mut seed,
);
seed
}
}
#[cfg(test)]
#[allow(clippy::unwrap_used)]
mod tests {
use super::*;
#[test]
fn test_mnemonic_to_seed() {
let mnemonic = "change me do not use me change me do not use me";
let seed = Mnemonic::to_seed(mnemonic, "");
let encoded_seed = base16::encode_lower(&seed);
let expected = "c5b2537b52b27b903b34c423783ced17c489e4385ec6d49d6a19a7f892ecd3917db36675de36bcbe3b8dbc6f803877f4155bdf83482ca5f0fc4282a61ac842a3";
assert_eq!(encoded_seed, expected);
}
#[test]
fn test_mnemonic_to_seed_with_pass() {
let mnemonic = "change me do not use me change me do not use me";
let seed = Mnemonic::to_seed(mnemonic, "password123");
let encoded_seed = base16::encode_lower(&seed);
let expected = "dfe3088b88e2eb8588482e8c56d9cde497c4e1f63fd29b480cbb0ed0227331d51301cfc2d461acce642868ecb618a37b4fd75d48dc6189674c55fbafd807d69c";
assert_eq!(encoded_seed, expected);
}
}