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
3234extern crate bitcoin_hashes;
35+ extern crate rand_core;
36+
3337#[ cfg( feature = "std" ) ]
3438extern crate unicode_normalization;
3539
@@ -38,8 +42,10 @@ extern crate rand;
3842#[ cfg( feature = "serde" ) ]
3943pub 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" ) ]
4450use 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" ) ]
110114impl 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" ) ]
455501impl 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" ) ]
472517impl 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
0 commit comments