1- use bip39:: { Mnemonic as Bip39Mnemonic , Language } ;
1+ use bip39:: { Language , Mnemonic as Bip39Mnemonic } ;
22use failure:: { format_err, Fallible } ;
3- use structopt:: StructOpt ;
43use regex:: Regex ;
4+ use structopt:: StructOpt ;
55
66mod master_secret;
77mod slip39;
88
99pub use master_secret:: MasterSecret ;
10- pub use slip39:: { Slip39 , ShareInspector } ;
11- use sssmc39:: { Share , combine_mnemonics } ;
10+ pub use slip39:: { ShareInspector , Slip39 } ;
11+ use sssmc39:: { combine_mnemonics , Share } ;
1212
1313#[ derive( Debug , StructOpt ) ]
14- #[ structopt( rename_all= "kebab" ) ]
14+ #[ structopt( rename_all = "kebab" ) ]
1515enum Options {
16- /// Generate master secret and split it to parts
17- ///
18- /// SLIP-0039 defines a 2-level split: The master secret is split into group secrets and then
19- /// those are split further into member secrets. You can define the required and total number of
20- /// members in each group, and also define how many groups are required to restore the master secret.
21- Generate {
22- #[ structopt( short, long, default_value = "256" ) ]
23- /// Length of the master secret in bits
24- bits : u16 ,
25- #[ structopt( flatten) ]
26- split_options : SplitOptions ,
27- } ,
28- /// Split a master secret encoded as a BIP-0039 mnemonic into parts
29- ///
30- /// SLIP-0039 defines a 2-level split: The master secret is split into group secrets and then
31- /// those are split further into member secrets. You can define the required and total number of
32- /// members in each group, and also define how many groups are required to restore the master secret.
33- Split {
34- #[ structopt( short, long, env = "SLIP39_ENTROPY" , hide_env_values = true ) ]
35- /// BIP-0039 mnemonic to split. Use double quotes around it, but preferably provide it
36- /// through an environment variable to avoid leaking it to other processes on this machine
37- entropy : String ,
38- #[ structopt( long) ]
39- /// If provided, mnemonic needs to be the master secret is decoded as hexadecimal, not as a
40- /// BIP39 mnemonic
41- hex : bool ,
42- #[ structopt( flatten) ]
43- split_options : SplitOptions ,
44- } ,
45- Combine {
46- #[ structopt( flatten) ]
47- password : Password ,
48- #[ structopt( long) ]
49- /// If provided, mnemonic needs to be the master secret is decoded as hexadecimal, not as a
50- /// BIP39 mnemonic
51- hex : bool ,
52- #[ structopt( short, long( "mnemonic" ) , required= true , number_of_values= 1 ) ]
53- mnemonics : Vec < String > ,
54- } ,
55- Inspect {
56- #[ structopt( short, long) ]
57- /// SLIP-0039 mnemonic to inspect. Use double quotes around it, but preferably provide it
58- /// through an environment variable to avoid leaking it to other processes on this machine
59- mnemonic : String ,
60- }
16+ /// Generate master secret and split it to parts
17+ ///
18+ /// SLIP-0039 defines a 2-level split: The master secret is split into group secrets and then
19+ /// those are split further into member secrets. You can define the required and total number of
20+ /// members in each group, and also define how many groups are required to restore the master secret.
21+ Generate {
22+ #[ structopt( short, long, default_value = "256" ) ]
23+ /// Length of the master secret in bits
24+ bits : u16 ,
25+ #[ structopt( flatten) ]
26+ split_options : SplitOptions ,
27+ } ,
28+ /// Split a master secret encoded as a BIP-0039 mnemonic into parts
29+ ///
30+ /// SLIP-0039 defines a 2-level split: The master secret is split into group secrets and then
31+ /// those are split further into member secrets. You can define the required and total number of
32+ /// members in each group, and also define how many groups are required to restore the master secret.
33+ Split {
34+ #[ structopt( short, long, env = "SLIP39_ENTROPY" , hide_env_values = true ) ]
35+ /// BIP-0039 mnemonic to split. Use double quotes around it, but preferably provide it
36+ /// through an environment variable to avoid leaking it to other processes on this machine
37+ entropy : String ,
38+ #[ structopt( long) ]
39+ /// If provided, mnemonic needs to be the master secret is decoded as hexadecimal, not as a
40+ /// BIP39 mnemonic
41+ hex : bool ,
42+ #[ structopt( flatten) ]
43+ split_options : SplitOptions ,
44+ } ,
45+ Combine {
46+ #[ structopt( flatten) ]
47+ password : Password ,
48+ #[ structopt( long) ]
49+ /// If provided, mnemonic needs to be the master secret is decoded as hexadecimal, not as a
50+ /// BIP39 mnemonic
51+ hex : bool ,
52+ #[ structopt( short, long( "mnemonic" ) , required = true , number_of_values = 1 ) ]
53+ mnemonics : Vec < String > ,
54+ } ,
55+ Inspect {
56+ #[ structopt( short, long) ]
57+ /// SLIP-0039 mnemonic to inspect. Use double quotes around it, but preferably provide it
58+ /// through an environment variable to avoid leaking it to other processes on this machine
59+ mnemonic : String ,
60+ } ,
6161}
6262
6363#[ derive( Debug , StructOpt ) ]
6464struct Password {
65- #[ structopt( short, long, env = "SLIP39_PASSWORD" , hide_env_values = true ) ]
66- /// Password that is required in addition to the mnemonics to restore the master secret. Preferably
67- /// provide it through an environment variable to avoid leaking it to other processes.
68- password : String ,
65+ #[ structopt( short, long, env = "SLIP39_PASSWORD" , hide_env_values = true ) ]
66+ /// Password that is required in addition to the mnemonics to restore the master secret. Preferably
67+ /// provide it through an environment variable to avoid leaking it to other processes.
68+ password : String ,
6969}
7070
71- fn parse_group_spec ( src : & str ) -> Fallible < ( u8 , u8 ) > {
72- let pattern = Regex :: new ( r"^(?P<group_threshold>\d+)(-?of-?|:|/)(?P<group_members>\d+)$" ) ?;
73- let captures = pattern. captures ( src) . ok_or_else ( || format_err ! ( "Group specification '{}' is invalid. Write something like '5of8'" , src) ) ?;
74- let group_threshold: u8 = captures[ "group_threshold" ] . parse ( ) ?;
75- let group_members: u8 = captures[ "group_members" ] . parse ( ) ?;
76- Ok ( ( group_threshold, group_members) )
71+ fn parse_group_spec ( src : & str ) -> Fallible < ( u8 , u8 ) > {
72+ let pattern = Regex :: new ( r"^(?P<group_threshold>\d+)(-?of-?|:|/)(?P<group_members>\d+)$" ) ?;
73+ let captures = pattern. captures ( src) . ok_or_else ( || {
74+ format_err ! (
75+ "Group specification '{}' is invalid. Write something like '5of8'" ,
76+ src
77+ )
78+ } ) ?;
79+ let group_threshold: u8 = captures[ "group_threshold" ] . parse ( ) ?;
80+ let group_members: u8 = captures[ "group_members" ] . parse ( ) ?;
81+ Ok ( ( group_threshold, group_members) )
7782}
7883
7984#[ derive( Debug , StructOpt ) ]
8085struct SplitOptions {
81- #[ structopt( flatten) ]
82- password : Password ,
83- #[ structopt( short, long) ]
84- /// Number of groups required for restoring the master secret (by default all groups are required)
85- required_groups : Option < u8 > ,
86- // TODO The sssmc39 crate does not handle well iteration_exponent values other than 0, so we disabled this parameter for now
87- // #[structopt(short, long, default_value = "0")]
88- // /// The higher this number, the safer and slower the splitting and combining is
89- // iterations: u8,
90- #[ structopt( short, long( "group" ) , parse( try_from_str = parse_group_spec) , required=true , number_of_values=1 ) ]
91- /// Specify required and total number of members for each group (e.g. 8-of-15).
92- /// Multiple groups need multiple occurences of this option.
93- groups : Vec < ( u8 , u8 ) >
86+ #[ structopt( flatten) ]
87+ password : Password ,
88+ #[ structopt( short, long) ]
89+ /// Number of groups required for restoring the master secret (by default all groups are required)
90+ required_groups : Option < u8 > ,
91+ #[ structopt( short, long, default_value = "0" ) ]
92+ /// The higher this number, the safer and slower the splitting and combining is
93+ iterations : u8 ,
94+ #[ structopt( short, long( "group" ) , parse( try_from_str = parse_group_spec) , required=true , number_of_values=1 ) ]
95+ /// Specify required and total number of members for each group (e.g. 8-of-15).
96+ /// Multiple groups need multiple occurences of this option.
97+ groups : Vec < ( u8 , u8 ) > ,
9498}
9599
96100impl SplitOptions {
97- fn split ( & self , master_secret : & MasterSecret ) -> Fallible < Slip39 > {
98- let required_groups = self . required_groups
99- . unwrap_or_else ( || self . groups . len ( ) as u8 ) ;
100- let slip39 = Slip39 :: new (
101- required_groups,
102- & self . groups ,
103- & master_secret,
104- & self . password . password ,
105- 0 // self.iterations
106- ) ?;
107- Ok ( slip39)
108- }
101+ fn split ( & self , master_secret : & MasterSecret ) -> Fallible < Slip39 > {
102+ let required_groups = self
103+ . required_groups
104+ . unwrap_or_else ( || self . groups . len ( ) as u8 ) ;
105+ let slip39 = Slip39 :: new (
106+ required_groups,
107+ & self . groups ,
108+ & master_secret,
109+ & self . password . password ,
110+ self . iterations ,
111+ ) ?;
112+ Ok ( slip39)
113+ }
109114}
110115
111116fn print_split ( options : & SplitOptions , master_secret : & MasterSecret ) -> Fallible < ( ) > {
112- let slip39 = options. split ( & master_secret) ?;
113- println ! ( "{}" , serde_json:: to_string_pretty( & slip39) ?) ;
114- Ok ( ( ) )
117+ let slip39 = options. split ( & master_secret) ?;
118+ println ! ( "{}" , serde_json:: to_string_pretty( & slip39) ?) ;
119+ Ok ( ( ) )
115120}
116121
117122fn main ( ) -> Fallible < ( ) > {
118- use Options :: * ;
119- let options = Options :: from_args ( ) ;
120- match options {
121- Generate { bits, split_options } => {
122- let master_secret = MasterSecret :: new ( bits) ?;
123- print_split ( & split_options, & master_secret) ?;
124- }
125- Split { entropy, hex, split_options } => {
126- let master_secret = if hex {
127- let bytes = hex:: decode ( entropy) ?;
128- MasterSecret :: from ( & bytes)
129- } else {
130- let bip39 = Bip39Mnemonic :: from_phrase ( entropy, Language :: English ) ?;
131- MasterSecret :: from ( bip39. entropy ( ) )
132- } ;
133- print_split ( & split_options, & master_secret) ?;
134- }
135- Combine { password, hex, mnemonics} => {
136- let mnemonics = mnemonics. iter ( )
137- . map ( |m| {
138- m. split_ascii_whitespace ( ) . map ( str:: to_owned) . collect ( )
139- } )
140- . collect ( ) ;
141- let master_secret = combine_mnemonics ( & mnemonics, & password. password ) ?;
142- let output = if hex {
143- hex:: encode ( master_secret)
144- } else {
145- let bip39 = bip39:: Mnemonic :: from_entropy ( & master_secret, Language :: English ) ?;
146- bip39. into_phrase ( )
147- } ;
148- println ! ( "{}" , output) ;
149- }
150- Inspect { mnemonic } => {
151- let words = mnemonic. split_ascii_whitespace ( ) . map ( str:: to_owned) . collect ( ) ;
152- let share = Share :: from_mnemonic ( & words) ?;
153- println ! ( "{}" , serde_json:: to_string_pretty( & ShareInspector :: from( & share) ) ?) ;
154- }
155- }
156- Ok ( ( ) )
157- }
123+ use Options :: * ;
124+ let options = Options :: from_args ( ) ;
125+ match options {
126+ Generate {
127+ bits,
128+ split_options,
129+ } => {
130+ let master_secret = MasterSecret :: new ( bits) ?;
131+ print_split ( & split_options, & master_secret) ?;
132+ }
133+ Split {
134+ entropy,
135+ hex,
136+ split_options,
137+ } => {
138+ let master_secret = if hex {
139+ let bytes = hex:: decode ( entropy) ?;
140+ MasterSecret :: from ( & bytes)
141+ } else {
142+ let bip39 = Bip39Mnemonic :: from_phrase ( entropy, Language :: English ) ?;
143+ MasterSecret :: from ( bip39. entropy ( ) )
144+ } ;
145+ print_split ( & split_options, & master_secret) ?;
146+ }
147+ Combine {
148+ password,
149+ hex,
150+ mnemonics,
151+ } => {
152+ let mnemonics = mnemonics
153+ . iter ( )
154+ . map ( |m| m. split_ascii_whitespace ( ) . map ( str:: to_owned) . collect ( ) )
155+ . collect :: < Vec < _ > > ( ) ;
156+ let master_secret = combine_mnemonics ( & mnemonics, & password. password ) ?;
157+ let output = if hex {
158+ hex:: encode ( master_secret)
159+ } else {
160+ let bip39 = bip39:: Mnemonic :: from_entropy ( & master_secret, Language :: English ) ?;
161+ bip39. into_phrase ( )
162+ } ;
163+ println ! ( "{}" , output) ;
164+ }
165+ Inspect { mnemonic } => {
166+ let words = mnemonic
167+ . split_ascii_whitespace ( )
168+ . map ( str:: to_owned)
169+ . collect :: < Vec < _ > > ( ) ;
170+ let share = Share :: from_mnemonic ( & words) ?;
171+ println ! (
172+ "{}" ,
173+ serde_json:: to_string_pretty( & ShareInspector :: from( & share) ) ?
174+ ) ;
175+ }
176+ }
177+ Ok ( ( ) )
178+ }
0 commit comments