Skip to content

Commit

Permalink
hsmtool.c: extend generatehsm to allow command-line args.
Browse files Browse the repository at this point in the history
Based on the patch by bstin <barry.github@capsmx.com>, which added a separate command,
this simply extends "generatehsm" to allow more options.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Changelog-Added: hsmtool: generatehsm can run non-interactive, taking options on the cmdline.
  • Loading branch information
rustyrussell committed Nov 11, 2024
1 parent 48f50a2 commit 7fcad3d
Show file tree
Hide file tree
Showing 2 changed files with 92 additions and 39 deletions.
21 changes: 21 additions & 0 deletions tests/test_wallet.py
Original file line number Diff line number Diff line change
Expand Up @@ -1383,6 +1383,27 @@ def test_hsmtool_generatehsm(node_factory):
# We can start the node with this hsm_secret
l1.start()
assert l1.info['id'] == '02244b73339edd004bc6dfbb953a87984c88e9e7c02ca14ef6ec593ca6be622ba7'
l1.stop()

# We can do the entire thing non-interactive!
os.remove(hsm_path)
subprocess.check_output(["valgrind", "--error-exitcode=7", "tools/hsmtool",
"generatehsm", hsm_path,
"en",
"abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about"])
assert open(hsm_path, "rb").read().hex() == "5eb00bbddcf069084889a8ab9155568165f5c453ccb85e70811aaed6f6da5fc1"

# Including passphrase
os.remove(hsm_path)
subprocess.check_output(["tools/hsmtool",
"generatehsm", hsm_path,
"en",
"ritual idle hat sunny universe pluck key alpha wing cake have wedding",
"This is actually not a passphrase"])

l1.start()
assert l1.info['id'] == '02244b73339edd004bc6dfbb953a87984c88e9e7c02ca14ef6ec593ca6be622ba7'
l1.stop()


# this test does a 'listtransactions' on a yet unconfirmed channel
Expand Down
110 changes: 71 additions & 39 deletions tools/hsmtool.c
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ static void show_usage(const char *progname)
"<path/to/hsm_secret>\n");
printf(" - guesstoremote <P2WPKH address> <node id> <tries> "
"<path/to/hsm_secret>\n");
printf(" - generatehsm <path/to/new/hsm_secret>\n");
printf(" - generatehsm <path/to/new/hsm_secret> [<language_id> <word list> [<password>]");
printf(" - checkhsm <path/to/new/hsm_secret>\n");
printf(" - dumponchaindescriptors [--show-secrets] <path/to/hsm_secret> [network]\n");
printf(" - makerune <path/to/hsm_secret>\n");
Expand Down Expand Up @@ -449,21 +449,31 @@ static int guess_to_remote(const char *address, struct node_id *node_id,
return 1;
}

struct wordlist_lang {
char *abbr;
char *name;
};

struct wordlist_lang languages[] = {
{"en", "English"},
{"es", "Spanish"},
{"fr", "French"},
{"it", "Italian"},
{"jp", "Japanese"},
{"zhs", "Chinese Simplified"},
{"zht", "Chinese Traditional"},
};

static bool check_lang(const char *abbr)
{
for (size_t i = 0; i < ARRAY_SIZE(languages); i++) {
if (streq(abbr, languages[i].abbr))
return true;
}
return false;
}

static void get_words(struct words **words) {
struct wordlist_lang {
char *abbr;
char *name;
};

struct wordlist_lang languages[] = {
{"en", "English"},
{"es", "Spanish"},
{"fr", "French"},
{"it", "Italian"},
{"jp", "Japanese"},
{"zhs", "Chinese Simplified"},
{"zht", "Chinese Traditional"},
};

printf("Select your language:\n");
for (size_t i = 0; i < ARRAY_SIZE(languages); i++) {
Expand All @@ -489,7 +499,7 @@ static void get_words(struct words **words) {
bip39_get_wordlist(languages[val].abbr, words);
}

static void get_mnemonic(char *mnemonic) {
static char *get_mnemonic(void) {
char *line = NULL;
size_t line_size = 0;

Expand All @@ -499,41 +509,53 @@ static void get_mnemonic(char *mnemonic) {
if (characters < 0)
errx(ERROR_USAGE, "Could not read line from stdin.");
line[characters-1] = '\0';
strcpy(mnemonic, line);
free(line);
return line;
}

static void read_mnemonic(char *mnemonic) {
static char *read_mnemonic(void) {
/* Get words for the mnemonic language */
struct words *words;
get_words(&words);

/* Get mnemonic */
get_mnemonic(mnemonic);
char *mnemonic;
mnemonic = get_mnemonic();

if (bip39_mnemonic_validate(words, mnemonic) != 0) {
errx(ERROR_USAGE, "Invalid mnemonic: \"%s\"", mnemonic);
}
return mnemonic;
}

static int generate_hsm(const char *hsm_secret_path)
static int generate_hsm(const char *hsm_secret_path,
const char *lang_id,
const char *mnemonic,
char *passphrase)
{
char mnemonic[BIP39_WORDLIST_LEN];
char *passphrase, *err;
char *err;
int exit_code = 0;

read_mnemonic(mnemonic);
printf("Warning: remember that different passphrases yield different "
"bitcoin wallets.\n");
printf("If left empty, no password is used (echo is disabled).\n");
printf("Enter your passphrase: \n");
fflush(stdout);
passphrase = read_stdin_pass_with_exit_code(&err, &exit_code);
if (!passphrase)
errx(exit_code, "%s", err);
if (strlen(passphrase) == 0) {
free(passphrase);
passphrase = NULL;
if (lang_id == NULL) {
mnemonic = read_mnemonic();
printf("Warning: remember that different passphrases yield different "
"bitcoin wallets.\n");
printf("If left empty, no password is used (echo is disabled).\n");
printf("Enter your passphrase: \n");
fflush(stdout);
passphrase = read_stdin_pass_with_exit_code(&err, &exit_code);
if (!passphrase)
errx(exit_code, "%s", err);
if (strlen(passphrase) == 0) {
free(passphrase);
passphrase = NULL;
}
} else {
struct words *words;

bip39_get_wordlist(lang_id, &words);

if (bip39_mnemonic_validate(words, mnemonic) != 0)
errx(ERROR_USAGE, "Invalid mnemonic: \"%s\"", mnemonic);
}

u8 bip32_seed[BIP39_SEED_LEN_512];
Expand Down Expand Up @@ -629,7 +651,7 @@ static int dumponchaindescriptors(const char *hsm_secret_path,

static int check_hsm(const char *hsm_secret_path)
{
char mnemonic[BIP39_WORDLIST_LEN];
char *mnemonic;
struct secret hsm_secret;
u8 bip32_seed[BIP39_SEED_LEN_512];
size_t bip32_seed_len;
Expand All @@ -651,7 +673,7 @@ static int check_hsm(const char *hsm_secret_path)
passphrase = NULL;
}

read_mnemonic(mnemonic);
mnemonic = read_mnemonic();
if (bip39_mnemonic_to_seed(mnemonic, passphrase, bip32_seed, sizeof(bip32_seed), &bip32_seed_len) != WALLY_OK)
errx(ERROR_LIBWALLY, "Unable to derive BIP32 seed from BIP39 mnemonic");

Expand Down Expand Up @@ -769,18 +791,28 @@ int main(int argc, char *argv[])
}

if (streq(method, "generatehsm")) {
if (argc != 3)
// argv[2] file, argv[3] lang_id, argv[4] word list, argv[5] passphrase
if (argc < 3 || argc > 6 || argc == 4)
show_usage(argv[0]);

char *hsm_secret_path = argv[2];
char *lang_id, *word_list, *passphrase;

/* if hsm_secret already exists we abort the process
* we do not want to lose someone else's funds */
struct stat st;
if (stat(hsm_secret_path, &st) == 0)
errx(ERROR_USAGE, "hsm_secret file at %s already exists", hsm_secret_path);

return generate_hsm(hsm_secret_path);
lang_id = (argc > 3 ? argv[3] : NULL);
if (lang_id && !check_lang(lang_id))
show_usage(argv[0]);

word_list = (argc > 4 ? argv[4] : NULL);
/* generate_hsm expects to free this, so use strdup */
passphrase = (argc > 5 ? strdup(argv[5]) : NULL);

return generate_hsm(hsm_secret_path, lang_id, word_list, passphrase);
}

if (streq(method, "dumponchaindescriptors")) {
Expand Down

0 comments on commit 7fcad3d

Please sign in to comment.