parent
779863ad21
commit
b8ea0b86bf
|
@ -8,8 +8,13 @@ use test::Bencher;
|
||||||
use bip39::*;
|
use bip39::*;
|
||||||
|
|
||||||
#[cfg(not(any(
|
#[cfg(not(any(
|
||||||
feature = "chinese-simplified", feature = "chinese-traditional", feature = "czech",
|
feature = "chinese-simplified",
|
||||||
feature = "french", feature = "italian", feature = "japanese", feature = "korean",
|
feature = "chinese-traditional",
|
||||||
|
feature = "czech",
|
||||||
|
feature = "french",
|
||||||
|
feature = "italian",
|
||||||
|
feature = "japanese",
|
||||||
|
feature = "korean",
|
||||||
feature = "spanish"
|
feature = "spanish"
|
||||||
)))]
|
)))]
|
||||||
const LANG: Language = Language::English;
|
const LANG: Language = Language::English;
|
||||||
|
@ -32,38 +37,38 @@ const LANG: Language = Language::Spanish;
|
||||||
|
|
||||||
#[bench]
|
#[bench]
|
||||||
fn validate(b: &mut Bencher) {
|
fn validate(b: &mut Bencher) {
|
||||||
let entropy = "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f".as_bytes();
|
let entropy = "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f".as_bytes();
|
||||||
let mnemonic = Mnemonic::from_entropy_in(LANG, &entropy).unwrap();
|
let mnemonic = Mnemonic::from_entropy_in(LANG, &entropy).unwrap();
|
||||||
assert_eq!(mnemonic.word_count(), 24);
|
assert_eq!(mnemonic.word_count(), 24);
|
||||||
let phrase = mnemonic.to_string();
|
let phrase = mnemonic.to_string();
|
||||||
|
|
||||||
b.iter(|| {
|
b.iter(|| {
|
||||||
let _ = Mnemonic::parse_in(Language::English, &phrase);
|
let _ = Mnemonic::parse_in(Language::English, &phrase);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
#[bench]
|
#[bench]
|
||||||
fn from_entropy(b: &mut Bencher) {
|
fn from_entropy(b: &mut Bencher) {
|
||||||
let entropy = "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f".as_bytes();
|
let entropy = "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f".as_bytes();
|
||||||
|
|
||||||
b.iter(|| {
|
b.iter(|| {
|
||||||
let _ = Mnemonic::from_entropy_in(LANG, &entropy).unwrap();
|
let _ = Mnemonic::from_entropy_in(LANG, &entropy).unwrap();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
#[bench]
|
#[bench]
|
||||||
fn new_mnemonic(b: &mut Bencher) {
|
fn new_mnemonic(b: &mut Bencher) {
|
||||||
b.iter(|| {
|
b.iter(|| {
|
||||||
let _ = Mnemonic::generate_in(LANG, 24);
|
let _ = Mnemonic::generate_in(LANG, 24);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
#[bench]
|
#[bench]
|
||||||
fn to_seed(b: &mut Bencher) {
|
fn to_seed(b: &mut Bencher) {
|
||||||
let entropy = "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f".as_bytes();
|
let entropy = "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f".as_bytes();
|
||||||
let m = Mnemonic::from_entropy_in(LANG, &entropy).unwrap();
|
let m = Mnemonic::from_entropy_in(LANG, &entropy).unwrap();
|
||||||
|
|
||||||
b.iter(|| {
|
b.iter(|| {
|
||||||
let _ = m.to_seed("");
|
let _ = m.to_seed("");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,59 +1,58 @@
|
||||||
|
|
||||||
/// Implement serde serialization based on the
|
/// Implement serde serialization based on the
|
||||||
/// fmt::Display and std::FromStr traits.
|
/// fmt::Display and std::FromStr traits.
|
||||||
macro_rules! serde_string_impl {
|
macro_rules! serde_string_impl {
|
||||||
($name:ident, $expecting:expr) => {
|
($name:ident, $expecting:expr) => {
|
||||||
#[cfg(feature = "serde")]
|
#[cfg(feature = "serde")]
|
||||||
impl<'de> $crate::serde::Deserialize<'de> for $name {
|
impl<'de> $crate::serde::Deserialize<'de> for $name {
|
||||||
fn deserialize<D>(deserializer: D) -> Result<$name, D::Error>
|
fn deserialize<D>(deserializer: D) -> Result<$name, D::Error>
|
||||||
where
|
where
|
||||||
D: $crate::serde::de::Deserializer<'de>,
|
D: $crate::serde::de::Deserializer<'de>,
|
||||||
{
|
{
|
||||||
use ::std::fmt::{self, Formatter};
|
use std::fmt::{self, Formatter};
|
||||||
use ::std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
struct Visitor;
|
struct Visitor;
|
||||||
impl<'de> $crate::serde::de::Visitor<'de> for Visitor {
|
impl<'de> $crate::serde::de::Visitor<'de> for Visitor {
|
||||||
type Value = $name;
|
type Value = $name;
|
||||||
|
|
||||||
fn expecting(&self, formatter: &mut Formatter) -> fmt::Result {
|
fn expecting(&self, formatter: &mut Formatter) -> fmt::Result {
|
||||||
formatter.write_str($expecting)
|
formatter.write_str($expecting)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
|
fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
|
||||||
where
|
where
|
||||||
E: $crate::serde::de::Error,
|
E: $crate::serde::de::Error,
|
||||||
{
|
{
|
||||||
$name::from_str(v).map_err(E::custom)
|
$name::from_str(v).map_err(E::custom)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_borrowed_str<E>(self, v: &'de str) -> Result<Self::Value, E>
|
fn visit_borrowed_str<E>(self, v: &'de str) -> Result<Self::Value, E>
|
||||||
where
|
where
|
||||||
E: $crate::serde::de::Error,
|
E: $crate::serde::de::Error,
|
||||||
{
|
{
|
||||||
self.visit_str(v)
|
self.visit_str(v)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_string<E>(self, v: String) -> Result<Self::Value, E>
|
fn visit_string<E>(self, v: String) -> Result<Self::Value, E>
|
||||||
where
|
where
|
||||||
E: $crate::serde::de::Error,
|
E: $crate::serde::de::Error,
|
||||||
{
|
{
|
||||||
self.visit_str(&v)
|
self.visit_str(&v)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
deserializer.deserialize_str(Visitor)
|
deserializer.deserialize_str(Visitor)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "serde")]
|
#[cfg(feature = "serde")]
|
||||||
impl<'de> $crate::serde::Serialize for $name {
|
impl<'de> $crate::serde::Serialize for $name {
|
||||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
where
|
where
|
||||||
S: $crate::serde::Serializer,
|
S: $crate::serde::Serializer,
|
||||||
{
|
{
|
||||||
serializer.collect_str(&self)
|
serializer.collect_str(&self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +1,12 @@
|
||||||
|
|
||||||
use core::fmt;
|
use core::fmt;
|
||||||
|
|
||||||
mod english;
|
|
||||||
#[cfg(feature = "chinese-simplified")]
|
#[cfg(feature = "chinese-simplified")]
|
||||||
mod chinese_simplified;
|
mod chinese_simplified;
|
||||||
#[cfg(feature = "chinese-traditional")]
|
#[cfg(feature = "chinese-traditional")]
|
||||||
mod chinese_traditional;
|
mod chinese_traditional;
|
||||||
#[cfg(feature = "czech")]
|
#[cfg(feature = "czech")]
|
||||||
mod czech;
|
mod czech;
|
||||||
|
mod english;
|
||||||
#[cfg(feature = "french")]
|
#[cfg(feature = "french")]
|
||||||
mod french;
|
mod french;
|
||||||
#[cfg(feature = "italian")]
|
#[cfg(feature = "italian")]
|
||||||
|
@ -142,7 +141,7 @@ impl Language {
|
||||||
None => return &[],
|
None => return &[],
|
||||||
};
|
};
|
||||||
let count = self.word_list()[first..].iter().take_while(|w| w.starts_with(prefix)).count();
|
let count = self.word_list()[first..].iter().take_while(|w| w.starts_with(prefix)).count();
|
||||||
&self.word_list()[first .. first + count]
|
&self.word_list()[first..first + count]
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the index of the word in the word list.
|
/// Get the index of the word in the word list.
|
||||||
|
@ -163,8 +162,13 @@ mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[cfg(all(
|
#[cfg(all(
|
||||||
feature = "chinese-simplified", feature = "chinese-traditional", feature = "czech",
|
feature = "chinese-simplified",
|
||||||
feature = "french", feature = "italian", feature = "japanese", feature = "korean",
|
feature = "chinese-traditional",
|
||||||
|
feature = "czech",
|
||||||
|
feature = "french",
|
||||||
|
feature = "italian",
|
||||||
|
feature = "japanese",
|
||||||
|
feature = "korean",
|
||||||
feature = "spanish"
|
feature = "spanish"
|
||||||
))]
|
))]
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -185,13 +189,22 @@ mod tests {
|
||||||
use bitcoin_hashes::{sha256, Hash, HashEngine};
|
use bitcoin_hashes::{sha256, Hash, HashEngine};
|
||||||
|
|
||||||
let checksums = [
|
let checksums = [
|
||||||
("5c5942792bd8340cb8b27cd592f1015edf56a8c5b26276ee18a482428e7c5726", Language::SimplifiedChinese),
|
(
|
||||||
("417b26b3d8500a4ae3d59717d7011952db6fc2fb84b807f3f94ac734e89c1b5f", Language::TraditionalChinese),
|
"5c5942792bd8340cb8b27cd592f1015edf56a8c5b26276ee18a482428e7c5726",
|
||||||
|
Language::SimplifiedChinese,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"417b26b3d8500a4ae3d59717d7011952db6fc2fb84b807f3f94ac734e89c1b5f",
|
||||||
|
Language::TraditionalChinese,
|
||||||
|
),
|
||||||
("7e80e161c3e93d9554c2efb78d4e3cebf8fc727e9c52e03b83b94406bdcc95fc", Language::Czech),
|
("7e80e161c3e93d9554c2efb78d4e3cebf8fc727e9c52e03b83b94406bdcc95fc", Language::Czech),
|
||||||
("2f5eed53a4727b4bf8880d8f3f199efc90e58503646d9ff8eff3a2ed3b24dbda", Language::English),
|
("2f5eed53a4727b4bf8880d8f3f199efc90e58503646d9ff8eff3a2ed3b24dbda", Language::English),
|
||||||
("ebc3959ab7801a1df6bac4fa7d970652f1df76b683cd2f4003c941c63d517e59", Language::French),
|
("ebc3959ab7801a1df6bac4fa7d970652f1df76b683cd2f4003c941c63d517e59", Language::French),
|
||||||
("d392c49fdb700a24cd1fceb237c1f65dcc128f6b34a8aacb58b59384b5c648c2", Language::Italian),
|
("d392c49fdb700a24cd1fceb237c1f65dcc128f6b34a8aacb58b59384b5c648c2", Language::Italian),
|
||||||
("2eed0aef492291e061633d7ad8117f1a2b03eb80a29d0e4e3117ac2528d05ffd", Language::Japanese),
|
(
|
||||||
|
"2eed0aef492291e061633d7ad8117f1a2b03eb80a29d0e4e3117ac2528d05ffd",
|
||||||
|
Language::Japanese,
|
||||||
|
),
|
||||||
("9e95f86c167de88f450f0aaf89e87f6624a57f973c67b516e338e8e8b8897f60", Language::Korean),
|
("9e95f86c167de88f450f0aaf89e87f6624a57f973c67b516e338e8e8b8897f60", Language::Korean),
|
||||||
("46846a5a0139d1e3cb77293e521c2865f7bcdb82c44e8d0a06a2cd0ecba48c0b", Language::Spanish),
|
("46846a5a0139d1e3cb77293e521c2865f7bcdb82c44e8d0a06a2cd0ecba48c0b", Language::Spanish),
|
||||||
];
|
];
|
||||||
|
@ -206,8 +219,11 @@ mod tests {
|
||||||
digest.input("\n".as_bytes());
|
digest.input("\n".as_bytes());
|
||||||
}
|
}
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
assert_eq!(sha256::Hash::from_engine(digest).to_string(), _sum,
|
assert_eq!(
|
||||||
"word list for language {} failed checksum check", lang,
|
sha256::Hash::from_engine(digest).to_string(),
|
||||||
|
_sum,
|
||||||
|
"word list for language {} failed checksum check",
|
||||||
|
lang,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -217,7 +233,7 @@ mod tests {
|
||||||
let lang = Language::English;
|
let lang = Language::English;
|
||||||
|
|
||||||
let res = lang.words_by_prefix("woo");
|
let res = lang.words_by_prefix("woo");
|
||||||
assert_eq!(res, ["wood","wool"]);
|
assert_eq!(res, ["wood", "wool"]);
|
||||||
|
|
||||||
let res = lang.words_by_prefix("");
|
let res = lang.words_by_prefix("");
|
||||||
assert_eq!(res.len(), 2048);
|
assert_eq!(res.len(), 2048);
|
||||||
|
@ -227,8 +243,13 @@ mod tests {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(all(
|
#[cfg(all(
|
||||||
feature = "chinese-simplified", feature = "chinese-traditional", feature = "czech",
|
feature = "chinese-simplified",
|
||||||
feature = "french", feature = "italian", feature = "japanese", feature = "korean",
|
feature = "chinese-traditional",
|
||||||
|
feature = "czech",
|
||||||
|
feature = "french",
|
||||||
|
feature = "italian",
|
||||||
|
feature = "japanese",
|
||||||
|
feature = "korean",
|
||||||
feature = "spanish"
|
feature = "spanish"
|
||||||
))]
|
))]
|
||||||
#[test]
|
#[test]
|
||||||
|
|
145
src/lib.rs
145
src/lib.rs
|
@ -25,7 +25,6 @@
|
||||||
#![deny(dead_code)]
|
#![deny(dead_code)]
|
||||||
#![deny(unused_imports)]
|
#![deny(unused_imports)]
|
||||||
#![deny(missing_docs)]
|
#![deny(missing_docs)]
|
||||||
|
|
||||||
#![cfg_attr(all(not(test), not(feature = "std")), no_std)]
|
#![cfg_attr(all(not(test), not(feature = "std")), no_std)]
|
||||||
|
|
||||||
#[cfg(any(test, feature = "std"))]
|
#[cfg(any(test, feature = "std"))]
|
||||||
|
@ -44,10 +43,10 @@ pub extern crate serde;
|
||||||
|
|
||||||
use core::{fmt, str};
|
use core::{fmt, str};
|
||||||
|
|
||||||
#[cfg(feature = "std")]
|
|
||||||
use std::error;
|
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
use std::error;
|
||||||
|
|
||||||
use bitcoin_hashes::{sha256, Hash};
|
use bitcoin_hashes::{sha256, Hash};
|
||||||
|
|
||||||
|
@ -114,14 +113,14 @@ pub enum Error {
|
||||||
impl fmt::Display for Error {
|
impl fmt::Display for Error {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
match *self {
|
match *self {
|
||||||
Error::BadWordCount(c) => write!(f,
|
Error::BadWordCount(c) => {
|
||||||
"mnemonic has a word count that is not a multiple of 6: {}", c,
|
write!(f, "mnemonic has a word count that is not a multiple of 6: {}", c,)
|
||||||
),
|
}
|
||||||
Error::UnknownWord(i) => write!(f,
|
Error::UnknownWord(i) => write!(f, "mnemonic contains an unknown word (word {})", i,),
|
||||||
"mnemonic contains an unknown word (word {})", i,
|
Error::BadEntropyBitCount(c) => write!(
|
||||||
),
|
f,
|
||||||
Error::BadEntropyBitCount(c) => write!(f,
|
"entropy was not between 128-256 bits or not a multiple of 32 bits: {} bits",
|
||||||
"entropy was not between 128-256 bits or not a multiple of 32 bits: {} bits", c,
|
c,
|
||||||
),
|
),
|
||||||
Error::InvalidChecksum => write!(f, "the mnemonic has an invalid checksum"),
|
Error::InvalidChecksum => write!(f, "the mnemonic has an invalid checksum"),
|
||||||
Error::AmbiguousLanguages(a) => {
|
Error::AmbiguousLanguages(a) => {
|
||||||
|
@ -231,8 +230,13 @@ impl Mnemonic {
|
||||||
/// let mut rng = rand::thread_rng();
|
/// let mut rng = rand::thread_rng();
|
||||||
/// let m = Mnemonic::generate_in_with(&mut rng, Language::English, 24).unwrap();
|
/// let m = Mnemonic::generate_in_with(&mut rng, Language::English, 24).unwrap();
|
||||||
/// ```
|
/// ```
|
||||||
pub fn generate_in_with<R>(rng: &mut R, language: Language, word_count: usize) -> Result<Mnemonic, Error>
|
pub fn generate_in_with<R>(
|
||||||
where R: rand_core::RngCore + rand_core::CryptoRng,
|
rng: &mut R,
|
||||||
|
language: Language,
|
||||||
|
word_count: usize,
|
||||||
|
) -> Result<Mnemonic, Error>
|
||||||
|
where
|
||||||
|
R: rand_core::RngCore + rand_core::CryptoRng,
|
||||||
{
|
{
|
||||||
if word_count < MIN_NB_WORDS || word_count % 6 != 0 || word_count > MAX_NB_WORDS {
|
if word_count < MIN_NB_WORDS || word_count % 6 != 0 || word_count > MAX_NB_WORDS {
|
||||||
return Err(Error::BadWordCount(word_count));
|
return Err(Error::BadWordCount(word_count));
|
||||||
|
@ -288,7 +292,8 @@ impl Mnemonic {
|
||||||
fn language_of_iter<'a, W: Iterator<Item = &'a str>>(words: W) -> Result<Language, Error> {
|
fn language_of_iter<'a, W: Iterator<Item = &'a str>>(words: W) -> Result<Language, Error> {
|
||||||
let mut words = words.peekable();
|
let mut words = words.peekable();
|
||||||
let langs = Language::all();
|
let langs = Language::all();
|
||||||
{ // Start scope to drop first_word so that words can be reborrowed later.
|
{
|
||||||
|
// Start scope to drop first_word so that words can be reborrowed later.
|
||||||
let first_word = words.peek().ok_or(Error::BadWordCount(0))?;
|
let first_word = words.peek().ok_or(Error::BadWordCount(0))?;
|
||||||
if first_word.len() == 0 {
|
if first_word.len() == 0 {
|
||||||
return Err(Error::BadWordCount(0));
|
return Err(Error::BadWordCount(0));
|
||||||
|
@ -404,7 +409,10 @@ impl Mnemonic {
|
||||||
|
|
||||||
/// Parse a mnemonic in the given language.
|
/// Parse a mnemonic in the given language.
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
pub fn parse_in<'a, S: Into<Cow<'a, str>>>(language: Language, s: S) -> Result<Mnemonic, Error> {
|
pub fn parse_in<'a, S: Into<Cow<'a, str>>>(
|
||||||
|
language: Language,
|
||||||
|
s: S,
|
||||||
|
) -> Result<Mnemonic, Error> {
|
||||||
let mut cow = s.into();
|
let mut cow = s.into();
|
||||||
Mnemonic::normalize_utf8_cow(&mut cow);
|
Mnemonic::normalize_utf8_cow(&mut cow);
|
||||||
Ok(Mnemonic::parse_in_normalized(language, cow.as_ref())?)
|
Ok(Mnemonic::parse_in_normalized(language, cow.as_ref())?)
|
||||||
|
@ -437,7 +445,12 @@ impl Mnemonic {
|
||||||
|
|
||||||
let nb_words = self.word_count();
|
let nb_words = self.word_count();
|
||||||
let mut seed = [0u8; PBKDF2_BYTES];
|
let mut seed = [0u8; PBKDF2_BYTES];
|
||||||
pbkdf2::pbkdf2(&self.0[0..nb_words], normalized_passphrase.as_bytes(), PBKDF2_ROUNDS, &mut seed);
|
pbkdf2::pbkdf2(
|
||||||
|
&self.0[0..nb_words],
|
||||||
|
normalized_passphrase.as_bytes(),
|
||||||
|
PBKDF2_ROUNDS,
|
||||||
|
&mut seed,
|
||||||
|
);
|
||||||
seed
|
seed
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -519,9 +532,13 @@ impl str::FromStr for Mnemonic {
|
||||||
|
|
||||||
fn from_str(s: &str) -> Result<Mnemonic, Error> {
|
fn from_str(s: &str) -> Result<Mnemonic, Error> {
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
{ Mnemonic::parse(s) }
|
{
|
||||||
|
Mnemonic::parse(s)
|
||||||
|
}
|
||||||
#[cfg(not(feature = "std"))]
|
#[cfg(not(feature = "std"))]
|
||||||
{ Mnemonic::parse_normalized(s) }
|
{
|
||||||
|
Mnemonic::parse_normalized(s)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -537,7 +554,10 @@ mod tests {
|
||||||
for lang in Language::all() {
|
for lang in Language::all() {
|
||||||
let m = Mnemonic::generate_in(*lang, 24).unwrap();
|
let m = Mnemonic::generate_in(*lang, 24).unwrap();
|
||||||
assert_eq!(*lang, Mnemonic::language_of_iter(m.word_iter()).unwrap());
|
assert_eq!(*lang, Mnemonic::language_of_iter(m.word_iter()).unwrap());
|
||||||
assert_eq!(*lang, Mnemonic::language_of_iter(m.to_string().split_whitespace()).unwrap());
|
assert_eq!(
|
||||||
|
*lang,
|
||||||
|
Mnemonic::language_of_iter(m.to_string().split_whitespace()).unwrap()
|
||||||
|
);
|
||||||
assert_eq!(*lang, Mnemonic::language_of(m.to_string()).unwrap());
|
assert_eq!(*lang, Mnemonic::language_of(m.to_string()).unwrap());
|
||||||
assert_eq!(*lang, Mnemonic::language_of(&m.to_string()).unwrap());
|
assert_eq!(*lang, Mnemonic::language_of(&m.to_string()).unwrap());
|
||||||
}
|
}
|
||||||
|
@ -700,30 +720,56 @@ mod tests {
|
||||||
let entropy = Vec::<u8>::from_hex(&vector.0).unwrap();
|
let entropy = Vec::<u8>::from_hex(&vector.0).unwrap();
|
||||||
let mnemonic_str = vector.1;
|
let mnemonic_str = vector.1;
|
||||||
let seed = Vec::<u8>::from_hex(&vector.2).unwrap();
|
let seed = Vec::<u8>::from_hex(&vector.2).unwrap();
|
||||||
|
|
||||||
let mnemonic = Mnemonic::from_entropy(&entropy).unwrap();
|
let mnemonic = Mnemonic::from_entropy(&entropy).unwrap();
|
||||||
|
|
||||||
assert_eq!(mnemonic, Mnemonic::parse_in_normalized(Language::English, mnemonic_str).unwrap(),
|
assert_eq!(
|
||||||
"failed vector: {}", mnemonic_str);
|
mnemonic,
|
||||||
assert_eq!(mnemonic, Mnemonic::parse_normalized(mnemonic_str).unwrap(),
|
Mnemonic::parse_in_normalized(Language::English, mnemonic_str).unwrap(),
|
||||||
"failed vector: {}", mnemonic_str);
|
"failed vector: {}",
|
||||||
assert_eq!(&seed[..], &mnemonic.to_seed_normalized("TREZOR")[..],
|
mnemonic_str
|
||||||
"failed vector: {}", mnemonic_str);
|
);
|
||||||
|
assert_eq!(
|
||||||
|
mnemonic,
|
||||||
|
Mnemonic::parse_normalized(mnemonic_str).unwrap(),
|
||||||
|
"failed vector: {}",
|
||||||
|
mnemonic_str
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
&seed[..],
|
||||||
|
&mnemonic.to_seed_normalized("TREZOR")[..],
|
||||||
|
"failed vector: {}",
|
||||||
|
mnemonic_str
|
||||||
|
);
|
||||||
|
|
||||||
#[cfg(features = "std")]
|
#[cfg(features = "std")]
|
||||||
{
|
{
|
||||||
assert_eq!(&mnemonic.to_string(), mnemonic_str,
|
assert_eq!(&mnemonic.to_string(), mnemonic_str, "failed vector: {}", mnemonic_str);
|
||||||
"failed vector: {}", mnemonic_str);
|
assert_eq!(
|
||||||
assert_eq!(mnemonic, Mnemonic::parse_in(Language::English, mnemonic_str).unwrap(),
|
mnemonic,
|
||||||
"failed vector: {}", mnemonic_str);
|
Mnemonic::parse_in(Language::English, mnemonic_str).unwrap(),
|
||||||
assert_eq!(mnemonic, Mnemonic::parse(mnemonic_str).unwrap(),
|
"failed vector: {}",
|
||||||
"failed vector: {}", mnemonic_str);
|
mnemonic_str
|
||||||
assert_eq!(&seed[..], &mnemonic.to_seed("TREZOR")[..],
|
);
|
||||||
"failed vector: {}", mnemonic_str);
|
assert_eq!(
|
||||||
assert_eq!(&entropy, &mnemonic.to_entropy(),
|
mnemonic,
|
||||||
"failed vector: {}", mnemonic_str);
|
Mnemonic::parse(mnemonic_str).unwrap(),
|
||||||
assert_eq!(&entropy, &mnemonic.to_entropy_array().0[0..entropy.len()],
|
"failed vector: {}",
|
||||||
"failed vector: {}", mnemonic_str);
|
mnemonic_str
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
&seed[..],
|
||||||
|
&mnemonic.to_seed("TREZOR")[..],
|
||||||
|
"failed vector: {}",
|
||||||
|
mnemonic_str
|
||||||
|
);
|
||||||
|
assert_eq!(&entropy, &mnemonic.to_entropy(), "failed vector: {}", mnemonic_str);
|
||||||
|
assert_eq!(
|
||||||
|
&entropy,
|
||||||
|
&mnemonic.to_entropy_array().0[0..entropy.len()],
|
||||||
|
"failed vector: {}",
|
||||||
|
mnemonic_str
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -765,22 +811,13 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_invalid_entropy() {
|
fn test_invalid_entropy() {
|
||||||
//between 128 and 256 bits, but not divisible by 32
|
//between 128 and 256 bits, but not divisible by 32
|
||||||
assert_eq!(
|
assert_eq!(Mnemonic::from_entropy(&vec![b'x'; 17]), Err(Error::BadEntropyBitCount(136)));
|
||||||
Mnemonic::from_entropy(&vec![b'x'; 17]),
|
|
||||||
Err(Error::BadEntropyBitCount(136))
|
|
||||||
);
|
|
||||||
|
|
||||||
//less than 128 bits
|
//less than 128 bits
|
||||||
assert_eq!(
|
assert_eq!(Mnemonic::from_entropy(&vec![b'x'; 4]), Err(Error::BadEntropyBitCount(32)));
|
||||||
Mnemonic::from_entropy(&vec![b'x'; 4]),
|
|
||||||
Err(Error::BadEntropyBitCount(32))
|
|
||||||
);
|
|
||||||
|
|
||||||
//greater than 256 bits
|
//greater than 256 bits
|
||||||
assert_eq!(
|
assert_eq!(Mnemonic::from_entropy(&vec![b'x'; 36]), Err(Error::BadEntropyBitCount(288)));
|
||||||
Mnemonic::from_entropy(&vec![b'x'; 36]),
|
|
||||||
Err(Error::BadEntropyBitCount(288))
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(all(feature = "japanese", feature = "std"))]
|
#[cfg(all(feature = "japanese", feature = "std"))]
|
||||||
|
@ -945,19 +982,17 @@ mod tests {
|
||||||
let mnemonic_str = vector.1;
|
let mnemonic_str = vector.1;
|
||||||
let passphrase = vector.2;
|
let passphrase = vector.2;
|
||||||
let seed = Vec::<u8>::from_hex(&vector.3).unwrap();
|
let seed = Vec::<u8>::from_hex(&vector.3).unwrap();
|
||||||
|
|
||||||
let mnemonic = Mnemonic::from_entropy_in(Language::Japanese, &entropy).unwrap();
|
let mnemonic = Mnemonic::from_entropy_in(Language::Japanese, &entropy).unwrap();
|
||||||
|
|
||||||
assert_eq!(seed, &mnemonic.to_seed(passphrase)[..],
|
assert_eq!(seed, &mnemonic.to_seed(passphrase)[..], "failed vector: {}", mnemonic_str);
|
||||||
"failed vector: {}", mnemonic_str);
|
|
||||||
let rt = Mnemonic::parse_in(Language::Japanese, mnemonic.to_string())
|
let rt = Mnemonic::parse_in(Language::Japanese, mnemonic.to_string())
|
||||||
.expect(&format!("vector: {}", mnemonic_str));
|
.expect(&format!("vector: {}", mnemonic_str));
|
||||||
assert_eq!(seed, &rt.to_seed(passphrase)[..]);
|
assert_eq!(seed, &rt.to_seed(passphrase)[..]);
|
||||||
|
|
||||||
let mnemonic = Mnemonic::parse_in(Language::Japanese, mnemonic_str)
|
let mnemonic = Mnemonic::parse_in(Language::Japanese, mnemonic_str)
|
||||||
.expect(&format!("vector: {}", mnemonic_str));
|
.expect(&format!("vector: {}", mnemonic_str));
|
||||||
assert_eq!(seed, &mnemonic.to_seed(passphrase)[..],
|
assert_eq!(seed, &mnemonic.to_seed(passphrase)[..], "failed vector: {}", mnemonic_str);
|
||||||
"failed vector: {}", mnemonic_str);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
use bitcoin_hashes::{hmac, sha512, Hash, HashEngine};
|
use bitcoin_hashes::{hmac, sha512, Hash, HashEngine};
|
||||||
|
|
||||||
const SALT_PREFIX: &'static str = "mnemonic";
|
const SALT_PREFIX: &'static str = "mnemonic";
|
||||||
|
@ -81,7 +80,7 @@ fn create_hmac_engine(mnemonic: &[&'static str]) -> hmac::HmacEngine<sha512::Has
|
||||||
fn u32_to_array_be(val: u32) -> [u8; 4] {
|
fn u32_to_array_be(val: u32) -> [u8; 4] {
|
||||||
let mut res = [0; 4];
|
let mut res = [0; 4];
|
||||||
for i in 0..4 {
|
for i in 0..4 {
|
||||||
res[i] = ((val >> (4 - i - 1)*8) & 0xff) as u8;
|
res[i] = ((val >> (4 - i - 1) * 8) & 0xff) as u8;
|
||||||
}
|
}
|
||||||
res
|
res
|
||||||
}
|
}
|
||||||
|
@ -98,7 +97,9 @@ pub(crate) fn pbkdf2(mnemonic: &[&'static str], unprefixed_salt: &[u8], c: usize
|
||||||
let prf = create_hmac_engine(mnemonic);
|
let prf = create_hmac_engine(mnemonic);
|
||||||
|
|
||||||
for (i, chunk) in res.chunks_mut(sha512::Hash::LEN).enumerate() {
|
for (i, chunk) in res.chunks_mut(sha512::Hash::LEN).enumerate() {
|
||||||
for v in chunk.iter_mut() { *v = 0; }
|
for v in chunk.iter_mut() {
|
||||||
|
*v = 0;
|
||||||
|
}
|
||||||
|
|
||||||
let mut salt = {
|
let mut salt = {
|
||||||
let mut prfc = prf.clone();
|
let mut prfc = prf.clone();
|
||||||
|
|
Loading…
Reference in New Issue