@@ -6,28 +6,42 @@ import (
66 "encoding/hex"
77 "errors"
88 "fmt"
9- "os"
10- "strings"
11- "syscall"
12-
139 "github.com/btcsuite/btcd/chaincfg"
1410 "github.com/btcsuite/btcutil/hdkeychain"
1511 "github.com/guggero/chantools/bip39"
1612 "golang.org/x/crypto/pbkdf2"
1713 "golang.org/x/crypto/ssh/terminal"
14+ "os"
15+ "strings"
16+ "syscall"
17+ )
18+
19+ const (
20+ BIP39MnemonicEnvName = "SEED_MNEMONIC"
21+ BIP39PassphraseEnvName = "SEED_PASSPHRASE"
1822)
1923
2024func ReadMnemonicFromTerminal (params * chaincfg.Params ) (* hdkeychain.ExtendedKey ,
2125 error ) {
2226
23- // We'll now prompt the user to enter in their 12 to 24 word mnemonic.
24- fmt .Printf ("Input your 12 to 24 word mnemonic separated by spaces: " )
27+ var err error
2528 reader := bufio .NewReader (os .Stdin )
26- mnemonicStr , err := reader .ReadString ('\n' )
27- if err != nil {
28- return nil , err
29+
30+ // To automate things with chantools, we also offer reading the seed
31+ // from environment variables.
32+ mnemonicStr := strings .TrimSpace (os .Getenv (BIP39MnemonicEnvName ))
33+
34+ if mnemonicStr == "" {
35+ // If there's no value in the environment, we'll now prompt the
36+ //user to enter in their 12 to 24 word mnemonic.
37+ fmt .Printf ("Input your 12 to 24 word mnemonic separated by " +
38+ "spaces: " )
39+ mnemonicStr , err = reader .ReadString ('\n' )
40+ if err != nil {
41+ return nil , err
42+ }
43+ fmt .Println ()
2944 }
30- fmt .Println ()
3145
3246 // We'll trim off extra spaces, and ensure the mnemonic is all
3347 // lower case.
@@ -40,62 +54,87 @@ func ReadMnemonicFromTerminal(params *chaincfg.Params) (*hdkeychain.ExtendedKey,
4054 "must be between 12 and 24 words" )
4155 }
4256
43- // Additionally, the user may have a passphrase, that will also
44- // need to be provided so the daemon can properly decipher the
45- // cipher seed.
46- fmt .Printf ("Input your cipher seed passphrase (press enter if " +
47- "your seed doesn't have a passphrase): " )
48- passphrase , err := terminal .ReadPassword (int (syscall .Stdin )) // nolint
49- if err != nil {
50- return nil , err
51- }
52- fmt .Println ()
57+ // Additionally, the user may have a passphrase, that will also need to
58+ // be provided so the daemon can properly decipher the cipher seed.
59+ // Try the environment variable first.
60+ passphrase := strings .TrimSpace (os .Getenv (BIP39PassphraseEnvName ))
5361
54- // Check that the mnemonic is valid.
55- _ , err = bip39 .EntropyFromMnemonic (mnemonicStr )
56- if err != nil {
57- return nil , err
58- }
62+ // Because we cannot differentiate between an empty and a non-existent
63+ // environment variable, we need a special character that indicates that
64+ // no passphrase should be used. We use a single dash (-) for that as
65+ // that would be too short for a passphrase anyway.
66+ var (
67+ passphraseBytes []byte
68+ seed []byte
69+ choice string
70+ )
71+ switch {
72+ // The user indicated in the environment variable that no passphrase
73+ // should be used. We don't set any value.
74+ case passphrase == "-" :
5975
60- var seed []byte
61- fmt .Printf ("Please choose passphrase mode:\n " +
62- " 0 - Default BIP39\n " +
63- " 1 - Passphrase to hex\n " +
64- " 2 - Digital Bitbox (extra round of PBKDF2)\n " +
65- "\n " +
66- "Choice [default 0]: " )
67- choice , err := reader .ReadString ('\n' )
68- if err != nil {
69- return nil , err
76+ // The environment variable didn't contain anything, we'll read the
77+ // passphrase from the terminal.
78+ case passphrase == "" :
79+ // Additionally, the user may have a passphrase, that will also
80+ // need to be provided so the daemon can properly decipher the
81+ // cipher seed.
82+ fmt .Printf ("Input your cipher seed passphrase (press enter " +
83+ "if your seed doesn't have a passphrase): " )
84+ passphraseBytes , err = terminal .ReadPassword (
85+ int (syscall .Stdin ), // nolint
86+ )
87+ if err != nil {
88+ return nil , err
89+ }
90+ fmt .Println ()
91+
92+ // Check that the mnemonic is valid.
93+ _ , err = bip39 .EntropyFromMnemonic (mnemonicStr )
94+ if err != nil {
95+ return nil , err
96+ }
97+
98+ fmt .Printf ("Please choose passphrase mode:\n " +
99+ " 0 - Default BIP39\n " +
100+ " 1 - Passphrase to hex\n " +
101+ " 2 - Digital Bitbox (extra round of PBKDF2)\n " +
102+ "\n " +
103+ "Choice [default 0]: " )
104+ choice , err = reader .ReadString ('\n' )
105+ if err != nil {
106+ return nil , err
107+ }
108+ fmt .Println ()
109+
110+ // There was a password in the environment, just convert it to bytes.
111+ default :
112+ passphraseBytes = []byte (passphrase )
70113 }
71- fmt .Println ()
72114
73115 switch strings .TrimSpace (choice ) {
74116 case "" , "0" :
75117 seed = pbkdf2 .Key (
76118 []byte (mnemonicStr ), append (
77- []byte ("mnemonic" ), passphrase ... ,
119+ []byte ("mnemonic" ), passphraseBytes ... ,
78120 ), 2048 , 64 , sha512 .New ,
79121 )
80122
81123 case "1" :
82- passphrase = []byte (hex .EncodeToString (passphrase ))
124+ p : = []byte (hex .EncodeToString (passphraseBytes ))
83125 seed = pbkdf2 .Key (
84- []byte (mnemonicStr ), append (
85- []byte ("mnemonic" ), passphrase ... ,
86- ), 2048 , 64 , sha512 .New ,
126+ []byte (mnemonicStr ), append ([]byte ("mnemonic" ), p ... ),
127+ 2048 , 64 , sha512 .New ,
87128 )
88129
89130 case "2" :
90- passphrase = pbkdf2 .Key (
91- passphrase , []byte ("Digital Bitbox" ), 20480 , 64 ,
131+ p := hex . EncodeToString ( pbkdf2 .Key (
132+ passphraseBytes , []byte ("Digital Bitbox" ), 20480 , 64 ,
92133 sha512 .New ,
93- )
94- passphrase = []byte (hex .EncodeToString (passphrase ))
134+ ))
95135 seed = pbkdf2 .Key (
96- []byte (mnemonicStr ), append (
97- []byte ("mnemonic" ), passphrase ... ,
98- ), 2048 , 64 , sha512 .New ,
136+ []byte (mnemonicStr ), append ([]byte ("mnemonic" ), p ... ),
137+ 2048 , 64 , sha512 .New ,
99138 )
100139
101140 default :
0 commit comments