1919//! TREZOR compatible mnemonic in english
2020//!
2121use bitcoin:: util:: bip158:: { BitStreamReader , BitStreamWriter } ;
22- use account:: Seed ;
22+ use account:: { Seed , MasterKeyEntropy } ;
2323use crypto:: {
2424 digest:: Digest ,
2525 hmac:: Hmac ,
@@ -28,13 +28,14 @@ use crypto::{
2828} ;
2929use error:: Error ;
3030use std:: io:: Cursor ;
31+ use rand:: { thread_rng, RngCore } ;
3132
3233#[ derive( Clone , Eq , PartialEq , Debug ) ]
33- pub struct Mnemonic ( Vec < & ' static str > ) ;
34+ pub struct Mnemonic ( Vec < usize > ) ;
3435
3536impl ToString for Mnemonic {
3637 fn to_string ( & self ) -> String {
37- self . 0 . as_slice ( ) . join ( " " )
38+ self . 0 . iter ( ) . map ( |i| WORDS [ * i ] ) . collect :: < Vec < _ > > ( ) . as_slice ( ) . join ( " " )
3839 }
3940}
4041
@@ -50,7 +51,7 @@ impl Mnemonic {
5051 }
5152
5253 pub fn iter ( & self ) -> impl Iterator < Item = & str > {
53- self . 0 . iter ( ) . map ( |s| * s )
54+ self . 0 . iter ( ) . map ( |s| WORDS [ * s ] )
5455 }
5556
5657 pub fn from_str ( s : & str ) -> Result < Mnemonic , Error > {
@@ -65,7 +66,7 @@ impl Mnemonic {
6566 let mut mnemonic = Vec :: new ( ) ;
6667 for word in & words {
6768 if let Ok ( idx) = WORDS . binary_search ( word) {
68- mnemonic. push ( WORDS [ idx] ) ;
69+ mnemonic. push ( idx) ;
6970 writer. write ( idx as u64 , 11 ) . unwrap ( ) ;
7071 } else {
7172 return Err ( Error :: Mnemonic ( "Mnemonic contains an unknown word" ) ) ;
@@ -81,6 +82,17 @@ impl Mnemonic {
8182 Ok ( Mnemonic ( mnemonic) )
8283 }
8384
85+ pub fn new_random ( entropy : MasterKeyEntropy ) -> Result < Mnemonic , Error > {
86+ let len = match entropy {
87+ MasterKeyEntropy :: Sufficient => 16 ,
88+ MasterKeyEntropy :: Double => 32 ,
89+ MasterKeyEntropy :: Paranoid => 64
90+ } ;
91+ let mut random = vec ! ( 0u8 ; len) ;
92+ thread_rng ( ) . fill_bytes ( random. as_mut_slice ( ) ) ;
93+ Self :: new ( random. as_slice ( ) )
94+ }
95+
8496 /// create a mnemonic for some data
8597 pub fn new ( data : & [ u8 ] ) -> Result < Mnemonic , Error > {
8698 if data. len ( ) % 4 != 0 {
@@ -93,11 +105,37 @@ impl Mnemonic {
93105 let mut cursor = Cursor :: new ( & with_checksum) ;
94106 let mut reader = BitStreamReader :: new ( & mut cursor) ;
95107 let mlen = data. len ( ) * 3 / 4 ;
96- let mut memo = Vec :: new ( ) ;
108+ let mut mnemonic = Vec :: new ( ) ;
97109 for _ in 0 ..mlen {
98- memo . push ( WORDS [ reader. read ( 11 ) . unwrap ( ) as usize ] ) ;
110+ mnemonic . push ( reader. read ( 11 ) . unwrap ( ) as usize ) ;
99111 }
100- Ok ( Mnemonic ( memo) )
112+ Ok ( Mnemonic ( mnemonic) )
113+ }
114+
115+ pub fn extend ( & self ) -> Result < Mnemonic , Error > {
116+ if self . 0 . len ( ) != 12 {
117+ return Err ( Error :: Mnemonic (
118+ "Can only extend mnemonic of 12 words to 24 words" ,
119+ ) ) ;
120+ }
121+ let mut data = Vec :: new ( ) ;
122+ let mut writer = BitStreamWriter :: new ( & mut data) ;
123+ for idx in & self . 0 {
124+ writer. write ( * idx as u64 , 11 ) . unwrap ( ) ;
125+ }
126+ for _ in 0 ..11 {
127+ writer. write ( thread_rng ( ) . next_u64 ( ) , 11 ) . unwrap ( ) ;
128+ }
129+ writer. write ( thread_rng ( ) . next_u64 ( ) , 3 ) . unwrap ( ) ;
130+ writer. flush ( ) . unwrap ( ) ;
131+ data. extend_from_slice ( Self :: checksum ( & data) . as_slice ( ) ) ;
132+ let mut cursor = Cursor :: new ( & data[ ..] ) ;
133+ let mut reader = BitStreamReader :: new ( & mut cursor) ;
134+ let mut mnemonic = Vec :: new ( ) ;
135+ for _ in 0 ..24 {
136+ mnemonic. push ( reader. read ( 11 ) . unwrap ( ) as usize ) ;
137+ }
138+ Ok ( Mnemonic ( mnemonic) )
101139 }
102140
103141 fn checksum ( data : & [ u8 ] ) -> Vec < u8 > {
@@ -180,6 +218,14 @@ mod test {
180218 )
181219 . is_err( ) ) ;
182220 }
221+
222+ #[ test]
223+ fn test_extend ( ) {
224+ let short = Mnemonic :: new_random ( MasterKeyEntropy :: Sufficient ) . unwrap ( ) ;
225+ let extended = short. extend ( ) . unwrap ( ) ;
226+ let check = Mnemonic :: from_str ( extended. to_string ( ) . as_str ( ) ) . unwrap ( ) ;
227+ assert ! ( short. iter( ) . zip( check. iter( ) ) . take( 12 ) . all( |( a, b) | * a == * b) ) ;
228+ }
183229}
184230
185231const WORDS : [ & str ; 2048 ] = [
0 commit comments