cli: make the command line more user friendly.

Also has to fix up tests.

Changelog-Fixed: cli doesn't required anymore to confirm the password if the `hsm_secret` is already encrypted.

Signed-off-by: Vincenzo Palazzo <vincenzopalazzodev@gmail.com>
This commit is contained in:
Vincenzo Palazzo
2022-03-10 22:26:20 +01:00
committed by Rusty Russell
parent 58ce4cf71c
commit 53806d1abd
7 changed files with 54 additions and 32 deletions

View File

@@ -14,5 +14,6 @@ typedef s32 errcode_t;
#define HSM_ERROR_IS_ENCRYPT 21
#define HSM_BAD_PASSWORD 22
#define HSM_PASSWORD_INPUT_ERR 23
#define ERROR_HSM_FILE 24
#endif /* LIGHTNING_COMMON_ERRCODE_H */

View File

@@ -1,6 +1,8 @@
#include "config.h"
#include <ccan/tal/str/str.h>
#include <common/errcode.h>
#include <common/hsm_encryption.h>
#include <errno.h>
#include <termios.h>
#include <unistd.h>
@@ -79,6 +81,17 @@ bool decrypt_hsm_secret(const struct secret *encryption_key,
return true;
}
/* Returns -1 on error (and sets errno), 0 if not encrypted, 1 if it is */
int is_hsm_secret_encrypted(const char *path)
{
struct stat st;
if (stat(path, &st) != 0)
return -1;
return st.st_size == ENCRYPTED_HSM_SECRET_LEN;
}
void discard_key(struct secret *key TAKES)
{
/* sodium_munlock() also zeroes the memory. */

View File

@@ -4,6 +4,7 @@
#include <bitcoin/privkey.h>
#include <ccan/tal/tal.h>
#include <sodium.h>
#include <sys/stat.h>
/* Length of the encrypted hsm secret header. */
#define HS_HEADER_LEN crypto_secretstream_xchacha20poly1305_HEADERBYTES
@@ -63,4 +64,6 @@ void discard_key(struct secret *key TAKES);
*/
char *read_stdin_pass_with_exit_code(char **reason, int *exit_code);
/** Returns -1 on error (and sets errno), 0 if not encrypted, 1 if it is */
int is_hsm_secret_encrypted(const char *path);
#endif /* LIGHTNING_COMMON_HSM_ENCRYPTION_H */

View File

@@ -96,11 +96,9 @@ struct ext_key *hsm_init(struct lightningd *ld)
* not passed, don't let hsmd use the first 32 bytes of the cypher as the
* actual secret. */
if (!ld->config.keypass) {
struct stat st;
if (stat("hsm_secret", &st) == 0 &&
st.st_size == ENCRYPTED_HSM_SECRET_LEN)
if (is_hsm_secret_encrypted("hsm_secret") == 1)
errx(HSM_ERROR_IS_ENCRYPT, "hsm_secret is encrypted, you need to pass the "
"--encrypted-hsm startup option.");
"--encrypted-hsm startup option.");
}
ld->hsm_fd = fds[0];

View File

@@ -502,6 +502,12 @@ static char *opt_important_plugin(const char *arg, struct lightningd *ld)
static char *opt_set_hsm_password(struct lightningd *ld)
{
char *passwd, *passwd_confirmation, *err_msg;
int is_encrypted;
is_encrypted = is_hsm_secret_encrypted("hsm_secret");
if (is_encrypted == -1)
return tal_fmt(NULL, "Could not access 'hsm_secret': %s",
strerror(errno));
printf("The hsm_secret is encrypted with a password. In order to "
"decrypt it and start the node you must provide the password.\n");
@@ -513,17 +519,19 @@ static char *opt_set_hsm_password(struct lightningd *ld)
passwd = read_stdin_pass_with_exit_code(&err_msg, &opt_exitcode);
if (!passwd)
return err_msg;
printf("Confirm hsm_secret password:\n");
fflush(stdout);
passwd_confirmation = read_stdin_pass_with_exit_code(&err_msg, &opt_exitcode);
if (!passwd_confirmation)
return err_msg;
if (!is_encrypted) {
printf("Confirm hsm_secret password:\n");
fflush(stdout);
passwd_confirmation = read_stdin_pass_with_exit_code(&err_msg, &opt_exitcode);
if (!passwd_confirmation)
return err_msg;
if (!streq(passwd, passwd_confirmation)) {
opt_exitcode = HSM_BAD_PASSWORD;
return "Passwords confirmation mismatch.";
if (!streq(passwd, passwd_confirmation)) {
opt_exitcode = HSM_BAD_PASSWORD;
return "Passwords confirmation mismatch.";
}
free(passwd_confirmation);
}
printf("\n");
ld->config.keypass = tal(NULL, struct secret);
@@ -534,7 +542,6 @@ static char *opt_set_hsm_password(struct lightningd *ld)
ld->encrypted_hsm = true;
free(passwd);
free(passwd_confirmation);
return NULL;
}

View File

@@ -1039,8 +1039,6 @@ def test_hsm_secret_encryption(node_factory):
wait_for_initialized=False)
l1.daemon.wait_for_log(r'Enter hsm_secret password')
write_all(master_fd, password[2:].encode("utf-8"))
l1.daemon.wait_for_log(r'Confirm hsm_secret password')
write_all(master_fd, password[2:].encode("utf-8"))
assert(l1.daemon.proc.wait(WAIT_TIMEOUT) == HSM_BAD_PASSWORD)
assert(l1.daemon.is_in_log("Wrong password for encrypted hsm_secret."))
@@ -1048,8 +1046,6 @@ 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')
write_all(master_fd, password.encode("utf-8"))
l1.daemon.wait_for_log(r'Confirm hsm_secret password')
write_all(master_fd, password.encode("utf-8"))
l1.daemon.wait_for_log("Server started with public key")
assert id == l1.rpc.getinfo()["id"]
l1.stop()
@@ -1057,7 +1053,6 @@ def test_hsm_secret_encryption(node_factory):
# We can restore the same wallet with the same password provided through stdin
l1.daemon.start(stdin=subprocess.PIPE, wait_for_initialized=False)
l1.daemon.proc.stdin.write(password.encode("utf-8"))
l1.daemon.proc.stdin.write(password.encode("utf-8"))
l1.daemon.proc.stdin.flush()
l1.daemon.wait_for_log("Server started with public key")
assert id == l1.rpc.getinfo()["id"]
@@ -1138,8 +1133,6 @@ def test_hsmtool_secret_decryption(node_factory):
l1.daemon.wait_for_log(r'The hsm_secret is encrypted')
write_all(master_fd, password.encode("utf-8"))
l1.daemon.wait_for_log(r'Confirm hsm_secret password')
write_all(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"]

View File

@@ -10,18 +10,17 @@
#include <common/configdir.h>
#include <common/derive_basepoints.h>
#include <common/descriptor_checksum.h>
#include <common/errcode.h>
#include <common/hsm_encryption.h>
#include <common/node_id.h>
#include <common/type_to_string.h>
#include <errno.h>
#include <fcntl.h>
#include <inttypes.h>
#include <sys/stat.h>
#include <unistd.h>
#include <wally_bip32.h>
#include <wally_bip39.h>
#define ERROR_HSM_FILE errno
#define ERROR_USAGE 2
#define ERROR_LIBSODIUM 3
#define ERROR_LIBWALLY 4
@@ -147,16 +146,24 @@ static void get_channel_seed(struct secret *channel_seed, struct node_id *peer_i
/* We detect an encrypted hsm_secret as a hsm_secret which is 73-bytes long. */
static bool hsm_secret_is_encrypted(const char *hsm_secret_path)
{
struct stat st;
switch (is_hsm_secret_encrypted(hsm_secret_path)) {
case -1:
err(ERROR_HSM_FILE, "Cannot open '%s'", hsm_secret_path);
case 1:
return true;
case 0: {
/* Extra sanity check on HSM file! */
struct stat st;
stat(hsm_secret_path, &st);
if (st.st_size != 32)
errx(ERROR_HSM_FILE,
"Invalid hsm_secret '%s' (neither plaintext "
"nor encrypted).", hsm_secret_path);
return false;
}
}
if (stat(hsm_secret_path, &st) != 0)
errx(ERROR_HSM_FILE, "Could not stat hsm_secret");
if (st.st_size != 32 && st.st_size != ENCRYPTED_HSM_SECRET_LEN)
errx(ERROR_HSM_FILE, "Invalid hsm_secret (neither plaintext "
"nor encrypted).");
return st.st_size == ENCRYPTED_HSM_SECRET_LEN;
abort();
}
static int decrypt_hsm(const char *hsm_secret_path)