From 56c223886ceaf2ad2abe0caca2318d0d068ee0cf Mon Sep 17 00:00:00 2001 From: Antoine Poinsot Date: Sun, 3 Jan 2021 01:40:42 +0100 Subject: [PATCH] lightning: confirm password on hsm_secret encryption Changelog-changed: lightningd: the `--encrypted-hsm` now asks you to confirm your password when first set Changelog-changed: hsmtool: the `encrypt` now asks you to confirm your password Signed-off-by: Antoine Poinsot --- lightningd/options.c | 11 +++++++++-- tests/test_wallet.py | 19 +++++++++++++++---- tools/hsmtool.c | 9 +++++++-- 3 files changed, 31 insertions(+), 8 deletions(-) diff --git a/lightningd/options.c b/lightningd/options.c index 119569f01..246e3d676 100644 --- a/lightningd/options.c +++ b/lightningd/options.c @@ -388,7 +388,7 @@ static char *opt_important_plugin(const char *arg, struct lightningd *ld) static char *opt_set_hsm_password(struct lightningd *ld) { struct termios current_term, temp_term; - char *passwd = NULL; + char *passwd = NULL, *passwd_confirmation = NULL; size_t passwd_size = 0; u8 salt[16] = "c-lightning\0\0\0\0\0"; ld->encrypted_hsm = true; @@ -408,12 +408,18 @@ static char *opt_set_hsm_password(struct lightningd *ld) return "Could not disable password echoing."; printf("The hsm_secret is encrypted with a password. In order to " "decrypt it and start the node you must provide the password.\n"); - printf("Enter hsm_secret password: "); + printf("Enter hsm_secret password:\n"); /* If we don't flush we might end up being buffered and we might seem * to hang while we wait for the password. */ fflush(stdout); if (getline(&passwd, &passwd_size, stdin) < 0) return "Could not read password from stdin."; + printf("Confirm hsm_secret password:\n"); + fflush(stdout); + if (getline(&passwd_confirmation, &passwd_size, stdin) < 0) + return "Could not read password confirmation from stdin."; + if (!streq(passwd, passwd_confirmation)) + return "Password confirmation mismatch."; if (passwd[strlen(passwd) - 1] == '\n') passwd[strlen(passwd) - 1] = '\0'; if (tcsetattr(fileno(stdin), TCSAFLUSH, ¤t_term) != 0) @@ -434,6 +440,7 @@ static char *opt_set_hsm_password(struct lightningd *ld) crypto_pwhash_ALG_ARGON2ID13) != 0) return "Could not derive a key from the password."; free(passwd); + free(passwd_confirmation); return NULL; } diff --git a/tests/test_wallet.py b/tests/test_wallet.py index 26cb7c4c7..57e049615 100644 --- a/tests/test_wallet.py +++ b/tests/test_wallet.py @@ -956,8 +956,9 @@ def test_hsm_secret_encryption(node_factory): l1.stop() l1.daemon.opts.update({"encrypted-hsm": None}) l1.daemon.start(stdin=slave_fd, wait_for_initialized=False) - l1.daemon.wait_for_log(r'The hsm_secret is encrypted') - + l1.daemon.wait_for_log(r'Enter hsm_secret password') + os.write(master_fd, password.encode("utf-8")) + l1.daemon.wait_for_log(r'Confirm hsm_secret password') os.write(master_fd, password.encode("utf-8")) l1.daemon.wait_for_log("Server started with public key") id = l1.rpc.getinfo()["id"] @@ -972,7 +973,9 @@ def test_hsm_secret_encryption(node_factory): l1.daemon.opts.update({"encrypted-hsm": None}) l1.daemon.start(stdin=slave_fd, stderr=subprocess.STDOUT, wait_for_initialized=False) - l1.daemon.wait_for_log(r'The hsm_secret is encrypted') + l1.daemon.wait_for_log(r'Enter hsm_secret password') + os.write(master_fd, password[2:].encode("utf-8")) + l1.daemon.wait_for_log(r'Confirm hsm_secret password') os.write(master_fd, password[2:].encode("utf-8")) assert(l1.daemon.proc.wait() == 1) assert(l1.daemon.is_in_log("Wrong password for encrypted hsm_secret.")) @@ -981,6 +984,8 @@ def test_hsm_secret_encryption(node_factory): l1.daemon.start(stdin=slave_fd, wait_for_initialized=False) l1.daemon.wait_for_log(r'The hsm_secret is encrypted') os.write(master_fd, password.encode("utf-8")) + l1.daemon.wait_for_log(r'Confirm hsm_secret password') + os.write(master_fd, password.encode("utf-8")) l1.daemon.wait_for_log("Server started with public key") assert id == l1.rpc.getinfo()["id"] @@ -1005,7 +1010,9 @@ def test_hsmtool_secret_decryption(node_factory): l1.stop() l1.daemon.opts.update({"encrypted-hsm": None}) l1.daemon.start(stdin=slave_fd, wait_for_initialized=False) - l1.daemon.wait_for_log(r'The hsm_secret is encrypted') + l1.daemon.wait_for_log(r'Enter hsm_secret password') + os.write(master_fd, password.encode("utf-8")) + l1.daemon.wait_for_log(r'Confirm hsm_secret password') os.write(master_fd, password.encode("utf-8")) l1.daemon.wait_for_log("Server started with public key") node_id = l1.rpc.getinfo()["id"] @@ -1038,6 +1045,8 @@ def test_hsmtool_secret_decryption(node_factory): stdout=subprocess.PIPE, stderr=subprocess.PIPE) hsmtool.wait_for_log(r"Enter hsm_secret password:") os.write(master_fd, password.encode("utf-8")) + hsmtool.wait_for_log(r"Confirm hsm_secret password:") + os.write(master_fd, password.encode("utf-8")) assert hsmtool.proc.wait(5) == 0 # Now we need to pass the encrypted-hsm startup option l1.stop() @@ -1051,6 +1060,8 @@ def test_hsmtool_secret_decryption(node_factory): l1.daemon.wait_for_log(r'The hsm_secret is encrypted') os.write(master_fd, password.encode("utf-8")) + l1.daemon.wait_for_log(r'Confirm hsm_secret password') + os.write(master_fd, password.encode("utf-8")) l1.daemon.wait_for_log("Server started with public key") print(node_id, l1.rpc.getinfo()["id"]) assert node_id == l1.rpc.getinfo()["id"] diff --git a/tools/hsmtool.c b/tools/hsmtool.c index 6693e2034..66dfb6022 100644 --- a/tools/hsmtool.c +++ b/tools/hsmtool.c @@ -253,7 +253,7 @@ static int encrypt_hsm(const char *hsm_secret_path) { int fd; struct secret key, hsm_secret; - char *passwd; + char *passwd, *passwd_confirmation; u8 salt[16] = "c-lightning\0\0\0\0\0"; crypto_secretstream_xchacha20poly1305_state crypto_state; u8 header[crypto_secretstream_xchacha20poly1305_HEADERBYTES]; @@ -266,8 +266,11 @@ static int encrypt_hsm(const char *hsm_secret_path) errx(ERROR_USAGE, "hsm_secret is already encrypted"); printf("Enter hsm_secret password:\n"); - /* TODO: make the user double check the password. */ passwd = read_stdin_pass(); + printf("Confirm hsm_secret password:\n"); + passwd_confirmation = read_stdin_pass(); + if (!streq(passwd, passwd_confirmation)) + errx(ERROR_USAGE, "Passwords confirmation mismatch."); get_hsm_secret(&hsm_secret, hsm_secret_path); dir = path_dirname(NULL, hsm_secret_path); @@ -296,6 +299,8 @@ static int encrypt_hsm(const char *hsm_secret_path) /* Once the encryption key derived, we don't need it anymore. */ if (passwd) free(passwd); + if (passwd_confirmation) + free(passwd_confirmation); /* Create a backup file, "just in case". */ rename(hsm_secret_path, backup);