Skip to content

Commit b7d77b4

Browse files
committed
hsmtool.c: extend generatehsm to allow command-line args.
Based on the patch by bstin <[email protected]>, which added a separate command, this simply extends "generatehsm" to allow more options. Signed-off-by: Rusty Russell <[email protected]> Changelog-Added: hsmtool: generatehsm can run non-interactive, taking options on the cmdline.
1 parent 087a29b commit b7d77b4

File tree

2 files changed

+91
-39
lines changed

2 files changed

+91
-39
lines changed

tests/test_wallet.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1383,6 +1383,27 @@ def test_hsmtool_generatehsm(node_factory):
13831383
# We can start the node with this hsm_secret
13841384
l1.start()
13851385
assert l1.info['id'] == '02244b73339edd004bc6dfbb953a87984c88e9e7c02ca14ef6ec593ca6be622ba7'
1386+
l1.stop()
1387+
1388+
# We can do the entire thing non-interactive!
1389+
os.remove(hsm_path)
1390+
subprocess.check_output(["tools/hsmtool",
1391+
"generatehsm", hsm_path,
1392+
"en",
1393+
"abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about"])
1394+
assert open(hsm_path, "rb").read().hex() == "5eb00bbddcf069084889a8ab9155568165f5c453ccb85e70811aaed6f6da5fc1"
1395+
1396+
# Including passphrase
1397+
os.remove(hsm_path)
1398+
subprocess.check_output(["tools/hsmtool",
1399+
"generatehsm", hsm_path,
1400+
"en",
1401+
"ritual idle hat sunny universe pluck key alpha wing cake have wedding",
1402+
"This is actually not a passphrase"])
1403+
1404+
l1.start()
1405+
assert l1.info['id'] == '02244b73339edd004bc6dfbb953a87984c88e9e7c02ca14ef6ec593ca6be622ba7'
1406+
l1.stop()
13861407

13871408

13881409
# this test does a 'listtransactions' on a yet unconfirmed channel

tools/hsmtool.c

Lines changed: 70 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ static void show_usage(const char *progname)
4343
"<path/to/hsm_secret>\n");
4444
printf(" - guesstoremote <P2WPKH address> <node id> <tries> "
4545
"<path/to/hsm_secret>\n");
46-
printf(" - generatehsm <path/to/new/hsm_secret>\n");
46+
printf(" - generatehsm <path/to/new/hsm_secret> [<language_id> <word list> [<password>]");
4747
printf(" - checkhsm <path/to/new/hsm_secret>\n");
4848
printf(" - dumponchaindescriptors [--show-secrets] <path/to/hsm_secret> [network]\n");
4949
printf(" - makerune <path/to/hsm_secret>\n");
@@ -450,21 +450,31 @@ static int guess_to_remote(const char *address, struct node_id *node_id,
450450
return 1;
451451
}
452452

453+
struct wordlist_lang {
454+
char *abbr;
455+
char *name;
456+
};
457+
458+
struct wordlist_lang languages[] = {
459+
{"en", "English"},
460+
{"es", "Spanish"},
461+
{"fr", "French"},
462+
{"it", "Italian"},
463+
{"jp", "Japanese"},
464+
{"zhs", "Chinese Simplified"},
465+
{"zht", "Chinese Traditional"},
466+
};
467+
468+
static bool check_lang(const char *abbr)
469+
{
470+
for (size_t i = 0; i < ARRAY_SIZE(languages); i++) {
471+
if (streq(abbr, languages[i].abbr))
472+
return true;
473+
}
474+
return false;
475+
}
476+
453477
static void get_words(struct words **words) {
454-
struct wordlist_lang {
455-
char *abbr;
456-
char *name;
457-
};
458-
459-
struct wordlist_lang languages[] = {
460-
{"en", "English"},
461-
{"es", "Spanish"},
462-
{"fr", "French"},
463-
{"it", "Italian"},
464-
{"jp", "Japanese"},
465-
{"zhs", "Chinese Simplified"},
466-
{"zht", "Chinese Traditional"},
467-
};
468478

469479
printf("Select your language:\n");
470480
for (size_t i = 0; i < ARRAY_SIZE(languages); i++) {
@@ -490,7 +500,7 @@ static void get_words(struct words **words) {
490500
bip39_get_wordlist(languages[val].abbr, words);
491501
}
492502

493-
static void get_mnemonic(char *mnemonic) {
503+
static char *get_mnemonic(void) {
494504
char *line = NULL;
495505
size_t line_size = 0;
496506

@@ -500,42 +510,53 @@ static void get_mnemonic(char *mnemonic) {
500510
if (characters < 0)
501511
errx(ERROR_USAGE, "Could not read line from stdin.");
502512
line[characters-1] = '\0';
503-
strcpy(mnemonic, line);
504-
free(line);
513+
return line;
505514
}
506515

507-
static void read_mnemonic(char *mnemonic) {
516+
static char *read_mnemonic(void) {
508517
/* Get words for the mnemonic language */
509518
struct words *words;
510519
get_words(&words);
511520

512521
/* Get mnemonic */
513-
get_mnemonic(mnemonic);
522+
char *mnemonic;
523+
mnemonic = get_mnemonic();
514524

515525
if (bip39_mnemonic_validate(words, mnemonic) != 0) {
516526
errx(ERROR_USAGE, "Invalid mnemonic: \"%s\"", mnemonic);
517527
}
528+
return mnemonic;
518529
}
519530

520-
static int generate_hsm(const char *hsm_secret_path)
531+
static int generate_hsm(const char *hsm_secret_path,
532+
const char *lang_id,
533+
const char *mnemonic,
534+
char *passphrase)
521535
{
522-
char mnemonic[BIP39_WORDLIST_LEN];
523-
char *passphrase;
524536
const char *err;
525537
int exit_code = 0;
526538

527-
read_mnemonic(mnemonic);
528-
printf("Warning: remember that different passphrases yield different "
529-
"bitcoin wallets.\n");
530-
printf("If left empty, no password is used (echo is disabled).\n");
531-
printf("Enter your passphrase: \n");
532-
fflush(stdout);
533-
passphrase = read_stdin_pass_with_exit_code(&err, &exit_code);
534-
if (!passphrase)
535-
errx(exit_code, "%s", err);
536-
if (strlen(passphrase) == 0) {
537-
free(passphrase);
538-
passphrase = NULL;
539+
if (lang_id == NULL) {
540+
mnemonic = read_mnemonic();
541+
printf("Warning: remember that different passphrases yield different "
542+
"bitcoin wallets.\n");
543+
printf("If left empty, no password is used (echo is disabled).\n");
544+
printf("Enter your passphrase: \n");
545+
fflush(stdout);
546+
passphrase = read_stdin_pass_with_exit_code(&err, &exit_code);
547+
if (!passphrase)
548+
errx(exit_code, "%s", err);
549+
if (strlen(passphrase) == 0) {
550+
free(passphrase);
551+
passphrase = NULL;
552+
}
553+
} else {
554+
struct words *words;
555+
556+
bip39_get_wordlist(lang_id, &words);
557+
558+
if (bip39_mnemonic_validate(words, mnemonic) != 0)
559+
errx(ERROR_USAGE, "Invalid mnemonic: \"%s\"", mnemonic);
539560
}
540561

541562
u8 bip32_seed[BIP39_SEED_LEN_512];
@@ -631,7 +652,7 @@ static int dumponchaindescriptors(const char *hsm_secret_path,
631652

632653
static int check_hsm(const char *hsm_secret_path)
633654
{
634-
char mnemonic[BIP39_WORDLIST_LEN];
655+
char *mnemonic;
635656
struct secret hsm_secret;
636657
u8 bip32_seed[BIP39_SEED_LEN_512];
637658
size_t bip32_seed_len;
@@ -654,7 +675,7 @@ static int check_hsm(const char *hsm_secret_path)
654675
passphrase = NULL;
655676
}
656677

657-
read_mnemonic(mnemonic);
678+
mnemonic = read_mnemonic();
658679
if (bip39_mnemonic_to_seed(mnemonic, passphrase, bip32_seed, sizeof(bip32_seed), &bip32_seed_len) != WALLY_OK)
659680
errx(ERROR_LIBWALLY, "Unable to derive BIP32 seed from BIP39 mnemonic");
660681

@@ -772,18 +793,28 @@ int main(int argc, char *argv[])
772793
}
773794

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

778800
char *hsm_secret_path = argv[2];
801+
char *lang_id, *word_list, *passphrase;
779802

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

786-
return generate_hsm(hsm_secret_path);
809+
lang_id = (argc > 3 ? argv[3] : NULL);
810+
if (lang_id && !check_lang(lang_id))
811+
show_usage(argv[0]);
812+
813+
word_list = (argc > 4 ? argv[4] : NULL);
814+
/* generate_hsm expects to free this, so use strdup */
815+
passphrase = (argc > 5 ? strdup(argv[5]) : NULL);
816+
817+
return generate_hsm(hsm_secret_path, lang_id, word_list, passphrase);
787818
}
788819

789820
if (streq(method, "dumponchaindescriptors")) {

0 commit comments

Comments
 (0)