First version, with static references

This commit is contained in:
Steven Roose 2020-06-07 00:46:54 +01:00
commit 6c1d9d1dca
No known key found for this signature in database
GPG Key ID: 2F2A88D7F8D68E87
14 changed files with 19372 additions and 0 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
/target
Cargo.lock

30
Cargo.toml Normal file
View File

@ -0,0 +1,30 @@
[package]
name = "bip39"
version = "0.1.0"
authors = ["Steven Roose <steven@stevenroose.org>"]
license = "CC0-1.0"
homepage = "https://github.com/rust-bitcoin/rust-bip39/"
repository = "https://github.com/rust-bitcoin/rust-bip39/"
documentation = "https://docs.rs/bip39/"
description = "Library for BIP-39 Bitcoin mnemonic codes"
keywords = [ "crypto", "bitcoin", "bip39", "mnemonic" ]
readme = "README.md"
[lib]
name = "bip39"
path = "src/lib.rs"
[features]
# Note: English is the standard for bip39 so always included
chinese-simplified = []
chinese-traditional = []
czech = []
french = []
italian = []
japanese = []
korean = []
spanish = []
[dependencies]
bitcoin_hashes = "0.7.6"
unicode-normalization = "=0.1.9"

122
LICENSE Normal file
View File

@ -0,0 +1,122 @@
Creative Commons Legal Code
CC0 1.0 Universal
CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN
ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS
INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES
REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS
PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM
THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED
HEREUNDER.
Statement of Purpose
The laws of most jurisdictions throughout the world automatically confer
exclusive Copyright and Related Rights (defined below) upon the creator
and subsequent owner(s) (each and all, an "owner") of an original work of
authorship and/or a database (each, a "Work").
Certain owners wish to permanently relinquish those rights to a Work for
the purpose of contributing to a commons of creative, cultural and
scientific works ("Commons") that the public can reliably and without fear
of later claims of infringement build upon, modify, incorporate in other
works, reuse and redistribute as freely as possible in any form whatsoever
and for any purposes, including without limitation commercial purposes.
These owners may contribute to the Commons to promote the ideal of a free
culture and the further production of creative, cultural and scientific
works, or to gain reputation or greater distribution for their Work in
part through the use and efforts of others.
For these and/or other purposes and motivations, and without any
expectation of additional consideration or compensation, the person
associating CC0 with a Work (the "Affirmer"), to the extent that he or she
is an owner of Copyright and Related Rights in the Work, voluntarily
elects to apply CC0 to the Work and publicly distribute the Work under its
terms, with knowledge of his or her Copyright and Related Rights in the
Work and the meaning and intended legal effect of CC0 on those rights.
1. Copyright and Related Rights. A Work made available under CC0 may be
protected by copyright and related or neighboring rights ("Copyright and
Related Rights"). Copyright and Related Rights include, but are not
limited to, the following:
i. the right to reproduce, adapt, distribute, perform, display,
communicate, and translate a Work;
ii. moral rights retained by the original author(s) and/or performer(s);
iii. publicity and privacy rights pertaining to a person's image or
likeness depicted in a Work;
iv. rights protecting against unfair competition in regards to a Work,
subject to the limitations in paragraph 4(a), below;
v. rights protecting the extraction, dissemination, use and reuse of data
in a Work;
vi. database rights (such as those arising under Directive 96/9/EC of the
European Parliament and of the Council of 11 March 1996 on the legal
protection of databases, and under any national implementation
thereof, including any amended or successor version of such
directive); and
vii. other similar, equivalent or corresponding rights throughout the
world based on applicable law or treaty, and any national
implementations thereof.
2. Waiver. To the greatest extent permitted by, but not in contravention
of, applicable law, Affirmer hereby overtly, fully, permanently,
irrevocably and unconditionally waives, abandons, and surrenders all of
Affirmer's Copyright and Related Rights and associated claims and causes
of action, whether now known or unknown (including existing as well as
future claims and causes of action), in the Work (i) in all territories
worldwide, (ii) for the maximum duration provided by applicable law or
treaty (including future time extensions), (iii) in any current or future
medium and for any number of copies, and (iv) for any purpose whatsoever,
including without limitation commercial, advertising or promotional
purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each
member of the public at large and to the detriment of Affirmer's heirs and
successors, fully intending that such Waiver shall not be subject to
revocation, rescission, cancellation, termination, or any other legal or
equitable action to disrupt the quiet enjoyment of the Work by the public
as contemplated by Affirmer's express Statement of Purpose.
3. Public License Fallback. Should any part of the Waiver for any reason
be judged legally invalid or ineffective under applicable law, then the
Waiver shall be preserved to the maximum extent permitted taking into
account Affirmer's express Statement of Purpose. In addition, to the
extent the Waiver is so judged Affirmer hereby grants to each affected
person a royalty-free, non transferable, non sublicensable, non exclusive,
irrevocable and unconditional license to exercise Affirmer's Copyright and
Related Rights in the Work (i) in all territories worldwide, (ii) for the
maximum duration provided by applicable law or treaty (including future
time extensions), (iii) in any current or future medium and for any number
of copies, and (iv) for any purpose whatsoever, including without
limitation commercial, advertising or promotional purposes (the
"License"). The License shall be deemed effective as of the date CC0 was
applied by Affirmer to the Work. Should any part of the License for any
reason be judged legally invalid or ineffective under applicable law, such
partial invalidity or ineffectiveness shall not invalidate the remainder
of the License, and in such case Affirmer hereby affirms that he or she
will not (i) exercise any of his or her remaining Copyright and Related
Rights in the Work or (ii) assert any associated claims and causes of
action with respect to the Work, in either case contrary to Affirmer's
express Statement of Purpose.
4. Limitations and Disclaimers.
a. No trademark or patent rights held by Affirmer are waived, abandoned,
surrendered, licensed or otherwise affected by this document.
b. Affirmer offers the Work as-is and makes no representations or
warranties of any kind concerning the Work, express, implied,
statutory or otherwise, including without limitation warranties of
title, merchantability, fitness for a particular purpose, non
infringement, or the absence of latent or other defects, accuracy, or
the present or absence of errors, whether or not discoverable, all to
the greatest extent permissible under applicable law.
c. Affirmer disclaims responsibility for clearing rights of other persons
that may apply to the Work or any use thereof, including without
limitation any person's Copyright and Related Rights in the Work.
Further, Affirmer disclaims responsibility for obtaining any necessary
consents, permissions or other rights required for any use of the
Work.
d. Affirmer understands and acknowledges that Creative Commons is not a
party to this document and has no duty or obligation with respect to
this CC0 or use of the Work.

2050
src/chinese_simplified.rs Normal file

File diff suppressed because it is too large Load Diff

2050
src/chinese_traditional.rs Normal file

File diff suppressed because it is too large Load Diff

2050
src/czech.rs Normal file

File diff suppressed because it is too large Load Diff

2050
src/english.rs Normal file

File diff suppressed because it is too large Load Diff

2050
src/french.rs Normal file

File diff suppressed because it is too large Load Diff

2050
src/italian.rs Normal file

File diff suppressed because it is too large Load Diff

2050
src/japanese.rs Normal file

File diff suppressed because it is too large Load Diff

2050
src/korean.rs Normal file

File diff suppressed because it is too large Load Diff

720
src/lib.rs Normal file
View File

@ -0,0 +1,720 @@
// Rust Bitcoin Library
// Written in 2020 by
// Steven Roose <steven@stevenroose.org>
// To the extent possible under law, the author(s) have dedicated all
// copyright and related and neighboring rights to this software to
// the public domain worldwide. This software is distributed without
// any warranty.
//
// You should have received a copy of the CC0 Public Domain Dedication
// along with this software.
// If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
//
//! # BIP39 Mnemonic Codes
//!
//! We currently don't implement seed generation from the phrase.
//!
//! https://github.com/bitcoin/bips/blob/master/bip-0039.mediawiki
//!
extern crate bitcoin_hashes;
extern crate unicode_normalization;
use std::{error, fmt, str};
use bitcoin_hashes::{sha256, Hash};
use unicode_normalization::UnicodeNormalization;
mod pbkdf2;
mod english;
#[cfg(feature = "chinese-simplified")]
mod chinese_simplified;
#[cfg(feature = "chinese-traditional")]
mod chinese_traditional;
#[cfg(feature = "czech")]
mod czech;
#[cfg(feature = "french")]
mod french;
#[cfg(feature = "italian")]
mod italian;
#[cfg(feature = "japanese")]
mod japanese;
#[cfg(feature = "korean")]
mod korean;
#[cfg(feature = "spanish")]
mod spanish;
#[cfg(feature = "japanese")]
/// The ideagrapic space that should be used for Japanese lists.
const IDEAGRAPHIC_SPACE: char = ' ';
/// A BIP39 error.
#[derive(Clone, PartialEq, Eq, Debug)]
pub enum Error {
/// Mnemonic has a word count that is not a multiple of 6.
BadWordCount(usize),
/// Mnemonic contains an unknown word.
UnknownWord(String),
/// Entropy was not a multiple of 32 bits.
/// Parameter is the number of bits in the entropy.
BadEntropyBitCount(usize),
/// The mnemonic has an invalid checksum.
InvalidChecksum,
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
Error::BadWordCount(c) => write!(f,
"mnemonic has a word count that is not a multiple of 6: {}", c,
),
Error::UnknownWord(ref w) => write!(f, "mnemonic contains an unknown word: {}", w),
Error::BadEntropyBitCount(c) => write!(f,
"entropy was not a multiple of 32 bits: {} bits", c,
),
Error::InvalidChecksum => write!(f, "the mnemonic has an invalid checksum"),
}
}
}
impl error::Error for Error {
fn cause(&self) -> Option<&error::Error> {
None
}
fn description(&self) -> &str {
"description() is deprecated; use Display"
}
}
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub enum Language {
/// The English language.
English,
#[cfg(feature = "chinese-simplified")]
/// The Simplified Chinese language.
SimplifiedChinese,
#[cfg(feature = "chinese-traditional")]
/// The Traditional Chinese language.
TraditionalChinese,
#[cfg(feature = "czech")]
/// The Czech language.
Czech,
#[cfg(feature = "french")]
/// The French language.
French,
#[cfg(feature = "italian")]
/// The Italian language.
Italian,
#[cfg(feature = "japanese")]
/// The Japanese language.
Japanese,
#[cfg(feature = "korean")]
/// The Korean language.
Korean,
#[cfg(feature = "spanish")]
/// The Spanish language.
Spanish,
}
impl Language {
/// The word list for this language.
fn word_list(self) -> &'static [&'static str; 2048] {
match self {
Language::English => &english::WORDS,
#[cfg(feature = "chinese-simplified")]
Language::SimplifiedChinese => &chinese_simplified::WORDS,
#[cfg(feature = "chinese-traditional")]
Language::TraditionalChinese => &chinese_traditional::WORDS,
#[cfg(feature = "czech")]
Language::Czech => &czech::WORDS,
#[cfg(feature = "french")]
Language::French => &french::WORDS,
#[cfg(feature = "italian")]
Language::Italian => &italian::WORDS,
#[cfg(feature = "japanese")]
Language::Japanese => &japanese::WORDS,
#[cfg(feature = "korean")]
Language::Korean => &korean::WORDS,
#[cfg(feature = "spanish")]
Language::Spanish => &spanish::WORDS,
}
}
}
impl fmt::Display for Language {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt::Debug::fmt(self, f)
}
}
/// A mnemonic code.
#[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub struct Mnemonic(Vec<&'static str>);
impl Mnemonic {
/// Create a new [Mnemonic] in the specified language from the given entropy.
/// Entropy must be a multiple of 32 bits (4 bytes).
pub fn from_entropy_in(language: Language, entropy: &[u8]) -> Result<Mnemonic, Error> {
let word_list = language.word_list();
if entropy.len() % 4 != 0 {
return Err(Error::BadEntropyBitCount(entropy.len() * 8));
}
let check = sha256::Hash::hash(&entropy);
let mut bits = vec![false; entropy.len() * 8 + entropy.len() / 4];
for i in 0..entropy.len() {
for j in 0..8 {
bits[i * 8 + j] = (entropy[i] & (1 << (7 - j))) > 0;
}
}
for i in 0..entropy.len() / 4 {
bits[8 * entropy.len() + i] = (check[i / 8] & (1 << (7 - (i % 8)))) > 0;
}
let mlen = entropy.len() * 3 / 4;
let mut words = Vec::new();
for i in 0..mlen {
let mut idx = 0;
for j in 0..11 {
if bits[i * 11 + j] {
idx += 1 << (10 - j);
}
}
words.push(word_list[idx]);
}
Ok(Mnemonic(words))
}
/// Create a new English [Mnemonic] in from the given entropy.
/// Entropy must be a multiple of 32 bits (4 bytes).
pub fn from_entropy(entropy: &[u8]) -> Result<Mnemonic, Error> {
Mnemonic::from_entropy_in(Language::English, entropy)
}
/// Parse a mnemonic in the given language.
pub fn from_str_in(language: Language, s: &str) -> Result<Mnemonic, Error> {
let word_list = language.word_list();
let normalized = s.nfkd().to_string();
let words: Vec<_> = normalized.split(' ').collect();
if words.len() < 6 || words.len() % 6 != 0 {
return Err(Error::BadWordCount(words.len()));
}
let mut mnemonic = Vec::with_capacity(words.len());
let mut bits = vec![false; words.len() * 11];
for (i, word) in words.iter().enumerate() {
if let Ok(idx) = word_list.binary_search(word) {
mnemonic.push(word_list[idx]);
for j in 0..11 {
bits[i * 11 + j] = idx >> (10 - j) & 1 == 1;
}
} else {
return Err(Error::UnknownWord(word.to_string()));
}
}
// Verify the checksum.
let mut entropy = vec![0u8; bits.len() / 33 * 4];
for i in 0..entropy.len() {
for j in 0..8 {
if bits[i * 8 + j] {
entropy[i] += 1 << (7 - j);
}
}
}
let check = sha256::Hash::hash(&entropy);
for i in 0..entropy.len() / 4 {
if bits[8 * entropy.len() + i] != ((check[i / 8] & (1 << (7 - (i % 8)))) > 0) {
return Err(Error::InvalidChecksum);
}
}
Ok(Mnemonic(mnemonic))
}
/// Convert this mnemonic to a vector of bytes in UTF-8 NKFD normalized.
pub fn to_bytes(&self) -> Vec<u8> {
self.to_string().nfkd().map(|c| c as u8).collect()
}
/// Convert to seed bytes.
pub fn to_seed(&self, passphrase: &str) -> Vec<u8> {
const PBKDF2_ROUNDS: usize = 2048;
const PBKDF2_BYTES: usize = 64;
let salt = format!("mnemonic{}", passphrase);
let normalized_salt = salt.nfkd().to_string();
let mut seed = vec![0u8; PBKDF2_BYTES];
pbkdf2::pbkdf2(&self.to_bytes(), &normalized_salt.as_bytes(), PBKDF2_ROUNDS, &mut seed);
seed
}
}
impl fmt::Display for Mnemonic {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut words = self.0.iter();
write!(f, "{}", words.next().expect("wordlist never empty"))?;
for word in words {
write!(f, " {}", word)?;
}
Ok(())
}
}
impl str::FromStr for Mnemonic {
type Err = Error;
fn from_str(s: &str) -> Result<Mnemonic, Error> {
let languages = [
Language::English,
#[cfg(feature = "chinese-simplified")]
Language::SimplifiedChinese,
#[cfg(feature = "chinese-traditional")]
Language::TraditionalChinese,
#[cfg(feature = "czech")]
Language::Czech,
#[cfg(feature = "french")]
Language::French,
#[cfg(feature = "italian")]
Language::Italian,
#[cfg(feature = "japanese")]
Language::Japanese,
#[cfg(feature = "korean")]
Language::Korean,
#[cfg(feature = "spanish")]
Language::Spanish,
];
let normalized = s.nfkd().to_string();
let first_word = normalized.splitn(2, ' ').next().unwrap();
if first_word.len() == 0 {
return Err(Error::BadWordCount(0));
}
for language in &languages {
if language.word_list().binary_search(&first_word).is_ok() {
return Mnemonic::from_str_in(*language, s);
}
}
Err(Error::UnknownWord(first_word.to_owned()))
}
}
#[cfg(test)]
mod test {
use super::*;
use std::io::Write;
use std::str::FromStr;
use bitcoin_hashes::{sha256, Hash};
use bitcoin_hashes::hex::FromHex;
#[cfg(all(
feature = "chinese-simplified", feature = "chinese-traditional", feature = "czech",
feature = "french", feature = "italian", feature = "japanese", feature = "korean",
feature = "spanish"
))]
#[test]
fn validate_list_checksums() {
//! In this test, we ensure that the wordlists are identical.
//!
//! They are as follows in the bips repository:
//! 5c5942792bd8340cb8b27cd592f1015edf56a8c5b26276ee18a482428e7c5726 chinese_simplified.txt
//! 417b26b3d8500a4ae3d59717d7011952db6fc2fb84b807f3f94ac734e89c1b5f chinese_traditional.txt
//! 7e80e161c3e93d9554c2efb78d4e3cebf8fc727e9c52e03b83b94406bdcc95fc czech.txt
//! 2f5eed53a4727b4bf8880d8f3f199efc90e58503646d9ff8eff3a2ed3b24dbda english.txt
//! ebc3959ab7801a1df6bac4fa7d970652f1df76b683cd2f4003c941c63d517e59 french.txt
//! d392c49fdb700a24cd1fceb237c1f65dcc128f6b34a8aacb58b59384b5c648c2 italian.txt
//! 2eed0aef492291e061633d7ad8117f1a2b03eb80a29d0e4e3117ac2528d05ffd japanese.txt
//! 9e95f86c167de88f450f0aaf89e87f6624a57f973c67b516e338e8e8b8897f60 korean.txt
//! 46846a5a0139d1e3cb77293e521c2865f7bcdb82c44e8d0a06a2cd0ecba48c0b spanish.txt
let checksums = [
(Language::SimplifiedChinese, "5c5942792bd8340cb8b27cd592f1015edf56a8c5b26276ee18a482428e7c5726"),
(Language::TraditionalChinese, "417b26b3d8500a4ae3d59717d7011952db6fc2fb84b807f3f94ac734e89c1b5f"),
(Language::Czech, "7e80e161c3e93d9554c2efb78d4e3cebf8fc727e9c52e03b83b94406bdcc95fc"),
(Language::English, "2f5eed53a4727b4bf8880d8f3f199efc90e58503646d9ff8eff3a2ed3b24dbda"),
(Language::French, "ebc3959ab7801a1df6bac4fa7d970652f1df76b683cd2f4003c941c63d517e59"),
(Language::Italian, "d392c49fdb700a24cd1fceb237c1f65dcc128f6b34a8aacb58b59384b5c648c2"),
(Language::Japanese, "2eed0aef492291e061633d7ad8117f1a2b03eb80a29d0e4e3117ac2528d05ffd"),
(Language::Korean, "9e95f86c167de88f450f0aaf89e87f6624a57f973c67b516e338e8e8b8897f60"),
(Language::Spanish, "46846a5a0139d1e3cb77293e521c2865f7bcdb82c44e8d0a06a2cd0ecba48c0b"),
];
for (lang, sum) in &checksums {
let mut digest = sha256::Hash::engine();
for word in lang.word_list().iter() {
write!(&mut digest, "{}\n", word).unwrap();
}
assert_eq!(sha256::Hash::from_engine(digest).to_string(), *sum,
"word list for language {} failed checksum check", lang,
);
}
}
/// Test a single test vector.
fn test_vector(entropy: &[u8], mnemonic_str: &str, passphrase: &str, seed: &[u8], language: Language) {
let mnemonic = Mnemonic::from_entropy_in(language, &entropy).unwrap();
assert_eq!(&mnemonic.to_string(), mnemonic_str,
"failed test vector in language {}: {}", language, mnemonic_str);
assert_eq!(mnemonic, Mnemonic::from_str_in(language, mnemonic_str).unwrap(),
"failed test vector in language {}: {}", language, mnemonic_str);
assert_eq!(mnemonic, Mnemonic::from_str(&mnemonic_str).unwrap(),
"failed test vector in language {}: {}", language, mnemonic_str);
assert_eq!(seed, &mnemonic.to_seed(passphrase)[..],
"failed test vector in language {}: {}", language, mnemonic_str);
}
#[test]
fn test_vectors_english() {
// These vectors are tuples of
// (entropy, mnemonic, seed)
let test_vectors = [
(
"00000000000000000000000000000000",
"abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about",
"c55257c360c07c72029aebc1b53c05ed0362ada38ead3e3e9efa3708e53495531f09a6987599d18264c1e1c92f2cf141630c7a3c4ab7c81b2f001698e7463b04",
),
(
"7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f",
"legal winner thank year wave sausage worth useful legal winner thank yellow",
"2e8905819b8723fe2c1d161860e5ee1830318dbf49a83bd451cfb8440c28bd6fa457fe1296106559a3c80937a1c1069be3a3a5bd381ee6260e8d9739fce1f607",
),
(
"80808080808080808080808080808080",
"letter advice cage absurd amount doctor acoustic avoid letter advice cage above",
"d71de856f81a8acc65e6fc851a38d4d7ec216fd0796d0a6827a3ad6ed5511a30fa280f12eb2e47ed2ac03b5c462a0358d18d69fe4f985ec81778c1b370b652a8",
),
(
"ffffffffffffffffffffffffffffffff",
"zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo wrong",
"ac27495480225222079d7be181583751e86f571027b0497b5b5d11218e0a8a13332572917f0f8e5a589620c6f15b11c61dee327651a14c34e18231052e48c069",
),
(
"000000000000000000000000000000000000000000000000",
"abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon agent",
"035895f2f481b1b0f01fcf8c289c794660b289981a78f8106447707fdd9666ca06da5a9a565181599b79f53b844d8a71dd9f439c52a3d7b3e8a79c906ac845fa",
),
(
"7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f",
"legal winner thank year wave sausage worth useful legal winner thank year wave sausage worth useful legal will",
"f2b94508732bcbacbcc020faefecfc89feafa6649a5491b8c952cede496c214a0c7b3c392d168748f2d4a612bada0753b52a1c7ac53c1e93abd5c6320b9e95dd",
),
(
"808080808080808080808080808080808080808080808080",
"letter advice cage absurd amount doctor acoustic avoid letter advice cage absurd amount doctor acoustic avoid letter always",
"107d7c02a5aa6f38c58083ff74f04c607c2d2c0ecc55501dadd72d025b751bc27fe913ffb796f841c49b1d33b610cf0e91d3aa239027f5e99fe4ce9e5088cd65",
),
(
"ffffffffffffffffffffffffffffffffffffffffffffffff",
"zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo when",
"0cd6e5d827bb62eb8fc1e262254223817fd068a74b5b449cc2f667c3f1f985a76379b43348d952e2265b4cd129090758b3e3c2c49103b5051aac2eaeb890a528",
),
(
"0000000000000000000000000000000000000000000000000000000000000000",
"abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon art",
"bda85446c68413707090a52022edd26a1c9462295029f2e60cd7c4f2bbd3097170af7a4d73245cafa9c3cca8d561a7c3de6f5d4a10be8ed2a5e608d68f92fcc8",
),
(
"7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f",
"legal winner thank year wave sausage worth useful legal winner thank year wave sausage worth useful legal winner thank year wave sausage worth title",
"bc09fca1804f7e69da93c2f2028eb238c227f2e9dda30cd63699232578480a4021b146ad717fbb7e451ce9eb835f43620bf5c514db0f8add49f5d121449d3e87",
),
(
"8080808080808080808080808080808080808080808080808080808080808080",
"letter advice cage absurd amount doctor acoustic avoid letter advice cage absurd amount doctor acoustic avoid letter advice cage absurd amount doctor acoustic bless",
"c0c519bd0e91a2ed54357d9d1ebef6f5af218a153624cf4f2da911a0ed8f7a09e2ef61af0aca007096df430022f7a2b6fb91661a9589097069720d015e4e982f",
),
(
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
"zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo vote",
"dd48c104698c30cfe2b6142103248622fb7bb0ff692eebb00089b32d22484e1613912f0a5b694407be899ffd31ed3992c456cdf60f5d4564b8ba3f05a69890ad",
),
(
"9e885d952ad362caeb4efe34a8e91bd2",
"ozone drill grab fiber curtain grace pudding thank cruise elder eight picnic",
"274ddc525802f7c828d8ef7ddbcdc5304e87ac3535913611fbbfa986d0c9e5476c91689f9c8a54fd55bd38606aa6a8595ad213d4c9c9f9aca3fb217069a41028",
),
(
"6610b25967cdcca9d59875f5cb50b0ea75433311869e930b",
"gravity machine north sort system female filter attitude volume fold club stay feature office ecology stable narrow fog",
"628c3827a8823298ee685db84f55caa34b5cc195a778e52d45f59bcf75aba68e4d7590e101dc414bc1bbd5737666fbbef35d1f1903953b66624f910feef245ac",
),
(
"68a79eaca2324873eacc50cb9c6eca8cc68ea5d936f98787c60c7ebc74e6ce7c",
"hamster diagram private dutch cause delay private meat slide toddler razor book happy fancy gospel tennis maple dilemma loan word shrug inflict delay length",
"64c87cde7e12ecf6704ab95bb1408bef047c22db4cc7491c4271d170a1b213d20b385bc1588d9c7b38f1b39d415665b8a9030c9ec653d75e65f847d8fc1fc440",
),
(
"c0ba5a8e914111210f2bd131f3d5e08d",
"scheme spot photo card baby mountain device kick cradle pact join borrow",
"ea725895aaae8d4c1cf682c1bfd2d358d52ed9f0f0591131b559e2724bb234fca05aa9c02c57407e04ee9dc3b454aa63fbff483a8b11de949624b9f1831a9612",
),
(
"6d9be1ee6ebd27a258115aad99b7317b9c8d28b6d76431c3",
"horn tenant knee talent sponsor spell gate clip pulse soap slush warm silver nephew swap uncle crack brave",
"fd579828af3da1d32544ce4db5c73d53fc8acc4ddb1e3b251a31179cdb71e853c56d2fcb11aed39898ce6c34b10b5382772db8796e52837b54468aeb312cfc3d",
),
(
"9f6a2878b2520799a44ef18bc7df394e7061a224d2c33cd015b157d746869863",
"panda eyebrow bullet gorilla call smoke muffin taste mesh discover soft ostrich alcohol speed nation flash devote level hobby quick inner drive ghost inside",
"72be8e052fc4919d2adf28d5306b5474b0069df35b02303de8c1729c9538dbb6fc2d731d5f832193cd9fb6aeecbc469594a70e3dd50811b5067f3b88b28c3e8d",
),
(
"23db8160a31d3e0dca3688ed941adbf3",
"cat swing flag economy stadium alone churn speed unique patch report train",
"deb5f45449e615feff5640f2e49f933ff51895de3b4381832b3139941c57b59205a42480c52175b6efcffaa58a2503887c1e8b363a707256bdd2b587b46541f5",
),
(
"8197a4a47f0425faeaa69deebc05ca29c0a5b5cc76ceacc0",
"light rule cinnamon wrap drastic word pride squirrel upgrade then income fatal apart sustain crack supply proud access",
"4cbdff1ca2db800fd61cae72a57475fdc6bab03e441fd63f96dabd1f183ef5b782925f00105f318309a7e9c3ea6967c7801e46c8a58082674c860a37b93eda02",
),
(
"066dca1a2bb7e8a1db2832148ce9933eea0f3ac9548d793112d9a95c9407efad",
"all hour make first leader extend hole alien behind guard gospel lava path output census museum junior mass reopen famous sing advance salt reform",
"26e975ec644423f4a4c4f4215ef09b4bd7ef924e85d1d17c4cf3f136c2863cf6df0a475045652c57eb5fb41513ca2a2d67722b77e954b4b3fc11f7590449191d",
),
(
"f30f8c1da665478f49b001d94c5fc452",
"vessel ladder alter error federal sibling chat ability sun glass valve picture",
"2aaa9242daafcee6aa9d7269f17d4efe271e1b9a529178d7dc139cd18747090bf9d60295d0ce74309a78852a9caadf0af48aae1c6253839624076224374bc63f",
),
(
"c10ec20dc3cd9f652c7fac2f1230f7a3c828389a14392f05",
"scissors invite lock maple supreme raw rapid void congress muscle digital elegant little brisk hair mango congress clump",
"7b4a10be9d98e6cba265566db7f136718e1398c71cb581e1b2f464cac1ceedf4f3e274dc270003c670ad8d02c4558b2f8e39edea2775c9e232c7cb798b069e88",
),
(
"f585c11aec520db57dd353c69554b21a89b20fb0650966fa0a9d6f74fd989d8f",
"void come effort suffer camp survey warrior heavy shoot primary clutch crush open amazing screen patrol group space point ten exist slush involve unfold",
"01f5bced59dec48e362f2c45b5de68b9fd6c92c6634f44d6d40aab69056506f0e35524a518034ddc1192e1dacd32c1ed3eaa3c3b131c88ed8e7e54c49a5d0998",
)
];
for vector in &test_vectors {
let entropy = Vec::<u8>::from_hex(&vector.0).unwrap();
let mnemonic = vector.1;
let seed = Vec::<u8>::from_hex(&vector.2).unwrap();
test_vector(&entropy, mnemonic, "TREZOR", &seed, Language::English);
}
}
#[test]
fn test_invalid_engish() {
// correct phrase:
// "letter advice cage absurd amount doctor acoustic avoid letter advice cage above"
assert_eq!(
Mnemonic::from_str(
"getter advice cage absurd amount doctor acoustic avoid letter advice cage above",
),
Err(Error::UnknownWord("getter".to_owned())),
);
assert_eq!(
Mnemonic::from_str(
"advice cage absurd amount doctor acoustic avoid letter advice cage above",
),
Err(Error::BadWordCount(11)),
);
assert_eq!(
Mnemonic::from_str(
"primary advice cage absurd amount doctor acoustic avoid letter advice cage above",
),
Err(Error::InvalidChecksum),
);
}
#[cfg(feature = "japanese")]
#[test]
fn test_vectors_japanese() {
// These vectors are tuples of
// (entropy, mnemonic, passphrase, seed)
let vectors = [
(
"00000000000000000000000000000000",
"あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あおぞら",
"㍍ガバヴァぱばぐゞちぢ十人十色",
"a262d6fb6122ecf45be09c50492b31f92e9beb7d9a845987a02cefda57a15f9c467a17872029a9e92299b5cbdf306e3a0ee620245cbd508959b6cb7ca637bd55",
),
(
"7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f",
"そつう れきだい ほんやく わかす りくつ ばいか ろせん やちん そつう れきだい ほんやく わかめ",
"㍍ガバヴァぱばぐゞちぢ十人十色",
"aee025cbe6ca256862f889e48110a6a382365142f7d16f2b9545285b3af64e542143a577e9c144e101a6bdca18f8d97ec3366ebf5b088b1c1af9bc31346e60d9",
),
(
"80808080808080808080808080808080",
"そとづら あまど おおう あこがれる いくぶん けいけん あたえる いよく そとづら あまど おおう あかちゃん",
"㍍ガバヴァぱばぐゞちぢ十人十色",
"e51736736ebdf77eda23fa17e31475fa1d9509c78f1deb6b4aacfbd760a7e2ad769c714352c95143b5c1241985bcb407df36d64e75dd5a2b78ca5d2ba82a3544",
),
(
"ffffffffffffffffffffffffffffffff",
"われる われる われる われる われる われる われる われる われる われる われる ろんぶん",
"㍍ガバヴァぱばぐゞちぢ十人十色",
"4cd2ef49b479af5e1efbbd1e0bdc117f6a29b1010211df4f78e2ed40082865793e57949236c43b9fe591ec70e5bb4298b8b71dc4b267bb96ed4ed282c8f7761c",
),
(
"000000000000000000000000000000000000000000000000",
"あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あらいぐま",
"㍍ガバヴァぱばぐゞちぢ十人十色",
"d99e8f1ce2d4288d30b9c815ae981edd923c01aa4ffdc5dee1ab5fe0d4a3e13966023324d119105aff266dac32e5cd11431eeca23bbd7202ff423f30d6776d69",
),
(
"7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f",
"そつう れきだい ほんやく わかす りくつ ばいか ろせん やちん そつう れきだい ほんやく わかす りくつ ばいか ろせん やちん そつう れいぎ",
"㍍ガバヴァぱばぐゞちぢ十人十色",
"eaaf171efa5de4838c758a93d6c86d2677d4ccda4a064a7136344e975f91fe61340ec8a615464b461d67baaf12b62ab5e742f944c7bd4ab6c341fbafba435716",
),
(
"808080808080808080808080808080808080808080808080",
"そとづら あまど おおう あこがれる いくぶん けいけん あたえる いよく そとづら あまど おおう あこがれる いくぶん けいけん あたえる いよく そとづら いきなり",
"㍍ガバヴァぱばぐゞちぢ十人十色",
"aec0f8d3167a10683374c222e6e632f2940c0826587ea0a73ac5d0493b6a632590179a6538287641a9fc9df8e6f24e01bf1be548e1f74fd7407ccd72ecebe425",
),
(
"ffffffffffffffffffffffffffffffffffffffffffffffff",
"われる われる われる われる われる われる われる われる われる われる われる われる われる われる われる われる われる りんご",
"㍍ガバヴァぱばぐゞちぢ十人十色",
"f0f738128a65b8d1854d68de50ed97ac1831fc3a978c569e415bbcb431a6a671d4377e3b56abd518daa861676c4da75a19ccb41e00c37d086941e471a4374b95",
),
(
"0000000000000000000000000000000000000000000000000000000000000000",
"あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん いってい",
"㍍ガバヴァぱばぐゞちぢ十人十色",
"23f500eec4a563bf90cfda87b3e590b211b959985c555d17e88f46f7183590cd5793458b094a4dccc8f05807ec7bd2d19ce269e20568936a751f6f1ec7c14ddd",
),
(
"7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f",
"そつう れきだい ほんやく わかす りくつ ばいか ろせん やちん そつう れきだい ほんやく わかす りくつ ばいか ろせん やちん そつう れきだい ほんやく わかす りくつ ばいか ろせん まんきつ",
"㍍ガバヴァぱばぐゞちぢ十人十色",
"cd354a40aa2e241e8f306b3b752781b70dfd1c69190e510bc1297a9c5738e833bcdc179e81707d57263fb7564466f73d30bf979725ff783fb3eb4baa86560b05",
),
(
"8080808080808080808080808080808080808080808080808080808080808080",
"そとづら あまど おおう あこがれる いくぶん けいけん あたえる いよく そとづら あまど おおう あこがれる いくぶん けいけん あたえる いよく そとづら あまど おおう あこがれる いくぶん けいけん あたえる うめる",
"㍍ガバヴァぱばぐゞちぢ十人十色",
"6b7cd1b2cdfeeef8615077cadd6a0625f417f287652991c80206dbd82db17bf317d5c50a80bd9edd836b39daa1b6973359944c46d3fcc0129198dc7dc5cd0e68",
),
(
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
"われる われる われる われる われる われる われる われる われる われる われる われる われる われる われる われる われる われる われる われる われる われる われる らいう",
"㍍ガバヴァぱばぐゞちぢ十人十色",
"a44ba7054ac2f9226929d56505a51e13acdaa8a9097923ca07ea465c4c7e294c038f3f4e7e4b373726ba0057191aced6e48ac8d183f3a11569c426f0de414623",
),
(
"77c2b00716cec7213839159e404db50d",
"せまい うちがわ あずき かろう めずらしい だんち ますく おさめる ていぼう あたる すあな えしゃく",
"㍍ガバヴァぱばぐゞちぢ十人十色",
"344cef9efc37d0cb36d89def03d09144dd51167923487eec42c487f7428908546fa31a3c26b7391a2b3afe7db81b9f8c5007336b58e269ea0bd10749a87e0193",
),
(
"b63a9c59a6e641f288ebc103017f1da9f8290b3da6bdef7b",
"ぬすむ ふっかつ うどん こうりつ しつじ りょうり おたがい せもたれ あつめる いちりゅう はんしゃ ごますり そんけい たいちょう らしんばん ぶんせき やすみ ほいく",
"㍍ガバヴァぱばぐゞちぢ十人十色",
"b14e7d35904cb8569af0d6a016cee7066335a21c1c67891b01b83033cadb3e8a034a726e3909139ecd8b2eb9e9b05245684558f329b38480e262c1d6bc20ecc4",
),
(
"3e141609b97933b66a060dcddc71fad1d91677db872031e85f4c015c5e7e8982",
"くのう てぬぐい そんかい すろっと ちきゅう ほあん とさか はくしゅ ひびく みえる そざい てんすう たんぴん くしょう すいようび みけん きさらぎ げざん ふくざつ あつかう はやい くろう おやゆび こすう",
"㍍ガバヴァぱばぐゞちぢ十人十色",
"32e78dce2aff5db25aa7a4a32b493b5d10b4089923f3320c8b287a77e512455443298351beb3f7eb2390c4662a2e566eec5217e1a37467af43b46668d515e41b",
),
(
"0460ef47585604c5660618db2e6a7e7f",
"あみもの いきおい ふいうち にげる ざんしょ じかん ついか はたん ほあん すんぽう てちがい わかめ",
"㍍ガバヴァぱばぐゞちぢ十人十色",
"0acf902cd391e30f3f5cb0605d72a4c849342f62bd6a360298c7013d714d7e58ddf9c7fdf141d0949f17a2c9c37ced1d8cb2edabab97c4199b142c829850154b",
),
(
"72f60ebac5dd8add8d2a25a797102c3ce21bc029c200076f",
"すろっと にくしみ なやむ たとえる へいこう すくう きない けってい とくべつ ねっしん いたみ せんせい おくりがな まかい とくい けあな いきおい そそぐ",
"㍍ガバヴァぱばぐゞちぢ十人十色",
"9869e220bec09b6f0c0011f46e1f9032b269f096344028f5006a6e69ea5b0b8afabbb6944a23e11ebd021f182dd056d96e4e3657df241ca40babda532d364f73",
),
(
"2c85efc7f24ee4573d2b81a6ec66cee209b2dcbd09d8eddc51e0215b0b68e416",
"かほご きうい ゆたか みすえる もらう がっこう よそう ずっと ときどき したうけ にんか はっこう つみき すうじつ よけい くげん もくてき まわり せめる げざい にげる にんたい たんそく ほそく",
"㍍ガバヴァぱばぐゞちぢ十人十色",
"713b7e70c9fbc18c831bfd1f03302422822c3727a93a5efb9659bec6ad8d6f2c1b5c8ed8b0b77775feaf606e9d1cc0a84ac416a85514ad59f5541ff5e0382481",
),
(
"eaebabb2383351fd31d703840b32e9e2",
"めいえん さのう めだつ すてる きぬごし ろんぱ はんこ まける たいおう さかいし ねんいり はぶらし",
"㍍ガバヴァぱばぐゞちぢ十人十色",
"06e1d5289a97bcc95cb4a6360719131a786aba057d8efd603a547bd254261c2a97fcd3e8a4e766d5416437e956b388336d36c7ad2dba4ee6796f0249b10ee961",
),
(
"7ac45cfe7722ee6c7ba84fbc2d5bd61b45cb2fe5eb65aa78",
"せんぱい おしえる ぐんかん もらう きあい きぼう やおや いせえび のいず じゅしん よゆう きみつ さといも ちんもく ちわわ しんせいじ とめる はちみつ",
"㍍ガバヴァぱばぐゞちぢ十人十色",
"1fef28785d08cbf41d7a20a3a6891043395779ed74503a5652760ee8c24dfe60972105ee71d5168071a35ab7b5bd2f8831f75488078a90f0926c8e9171b2bc4a",
),
(
"4fa1a8bc3e6d80ee1316050e862c1812031493212b7ec3f3bb1b08f168cabeef",
"こころ いどう きあつ そうがんきょう へいあん せつりつ ごうせい はいち いびき きこく あんい おちつく きこえる けんとう たいこ すすめる はっけん ていど はんおん いんさつ うなぎ しねま れいぼう みつかる",
"㍍ガバヴァぱばぐゞちぢ十人十色",
"43de99b502e152d4c198542624511db3007c8f8f126a30818e856b2d8a20400d29e7a7e3fdd21f909e23be5e3c8d9aee3a739b0b65041ff0b8637276703f65c2",
),
(
"18ab19a9f54a9274f03e5209a2ac8a91",
"うりきれ さいせい じゆう むろん とどける ぐうたら はいれつ ひけつ いずれ うちあわせ おさめる おたく",
"㍍ガバヴァぱばぐゞちぢ十人十色",
"3d711f075ee44d8b535bb4561ad76d7d5350ea0b1f5d2eac054e869ff7963cdce9581097a477d697a2a9433a0c6884bea10a2193647677977c9820dd0921cbde",
),
(
"18a2e1d81b8ecfb2a333adcb0c17a5b9eb76cc5d05db91a4",
"うりきれ うねる せっさたくま きもち めんきょ へいたく たまご ぜっく びじゅつかん さんそ むせる せいじ ねくたい しはらい せおう ねんど たんまつ がいけん",
"㍍ガバヴァぱばぐゞちぢ十人十色",
"753ec9e333e616e9471482b4b70a18d413241f1e335c65cd7996f32b66cf95546612c51dcf12ead6f805f9ee3d965846b894ae99b24204954be80810d292fcdd",
),
(
"15da872c95a13dd738fbf50e427583ad61f18fd99f628c417a61cf8343c90419",
"うちゅう ふそく ひしょ がちょう うけもつ めいそう みかん そざい いばる うけとる さんま さこつ おうさま ぱんつ しひょう めした たはつ いちぶ つうじょう てさぎょう きつね みすえる いりぐち かめれおん",
"㍍ガバヴァぱばぐゞちぢ十人十色",
"346b7321d8c04f6f37b49fdf062a2fddc8e1bf8f1d33171b65074531ec546d1d3469974beccb1a09263440fc92e1042580a557fdce314e27ee4eabb25fa5e5fe",
)
];
for vector in &vectors {
let entropy = Vec::<u8>::from_hex(&vector.0).unwrap();
let mnemonic = vector.1;
let passphrase = vector.2;
let seed = Vec::<u8>::from_hex(&vector.3).unwrap();
test_vector(&entropy, mnemonic, passphrase, &seed, Language::Japanese);
}
}
}

48
src/pbkdf2.rs Normal file
View File

@ -0,0 +1,48 @@
use bitcoin_hashes::{hmac, sha512, Hash, HashEngine};
// Method borrowed from rust-bitcoin's endian module.
#[inline]
fn u32_to_array_be(val: u32) -> [u8; 4] {
debug_assert_eq!(::std::mem::size_of::<u32>(), 4); // size_of isn't a constfn in 1.22
let mut res = [0; 4];
for i in 0..4 {
res[i] = ((val >> (4 - i - 1)*8) & 0xff) as u8;
}
res
}
#[inline]
fn xor(res: &mut [u8], salt: &[u8]) {
debug_assert!(salt.len() >= res.len(), "length mismatch in xor");
res.iter_mut().zip(salt.iter()).for_each(|(a, b)| *a ^= b);
}
/// PBKDF2-HMAC-SHA512 implementation using bitcoin_hashes.
pub(crate) fn pbkdf2(passphrase: &[u8], salt: &[u8], c: usize, res: &mut [u8]) {
let prf = hmac::HmacEngine::<sha512::Hash>::new(passphrase);
for (i, chunk) in res.chunks_mut(sha512::Hash::LEN).enumerate() {
for v in chunk.iter_mut() { *v = 0; }
let mut salt = {
let mut prfc = prf.clone();
prfc.input(salt);
prfc.input(&u32_to_array_be((i + 1) as u32));
let salt = hmac::Hmac::from_engine(prfc).into_inner();
xor(chunk, &salt);
salt
};
for _ in 1..c {
let mut prfc = prf.clone();
prfc.input(&salt);
salt = hmac::Hmac::from_engine(prfc).into_inner();
xor(chunk, &salt);
}
}
}

2050
src/spanish.rs Normal file

File diff suppressed because it is too large Load Diff