Skip to content

Commit

Permalink
cli: make the command line more user friendly.
Browse files Browse the repository at this point in the history
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 <[email protected]>
  • Loading branch information
vincenzopalazzo authored and rustyrussell committed Mar 23, 2022
1 parent 58ce4cf commit 53806d1
Show file tree
Hide file tree
Showing 7 changed files with 55 additions and 33 deletions.
1 change: 1 addition & 0 deletions common/errcode.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
13 changes: 13 additions & 0 deletions common/hsm_encryption.c
Original file line number Diff line number Diff line change
@@ -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>

Expand Down Expand Up @@ -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. */
Expand Down
3 changes: 3 additions & 0 deletions common/hsm_encryption.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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 */
6 changes: 2 additions & 4 deletions lightningd/hsm_control.c
Original file line number Diff line number Diff line change
Expand Up @@ -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];
Expand Down
29 changes: 18 additions & 11 deletions lightningd/options.c
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand All @@ -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 (!streq(passwd, passwd_confirmation)) {
opt_exitcode = HSM_BAD_PASSWORD;
return "Passwords confirmation mismatch.";
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.";
}
free(passwd_confirmation);
}

printf("\n");

ld->config.keypass = tal(NULL, struct secret);
Expand All @@ -534,7 +542,6 @@ static char *opt_set_hsm_password(struct lightningd *ld)

ld->encrypted_hsm = true;
free(passwd);
free(passwd_confirmation);

return NULL;
}
Expand Down
7 changes: 0 additions & 7 deletions tests/test_wallet.py
Original file line number Diff line number Diff line change
Expand Up @@ -1039,25 +1039,20 @@ 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."))

# Test we can restore the same wallet with the same password
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()

# 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"]
Expand Down Expand Up @@ -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"]
Expand Down
29 changes: 18 additions & 11 deletions tools/hsmtool.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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;

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).");
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;
}
}

return st.st_size == ENCRYPTED_HSM_SECRET_LEN;
abort();
}

static int decrypt_hsm(const char *hsm_secret_path)
Expand Down

0 comments on commit 53806d1

Please sign in to comment.