Skip to content

Commit 1786f61

Browse files
authored
Merge pull request #8 from rust-bitcoin/sebas-comments
Fixing minor things
2 parents 1f1d761 + 04e139c commit 1786f61

File tree

5 files changed

+102
-69
lines changed

5 files changed

+102
-69
lines changed

.travis.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@ before_install:
1919
script:
2020
- cargo build --verbose
2121
- cargo test --verbose
22-
- cargo build --verbose --no-default-features
23-
- cargo test --verbose --no-default-features
22+
- cargo build --verbose --no-default-features --features all-languages
23+
- cargo test --verbose --no-default-features --features all-languages
2424
- cargo build --verbose --features rand,all-languages
2525
- cargo test --verbose --features rand,all-languages
2626
# benchmarks

Cargo.toml

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ path = "src/lib.rs"
1616

1717
[features]
1818
default = [ "std" ]
19-
std = []
19+
std = [ "unicode-normalization", "serde/std" ]
2020

2121
# Note: English is the standard for bip39 so always included
2222
chinese-simplified = []
@@ -41,10 +41,11 @@ all-languages = [
4141

4242
[dependencies]
4343
bitcoin_hashes = "0.9.4"
44-
unicode-normalization = "=0.1.9"
44+
rand_core = "0.4.0"
4545

46+
unicode-normalization = { version = "=0.1.9", optional = true }
4647
rand = { version = "0.6.0", optional = true }
47-
serde = { version = "1.0", optional = true }
48+
serde = { version = "1.0", default-features = false, optional = true }
4849

4950
[dev-dependencies]
5051
rand = { version = "0.6.0", optional = false }

src/language/mod.rs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11

2-
#[cfg(feature = "std")]
3-
use std::fmt;
2+
use core::fmt;
43

54
mod english;
65
#[cfg(feature = "chinese-simplified")]
@@ -153,7 +152,6 @@ impl Language {
153152
}
154153
}
155154

156-
#[cfg(feature = "std")]
157155
impl fmt::Display for Language {
158156
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
159157
fmt::Debug::fmt(self, f)

src/lib.rs

Lines changed: 95 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,13 @@
2727
#![deny(missing_docs)]
2828

2929
#![cfg_attr(all(not(test), not(feature = "std")), no_std)]
30-
#[cfg(any(test, feature = "std"))] pub extern crate core;
30+
31+
#[cfg(any(test, feature = "std"))]
32+
pub extern crate core;
3133

3234
extern crate bitcoin_hashes;
35+
extern crate rand_core;
36+
3337
#[cfg(feature = "std")]
3438
extern crate unicode_normalization;
3539

@@ -38,8 +42,10 @@ extern crate rand;
3842
#[cfg(feature = "serde")]
3943
pub extern crate serde;
4044

45+
use core::{fmt, str};
46+
4147
#[cfg(feature = "std")]
42-
use std::{error, fmt, str};
48+
use std::error;
4349
#[cfg(feature = "std")]
4450
use std::borrow::Cow;
4551

@@ -75,7 +81,6 @@ impl AmbiguousLanguages {
7581
}
7682

7783
/// An iterator over the possible languages.
78-
#[cfg(feature = "std")]
7984
pub fn iter(&self) -> impl Iterator<Item = Language> + '_ {
8085
Language::all().iter().enumerate().filter(move |(i, _)| self.0[*i]).map(|(_, l)| *l)
8186
}
@@ -106,7 +111,6 @@ pub enum Error {
106111
AmbiguousLanguages(AmbiguousLanguages),
107112
}
108113

109-
#[cfg(feature = "std")]
110114
impl fmt::Display for Error {
111115
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
112116
match *self {
@@ -120,7 +124,17 @@ impl fmt::Display for Error {
120124
"entropy was not between 128-256 bits or not a multiple of 32 bits: {} bits", c,
121125
),
122126
Error::InvalidChecksum => write!(f, "the mnemonic has an invalid checksum"),
123-
Error::AmbiguousLanguages(a) => write!(f, "ambiguous word list: {:?}", a.to_vec()),
127+
Error::AmbiguousLanguages(a) => {
128+
write!(f, "ambiguous word list: ")?;
129+
for (i, lang) in a.iter().enumerate() {
130+
if i == 0 {
131+
write!(f, "{}", lang)?;
132+
} else {
133+
write!(f, ", {}", lang)?;
134+
}
135+
}
136+
Ok(())
137+
}
124138
}
125139
}
126140
}
@@ -130,7 +144,7 @@ impl error::Error for Error {}
130144

131145
/// A mnemonic code.
132146
///
133-
/// The [std::str::FromStr] implementation will try to determine the language of the
147+
/// The [core::str::FromStr] implementation will try to determine the language of the
134148
/// mnemonic from all the supported languages. (Languages have to be explicitly enabled using
135149
/// the Cargo features.)
136150
///
@@ -202,23 +216,63 @@ impl Mnemonic {
202216
Mnemonic::from_entropy_in(Language::English, entropy)
203217
}
204218

205-
/// Generate a new [Mnemonic] in the given language.
219+
/// Generate a new [Mnemonic] in the given language
220+
/// with the given randomness source.
206221
/// For the different supported word counts, see documentation on [Mnemonic].
207-
#[cfg(feature = "rand")]
208-
pub fn generate_in(language: Language, word_count: usize) -> Result<Mnemonic, Error> {
222+
///
223+
/// Example:
224+
///
225+
/// ```
226+
/// extern crate rand;
227+
/// extern crate bip39;
228+
///
229+
/// use bip39::{Mnemonic, Language};
230+
///
231+
/// let mut rng = rand::thread_rng();
232+
/// let m = Mnemonic::generate_in_with(&mut rng, Language::English, 24).unwrap();
233+
/// ```
234+
pub fn generate_in_with<R>(rng: &mut R, language: Language, word_count: usize) -> Result<Mnemonic, Error>
235+
where R: rand_core::RngCore + rand_core::CryptoRng,
236+
{
209237
if word_count < MIN_NB_WORDS || word_count % 6 != 0 || word_count > MAX_NB_WORDS {
210238
return Err(Error::BadWordCount(word_count));
211239
}
212240

213241
let entropy_bytes = (word_count / 3) * 4;
214-
let mut rng = rand::thread_rng();
215242
let mut entropy = [0u8; (MAX_NB_WORDS / 3) * 4];
216-
rand::RngCore::fill_bytes(&mut rng, &mut entropy[0..entropy_bytes]);
243+
rand_core::RngCore::fill_bytes(rng, &mut entropy[0..entropy_bytes]);
217244
Mnemonic::from_entropy_in(language, &entropy[0..entropy_bytes])
218245
}
219246

247+
/// Generate a new [Mnemonic] in the given language.
248+
/// For the different supported word counts, see documentation on [Mnemonic].
249+
///
250+
/// Example:
251+
///
252+
/// ```
253+
/// extern crate bip39;
254+
///
255+
/// use bip39::{Mnemonic, Language};
256+
///
257+
/// let m = Mnemonic::generate_in(Language::English, 24).unwrap();
258+
/// ```
259+
#[cfg(feature = "rand")]
260+
pub fn generate_in(language: Language, word_count: usize) -> Result<Mnemonic, Error> {
261+
Mnemonic::generate_in_with(&mut rand::thread_rng(), language, word_count)
262+
}
263+
220264
/// Generate a new [Mnemonic] in English.
221265
/// For the different supported word counts, see documentation on [Mnemonic].
266+
///
267+
/// Example:
268+
///
269+
/// ```
270+
/// extern crate bip39;
271+
///
272+
/// use bip39::{Mnemonic,};
273+
///
274+
/// let m = Mnemonic::generate(24).unwrap();
275+
/// ```
222276
#[cfg(feature = "rand")]
223277
pub fn generate(word_count: usize) -> Result<Mnemonic, Error> {
224278
Mnemonic::generate_in(Language::English, word_count)
@@ -240,18 +294,8 @@ impl Mnemonic {
240294
return Err(Error::BadWordCount(0));
241295
}
242296

243-
// For efficiency reasons, we add a special case for when there's
244-
// only a single language enabled.
245-
if langs.len() == 1 {
246-
let lang = langs[0];
247-
return if lang.find_word(first_word).is_some() {
248-
Ok(lang)
249-
} else {
250-
Err(Error::UnknownWord(0))
251-
};
252-
}
253-
254-
// Otherwise we first try wordlists that have guaranteed unique words.
297+
// We first try find the first word in wordlists that
298+
// have guaranteed unique words.
255299
for language in langs.iter().filter(|l| l.unique_words()) {
256300
if language.find_word(first_word).is_some() {
257301
return Ok(*language);
@@ -271,20 +315,22 @@ impl Mnemonic {
271315
for (idx, word) in words.enumerate() {
272316
// Scrap languages that don't have this word.
273317
for (i, lang) in langs.iter().enumerate() {
274-
if possible[i] {
275-
possible[i] = lang.find_word(word).is_some();
276-
}
318+
possible[i] &= lang.find_word(word).is_some();
277319
}
278320

279-
// If there is just one language left, return it.
280-
let nb_possible = possible.iter().filter(|p| **p).count();
281-
if nb_possible == 1 {
282-
return Ok(*possible.iter().zip(langs.iter()).find(|&(p, _)| *p).map(|(_, l)| l).unwrap());
283-
}
284-
285-
// If all languages were eliminated, it's an invalid word.
286-
if nb_possible == 0 {
287-
return Err(Error::UnknownWord(idx));
321+
// Get an iterator over remaining possible languages.
322+
let mut iter = possible.iter().zip(langs.iter()).filter(|(p, _)| **p).map(|(_, l)| l);
323+
324+
match iter.next() {
325+
// If all languages were eliminated, it's an invalid word.
326+
None => return Err(Error::UnknownWord(idx)),
327+
// If not, see if there is a second one remaining.
328+
Some(remaining) => {
329+
if iter.next().is_none() {
330+
// No second remaining, we found our language.
331+
return Ok(*remaining);
332+
}
333+
}
288334
}
289335
}
290336

@@ -389,8 +435,9 @@ impl Mnemonic {
389435
const PBKDF2_ROUNDS: usize = 2048;
390436
const PBKDF2_BYTES: usize = 64;
391437

438+
let nb_words = self.word_count();
392439
let mut seed = [0u8; PBKDF2_BYTES];
393-
pbkdf2::pbkdf2(&self.0, normalized_passphrase.as_bytes(), PBKDF2_ROUNDS, &mut seed);
440+
pbkdf2::pbkdf2(&self.0[0..nb_words], normalized_passphrase.as_bytes(), PBKDF2_ROUNDS, &mut seed);
394441
seed
395442
}
396443

@@ -451,7 +498,6 @@ impl Mnemonic {
451498
}
452499
}
453500

454-
#[cfg(feature = "std")]
455501
impl fmt::Display for Mnemonic {
456502
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
457503
for i in 0..self.0.len() {
@@ -468,12 +514,14 @@ impl fmt::Display for Mnemonic {
468514
}
469515
}
470516

471-
#[cfg(feature = "std")]
472517
impl str::FromStr for Mnemonic {
473518
type Err = Error;
474519

475520
fn from_str(s: &str) -> Result<Mnemonic, Error> {
476-
Mnemonic::parse(s)
521+
#[cfg(feature = "std")]
522+
{ Mnemonic::parse(s) }
523+
#[cfg(not(feature = "std"))]
524+
{ Mnemonic::parse_normalized(s) }
477525
}
478526
}
479527

@@ -483,17 +531,6 @@ mod tests {
483531

484532
use bitcoin_hashes::hex::FromHex;
485533

486-
#[cfg(feature = "rand")]
487-
#[test]
488-
fn test_bit_counts() {
489-
let m = Mnemonic::generate(12).unwrap();
490-
assert_eq!(m.word_count(), 12);
491-
let m = Mnemonic::generate(18).unwrap();
492-
assert_eq!(m.word_count(), 18);
493-
let m = Mnemonic::generate(24).unwrap();
494-
assert_eq!(m.word_count(), 24);
495-
}
496-
497534
#[cfg(feature = "rand")]
498535
#[test]
499536
fn test_language_of() {
@@ -524,6 +561,14 @@ mod tests {
524561
assert_eq!(amb.iter().collect::<Vec<_>>(), present_vec);
525562
}
526563

564+
#[cfg(feature = "rand")]
565+
#[test]
566+
fn test_generate() {
567+
let _ = Mnemonic::generate(24).unwrap();
568+
let _ = Mnemonic::generate_in(Language::English, 24).unwrap();
569+
let _ = Mnemonic::generate_in_with(&mut rand::thread_rng(), Language::English, 24).unwrap();
570+
}
571+
527572
#[test]
528573
fn test_vectors_english() {
529574
// These vectors are tuples of

src/pbkdf2.rs

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,6 @@ fn mnemonic_byte_len(mnemonic: &[&'static str]) -> usize {
88
let mut len = 0;
99
for i in 0..mnemonic.len() {
1010
let word = &mnemonic[i];
11-
if word.is_empty() {
12-
break;
13-
}
1411
if i > 0 {
1512
len += 1;
1613
}
@@ -23,9 +20,6 @@ fn mnemonic_byte_len(mnemonic: &[&'static str]) -> usize {
2320
fn mnemonic_write_into(mnemonic: &[&'static str], engine: &mut sha512::HashEngine) {
2421
for i in 0..mnemonic.len() {
2522
let word = &mnemonic[i];
26-
if word.is_empty() {
27-
break;
28-
}
2923
if i > 0 {
3024
engine.input(" ".as_bytes());
3125
}
@@ -61,9 +55,6 @@ fn create_hmac_engine(mnemonic: &[&'static str]) -> hmac::HmacEngine<sha512::Has
6155
let mut cursor = 0;
6256
for i in 0..mnemonic.len() {
6357
let word = &mnemonic[i];
64-
if word.is_empty() {
65-
break;
66-
}
6758
if i > 0 {
6859
ipad[cursor] ^= ' ' as u8;
6960
opad[cursor] ^= ' ' as u8;
@@ -88,8 +79,6 @@ fn create_hmac_engine(mnemonic: &[&'static str]) -> hmac::HmacEngine<sha512::Has
8879
// Method borrowed from rust-bitcoin's endian module.
8980
#[inline]
9081
fn u32_to_array_be(val: u32) -> [u8; 4] {
91-
debug_assert_eq!(::core::mem::size_of::<u32>(), 4); // size_of isn't a constfn in 1.22
92-
9382
let mut res = [0; 4];
9483
for i in 0..4 {
9584
res[i] = ((val >> (4 - i - 1)*8) & 0xff) as u8;

0 commit comments

Comments
 (0)