pub use crate::errors::Bip39Error;
use bip39::{Language, Mnemonic, MnemonicType};
#[cfg(feature = "zeroize")]
use zeroize::{Zeroize, ZeroizeOnDrop};
pub type Bip39Result<T> = std::result::Result<T, Bip39Error>;
#[derive(Clone, Copy, Debug)]
pub struct Bip39Config {
pub language: Language,
pub word_count: usize,
}
impl Default for Bip39Config {
fn default() -> Self {
Self {
language: Language::English,
word_count: 12,
}
}
}
impl Bip39Config {
pub fn validate(&self) -> Bip39Result<()> {
validate_word_count(self.word_count).map(|_| ())
}
}
fn validate_word_count(word_count: usize) -> Bip39Result<MnemonicType> {
match word_count {
12 => Ok(MnemonicType::Words12),
24 => Ok(MnemonicType::Words24),
_ => Err(Bip39Error::InvalidWordCount(word_count)),
}
}
#[derive(Clone, Debug)]
pub struct NormalizedMnemonic {
inner: Mnemonic,
}
impl NormalizedMnemonic {
pub fn phrase(&self) -> String {
self.inner.to_string()
}
pub fn language(&self) -> Language {
self.inner.language()
}
pub fn as_mnemonic(&self) -> &Mnemonic {
&self.inner
}
}
pub fn parse_mnemonic(phrase: &str) -> Bip39Result<NormalizedMnemonic> {
let mnemonic = Mnemonic::from_phrase(phrase, Language::English)
.map_err(|e| Bip39Error::Mnemonic(e.to_string()))?;
let word_count = phrase.split_whitespace().count();
validate_word_count(word_count)?;
Ok(NormalizedMnemonic { inner: mnemonic })
}
pub fn generate_mnemonic_with(config: Bip39Config) -> Bip39Result<NormalizedMnemonic> {
let m_type = validate_word_count(config.word_count)?;
let mnemonic = Mnemonic::new(m_type, config.language);
Ok(NormalizedMnemonic { inner: mnemonic })
}
pub fn generate_mnemonic() -> Bip39Result<String> {
Ok(generate_mnemonic_with(Bip39Config::default())?.phrase())
}
pub fn derive_seed(phrase: &str, passphrase: &str) -> Bip39Result<Seed> {
let mnemonic = parse_mnemonic(phrase)?;
Ok(derive_seed_from_mnemonic(&mnemonic, passphrase))
}
pub fn derive_seed_from_mnemonic(mnemonic: &NormalizedMnemonic, passphrase: &str) -> Seed {
let b_seed = bip39::Seed::new(&mnemonic.inner, passphrase);
let mut bytes = [0u8; 64];
bytes.copy_from_slice(b_seed.as_bytes());
Seed::new(bytes)
}
pub fn validate_mnemonic(phrase: &str) -> Bip39Result<()> {
parse_mnemonic(phrase).map(|_| ())
}
#[cfg_attr(feature = "zeroize", derive(Zeroize, ZeroizeOnDrop))]
pub struct Seed([u8; 64]);
impl Seed {
pub fn new(bytes: [u8; 64]) -> Self {
Self(bytes)
}
pub fn as_bytes(&self) -> &[u8] {
&self.0
}
pub fn into_inner(self) -> [u8; 64] {
self.0
}
}
impl AsRef<[u8]> for Seed {
fn as_ref(&self) -> &[u8] {
self.as_bytes()
}
}
impl core::fmt::Debug for Seed {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.write_str("Seed([REDACTED])")
}
}