First version, with static references
This commit is contained in:
commit
6c1d9d1dca
|
@ -0,0 +1,2 @@
|
||||||
|
/target
|
||||||
|
Cargo.lock
|
|
@ -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"
|
|
@ -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.
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue