diff --git a/lnd/aezeed.go b/lnd/aezeed.go index a80ec51..98528bd 100644 --- a/lnd/aezeed.go +++ b/lnd/aezeed.go @@ -15,6 +15,11 @@ import ( "golang.org/x/crypto/ssh/terminal" ) +const ( + memonicEnvName = "AEZEED_MNEMONIC" + passphraseEnvName = "AEZEED_PASSPHRASE" +) + var ( numberDotsRegex = regexp.MustCompile("[\\d.\\-\\n\\r\\t]*") multipleSpaces = regexp.MustCompile(" [ ]+") @@ -23,12 +28,21 @@ var ( func ReadAezeed(params *chaincfg.Params) (*hdkeychain.ExtendedKey, time.Time, error) { - // We'll now prompt the user to enter in their 24-word mnemonic. - fmt.Printf("Input your 24-word mnemonic separated by spaces: ") - reader := bufio.NewReader(os.Stdin) - mnemonicStr, err := reader.ReadString('\n') - if err != nil { - return nil, time.Unix(0, 0), err + // To automate things with chantools, we also offer reading the seed + // from environment variables. + mnemonicStr := strings.TrimSpace(os.Getenv(memonicEnvName)) + + // If nothing is set in the environment, read the seed from the + // terminal. + if mnemonicStr == "" { + var err error + // We'll now prompt the user to enter in their 24-word mnemonic. + fmt.Printf("Input your 24-word mnemonic separated by spaces: ") + reader := bufio.NewReader(os.Stdin) + mnemonicStr, err = reader.ReadString('\n') + if err != nil { + return nil, time.Unix(0, 0), err + } } // We'll trim off extra spaces, and ensure the mnemonic is all @@ -53,23 +67,46 @@ func ReadAezeed(params *chaincfg.Params) (*hdkeychain.ExtendedKey, time.Time, len(cipherSeedMnemonic), 24) } - // Additionally, the user may have a passphrase, that will also - // need to be provided so the daemon can properly decipher the - // cipher seed. - fmt.Printf("Input your cipher seed passphrase (press enter if " + - "your seed doesn't have a passphrase): ") - passphrase, err := terminal.ReadPassword(int(syscall.Stdin)) // nolint - if err != nil { - return nil, time.Unix(0, 0), err + // Additionally, the user may have a passphrase, that will also need to + // be provided so the daemon can properly decipher the cipher seed. + // Try the environment variable first. + passphrase := strings.TrimSpace(os.Getenv(passphraseEnvName)) + + // Because we cannot differentiate between an empty and a non-existent + // environment variable, we need a special character that indicates that + // no passphrase should be used. We use a single dash (-) for that as + // that would be too short for a passphrase anyway. + var passphraseBytes []byte + switch { + // The user indicated in the environment variable that no passphrase + // should be used. We don't set any value. + case passphrase == "-": + + // The environment variable didn't contain anything, we'll read the + // passphrase from the terminal. + case passphrase == "": + fmt.Printf("Input your cipher seed passphrase (press enter " + + "if your seed doesn't have a passphrase): ") + var err error + passphraseBytes, err = terminal.ReadPassword( + int(syscall.Stdin), // nolint + ) + if err != nil { + return nil, time.Unix(0, 0), err + } + fmt.Println() + + // There was a password in the environment, just convert it to bytes. + default: + passphraseBytes = []byte(passphrase) } - fmt.Println() var mnemonic aezeed.Mnemonic copy(mnemonic[:], cipherSeedMnemonic) // If we're unable to map it back into the ciphertext, then either the // mnemonic is wrong, or the passphrase is wrong. - cipherSeed, err := mnemonic.ToCipherSeed(passphrase) + cipherSeed, err := mnemonic.ToCipherSeed(passphraseBytes) if err != nil { return nil, time.Unix(0, 0), fmt.Errorf("failed to decrypt "+ "seed with passphrase: %v", err)