2021-12-04 21:53:56 +10:30
|
|
|
#include "config.h"
|
2020-10-28 23:59:49 +01:00
|
|
|
#include <ccan/array_size/array_size.h>
|
2019-10-22 12:41:08 +02:00
|
|
|
#include <ccan/crypto/hkdf_sha256/hkdf_sha256.h>
|
2019-10-19 16:29:36 +02:00
|
|
|
#include <ccan/err/err.h>
|
|
|
|
|
#include <ccan/noerr/noerr.h>
|
|
|
|
|
#include <ccan/read_write_all/read_write_all.h>
|
2023-04-11 12:01:31 +09:30
|
|
|
#include <ccan/rune/rune.h>
|
2023-04-07 21:24:14 +09:30
|
|
|
#include <ccan/tal/grab_file/grab_file.h>
|
2020-10-22 11:27:07 +10:30
|
|
|
#include <ccan/tal/path/path.h>
|
2020-10-30 15:01:32 +01:00
|
|
|
#include <ccan/tal/str/str.h>
|
2019-11-25 16:08:42 +01:00
|
|
|
#include <common/bech32.h>
|
2023-10-26 13:45:54 +10:30
|
|
|
#include <common/bech32_util.h>
|
2023-07-31 09:41:09 +05:30
|
|
|
#include <common/codex32.h>
|
2019-10-22 12:41:08 +02:00
|
|
|
#include <common/derive_basepoints.h>
|
2020-10-30 15:01:32 +01:00
|
|
|
#include <common/descriptor_checksum.h>
|
2022-03-10 22:26:20 +01:00
|
|
|
#include <common/errcode.h>
|
2025-10-24 13:57:43 +10:30
|
|
|
#include <common/hsm_secret.h>
|
2024-05-26 16:31:58 -04:00
|
|
|
#include <common/key_derive.h>
|
2025-10-24 13:57:44 +10:30
|
|
|
#include <common/memleak.h>
|
2024-03-20 11:17:55 +10:30
|
|
|
#include <common/utils.h>
|
2024-05-26 16:31:58 -04:00
|
|
|
#include <common/utxo.h>
|
2019-10-19 16:29:36 +02:00
|
|
|
#include <errno.h>
|
|
|
|
|
#include <fcntl.h>
|
2019-10-22 12:41:08 +02:00
|
|
|
#include <inttypes.h>
|
2025-10-22 19:44:27 +10:30
|
|
|
#include <sys/stat.h>
|
2019-10-19 16:29:36 +02:00
|
|
|
#include <unistd.h>
|
2021-09-16 14:30:42 +09:30
|
|
|
#include <wally_bip32.h>
|
2020-10-28 23:59:49 +01:00
|
|
|
#include <wally_bip39.h>
|
2019-10-19 16:29:36 +02:00
|
|
|
|
|
|
|
|
#define ERROR_USAGE 2
|
|
|
|
|
#define ERROR_LIBSODIUM 3
|
2019-10-22 12:41:08 +02:00
|
|
|
#define ERROR_LIBWALLY 4
|
|
|
|
|
#define ERROR_KEYDERIV 5
|
2020-10-28 23:59:49 +01:00
|
|
|
#define ERROR_LANG_NOT_SUPPORTED 6
|
2021-01-02 22:47:03 +01:00
|
|
|
#define ERROR_TERM 7
|
2019-10-19 16:29:36 +02:00
|
|
|
|
2025-10-24 13:57:44 +10:30
|
|
|
/* We don't implement leak detection, since we don't persist. */
|
|
|
|
|
void *notleak_(void *ptr, bool plus_children)
|
|
|
|
|
{
|
|
|
|
|
return ptr;
|
|
|
|
|
}
|
|
|
|
|
|
2020-06-29 14:28:46 +08:00
|
|
|
static void show_usage(const char *progname)
|
2019-10-19 16:29:36 +02:00
|
|
|
{
|
2020-06-29 14:28:46 +08:00
|
|
|
printf("%s <method> [arguments]\n", progname);
|
2019-10-19 16:29:36 +02:00
|
|
|
printf("methods:\n");
|
2025-10-24 13:57:43 +10:30
|
|
|
printf(" - decrypt <path/to/hsm_secret> [LEGACY - binary format only]\n");
|
|
|
|
|
printf(" - encrypt <path/to/hsm_secret> [LEGACY - binary format only]\n");
|
2019-10-22 12:41:08 +02:00
|
|
|
printf(" - dumpcommitments <node id> <channel dbid> <depth> "
|
2025-10-24 13:57:43 +10:30
|
|
|
"<path/to/hsm_secret>\n");
|
2019-11-25 16:08:42 +01:00
|
|
|
printf(" - guesstoremote <P2WPKH address> <node id> <tries> "
|
2025-10-24 13:57:43 +10:30
|
|
|
"<path/to/hsm_secret>\n");
|
|
|
|
|
printf(" - generatehsm <path/to/new/hsm_secret>\n");
|
2024-05-26 16:31:58 -04:00
|
|
|
printf(" - derivetoremote <node id> <channel dbid> [<cmt pt>] "
|
|
|
|
|
"<path/to/hsm_secret>\n");
|
2022-07-20 12:00:25 +09:30
|
|
|
printf(" - checkhsm <path/to/new/hsm_secret>\n");
|
2024-01-10 14:22:48 +01:00
|
|
|
printf(" - dumponchaindescriptors [--show-secrets] <path/to/hsm_secret> [network]\n");
|
2023-04-11 12:01:31 +09:30
|
|
|
printf(" - makerune <path/to/hsm_secret>\n");
|
2026-01-13 13:19:15 +10:30
|
|
|
printf(" - getsecret <path/to/hsm_secret> [<id>]\n");
|
2023-11-24 16:51:30 +01:00
|
|
|
printf(" - getemergencyrecover <path/to/emergency.recover>\n");
|
2024-09-06 11:43:35 +09:30
|
|
|
printf(" - getnodeid <path/to/hsm_secret>\n");
|
2019-10-19 16:29:36 +02:00
|
|
|
exit(0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool ensure_hsm_secret_exists(int fd, const char *path)
|
|
|
|
|
{
|
|
|
|
|
const char *config_dir = path_dirname(NULL, path);
|
|
|
|
|
if (fsync(fd) != 0) {
|
|
|
|
|
close(fd);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
if (close(fd) != 0)
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
fd = open(config_dir, O_RDONLY);
|
|
|
|
|
if (fd < 0)
|
|
|
|
|
return false;
|
|
|
|
|
if (fsync(fd) != 0) {
|
|
|
|
|
close(fd);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
close(fd);
|
|
|
|
|
tal_free(config_dir);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
2025-10-24 13:57:43 +10:30
|
|
|
/* Load hsm_secret using the unified interface */
|
|
|
|
|
static struct hsm_secret *load_hsm_secret(const tal_t *ctx, const char *hsm_secret_path)
|
2019-10-22 12:41:08 +02:00
|
|
|
{
|
2025-10-24 13:57:51 +10:30
|
|
|
u8 *contents = grab_file_raw(tmpctx, hsm_secret_path);
|
2025-10-24 13:57:43 +10:30
|
|
|
const char *passphrase = NULL;
|
|
|
|
|
struct hsm_secret *hsms;
|
|
|
|
|
enum hsm_secret_error error;
|
2019-10-22 12:41:08 +02:00
|
|
|
|
2025-10-24 13:57:43 +10:30
|
|
|
if (!contents)
|
|
|
|
|
err(EXITCODE_ERROR_HSM_FILE, "Reading hsm_secret");
|
2019-10-22 12:41:08 +02:00
|
|
|
|
2025-10-24 13:57:43 +10:30
|
|
|
/* Get passphrase if needed */
|
2025-10-24 13:57:51 +10:30
|
|
|
if (hsm_secret_needs_passphrase(contents, tal_bytelen(contents))) {
|
2025-10-24 13:57:43 +10:30
|
|
|
printf("Enter hsm_secret password:\n");
|
|
|
|
|
fflush(stdout);
|
|
|
|
|
passphrase = read_stdin_pass(tmpctx, &error);
|
|
|
|
|
if (!passphrase)
|
|
|
|
|
errx(EXITCODE_ERROR_HSM_FILE, "Could not read password: %s", hsm_secret_error_str(error));
|
|
|
|
|
}
|
2019-10-22 12:41:08 +02:00
|
|
|
|
2025-10-24 13:57:51 +10:30
|
|
|
hsms = extract_hsm_secret(ctx, contents, tal_bytelen(contents), passphrase, &error);
|
2025-10-24 13:57:43 +10:30
|
|
|
if (!hsms) {
|
|
|
|
|
err(EXITCODE_ERROR_HSM_FILE, "%s", hsm_secret_error_str(error));
|
|
|
|
|
}
|
|
|
|
|
return hsms;
|
2019-10-22 12:41:08 +02:00
|
|
|
}
|
|
|
|
|
|
2025-10-24 13:57:43 +10:30
|
|
|
/* Legacy function - only works with binary encrypted format */
|
|
|
|
|
static void decrypt_hsm(const char *hsm_secret_path)
|
2019-10-22 12:41:08 +02:00
|
|
|
{
|
2025-10-24 13:57:43 +10:30
|
|
|
int fd;
|
|
|
|
|
struct hsm_secret *hsms;
|
|
|
|
|
const char *dir, *backup;
|
2021-01-03 16:59:24 +01:00
|
|
|
|
2025-10-24 13:57:43 +10:30
|
|
|
/* Check if it's a format we can decrypt */
|
2025-10-24 13:57:51 +10:30
|
|
|
u8 *contents = grab_file_raw(tmpctx, hsm_secret_path);
|
2025-10-24 13:57:43 +10:30
|
|
|
if (!contents)
|
|
|
|
|
err(EXITCODE_ERROR_HSM_FILE, "Reading hsm_secret");
|
2021-01-02 22:47:03 +01:00
|
|
|
|
2025-10-24 13:57:51 +10:30
|
|
|
enum hsm_secret_type type = detect_hsm_secret_type(contents, tal_bytelen(contents));
|
2023-04-07 21:24:14 +09:30
|
|
|
|
2025-10-24 13:57:43 +10:30
|
|
|
if (type != HSM_SECRET_ENCRYPTED) {
|
|
|
|
|
errx(ERROR_USAGE, "decrypt command only works on legacy encrypted binary format (73 bytes).\n"
|
|
|
|
|
"Current file is: %s\n"
|
|
|
|
|
"For mnemonic formats, use the generatehsm command to create a new hsm_secret instead.",
|
|
|
|
|
format_type_name(type));
|
2023-04-07 21:24:14 +09:30
|
|
|
}
|
|
|
|
|
|
2025-10-24 13:57:43 +10:30
|
|
|
/* Load the hsm_secret */
|
|
|
|
|
hsms = load_hsm_secret(tmpctx, hsm_secret_path);
|
2021-01-02 22:47:03 +01:00
|
|
|
|
2020-01-09 20:57:20 +01:00
|
|
|
dir = path_dirname(NULL, hsm_secret_path);
|
|
|
|
|
backup = path_join(dir, dir, "hsm_secret.backup");
|
|
|
|
|
|
2019-10-19 16:29:36 +02:00
|
|
|
/* Create a backup file, "just in case". */
|
2020-01-09 20:57:20 +01:00
|
|
|
rename(hsm_secret_path, backup);
|
2019-10-19 16:29:36 +02:00
|
|
|
fd = open(hsm_secret_path, O_CREAT|O_EXCL|O_WRONLY, 0400);
|
|
|
|
|
if (fd < 0)
|
2025-10-24 13:57:43 +10:30
|
|
|
err(EXITCODE_ERROR_HSM_FILE, "Could not open new hsm_secret");
|
2019-10-19 16:29:36 +02:00
|
|
|
|
2025-10-24 13:57:47 +10:30
|
|
|
if (!write_all(fd, hsms->secret_data, tal_bytelen(hsms->secret_data))) {
|
2019-10-19 16:29:36 +02:00
|
|
|
unlink_noerr(hsm_secret_path);
|
|
|
|
|
close(fd);
|
|
|
|
|
rename("hsm_secret.backup", hsm_secret_path);
|
2025-10-24 13:57:43 +10:30
|
|
|
err(EXITCODE_ERROR_HSM_FILE,
|
2019-10-19 16:29:36 +02:00
|
|
|
"Failure writing plaintext seed to hsm_secret.");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Be as paranoïd as in hsmd with the file state on disk. */
|
|
|
|
|
if (!ensure_hsm_secret_exists(fd, hsm_secret_path)) {
|
|
|
|
|
unlink_noerr(hsm_secret_path);
|
2020-01-09 20:57:20 +01:00
|
|
|
rename(backup, hsm_secret_path);
|
2022-07-20 11:59:25 +09:30
|
|
|
errx(EXITCODE_ERROR_HSM_FILE,
|
2019-10-19 16:29:36 +02:00
|
|
|
"Could not ensure hsm_secret existence.");
|
|
|
|
|
}
|
2020-01-09 20:57:20 +01:00
|
|
|
unlink_noerr(backup);
|
|
|
|
|
tal_free(dir);
|
2019-10-19 16:29:36 +02:00
|
|
|
|
2021-06-15 00:57:47 +02:00
|
|
|
printf("Successfully decrypted hsm_secret, be careful now :-).\n");
|
2019-10-19 16:29:36 +02:00
|
|
|
}
|
|
|
|
|
|
2025-10-24 13:57:43 +10:30
|
|
|
/* Legacy function - only works with binary plain format */
|
|
|
|
|
static void encrypt_hsm(const char *hsm_secret_path)
|
2023-07-31 09:41:09 +05:30
|
|
|
{
|
2025-10-24 13:57:43 +10:30
|
|
|
int fd;
|
|
|
|
|
struct hsm_secret *hsms;
|
|
|
|
|
u8 encrypted_hsm_secret[ENCRYPTED_HSM_SECRET_LEN];
|
|
|
|
|
const char *passwd, *passwd_confirmation;
|
|
|
|
|
const char *dir, *backup;
|
|
|
|
|
enum hsm_secret_error pass_err;
|
|
|
|
|
|
|
|
|
|
/* Check if it's a format we can encrypt */
|
2025-10-24 13:57:51 +10:30
|
|
|
u8 *contents = grab_file_raw(tmpctx, hsm_secret_path);
|
2025-10-24 13:57:43 +10:30
|
|
|
if (!contents)
|
|
|
|
|
err(EXITCODE_ERROR_HSM_FILE, "Reading hsm_secret");
|
2023-08-01 10:59:54 +09:30
|
|
|
|
2025-10-24 13:57:51 +10:30
|
|
|
enum hsm_secret_type type = detect_hsm_secret_type(contents, tal_bytelen(contents));
|
2023-07-31 09:41:09 +05:30
|
|
|
|
2025-10-24 13:57:43 +10:30
|
|
|
if (type != HSM_SECRET_PLAIN) {
|
|
|
|
|
errx(ERROR_USAGE, "encrypt command only works on legacy plain binary format (32 bytes).\n"
|
|
|
|
|
"Current file is: %s\n"
|
|
|
|
|
"For mnemonic formats, the passphrase is already integrated into the format.",
|
|
|
|
|
format_type_name(type));
|
2023-10-26 13:45:54 +10:30
|
|
|
}
|
|
|
|
|
|
2025-10-24 13:57:43 +10:30
|
|
|
/* Load the hsm_secret */
|
|
|
|
|
hsms = load_hsm_secret(tmpctx, hsm_secret_path);
|
2021-01-02 22:47:03 +01:00
|
|
|
|
|
|
|
|
printf("Enter hsm_secret password:\n");
|
2021-01-03 14:54:13 +01:00
|
|
|
fflush(stdout);
|
2025-10-24 13:57:43 +10:30
|
|
|
passwd = read_stdin_pass(tmpctx, &pass_err);
|
2021-01-03 14:54:13 +01:00
|
|
|
if (!passwd)
|
2025-10-24 13:57:43 +10:30
|
|
|
errx(EXITCODE_ERROR_HSM_FILE, "Could not read password: %s", hsm_secret_error_str(pass_err));
|
|
|
|
|
|
2021-01-03 01:40:42 +01:00
|
|
|
printf("Confirm hsm_secret password:\n");
|
2021-01-03 14:54:13 +01:00
|
|
|
fflush(stdout);
|
2025-10-24 13:57:43 +10:30
|
|
|
passwd_confirmation = read_stdin_pass(tmpctx, &pass_err);
|
2021-01-03 14:54:13 +01:00
|
|
|
if (!passwd_confirmation)
|
2025-10-24 13:57:43 +10:30
|
|
|
errx(EXITCODE_ERROR_HSM_FILE, "Could not read password: %s", hsm_secret_error_str(pass_err));
|
|
|
|
|
|
2021-01-03 01:40:42 +01:00
|
|
|
if (!streq(passwd, passwd_confirmation))
|
|
|
|
|
errx(ERROR_USAGE, "Passwords confirmation mismatch.");
|
2021-01-02 22:47:03 +01:00
|
|
|
|
2020-01-09 20:57:20 +01:00
|
|
|
dir = path_dirname(NULL, hsm_secret_path);
|
|
|
|
|
backup = path_join(dir, dir, "hsm_secret.backup");
|
2019-10-19 16:55:51 +02:00
|
|
|
|
2025-10-24 13:57:43 +10:30
|
|
|
/* Create encryption key and encrypt */
|
|
|
|
|
struct secret *encryption_key = get_encryption_key(tmpctx, passwd);
|
|
|
|
|
if (!encryption_key)
|
|
|
|
|
errx(ERROR_LIBSODIUM, "Could not derive encryption key");
|
|
|
|
|
|
2025-10-24 13:57:47 +10:30
|
|
|
struct secret legacy_secret;
|
|
|
|
|
memcpy(legacy_secret.data, hsms->secret_data, 32);
|
|
|
|
|
if (!encrypt_legacy_hsm_secret(encryption_key, &legacy_secret, encrypted_hsm_secret))
|
2021-01-03 16:35:58 +01:00
|
|
|
errx(ERROR_LIBSODIUM, "Could not encrypt the hsm_secret seed.");
|
2019-10-19 16:55:51 +02:00
|
|
|
|
|
|
|
|
/* Create a backup file, "just in case". */
|
2020-01-09 20:57:20 +01:00
|
|
|
rename(hsm_secret_path, backup);
|
2019-10-19 16:55:51 +02:00
|
|
|
fd = open(hsm_secret_path, O_CREAT|O_EXCL|O_WRONLY, 0400);
|
|
|
|
|
if (fd < 0)
|
2025-10-24 13:57:43 +10:30
|
|
|
err(EXITCODE_ERROR_HSM_FILE, "Could not open new hsm_secret");
|
2019-10-19 16:55:51 +02:00
|
|
|
|
|
|
|
|
/* Write the encrypted hsm_secret. */
|
2025-10-24 13:57:43 +10:30
|
|
|
if (!write_all(fd, encrypted_hsm_secret,
|
|
|
|
|
ENCRYPTED_HSM_SECRET_LEN)) {
|
2019-10-19 16:55:51 +02:00
|
|
|
unlink_noerr(hsm_secret_path);
|
|
|
|
|
close(fd);
|
2020-01-09 20:57:20 +01:00
|
|
|
rename(backup, hsm_secret_path);
|
2025-10-24 13:57:43 +10:30
|
|
|
err(EXITCODE_ERROR_HSM_FILE, "Failure writing cipher to hsm_secret.");
|
2019-10-19 16:55:51 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Be as paranoïd as in hsmd with the file state on disk. */
|
|
|
|
|
if (!ensure_hsm_secret_exists(fd, hsm_secret_path)) {
|
|
|
|
|
unlink_noerr(hsm_secret_path);
|
2020-01-09 20:57:20 +01:00
|
|
|
rename(backup, hsm_secret_path);
|
2022-07-20 11:59:25 +09:30
|
|
|
errx(EXITCODE_ERROR_HSM_FILE, "Could not ensure hsm_secret existence.");
|
2019-10-19 16:55:51 +02:00
|
|
|
}
|
2020-01-09 20:57:20 +01:00
|
|
|
unlink_noerr(backup);
|
|
|
|
|
tal_free(dir);
|
2019-10-19 16:55:51 +02:00
|
|
|
|
2021-06-15 00:57:47 +02:00
|
|
|
printf("Successfully encrypted hsm_secret. You'll now have to pass the "
|
2019-10-19 16:55:51 +02:00
|
|
|
"--encrypted-hsm startup option.\n");
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-24 13:57:43 +10:30
|
|
|
/* Taken from hsmd. */
|
|
|
|
|
static void get_channel_seed(struct secret *channel_seed, const struct node_id *peer_id,
|
|
|
|
|
u64 dbid, struct secret *hsm_secret)
|
|
|
|
|
{
|
|
|
|
|
struct secret channel_base;
|
|
|
|
|
u8 input[sizeof(peer_id->k) + sizeof(dbid)];
|
|
|
|
|
const char *info = "per-peer seed";
|
|
|
|
|
|
|
|
|
|
hkdf_sha256(&channel_base, sizeof(struct secret), NULL, 0,
|
|
|
|
|
hsm_secret, sizeof(*hsm_secret),
|
|
|
|
|
"peer seed", strlen("peer seed"));
|
|
|
|
|
memcpy(input, peer_id->k, sizeof(peer_id->k));
|
|
|
|
|
BUILD_ASSERT(sizeof(peer_id->k) == PUBKEY_CMPR_LEN);
|
|
|
|
|
memcpy(input + PUBKEY_CMPR_LEN, &dbid, sizeof(dbid));
|
|
|
|
|
|
|
|
|
|
hkdf_sha256(channel_seed, sizeof(*channel_seed),
|
|
|
|
|
input, sizeof(input),
|
|
|
|
|
&channel_base, sizeof(channel_base),
|
|
|
|
|
info, strlen(info));
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-13 13:19:15 +10:30
|
|
|
static void print_secret(const char *hsm_secret_path, const char *id, bool must_be_oldstyle)
|
2025-10-24 13:57:43 +10:30
|
|
|
{
|
|
|
|
|
struct secret hsm_secret;
|
|
|
|
|
char *bip93;
|
|
|
|
|
const char *err;
|
|
|
|
|
struct hsm_secret *hsms = load_hsm_secret(tmpctx, hsm_secret_path);
|
|
|
|
|
|
2026-01-13 13:19:15 +10:30
|
|
|
switch (hsms->type) {
|
|
|
|
|
case HSM_SECRET_ENCRYPTED:
|
|
|
|
|
errx(ERROR_USAGE, "Encrypted hsm_secret");
|
|
|
|
|
case HSM_SECRET_MNEMONIC_NO_PASS:
|
|
|
|
|
if (must_be_oldstyle)
|
|
|
|
|
errx(ERROR_USAGE, "Cannot use getcodexsecret with modern nodes: use getsecret");
|
|
|
|
|
printf("%s\n", hsms->mnemonic);
|
|
|
|
|
return;
|
|
|
|
|
case HSM_SECRET_MNEMONIC_WITH_PASS:
|
|
|
|
|
errx(ERROR_USAGE, "hsm_secret with passphrase");
|
|
|
|
|
case HSM_SECRET_PLAIN:
|
|
|
|
|
if (id == NULL)
|
|
|
|
|
errx(ERROR_USAGE, "Must set 'id' for a codex32 secret");
|
|
|
|
|
/* Extract first 32 bytes for legacy compatibility */
|
|
|
|
|
memcpy(hsm_secret.data, hsms->secret_data, 32);
|
|
|
|
|
|
|
|
|
|
err = codex32_secret_encode(tmpctx, "cl", id, 0, hsm_secret.data, 32, &bip93);
|
|
|
|
|
if (err)
|
|
|
|
|
errx(ERROR_USAGE, "%s", err);
|
|
|
|
|
|
|
|
|
|
printf("%s\n", bip93);
|
|
|
|
|
return;
|
|
|
|
|
case HSM_SECRET_INVALID:
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
/* Never happens. */
|
|
|
|
|
abort();
|
2025-10-24 13:57:43 +10:30
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void print_emergencyrecover(const char *emer_rec_path)
|
|
|
|
|
{
|
2025-10-24 13:57:51 +10:30
|
|
|
u8 *scb = grab_file_raw(tmpctx, emer_rec_path);
|
2025-10-24 13:57:43 +10:30
|
|
|
char *output, *hrp = "clnemerg";
|
|
|
|
|
if (!scb) {
|
|
|
|
|
err(EXITCODE_ERROR_HSM_FILE, "Reading emergency.recover");
|
|
|
|
|
}
|
|
|
|
|
u5 *data = tal_arr(tmpctx, u5, 0);
|
|
|
|
|
|
2025-10-24 13:57:51 +10:30
|
|
|
bech32_push_bits(&data, scb, tal_bytelen(scb) * 8);
|
2025-10-24 13:57:43 +10:30
|
|
|
output = tal_arr(tmpctx, char, strlen(hrp) + tal_count(data) + 8);
|
|
|
|
|
|
|
|
|
|
bech32_encode(output, hrp, data, tal_count(data), (size_t)-1,
|
|
|
|
|
BECH32_ENCODING_BECH32);
|
|
|
|
|
|
|
|
|
|
printf("%s\n", output);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void dump_commitments_infos(struct node_id *node_id, u64 channel_id,
|
|
|
|
|
u64 depth, char *hsm_secret_path)
|
2019-10-22 12:41:08 +02:00
|
|
|
{
|
|
|
|
|
struct sha256 shaseed;
|
|
|
|
|
struct secret hsm_secret, channel_seed, per_commitment_secret;
|
|
|
|
|
struct pubkey per_commitment_point;
|
2025-10-24 13:57:43 +10:30
|
|
|
struct hsm_secret *hsms = load_hsm_secret(tmpctx, hsm_secret_path);
|
2025-10-24 13:57:47 +10:30
|
|
|
/* Extract first 32 bytes for legacy compatibility */
|
|
|
|
|
memcpy(hsm_secret.data, hsms->secret_data, 32);
|
2019-10-22 12:41:08 +02:00
|
|
|
get_channel_seed(&channel_seed, node_id, channel_id, &hsm_secret);
|
|
|
|
|
|
|
|
|
|
derive_shaseed(&channel_seed, &shaseed);
|
2024-03-20 11:17:52 +10:30
|
|
|
printf("shaseed: %s\n", fmt_sha256(tmpctx, &shaseed));
|
2019-10-22 12:41:08 +02:00
|
|
|
for (u64 i = 0; i < depth; i++) {
|
|
|
|
|
if (!per_commit_secret(&shaseed, &per_commitment_secret, i))
|
2021-01-03 01:14:33 +01:00
|
|
|
errx(ERROR_KEYDERIV, "Could not derive secret #%"PRIu64, i);
|
2019-10-22 12:41:08 +02:00
|
|
|
printf("commit secret #%"PRIu64": %s\n",
|
|
|
|
|
i, tal_hexstr(tmpctx, per_commitment_secret.data,
|
|
|
|
|
sizeof(per_commitment_secret.data)));
|
|
|
|
|
if (!per_commit_point(&shaseed, &per_commitment_point, i))
|
2021-01-03 01:14:33 +01:00
|
|
|
errx(ERROR_KEYDERIV, "Could not derive point #%"PRIu64, i);
|
2019-10-22 12:41:08 +02:00
|
|
|
printf("commit point #%"PRIu64": %s\n",
|
2024-03-20 11:17:52 +10:30
|
|
|
i, fmt_pubkey(tmpctx, &per_commitment_point));
|
2019-10-22 12:41:08 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-24 13:57:43 +10:30
|
|
|
static void guess_to_remote(const char *address, struct node_id *node_id,
|
|
|
|
|
u64 tries, char *hsm_secret_path)
|
2019-11-25 16:08:42 +01:00
|
|
|
{
|
|
|
|
|
struct secret hsm_secret, channel_seed, basepoint_secret;
|
|
|
|
|
struct pubkey basepoint;
|
|
|
|
|
struct ripemd160 pubkeyhash;
|
|
|
|
|
u8 goal_pubkeyhash[20];
|
|
|
|
|
char hrp[strlen(address) - 6];
|
2023-04-07 21:24:14 +09:30
|
|
|
int witver;
|
2019-11-25 16:08:42 +01:00
|
|
|
size_t witlen;
|
|
|
|
|
|
|
|
|
|
/* Get the hrp to accept addresses from any network. */
|
2021-06-09 09:25:46 +09:30
|
|
|
if (bech32_decode(hrp, goal_pubkeyhash, &witlen, address, 90) != BECH32_ENCODING_BECH32)
|
2019-11-25 16:08:42 +01:00
|
|
|
errx(ERROR_USAGE, "Could not get address' network");
|
|
|
|
|
if (segwit_addr_decode(&witver, goal_pubkeyhash, &witlen, hrp, address) != 1)
|
|
|
|
|
errx(ERROR_USAGE, "Wrong bech32 address");
|
|
|
|
|
|
2025-10-24 13:57:43 +10:30
|
|
|
struct hsm_secret *hsms = load_hsm_secret(tmpctx, hsm_secret_path);
|
2025-10-24 13:57:47 +10:30
|
|
|
memcpy(hsm_secret.data, hsms->secret_data, 32);
|
2019-11-25 16:08:42 +01:00
|
|
|
|
|
|
|
|
for (u64 dbid = 1; dbid < tries ; dbid++) {
|
|
|
|
|
get_channel_seed(&channel_seed, node_id, dbid, &hsm_secret);
|
|
|
|
|
if (!derive_payment_basepoint(&channel_seed,
|
|
|
|
|
&basepoint, &basepoint_secret))
|
|
|
|
|
errx(ERROR_KEYDERIV, "Could not derive basepoints for dbid %"PRIu64
|
|
|
|
|
" and channel seed %s.", dbid,
|
2024-03-20 11:17:52 +10:30
|
|
|
fmt_secret(tmpctx, &channel_seed));
|
2019-11-25 16:08:42 +01:00
|
|
|
|
|
|
|
|
pubkey_to_hash160(&basepoint, &pubkeyhash);
|
|
|
|
|
if (memcmp(pubkeyhash.u.u8, goal_pubkeyhash, 20) == 0) {
|
|
|
|
|
printf("bech32 : %s\n", address);
|
|
|
|
|
printf("pubkey hash : %s\n",
|
2025-10-24 13:57:43 +10:30
|
|
|
tal_hexstr(tmpctx, pubkeyhash.u.u8, 20));
|
2019-11-25 16:08:42 +01:00
|
|
|
printf("pubkey : %s \n",
|
2025-10-24 13:57:43 +10:30
|
|
|
fmt_pubkey(tmpctx, &basepoint));
|
2019-11-25 16:08:42 +01:00
|
|
|
printf("privkey : %s \n",
|
2025-10-24 13:57:43 +10:30
|
|
|
fmt_secret(tmpctx, &basepoint_secret));
|
|
|
|
|
return;
|
2019-11-25 16:08:42 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-24 13:57:43 +10:30
|
|
|
errx(ERROR_USAGE, "Could not find any basepoint matching the provided witness programm.\n"
|
|
|
|
|
"Are you sure that the channel used `option_static_remotekey` ?");
|
2024-05-26 16:31:58 -04:00
|
|
|
}
|
|
|
|
|
|
2025-10-24 13:57:43 +10:30
|
|
|
static void generate_hsm(const char *hsm_secret_path)
|
2024-11-13 17:52:07 +10:30
|
|
|
{
|
2025-10-24 13:57:43 +10:30
|
|
|
const char *mnemonic, *passphrase;
|
|
|
|
|
enum hsm_secret_error error;
|
2020-10-28 23:59:49 +01:00
|
|
|
|
2025-10-24 13:57:43 +10:30
|
|
|
/* Get mnemonic from user using consistent interface */
|
|
|
|
|
mnemonic = read_stdin_mnemonic(tmpctx, &error);
|
|
|
|
|
if (!mnemonic)
|
|
|
|
|
errx(EXITCODE_ERROR_HSM_FILE, "Could not read mnemonic: %s", hsm_secret_error_str(error));
|
2020-10-28 23:59:49 +01:00
|
|
|
|
2025-10-24 13:57:43 +10:30
|
|
|
/* Get optional passphrase */
|
|
|
|
|
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");
|
2021-01-03 00:51:48 +01:00
|
|
|
fflush(stdout);
|
2025-10-24 13:57:43 +10:30
|
|
|
passphrase = read_stdin_pass(tmpctx, &error);
|
|
|
|
|
if (!passphrase)
|
|
|
|
|
errx(EXITCODE_ERROR_HSM_FILE, "Could not read passphrase: %s", hsm_secret_error_str(error));
|
|
|
|
|
if (streq(passphrase, "")) {
|
|
|
|
|
passphrase = NULL;
|
2020-10-28 23:59:49 +01:00
|
|
|
}
|
2024-11-13 17:52:07 +10:30
|
|
|
|
2025-10-24 13:57:43 +10:30
|
|
|
/* Write to file using your new mnemonic format */
|
|
|
|
|
int fd = open(hsm_secret_path, O_CREAT|O_EXCL|O_WRONLY, 0400);
|
|
|
|
|
if (fd < 0) {
|
|
|
|
|
err(ERROR_USAGE, "Unable to create hsm_secret file");
|
2021-01-02 23:41:24 +01:00
|
|
|
}
|
2020-10-28 23:59:49 +01:00
|
|
|
|
2025-10-24 13:57:43 +10:30
|
|
|
/* Hash the derived seed for validation */
|
|
|
|
|
struct sha256 seed_hash;
|
|
|
|
|
if (!derive_seed_hash(mnemonic, passphrase, &seed_hash))
|
|
|
|
|
errx(ERROR_USAGE, "Error deriving seed from mnemonic");
|
2020-10-28 23:59:49 +01:00
|
|
|
|
2025-10-24 13:57:43 +10:30
|
|
|
/* Write seed hash (32 bytes) + mnemonic */
|
|
|
|
|
if (!write_all(fd, &seed_hash, sizeof(seed_hash)))
|
|
|
|
|
err(ERROR_USAGE, "Error writing seed hash to hsm_secret file");
|
2020-10-28 23:59:49 +01:00
|
|
|
|
2025-10-24 13:57:43 +10:30
|
|
|
/* Write the mnemonic */
|
|
|
|
|
if (!write_all(fd, mnemonic, strlen(mnemonic)))
|
|
|
|
|
err(ERROR_USAGE, "Error writing mnemonic to hsm_secret file");
|
2020-10-28 23:59:49 +01:00
|
|
|
|
|
|
|
|
if (fsync(fd) != 0)
|
2025-10-24 13:57:43 +10:30
|
|
|
err(ERROR_USAGE, "Error fsyncing hsm_secret file");
|
2020-10-28 23:59:49 +01:00
|
|
|
|
|
|
|
|
if (close(fd) != 0)
|
2025-10-24 13:57:43 +10:30
|
|
|
err(ERROR_USAGE, "Error closing hsm_secret file");
|
2020-10-28 23:59:49 +01:00
|
|
|
|
|
|
|
|
printf("New hsm_secret file created at %s\n", hsm_secret_path);
|
2025-10-24 13:57:43 +10:30
|
|
|
printf("Format: %s\n", passphrase ? "mnemonic with passphrase" : "mnemonic without passphrase");
|
|
|
|
|
if (passphrase) {
|
|
|
|
|
printf("Remember your passphrase - it's required to use this hsm_secret!\n");
|
|
|
|
|
}
|
2020-10-28 23:59:49 +01:00
|
|
|
|
2025-10-24 13:57:43 +10:30
|
|
|
/* passphrase and mnemonic will be automatically cleaned up by tmpctx */
|
2020-10-28 23:59:49 +01:00
|
|
|
}
|
|
|
|
|
|
2025-10-24 13:57:43 +10:30
|
|
|
static void derive_to_remote(const struct unilateral_close_info *info, const char *hsm_secret_path)
|
|
|
|
|
{
|
|
|
|
|
struct secret hsm_secret, channel_seed, basepoint_secret;
|
|
|
|
|
struct pubkey basepoint;
|
|
|
|
|
struct privkey privkey;
|
|
|
|
|
|
|
|
|
|
secp256k1_ctx = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY
|
|
|
|
|
| SECP256K1_CONTEXT_SIGN);
|
|
|
|
|
|
|
|
|
|
struct hsm_secret *hsms = load_hsm_secret(tmpctx, hsm_secret_path);
|
2025-10-24 13:57:47 +10:30
|
|
|
/* Copy first 32 bytes of secret_data to the local secret struct */
|
|
|
|
|
memcpy(hsm_secret.data, hsms->secret_data, sizeof(hsm_secret.data));
|
2025-10-24 13:57:43 +10:30
|
|
|
get_channel_seed(&channel_seed, &info->peer_id, info->channel_id, &hsm_secret);
|
|
|
|
|
if (!derive_payment_basepoint(&channel_seed, &basepoint, &basepoint_secret))
|
|
|
|
|
errx(ERROR_KEYDERIV, "Could not derive basepoints for dbid %"PRIu64
|
|
|
|
|
" and channel seed %s.", info->channel_id,
|
|
|
|
|
fmt_secret(tmpctx, &channel_seed));
|
|
|
|
|
if (!info->commitment_point)
|
|
|
|
|
privkey.secret = basepoint_secret;
|
|
|
|
|
else if (!derive_simple_privkey(&basepoint_secret, &basepoint, info->commitment_point, &privkey))
|
|
|
|
|
errx(ERROR_KEYDERIV, "Could not derive simple privkey for dbid %"PRIu64
|
|
|
|
|
", channel seed %s, and commitment point %s.", info->channel_id,
|
|
|
|
|
fmt_secret(tmpctx, &channel_seed),
|
|
|
|
|
fmt_pubkey(tmpctx, info->commitment_point));
|
|
|
|
|
printf("privkey : %s\n", fmt_secret(tmpctx, &privkey.secret));
|
|
|
|
|
}
|
2026-01-13 22:52:03 +10:30
|
|
|
|
2025-10-24 13:57:43 +10:30
|
|
|
static void dumponchaindescriptors(const char *hsm_secret_path,
|
|
|
|
|
const u32 version, bool show_secrets)
|
2020-10-30 15:01:32 +01:00
|
|
|
{
|
|
|
|
|
u8 bip32_seed[BIP32_ENTROPY_LEN_256];
|
|
|
|
|
u32 salt = 0;
|
|
|
|
|
struct ext_key master_extkey;
|
2024-01-10 09:56:27 +01:00
|
|
|
char *enc_xkey, *descriptor;
|
2020-10-30 15:01:32 +01:00
|
|
|
struct descriptor_checksum checksum;
|
2025-10-24 13:57:43 +10:30
|
|
|
struct hsm_secret *hsms = load_hsm_secret(tmpctx, hsm_secret_path);
|
2026-01-13 22:52:03 +10:30
|
|
|
const char *path;
|
|
|
|
|
|
|
|
|
|
/* For BIP 86, we derive from subkey: m/86'/0'/0' */
|
|
|
|
|
if (use_bip86_derivation(tal_bytelen(hsms->secret_data))) {
|
|
|
|
|
/* First create the master key from the seed */
|
|
|
|
|
struct ext_key master_key, bip86_base;
|
|
|
|
|
u32 base_path[3];
|
|
|
|
|
base_path[0] = 86 | 0x80000000; /* 86' */
|
|
|
|
|
base_path[1] = 0x80000000; /* 0' */
|
|
|
|
|
base_path[2] = 0x80000000; /* 0' */
|
|
|
|
|
|
|
|
|
|
if (bip32_key_from_seed(hsms->secret_data, tal_bytelen(hsms->secret_data),
|
|
|
|
|
version, 0, &master_key) != WALLY_OK) {
|
|
|
|
|
errx(ERROR_LIBWALLY,
|
|
|
|
|
"Failed to create master key from BIP32 seed");
|
|
|
|
|
}
|
2020-10-30 15:01:32 +01:00
|
|
|
|
2026-01-13 22:52:03 +10:30
|
|
|
/* Derive the BIP86 base key */
|
|
|
|
|
if (bip32_key_from_parent_path(&master_key, base_path, 3, BIP32_FLAG_KEY_PRIVATE, &bip86_base) != WALLY_OK) {
|
|
|
|
|
errx(ERROR_LIBWALLY,
|
|
|
|
|
"Failed to derive BIP86 base key");
|
|
|
|
|
}
|
|
|
|
|
master_extkey = bip86_base;
|
|
|
|
|
/* Remaining path is 0/ */
|
|
|
|
|
path = "0";
|
|
|
|
|
} else {
|
|
|
|
|
struct secret hsm_secret;
|
|
|
|
|
|
|
|
|
|
/* Extract first 32 bytes for legacy compatibility */
|
|
|
|
|
memcpy(hsm_secret.data, hsms->secret_data, 32);
|
|
|
|
|
|
|
|
|
|
/* The root seed is derived from hsm_secret using hkdf.. */
|
|
|
|
|
do {
|
|
|
|
|
hkdf_sha256(bip32_seed, sizeof(bip32_seed),
|
|
|
|
|
&salt, sizeof(salt),
|
|
|
|
|
&hsm_secret, sizeof(hsm_secret),
|
|
|
|
|
"bip32 seed", strlen("bip32 seed"));
|
|
|
|
|
salt++;
|
|
|
|
|
/* ..Which is used to derive m/ */
|
|
|
|
|
} while (bip32_key_from_seed(bip32_seed, sizeof(bip32_seed),
|
|
|
|
|
version, 0, &master_extkey) != WALLY_OK);
|
|
|
|
|
/* Remaining path is 0/0/ */
|
|
|
|
|
path = "0/0";
|
|
|
|
|
}
|
2020-10-30 15:01:32 +01:00
|
|
|
|
2024-01-10 09:56:27 +01:00
|
|
|
if (show_secrets) {
|
|
|
|
|
if (bip32_key_to_base58(&master_extkey, BIP32_FLAG_KEY_PRIVATE,
|
|
|
|
|
&enc_xkey) != WALLY_OK)
|
|
|
|
|
errx(ERROR_LIBWALLY, "Can't encode xpriv");
|
|
|
|
|
} else {
|
|
|
|
|
if (bip32_key_to_base58(&master_extkey, BIP32_FLAG_KEY_PUBLIC,
|
|
|
|
|
&enc_xkey) != WALLY_OK)
|
|
|
|
|
errx(ERROR_LIBWALLY, "Can't encode xpub");
|
|
|
|
|
}
|
2020-10-30 15:01:32 +01:00
|
|
|
|
2023-07-11 05:29:41 +09:30
|
|
|
/* Now we format the descriptor strings (we only ever create P2TR, P2WPKH, and
|
2020-10-30 15:01:32 +01:00
|
|
|
* P2SH-P2WPKH outputs). */
|
2026-01-13 22:52:03 +10:30
|
|
|
descriptor = tal_fmt(NULL, "wpkh(%s/%s/*)", enc_xkey, path);
|
2020-10-30 15:01:32 +01:00
|
|
|
if (!descriptor_checksum(descriptor, strlen(descriptor), &checksum))
|
|
|
|
|
errx(ERROR_LIBWALLY, "Can't derive descriptor checksum for wpkh");
|
|
|
|
|
printf("%s#%s\n", descriptor, checksum.csum);
|
|
|
|
|
tal_free(descriptor);
|
|
|
|
|
|
2026-01-13 22:52:03 +10:30
|
|
|
descriptor = tal_fmt(NULL, "sh(wpkh(%s/%s/*))", enc_xkey, path);
|
2020-10-30 15:01:32 +01:00
|
|
|
if (!descriptor_checksum(descriptor, strlen(descriptor), &checksum))
|
|
|
|
|
errx(ERROR_LIBWALLY, "Can't derive descriptor checksum for sh(wpkh)");
|
|
|
|
|
printf("%s#%s\n", descriptor, checksum.csum);
|
|
|
|
|
tal_free(descriptor);
|
|
|
|
|
|
2026-01-13 22:52:03 +10:30
|
|
|
descriptor = tal_fmt(NULL, "tr(%s/%s/*)", enc_xkey, path);
|
2023-07-11 05:29:41 +09:30
|
|
|
if (!descriptor_checksum(descriptor, strlen(descriptor), &checksum))
|
|
|
|
|
errx(ERROR_LIBWALLY, "Can't derive descriptor checksum for tr");
|
|
|
|
|
printf("%s#%s\n", descriptor, checksum.csum);
|
|
|
|
|
tal_free(descriptor);
|
|
|
|
|
|
2024-01-10 09:56:27 +01:00
|
|
|
wally_free_string(enc_xkey);
|
2020-10-30 15:01:32 +01:00
|
|
|
}
|
|
|
|
|
|
2025-10-24 13:57:43 +10:30
|
|
|
/* Check HSM secret by comparing with backup mnemonic */
|
|
|
|
|
static void check_hsm(const char *hsm_secret_path)
|
2022-07-20 12:00:25 +09:30
|
|
|
{
|
2025-10-24 13:57:43 +10:30
|
|
|
struct secret file_secret, derived_secret;
|
2022-07-20 12:00:25 +09:30
|
|
|
u8 bip32_seed[BIP39_SEED_LEN_512];
|
|
|
|
|
size_t bip32_seed_len;
|
2025-10-24 13:57:43 +10:30
|
|
|
const char *mnemonic_passphrase, *mnemonic;
|
|
|
|
|
enum hsm_secret_error err;
|
2022-07-20 12:00:25 +09:30
|
|
|
|
2025-10-24 13:57:43 +10:30
|
|
|
/* Load the hsm_secret (handles decryption automatically if needed) */
|
|
|
|
|
struct hsm_secret *hsms = load_hsm_secret(tmpctx, hsm_secret_path);
|
2025-10-24 13:57:47 +10:30
|
|
|
/* Extract first 32 bytes for legacy compatibility */
|
|
|
|
|
memcpy(file_secret.data, hsms->secret_data, 32);
|
2022-07-20 12:00:25 +09:30
|
|
|
|
2025-10-24 13:57:43 +10:30
|
|
|
/* Ask user for their backup mnemonic passphrase */
|
2022-07-20 12:00:25 +09:30
|
|
|
printf("Warning: remember that different passphrases yield different "
|
|
|
|
|
"bitcoin wallets.\n");
|
|
|
|
|
printf("If left empty, no password is used (echo is disabled).\n");
|
2025-10-24 13:57:43 +10:30
|
|
|
printf("Enter your mnemonic passphrase: \n");
|
2022-07-20 12:00:25 +09:30
|
|
|
fflush(stdout);
|
2025-10-24 13:57:43 +10:30
|
|
|
mnemonic_passphrase = read_stdin_pass(tmpctx, &err);
|
|
|
|
|
if (!mnemonic_passphrase)
|
|
|
|
|
errx(EXITCODE_ERROR_HSM_FILE, "Could not read passphrase: %s", hsm_secret_error_str(err));
|
|
|
|
|
if (streq(mnemonic_passphrase, "")) {
|
|
|
|
|
mnemonic_passphrase = NULL;
|
2022-07-20 12:00:25 +09:30
|
|
|
}
|
|
|
|
|
|
2025-10-24 13:57:43 +10:30
|
|
|
/* Ask user for their backup mnemonic using consistent interface */
|
|
|
|
|
mnemonic = read_stdin_mnemonic(tmpctx, &err);
|
|
|
|
|
if (!mnemonic)
|
|
|
|
|
errx(EXITCODE_ERROR_HSM_FILE, "Could not read mnemonic: %s", hsm_secret_error_str(err));
|
|
|
|
|
|
|
|
|
|
/* Derive seed from user's backup mnemonic + passphrase */
|
|
|
|
|
if (bip39_mnemonic_to_seed(mnemonic, mnemonic_passphrase, bip32_seed, sizeof(bip32_seed), &bip32_seed_len) != WALLY_OK)
|
2022-07-20 12:00:25 +09:30
|
|
|
errx(ERROR_LIBWALLY, "Unable to derive BIP32 seed from BIP39 mnemonic");
|
|
|
|
|
|
2025-10-24 13:57:43 +10:30
|
|
|
/* Copy first 32 bytes to our secret for comparison */
|
|
|
|
|
memcpy(derived_secret.data, bip32_seed, sizeof(derived_secret.data));
|
|
|
|
|
|
|
|
|
|
/* Compare the seeds */
|
|
|
|
|
if (memcmp(derived_secret.data, file_secret.data, sizeof(file_secret.data)) != 0)
|
2022-07-20 12:00:25 +09:30
|
|
|
errx(ERROR_KEYDERIV, "resulting hsm_secret did not match");
|
|
|
|
|
|
|
|
|
|
printf("OK\n");
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-24 13:57:43 +10:30
|
|
|
static void make_rune(const char *hsm_secret_path)
|
2023-04-11 12:01:31 +09:30
|
|
|
{
|
|
|
|
|
struct secret hsm_secret, derived_secret, rune_secret;
|
|
|
|
|
struct rune *master_rune, *rune;
|
2025-10-24 13:57:43 +10:30
|
|
|
struct hsm_secret *hsms = load_hsm_secret(tmpctx, hsm_secret_path);
|
2025-10-24 13:57:47 +10:30
|
|
|
/* Extract first 32 bytes for legacy compatibility */
|
|
|
|
|
memcpy(hsm_secret.data, hsms->secret_data, 32);
|
2023-04-11 12:01:31 +09:30
|
|
|
|
|
|
|
|
/* HSM derives a root secret for `makesecret` */
|
|
|
|
|
hkdf_sha256(&derived_secret, sizeof(struct secret), NULL, 0,
|
|
|
|
|
&hsm_secret, sizeof(hsm_secret),
|
|
|
|
|
"derived secrets", strlen("derived secrets"));
|
|
|
|
|
|
|
|
|
|
/* Commando derives secret using makesecret "commando" */
|
|
|
|
|
hkdf_sha256(&rune_secret, sizeof(struct secret), NULL, 0,
|
|
|
|
|
&derived_secret, sizeof(derived_secret),
|
|
|
|
|
"commando", strlen("commando"));
|
|
|
|
|
|
|
|
|
|
master_rune = rune_new(tmpctx,
|
|
|
|
|
rune_secret.data,
|
|
|
|
|
ARRAY_SIZE(rune_secret.data),
|
|
|
|
|
NULL);
|
|
|
|
|
rune = rune_derive_start(tmpctx, master_rune, "0");
|
|
|
|
|
printf("%s\n", rune_to_base64(tmpctx, rune));
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-24 13:57:43 +10:30
|
|
|
static void print_node_id(const char *hsm_secret_path)
|
2024-09-06 11:43:35 +09:30
|
|
|
{
|
|
|
|
|
u32 salt = 0;
|
|
|
|
|
struct secret hsm_secret;
|
|
|
|
|
struct privkey node_privkey;
|
|
|
|
|
struct pubkey node_id;
|
2025-10-24 13:57:43 +10:30
|
|
|
struct hsm_secret *hsms = load_hsm_secret(tmpctx, hsm_secret_path);
|
2025-10-24 13:57:47 +10:30
|
|
|
/* Extract first 32 bytes for legacy compatibility */
|
|
|
|
|
memcpy(hsm_secret.data, hsms->secret_data, 32);
|
2024-09-06 11:43:35 +09:30
|
|
|
|
|
|
|
|
/*~ So, there is apparently a 1 in 2^127 chance that a random value is
|
|
|
|
|
* not a valid private key, so this never actually loops. */
|
|
|
|
|
do {
|
|
|
|
|
/*~ ccan/crypto/hkdf_sha256 implements RFC5869 "Hardened Key
|
|
|
|
|
* Derivation Functions". That means that if a derived key
|
|
|
|
|
* leaks somehow, the other keys are not compromised. */
|
|
|
|
|
hkdf_sha256(&node_privkey, sizeof(node_privkey),
|
|
|
|
|
&salt, sizeof(salt),
|
|
|
|
|
&hsm_secret,
|
|
|
|
|
sizeof(hsm_secret),
|
|
|
|
|
"nodeid", 6);
|
|
|
|
|
salt++;
|
|
|
|
|
} while (!secp256k1_ec_pubkey_create(secp256k1_ctx, &node_id.pubkey,
|
|
|
|
|
node_privkey.secret.data));
|
|
|
|
|
|
|
|
|
|
printf("%s\n", fmt_pubkey(tmpctx, &node_id));
|
|
|
|
|
}
|
|
|
|
|
|
2019-10-19 16:29:36 +02:00
|
|
|
int main(int argc, char *argv[])
|
|
|
|
|
{
|
|
|
|
|
const char *method;
|
|
|
|
|
|
|
|
|
|
setup_locale();
|
|
|
|
|
err_set_progname(argv[0]);
|
2024-11-06 13:50:12 +10:30
|
|
|
secp256k1_ctx = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY
|
|
|
|
|
| SECP256K1_CONTEXT_SIGN);
|
2019-10-19 16:29:36 +02:00
|
|
|
|
2019-11-25 17:39:16 +01:00
|
|
|
method = argc > 1 ? argv[1] : NULL;
|
2019-10-19 16:29:36 +02:00
|
|
|
if (!method)
|
2020-06-29 14:28:46 +08:00
|
|
|
show_usage(argv[0]);
|
2019-10-19 16:29:36 +02:00
|
|
|
|
|
|
|
|
if (streq(method, "decrypt")) {
|
2021-01-02 22:47:03 +01:00
|
|
|
if (argc < 3)
|
2020-06-29 14:28:46 +08:00
|
|
|
show_usage(argv[0]);
|
2025-10-24 13:57:43 +10:30
|
|
|
decrypt_hsm(argv[2]);
|
|
|
|
|
} else if (streq(method, "encrypt")) {
|
2021-01-02 22:47:03 +01:00
|
|
|
if (argc < 3)
|
2020-06-29 14:28:46 +08:00
|
|
|
show_usage(argv[0]);
|
2025-10-24 13:57:43 +10:30
|
|
|
encrypt_hsm(argv[2]);
|
2019-10-19 16:55:51 +02:00
|
|
|
|
2025-10-24 13:57:43 +10:30
|
|
|
} else if (streq(method, "dumpcommitments")) {
|
2021-01-02 22:47:03 +01:00
|
|
|
/* node_id channel_id depth hsm_secret */
|
2020-07-07 18:32:15 +08:00
|
|
|
if (argc < 6)
|
2020-06-29 14:28:46 +08:00
|
|
|
show_usage(argv[0]);
|
2019-10-22 12:41:08 +02:00
|
|
|
struct node_id node_id;
|
|
|
|
|
if (!node_id_from_hexstr(argv[2], strlen(argv[2]), &node_id))
|
2021-01-03 01:14:33 +01:00
|
|
|
errx(ERROR_USAGE, "Bad node id");
|
2025-10-24 13:57:43 +10:30
|
|
|
dump_commitments_infos(&node_id, atol(argv[3]), atol(argv[4]),
|
|
|
|
|
argv[5]);
|
2019-10-22 12:41:08 +02:00
|
|
|
|
2025-10-24 13:57:43 +10:30
|
|
|
} else if (streq(method, "guesstoremote")) {
|
2021-01-02 22:47:03 +01:00
|
|
|
/* address node_id depth hsm_secret */
|
2020-07-07 18:32:15 +08:00
|
|
|
if (argc < 6)
|
2020-06-29 14:28:46 +08:00
|
|
|
show_usage(argv[0]);
|
2019-11-25 16:08:42 +01:00
|
|
|
struct node_id node_id;
|
|
|
|
|
if (!node_id_from_hexstr(argv[3], strlen(argv[3]), &node_id))
|
|
|
|
|
errx(ERROR_USAGE, "Bad node id");
|
2025-10-24 13:57:43 +10:30
|
|
|
guess_to_remote(argv[2], &node_id, atol(argv[4]),
|
|
|
|
|
argv[5]);
|
2019-11-25 16:08:42 +01:00
|
|
|
|
2025-10-24 13:57:43 +10:30
|
|
|
} else if (streq(method, "derivetoremote")) {
|
2024-05-26 16:31:58 -04:00
|
|
|
/* node_id channel_id [commitment_point] hsm_secret */
|
|
|
|
|
if (argc < 5 || argc > 6)
|
|
|
|
|
show_usage(argv[0]);
|
|
|
|
|
struct unilateral_close_info info = { };
|
|
|
|
|
struct pubkey commitment_point;
|
|
|
|
|
if (!node_id_from_hexstr(argv[2], strlen(argv[2]), &info.peer_id))
|
|
|
|
|
errx(ERROR_USAGE, "Bad node id");
|
|
|
|
|
info.channel_id = atoll(argv[3]);
|
|
|
|
|
if (argc == 6) {
|
|
|
|
|
if (!pubkey_from_hexstr(argv[4], strlen(argv[4]), &commitment_point))
|
|
|
|
|
errx(ERROR_USAGE, "Bad commitment point");
|
|
|
|
|
info.commitment_point = &commitment_point;
|
|
|
|
|
}
|
2025-10-24 13:57:43 +10:30
|
|
|
derive_to_remote(&info, argv[argc - 1]);
|
2024-05-26 16:31:58 -04:00
|
|
|
|
2025-10-24 13:57:43 +10:30
|
|
|
} else if (streq(method, "generatehsm")) {
|
|
|
|
|
if (argc != 3)
|
2020-10-28 23:59:49 +01:00
|
|
|
show_usage(argv[0]);
|
|
|
|
|
|
|
|
|
|
char *hsm_secret_path = argv[2];
|
|
|
|
|
|
|
|
|
|
/* 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);
|
|
|
|
|
|
2025-10-24 13:57:43 +10:30
|
|
|
generate_hsm(hsm_secret_path);
|
|
|
|
|
} else if (streq(method, "dumponchaindescriptors")) {
|
2024-01-10 14:22:48 +01:00
|
|
|
char *fname = NULL;
|
2021-01-02 22:47:03 +01:00
|
|
|
char *net = NULL;
|
2024-01-10 09:56:27 +01:00
|
|
|
bool show_secrets = false;
|
2024-01-10 14:22:48 +01:00
|
|
|
bool only_arguments = false;
|
2023-06-30 21:56:16 +02:00
|
|
|
u32 version;
|
2021-01-02 22:47:03 +01:00
|
|
|
|
2020-10-30 15:01:32 +01:00
|
|
|
if (argc < 3)
|
|
|
|
|
show_usage(argv[0]);
|
|
|
|
|
|
2024-01-10 14:22:48 +01:00
|
|
|
for (int i = 2; i < argc; ++i) {
|
|
|
|
|
char *next = argv[i];
|
|
|
|
|
|
|
|
|
|
if (only_arguments || next[0] != '-') {
|
|
|
|
|
// this is an argument
|
|
|
|
|
if (!fname) {
|
|
|
|
|
fname = next;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
if (!net) {
|
|
|
|
|
net = next;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
errx(ERROR_USAGE,
|
|
|
|
|
"Argument '%s' was not expected.", next);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (streq(next, "--")) {
|
|
|
|
|
only_arguments = true;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// we are processing an option here
|
|
|
|
|
if (streq(next, "--show-secrets")) {
|
|
|
|
|
show_secrets = true;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
errx(ERROR_USAGE, "Option '%s' is not recognized.",
|
|
|
|
|
next);
|
|
|
|
|
}
|
2021-01-02 22:47:03 +01:00
|
|
|
|
2024-12-02 14:55:53 -08:00
|
|
|
if (net && (streq(net, "testnet") || streq(net, "testnet4") || streq(net, "signet")))
|
2023-06-30 21:56:16 +02:00
|
|
|
version = BIP32_VER_TEST_PRIVATE;
|
2021-01-19 11:30:45 +01:00
|
|
|
else if (net && !streq(net, "bitcoin"))
|
2021-01-02 22:47:03 +01:00
|
|
|
errx(ERROR_USAGE, "Network '%s' not supported."
|
|
|
|
|
" Supported networks: bitcoin (default),"
|
2023-06-30 21:56:16 +02:00
|
|
|
" testnet and signet", net);
|
2021-01-02 22:47:03 +01:00
|
|
|
else
|
2023-06-30 21:56:16 +02:00
|
|
|
version = BIP32_VER_MAIN_PRIVATE;
|
2020-11-01 15:06:35 +01:00
|
|
|
|
2025-10-24 13:57:43 +10:30
|
|
|
dumponchaindescriptors(fname, version, show_secrets);
|
|
|
|
|
} else if (streq(method, "checkhsm")) {
|
2022-07-20 12:00:25 +09:30
|
|
|
if (argc < 3)
|
|
|
|
|
show_usage(argv[0]);
|
2025-10-24 13:57:43 +10:30
|
|
|
check_hsm(argv[2]);
|
|
|
|
|
} else if (streq(method, "makerune")) {
|
2023-04-11 12:01:31 +09:30
|
|
|
if (argc < 3)
|
|
|
|
|
show_usage(argv[0]);
|
2025-10-24 13:57:43 +10:30
|
|
|
make_rune(argv[2]);
|
2026-01-13 13:19:15 +10:30
|
|
|
} else if(streq(method, "getsecret")) {
|
2026-01-13 13:19:19 +10:30
|
|
|
if (argc < 3 || argc > 4)
|
2026-01-13 13:19:15 +10:30
|
|
|
show_usage(argv[0]);
|
|
|
|
|
print_secret(argv[2], argv[3], false);
|
2025-10-24 13:57:43 +10:30
|
|
|
} else if(streq(method, "getcodexsecret")) {
|
2026-01-13 13:19:19 +10:30
|
|
|
if (argc != 4)
|
2023-07-31 09:41:09 +05:30
|
|
|
show_usage(argv[0]);
|
2026-01-13 13:19:15 +10:30
|
|
|
print_secret(argv[2], argv[3], true);
|
2025-10-24 13:57:43 +10:30
|
|
|
} else if(streq(method, "getemergencyrecover")) {
|
2023-10-26 13:45:54 +10:30
|
|
|
if (argc < 3)
|
|
|
|
|
show_usage(argv[0]);
|
2025-10-24 13:57:43 +10:30
|
|
|
print_emergencyrecover(argv[2]);
|
|
|
|
|
} else if (streq(method, "getnodeid")) {
|
2024-09-06 11:43:35 +09:30
|
|
|
if (argc < 3)
|
|
|
|
|
show_usage(argv[0]);
|
2025-10-24 13:57:43 +10:30
|
|
|
print_node_id(argv[2]);
|
|
|
|
|
} else {
|
|
|
|
|
show_usage(argv[0]);
|
2024-09-06 11:43:35 +09:30
|
|
|
}
|
|
|
|
|
|
2025-10-24 13:57:43 +10:30
|
|
|
return 0;
|
2019-10-19 16:29:36 +02:00
|
|
|
}
|