use bitreader::BitReader;
use bit_vec::BitVec;
use data_encoding::hex;
use ::crypto::{gen_random_bytes, sha256};
use ::error::{Error, ErrorKind};
use ::mnemonic_type::MnemonicType;
use ::language::Language;
use ::util::bit_from_u16_as_u11;
use ::seed::Seed;
#[derive(Debug)]
pub struct Mnemonic {
string: String,
seed: Seed,
lang: Language,
entropy: Vec<u8>,
}
impl Mnemonic {
pub fn new<S>(mnemonic_type: MnemonicType,
lang: Language,
password: S) -> Result<Mnemonic, Error> where S: Into<String> {
let entropy_bits = mnemonic_type.entropy_bits();
let num_words = mnemonic_type.word_count();
let word_list = lang.get_wordlist();
let entropy = gen_random_bytes(entropy_bits / 8)?;
let entropy_hash = sha256(entropy.as_ref());
let mut combined = Vec::from(entropy);
combined.extend(&entropy_hash);
let mut reader = BitReader::new(combined.as_ref());
let mut words: Vec<&str> = Vec::new();
for _ in 0..num_words {
let n = reader.read_u16(11);
words.push(word_list[n.unwrap() as usize].as_ref());
}
let string = words.join(" ");
Mnemonic::from_string(string, lang, password.into())
}
pub fn from_string<S>(string: S,
lang: Language,
password: S) -> Result<Mnemonic, Error> where S: Into<String> {
let m = string.into();
let p = password.into();
let entropy = Mnemonic::entropy(&*m, lang)?;
let seed = Seed::generate(&m.as_bytes(), &p);
let mnemonic = Mnemonic {
string: (&m).clone(),
seed: seed,
lang: lang,
entropy: entropy
};
Ok(mnemonic)
}
pub fn validate<S>(string: S,
lang: Language) -> Result<(), Error> where S: Into<String> {
Mnemonic::entropy(string, lang).and(Ok(()))
}
fn entropy<S>(string: S,
lang: Language) -> Result<Vec<u8>, Error> where S: Into<String> {
let m = string.into();
let mnemonic_type = MnemonicType::for_phrase(&*m)?;
let entropy_bits = mnemonic_type.entropy_bits();
let checksum_bits = mnemonic_type.checksum_bits();
let word_map = lang.get_wordmap();
let mut to_validate: BitVec = BitVec::new();
for word in m.split(" ").into_iter() {
let n = match word_map.get(word) {
Some(n) => n,
None => return Err(ErrorKind::InvalidWord.into())
};
for i in 0..11 {
let bit = bit_from_u16_as_u11(*n, i);
to_validate.push(bit);
}
}
let mut checksum_to_validate = BitVec::new();
&checksum_to_validate.extend((&to_validate).into_iter().skip(entropy_bits).take(checksum_bits));
assert!(checksum_to_validate.len() == checksum_bits, "invalid checksum size");
let mut entropy_to_validate = BitVec::new();
&entropy_to_validate.extend((&to_validate).into_iter().take(entropy_bits));
assert!(entropy_to_validate.len() == entropy_bits, "invalid entropy size");
let entropy = entropy_to_validate.to_bytes();
let hash = sha256(entropy.as_ref());
let entropy_hash_to_validate_bits = BitVec::from_bytes(hash.as_ref());
let mut new_checksum = BitVec::new();
&new_checksum.extend(entropy_hash_to_validate_bits.into_iter().take(checksum_bits));
assert!(new_checksum.len() == checksum_bits, "invalid new checksum size");
if !(new_checksum == checksum_to_validate) {
return Err(ErrorKind::InvalidChecksum.into())
}
Ok(entropy)
}
pub fn get_entropy(&self) -> Vec<u8> {
self.entropy.clone()
}
pub fn as_str(&self) -> &str {
self.string.as_ref()
}
pub fn get_string(&self) -> String {
self.string.clone()
}
pub fn get_language(&self) -> Language {
self.lang
}
pub fn as_seed(&self) -> &Seed {
&self.seed
}
pub fn get_seed(&self) -> Seed {
self.seed.to_owned()
}
pub fn get_entropy_hex(&self) -> String {
let hex = hex::encode(self.as_entropy());
hex
}
pub fn as_entropy(&self) -> &[u8] {
self.entropy.as_ref()
}
}
impl AsRef<str> for Mnemonic {
fn as_ref(&self) -> &str {
self.as_str()
}
}