mod error;
mod language;
pub use self::error::Error;
pub use self::language::{Language, BIP39_ENGLISH_WORDLIST};
pub use crate::hdwallet::bip32::{generate_key, HDPath};
use crate::keystore::{Kdf, Prf};
use num::bigint::BigUint;
use num::{FromPrimitive, ToPrimitive};
use rand::{OsRng, Rng};
use sha2::{self, Digest};
use std::ops::{BitAnd, BitOr, Shr, Shl};
use crate::mnemonic::error::Error::MnemonicError;
const PBKDF2_ROUNDS: usize = 2048;
const INDEX_BIT_SIZE: usize = 11;
#[derive(Debug, Clone)]
pub struct Mnemonic {
language: Language,
words: Vec<String>,
}
#[derive(Debug, Clone, PartialEq, Copy)]
pub struct MnemonicSize {
bits_length: usize,
checksum_length: usize
}
pub struct StandardMnemonic {}
impl StandardMnemonic {
pub fn secure() -> MnemonicSize {
StandardMnemonic::size24()
}
pub fn simple() -> MnemonicSize {
StandardMnemonic::size15()
}
pub fn size12() -> MnemonicSize {
MnemonicSize::from_length(12).unwrap()
}
pub fn size15() -> MnemonicSize {
MnemonicSize::from_length(15).unwrap()
}
pub fn size18() -> MnemonicSize {
MnemonicSize::from_length(18).unwrap()
}
pub fn size21() -> MnemonicSize {
MnemonicSize::from_length(21).unwrap()
}
pub fn size24() -> MnemonicSize {
MnemonicSize::from_length(24).unwrap()
}
}
impl MnemonicSize {
pub fn standard() -> [MnemonicSize; 5] {
[
MnemonicSize::from_length(12).unwrap(),
MnemonicSize::from_length(15).unwrap(),
MnemonicSize::from_length(18).unwrap(),
MnemonicSize::from_length(21).unwrap(),
MnemonicSize::from_length(24).unwrap()
]
}
pub fn from_length(words: usize) -> Result<MnemonicSize, Error> {
match words {
12 => Ok(MnemonicSize { bits_length: 128, checksum_length: 4 }),
15 => Ok(MnemonicSize { bits_length: 160, checksum_length: 5 }),
18 => Ok(MnemonicSize { bits_length: 192, checksum_length: 6 }),
21 => Ok(MnemonicSize { bits_length: 224, checksum_length: 7 }),
24 => Ok(MnemonicSize { bits_length: 256, checksum_length: 8 }),
_ => Err(Error::MnemonicError(format!("Invalid mnemonic size: {}", words)))
}
}
pub fn from_entropy(entropy: &[u8]) -> Result<MnemonicSize, Error> {
let all = MnemonicSize::standard();
let entropy_len = entropy.len();
let found = all.iter().find(
|x| x.entropy_bytes_length() == entropy_len);
match found {
Some(&m) => Ok(m),
None => Err(Error::MnemonicError(format!("Invalid entropy size: {}", entropy_len)))
}
}
pub fn words_count(&self) -> usize {
(self.bits_length + self.checksum_length) / 11
}
pub fn entropy_bytes_length(&self) -> usize {
self.bits_length / 8
}
pub fn full_bytes_length(&self) -> usize {
let mut rem = 8 - (self.bits_length + self.checksum_length) % 8;
if rem == 8 {
rem = 0
}
(self.bits_length + self.checksum_length + rem) / 8
}
pub fn entropy(&self) -> Result<Vec<u8>, Error> {
gen_entropy(self.entropy_bytes_length())
}
pub fn checksum_bits(&self, full: u8) -> u8 {
if self.checksum_length == 8 {
full
} else {
let mut mask: u8 = 0;
for i in 0..self.checksum_length {
mask |= 1;
mask = mask << 1;
}
mask = mask << (8 - self.checksum_length - 1) as u8;
let bits = full & mask;
bits >> (8 - self.checksum_length) as u8
}
}
}
impl Mnemonic {
pub fn new(lang: Language, size: MnemonicSize) -> Result<Mnemonic, Error> {
Mnemonic::from_entropy(lang, size.entropy()?.as_slice())
}
pub fn from_entropy(lang: Language, entropy: &[u8]) -> Result<Mnemonic, Error> {
let size = MnemonicSize::from_entropy(entropy)?;
let mut ent = entropy.to_owned();
let checksum = checksum(&ent, size);
ent = with_checksum(&ent, checksum, size);
let indexes = get_indexes(&ent, size)?;
let mut w = Vec::new();
for i in &indexes {
w.push(BIP39_ENGLISH_WORDLIST[*i].clone());
}
Ok(Mnemonic {
language: lang,
words: w,
})
}
pub fn sentence(&self) -> String {
let mut s = String::new();
for (i, w) in self.words.iter().enumerate() {
s.push_str(w);
if i != self.words.len() - 1 {
s.push_str(" ");
}
}
s
}
pub fn seed(&self, password: Option<&str>) -> Vec<u8> {
let passphrase = match password {
Some(p) => "mnemonic".to_string() + &p.to_string(),
None => "mnemonic".to_string()
};
let prf = Kdf::Pbkdf2 {
prf: Prf::HmacSha512,
c: PBKDF2_ROUNDS as u32,
};
prf.derive(64, passphrase.as_bytes(), &self.sentence())
}
pub fn try_from(lang: Language, src: &str) -> Result<Self, Error> {
let w: Vec<String> = src
.to_string()
.split_whitespace()
.map(|w| w.to_string())
.collect();
match w.len() {
0 => Err(Error::MnemonicError("empty initial sentence".to_string())),
l => {
MnemonicSize::from_length(l)?;
Ok(Mnemonic {
language: lang,
words: w,
})
}
}
}
}
pub fn gen_entropy(byte_length: usize) -> Result<Vec<u8>, Error> {
let mut rng = OsRng::new()?;
let entropy = rng.gen_iter::<u8>().take(byte_length).collect::<Vec<u8>>();
Ok(entropy)
}
fn checksum(data: &[u8], size: MnemonicSize) -> u8 {
let mut hash = sha2::Sha256::new();
hash.input(data);
let val = hash.result()[0];
size.checksum_bits(val)
}
fn with_checksum(entropy: &[u8], checksum: u8, size: MnemonicSize) -> Vec<u8> {
let mut copy = Vec::from(entropy);
if size.checksum_length == 8 {
copy.push(checksum);
copy
} else {
let empty_bits = (8 - size.checksum_length) as u8;
let checksum_corrected = checksum << empty_bits;
copy.push(checksum_corrected);
let full_size = size.full_bytes_length();
let mut data = BigUint::from_bytes_be(copy.as_slice());
data = data.clone().shr(empty_bits as usize);
let result = data.to_bytes_be().to_vec();
if result.len() < full_size {
let mut with_zeroes: Vec<u8> = vec![0; full_size - result.len()];
with_zeroes.extend_from_slice(result.as_slice());
with_zeroes
} else {
result
}
}
}
fn get_indexes(entropy: &[u8], size: MnemonicSize) -> Result<Vec<usize>, Error> {
if entropy.len() != size.full_bytes_length() {
return Err(Error::MnemonicError(format!(
"invalid entropy length (required: {}, received: {})",
size.full_bytes_length(),
entropy.len()
)));
}
let mut data = BigUint::from_bytes_be(entropy);
let mut base_mask = BigUint::from_u16(0b11111111111).expect("expect initialize word index");
let mut out: Vec<usize> = Vec::with_capacity(size.words_count());
for i in 0..size.words_count() {
let pos = (size.words_count() - 1 - i) * INDEX_BIT_SIZE;
match data.clone().shr(pos).bitand(&base_mask.clone()).to_usize() {
Some(v) => out.push(v),
None => {
return Err(Error::MnemonicError(
"can't extract words indexes".to_string(),
))
}
}
}
Ok(out)
}
#[cfg(test)]
mod tests {
use super::*;
use hex::FromHex;
use crate::ToHex;
#[test]
fn keeps_zeroes_with_checksum() {
let zeroes = vec![0; 2]; let act = with_checksum(zeroes.as_slice(), 15, StandardMnemonic::size12());
assert_eq!(act.to_hex(), "000000000000000000000000000000000f");
let zeroes = Vec::from_hex(
"00000000000000000000000000000000000000000000000000000000000000").unwrap();
let act = with_checksum(zeroes.as_slice(), 15, StandardMnemonic::size24());
assert_eq!(act.to_hex(), "000000000000000000000000000000000000000000000000000000000000000f");
let zeroes = Vec::from_hex(
"000100").unwrap();
let act = with_checksum(zeroes.as_slice(), 15, StandardMnemonic::size12());
assert_eq!(act.to_hex(), "000000000000000000000000000000100f");
let zeroes = Vec::from_hex("000000000000000000000000000000000000000000000000").unwrap();
let act = with_checksum(zeroes.as_slice(), 0b100111, StandardMnemonic::size18());
assert_eq!(act.to_hex(), "00000000000000000000000000000000000000000000000027");
}
#[test]
fn should_extract_length() {
assert_eq!(MnemonicSize::from_length(12).unwrap(), StandardMnemonic::size12());
assert_eq!(MnemonicSize::from_length(15).unwrap(), StandardMnemonic::size15());
assert_eq!(MnemonicSize::from_length(18).unwrap(), StandardMnemonic::size18());
assert_eq!(MnemonicSize::from_length(21).unwrap(), StandardMnemonic::size21());
assert_eq!(MnemonicSize::from_length(24).unwrap(), StandardMnemonic::size24());
}
#[test]
fn should_fail_on_invalid_length() {
assert!(MnemonicSize::from_length(0).is_err());
assert!(MnemonicSize::from_length(1).is_err());
assert!(MnemonicSize::from_length(11).is_err());
assert!(MnemonicSize::from_length(32).is_err());
}
#[test]
fn should_calc_entropy_length() {
assert_eq!(StandardMnemonic::size12().entropy_bytes_length(), 16);
assert_eq!(StandardMnemonic::size15().entropy_bytes_length(), 20);
assert_eq!(StandardMnemonic::size18().entropy_bytes_length(), 24);
assert_eq!(StandardMnemonic::size21().entropy_bytes_length(), 28);
assert_eq!(StandardMnemonic::size24().entropy_bytes_length(), 32);
}
#[test]
fn should_calc_full_length() {
assert_eq!(StandardMnemonic::size12().full_bytes_length(), 17);
assert_eq!(StandardMnemonic::size15().full_bytes_length(), 21);
assert_eq!(StandardMnemonic::size18().full_bytes_length(), 25);
assert_eq!(StandardMnemonic::size21().full_bytes_length(), 29);
assert_eq!(StandardMnemonic::size24().full_bytes_length(), 33);
}
#[test]
fn generates_correct_entropy_length() {
assert_eq!(StandardMnemonic::size12().entropy().unwrap().len(), 16);
assert_eq!(StandardMnemonic::size15().entropy().unwrap().len(), 20);
assert_eq!(StandardMnemonic::size18().entropy().unwrap().len(), 24);
assert_eq!(StandardMnemonic::size21().entropy().unwrap().len(), 28);
assert_eq!(StandardMnemonic::size24().entropy().unwrap().len(), 32);
}
#[test]
fn generates_correct_mnemonic_length() {
assert_eq!(
Mnemonic::new(Language::English, StandardMnemonic::size12()).unwrap().words.len(),
12);
assert_eq!(
Mnemonic::new(Language::English, StandardMnemonic::size15()).unwrap().words.len(),
15);
assert_eq!(
Mnemonic::new(Language::English, StandardMnemonic::size18()).unwrap().words.len(),
18);
assert_eq!(
Mnemonic::new(Language::English, StandardMnemonic::size21()).unwrap().words.len(),
21);
assert_eq!(
Mnemonic::new(Language::English, StandardMnemonic::size24()).unwrap().words.len(),
24);
}
#[test]
fn should_generate_entropy() {
let mut ent = gen_entropy(32);
assert!(ent.is_ok());
assert_eq!(ent.unwrap().len(), 32);
ent = gen_entropy(2);
assert!(ent.is_ok());
assert_eq!(ent.unwrap().len(), 2);
}
#[test]
fn should_generate_indexes_12words() {
let mut ent = gen_entropy(16).unwrap();
ent = with_checksum(ent.as_slice(), 1, StandardMnemonic::size12());
let res = get_indexes(&ent, StandardMnemonic::size12());
assert!(res.is_ok());
let mut indexes = res.unwrap();
assert_eq!(indexes.len(), 12);
indexes = indexes.into_iter().filter(|v| *v > 2048).collect();
assert_eq!(indexes.len(), 0);
}
#[test]
fn should_generate_indexes_15words() {
let mut ent = gen_entropy(20).unwrap();
ent = with_checksum(ent.as_slice(), 1, StandardMnemonic::size15());
let res = get_indexes(&ent, StandardMnemonic::size15());
assert!(res.is_ok());
let mut indexes = res.unwrap();
assert_eq!(indexes.len(), 15);
indexes = indexes.into_iter().filter(|v| *v > 2048).collect();
assert_eq!(indexes.len(), 0);
}
#[test]
fn should_generate_indexes_18words() {
let mut ent = gen_entropy(24).unwrap();
ent = with_checksum(ent.as_slice(), 1, StandardMnemonic::size18());
let res = get_indexes(&ent, StandardMnemonic::size18());
assert!(res.is_ok());
let mut indexes = res.unwrap();
assert_eq!(indexes.len(), 18);
indexes = indexes.into_iter().filter(|v| *v > 2048).collect();
assert_eq!(indexes.len(), 0);
}
#[test]
fn should_generate_indexes_21words() {
let mut ent = gen_entropy(28).unwrap();
ent = with_checksum(ent.as_slice(), 1, StandardMnemonic::size21());
let res = get_indexes(&ent, StandardMnemonic::size21());
assert!(res.is_ok());
let mut indexes = res.unwrap();
assert_eq!(indexes.len(), 21);
indexes = indexes.into_iter().filter(|v| *v > 2048).collect();
assert_eq!(indexes.len(), 0);
}
#[test]
fn should_generate_indexes_24words() {
let mut ent = gen_entropy(32).unwrap();
ent = with_checksum(ent.as_slice(), 1, StandardMnemonic::size24());
let res = get_indexes(&ent, StandardMnemonic::size24());
assert!(res.is_ok());
let mut indexes = res.unwrap();
assert_eq!(indexes.len(), 24);
indexes = indexes.into_iter().filter(|v| *v > 2048).collect();
assert_eq!(indexes.len(), 0);
}
#[test]
fn should_fail_generate_indexes() {
let res = get_indexes(&vec![0u8, 1u8], StandardMnemonic::size24());
assert!(res.is_err())
}
#[test]
fn get_index_24() {
let mut entropy = Vec::from_hex(
"0000000000000000000000000000000000000000000000000000000000000000").unwrap();
entropy.push(0b01100110);
let res = get_indexes(
entropy.as_slice(),
StandardMnemonic::size24()
).unwrap();
let exp: Vec<usize> = vec!(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 102);
assert_eq!(res, exp);
let mut entropy = Vec::from_hex(
"7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f").unwrap();
entropy.push(0b00010111);
let res = get_indexes(
entropy.as_slice(),
StandardMnemonic::size24()
).unwrap();
let exp: Vec<usize> = vec!(1019, 2015, 1790, 2039, 1983, 1533, 2031, 1919,
1019, 2015, 1790, 2039, 1983, 1533, 2031, 1919,
1019, 2015, 1790, 2039, 1983, 1533, 2031, 1815);
assert_eq!(res, exp);
}
#[test]
fn get_index_18() {
let mut entropy = Vec::from_hex(
"ffffffffffffffffffffffffffffffffffffffffffffffffd1").unwrap();
let res = get_indexes(
entropy.as_slice(),
StandardMnemonic::size18()
).unwrap();
let exp: Vec<usize> = vec!(2047, 2047, 2047, 2047, 2047, 2047,
2047, 2047, 2047, 2047, 2047, 2047,
2047, 2047, 2047, 2047, 2047, 2001);
assert_eq!(res, exp);
}
#[test]
fn should_convert_to_seed() {
let entropy = StandardMnemonic::size24().entropy().unwrap();
let mnemonic = Mnemonic::from_entropy(Language::English, &entropy).unwrap();
let seed = mnemonic.seed(Some("12345"));
assert_eq!(seed.len(), 64);
}
#[test]
fn should_convert_to_sentence() {
let entropy = StandardMnemonic::size24().entropy().unwrap();
let mnemonic = Mnemonic::from_entropy(Language::English, &entropy).unwrap();
let s: Vec<String> = mnemonic
.sentence()
.split_whitespace()
.map(|w| w.to_string())
.collect();
assert_eq!(s, mnemonic.words)
}
#[test]
fn should_generate_english_mnemonic() {
let entropy = vec![0u8; 32];
let res = Mnemonic::from_entropy(Language::English, &entropy);
assert!(res.is_ok());
let mnemonic = res.unwrap();
assert_eq!(
mnemonic.sentence(),
"abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon \
abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon \
abandon abandon abandon art"
);
let entropy = mnemonic.seed(Some("TREZOR"));
assert_eq!(entropy, Vec::from_hex("bda85446c68413707090a52022edd26a\
1c9462295029f2e60cd7c4f2bbd309717\
0af7a4d73245cafa9c3cca8d561a7c3de6\
f5d4a10be8ed2a5e608d68f92fcc8").unwrap());
}
#[test]
fn should_create_from_sentence_12() {
let s = "ozone drill grab fiber curtain grace pudding thank cruise elder eight picnic";
let mnemonic = Mnemonic::try_from(Language::English, s).unwrap();
let w: Vec<String> = s
.to_string()
.split_whitespace()
.map(|w| w.to_string())
.collect();
assert_eq!(w, mnemonic.words);
assert_eq!(mnemonic.seed(Some("TREZOR")), Vec::from_hex("274ddc525802f7c828d8ef7ddbcdc530\
4e87ac3535913611fbbfa986d0c9e547\
6c91689f9c8a54fd55bd38606aa6a859\
5ad213d4c9c9f9aca3fb217069a41028").unwrap());
}
#[test]
fn should_be_compatible_with_bip39js() {
let s = "abandon abandon abandon abandon abandon abandon abandon abandon abandon \
abandon abandon about";
let mnemonic = Mnemonic::try_from(Language::English, s).unwrap();
assert_eq!(mnemonic.seed(Some("TREZOR")), Vec::from_hex("c55257c360c07c72029aebc1b53c05ed03\
62ada38ead3e3e9efa3708e53495531f0\
9a6987599d18264c1e1c92f2cf141630c7a3c4ab7c81b2f001698e7463b04").unwrap());
}
#[test]
fn should_be_compatible_with_bip39js_emptypass() {
let s = "abandon abandon abandon abandon abandon abandon abandon abandon abandon \
abandon abandon about";
let mnemonic = Mnemonic::try_from(Language::English, s).unwrap();
assert_eq!(mnemonic.seed(None), Vec::from_hex("5eb00bbddcf069084889a8ab9155568165f5c4\
53ccb85e70811aaed6f6da5fc19a5ac40b3\
89cd370d086206dec8aa6c43daea6690f20ad3d8d48b2d2ce9e38e4").unwrap());
}
#[test]
fn should_create_from_sentence_24() {
let s = "beyond stage sleep clip because twist token leaf atom beauty genius food \
business side grid unable middle armed observe pair crouch tonight away coconut";
let mnemonic = Mnemonic::try_from(Language::English, s).unwrap();
let w: Vec<String> = s
.to_string()
.split_whitespace()
.map(|w| w.to_string())
.collect();
assert_eq!(w, mnemonic.words);
assert_eq!(mnemonic.seed(Some("TREZOR")), Vec::from_hex("b15509eaa2d09d3efd3e006ef42151b3\
0367dc6e3aa5e44caba3fe4d3e352e65\
101fbdb86a96776b91946ff06f8eac59\
4dc6ee1d3e82a42dfe1b40fef6bcc3fd").unwrap());
}
#[test]
fn should_create_from_sentence_15() {
let s = "hover involve coyote admit barrel lawsuit near genuine divide ghost music episode dish churn castle";
let mnemonic = Mnemonic::try_from(Language::English, s).unwrap();
let w: Vec<String> = s
.to_string()
.split_whitespace()
.map(|w| w.to_string())
.collect();
assert_eq!(mnemonic.words, w);
assert_eq!(mnemonic.seed(None), Vec::from_hex(
"455f5de41e8ec000a32c26c3f411903020269f70fef532aed49f9a1f9cf1a300752f476bd88764449e9b5728b5a67020b5536b60947bf1123a4a6e100845afc6"
).unwrap());
assert_eq!(mnemonic.seed(Some("test")), Vec::from_hex(
"a49a8045f542196e4d0c8af8bd9e80853bb4582db8df4d57fda69d5301fc2b65d984b4c8fa6d374e1507b4c30972d7950c5390e239f27961f79396974e600eef"
).unwrap());
}
#[test]
fn should_create_from_entropy_15() {
let entropy = Vec::from_hex(
"ffffffffffffffffffffffffffffffffffffffffffffffff"
).unwrap();
let mnemonic = Mnemonic::from_entropy(Language::English, entropy.as_slice()).unwrap();
let words: Vec<String> = "zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo when"
.split_whitespace()
.map(|w| w.to_string())
.collect();
assert_eq!(mnemonic.words, words);
assert_eq!(mnemonic.seed(None).to_hex(),
"d2911131a6dda23ac4441d1b66e2113ec6324354523acfa20899a2dcb3087849264e91f8ec5d75355f0f617be15369ffa13c3d18c8156b97cd2618ac693f759f"
);
}
#[test]
fn should_create_from_entropy_18() {
let entropy = vec![0; 24];
let mnemonic = Mnemonic::from_entropy(Language::English, entropy.as_slice()).unwrap();
let words: Vec<String> = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon agent"
.split_whitespace()
.map(|w| w.to_string())
.collect();
assert_eq!(mnemonic.words, words);
assert_eq!(mnemonic.seed(None).to_hex(),
"4975bb3d1faf5308c86a30893ee903a976296609db223fd717e227da5a813a34dc1428b71c84a787fc51f3b9f9dc28e9459f48c08bd9578e9d1b170f2d7ea506"
);
let entropy = Vec::from_hex(
"7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f"
).unwrap();
let mnemonic = Mnemonic::from_entropy(Language::English, entropy.as_slice()).unwrap();
let words: Vec<String> = "legal winner thank year wave sausage worth useful legal winner thank year wave sausage worth useful legal will"
.split_whitespace()
.map(|w| w.to_string())
.collect();
assert_eq!(mnemonic.words, words);
assert_eq!(mnemonic.seed(None).to_hex(),
"b059400ce0f55498a5527667e77048bb482ff6daa16c37b4b9e8af70c85b3f4df588004f19812a1a027c9a51e5e94259a560268e91cd10e206451a129826e740"
);
let entropy = Vec::from_hex(
"808080808080808080808080808080808080808080808080"
).unwrap();
let mnemonic = Mnemonic::from_entropy(Language::English, entropy.as_slice()).unwrap();
let words: Vec<String> = "letter advice cage absurd amount doctor acoustic avoid letter advice cage absurd amount doctor acoustic avoid letter always"
.split_whitespace()
.map(|w| w.to_string())
.collect();
assert_eq!(mnemonic.words, words);
assert_eq!(mnemonic.seed(None).to_hex(),
"04d5f77103510c41d610f7f5fb3f0badc77c377090815cee808ea5d2f264fdfabf7c7ded4be6d4c6d7cdb021ba4c777b0b7e57ca8aa6de15aeb9905dba674d66"
);
let entropy = Vec::from_hex(
"ffffffffffffffffffffffffffffffffffffffffffffffff"
).unwrap();
let mnemonic = Mnemonic::from_entropy(Language::English, entropy.as_slice()).unwrap();
let words: Vec<String> = "zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo when"
.split_whitespace()
.map(|w| w.to_string())
.collect();
assert_eq!(mnemonic.words, words);
assert_eq!(mnemonic.seed(None).to_hex(),
"d2911131a6dda23ac4441d1b66e2113ec6324354523acfa20899a2dcb3087849264e91f8ec5d75355f0f617be15369ffa13c3d18c8156b97cd2618ac693f759f"
);
}
#[test]
fn should_create_from_entropy_24() {
let entropy = Vec::from_hex(
"7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f"
).unwrap();
let mnemonic = Mnemonic::from_entropy(Language::English, entropy.as_slice()).unwrap();
let words: Vec<String> = "legal winner thank year wave sausage worth useful legal winner thank year wave sausage worth useful legal winner thank year wave sausage worth title"
.split_whitespace()
.map(|w| w.to_string())
.collect();
assert_eq!(mnemonic.words, words);
assert_eq!(mnemonic.seed(None).to_hex(),
"761914478ebf6fe16185749372e91549361af22b386de46322cf8b1ba7e92e80c4af05196f742be1e63aab603899842ddadf4e7248d8e43870a4b6ff9bf16324"
);
}
#[test]
fn should_fail_from_empty() {
let s = "";
let mnemonic = Mnemonic::try_from(Language::English, s);
assert!(mnemonic.is_err())
}
#[test]
fn should_fail_from_longer() {
let s = "test test test test test test test test test test test test test test test test \
test test test test test test test test test test test test test";
let mnemonic = Mnemonic::try_from(Language::English, s);
assert!(mnemonic.is_err())
}
#[test]
fn should_fail_from_outrange() {
let s = "test test test test test test test test test test test test test test test test";
let mnemonic = Mnemonic::try_from(Language::English, s);
assert!(mnemonic.is_err())
}
#[test]
fn checksum_for_15() {
let value = Vec::from_hex("ffffffffffffffffffffffffffffffffffffffff").unwrap();
let act = checksum(value.as_slice(), StandardMnemonic::size15());
assert_eq!(act, 0b10011);
}
#[test]
fn checksum_for_18() {
let value = Vec::from_hex("000000000000000000000000000000000000000000000000").unwrap();
let act = checksum(value.as_slice(), StandardMnemonic::size18());
assert_eq!(act, 0b100111);
let value = Vec::from_hex("ffffffffffffffffffffffffffffffffffffffffffffffff").unwrap();
let act = checksum(value.as_slice(), StandardMnemonic::size18());
assert_eq!(act, 0b010001);
let value = Vec::from_hex("dd3e87806994a424a6161109bdbe195f585598e5f090b66a").unwrap();
let act = checksum(value.as_slice(), StandardMnemonic::size18());
assert_eq!(act, 0b001011);
let value = Vec::from_hex("f526453799d708306056bf170f640efd8cb9b8cc139df865").unwrap();
let act = checksum(value.as_slice(), StandardMnemonic::size18());
assert_eq!(act, 0b111011);
}
#[test]
fn checksum_for_21() {
let value = Vec::from_hex("ffffffffffffffffffffffffffffffffffffffffffffffffffffffff").unwrap();
let act = checksum(value.as_slice(), StandardMnemonic::size21());
assert_eq!(act, 0b0011001);
let value = Vec::from_hex("1205c0b2e048ceef790d0433a902a070d0744af9b5e88edf7923c561").unwrap();
let act = checksum(value.as_slice(), StandardMnemonic::size21());
assert_eq!(act, 0b0011011);
let value = Vec::from_hex("b579f8e1dfc739a36a90a1f94cb33aef1bc28f43dc3533c829d5e935").unwrap();
let act = checksum(value.as_slice(), StandardMnemonic::size21());
assert_eq!(act, 0b0000000);
let value = Vec::from_hex("b579f8e1dfc739a36a90a1f94cb33aef1bc28f43dc3533c829d5e95c").unwrap();
let act = checksum(value.as_slice(), StandardMnemonic::size21());
assert_eq!(act, 0b0000001);
}
#[test]
fn checksum_for_24() {
let value = Vec::from_hex("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff").unwrap();
let act = checksum(value.as_slice(), StandardMnemonic::size24());
assert_eq!(act, 0b10101111);
let value = Vec::from_hex("e28a37058c7f5112ec9e16a3437cf363a2572d70b6ceb3b69654476253ed12fa").unwrap();
let act = checksum(value.as_slice(), StandardMnemonic::size24());
assert_eq!(act, 0b10111111);
}
}