From cc0f66f07b62f1f1c3face715b64a886d5950e9f Mon Sep 17 00:00:00 2001 From: Matt Whitlock Date: Sun, 26 May 2024 16:31:58 -0400 Subject: [PATCH] hsmtool: implement new "derivetoremote" method This method has a similar purpose as "guesstoremote" but is for use when the channel's database ID is known. It produces the private key that can spend the to_remote output of the peer's commitment transaction. It assumes the channel was negotiated with option_static_remotekey unless the optional per-commitment point argument is provided. Changelog-Added: hsmtool has a new `derivetoremote` method. --- doc/lightning-hsmtool.8.md | 9 +++++++ tools/Makefile | 2 +- tools/hsmtool.c | 49 +++++++++++++++++++++++++++++++++++++- 3 files changed, 58 insertions(+), 2 deletions(-) diff --git a/doc/lightning-hsmtool.8.md b/doc/lightning-hsmtool.8.md index 0a9b3c70b..e198d6cfb 100644 --- a/doc/lightning-hsmtool.8.md +++ b/doc/lightning-hsmtool.8.md @@ -53,6 +53,15 @@ and is usually no greater than the number of channels that the node has ever had. Specify *password* if the `hsm_secret_path` is encrypted. +**derivetoremote** *node\_id* *channel\_dbid* \[*commitment\_point*\] *hsm\_secret* + Derive the private key to our funds from a remote unilateral close of a channel. +The peer must be the one to close the channel (and the funds will remain +unrecoverable until the channel is closed). +*channel\_dbid* is the identifier of the channel in the CLN database. +If *commitment\_point* is omitted, then the channel is assumed to have been +negotiated with `option_static_remotekey`; +otherwise, *commitment\_point* is the remote per-commitment point. + **generatehsm** *hsm\_secret\_path* \[*lang* *seed\_phrase* \[*passphrase*\]\] Generates a new hsm\_secret using BIP39. If lang, seed\_phrase and optional passphrase are not provided they will be prompted for. *lang* can be "en" (English), "es" (Spanish), "fr" (French), "it" ("Italian"), "jp" (Japanese), "zhs" (Chinese Simplified) or "zht" ("Chinese Traditional"). Note that the seed phrase consists of multiple words, so should be surrounded by quotes. diff --git a/tools/Makefile b/tools/Makefile index dc8e2a5de..6b92ebdd5 100644 --- a/tools/Makefile +++ b/tools/Makefile @@ -18,7 +18,7 @@ tools/headerversions: $(FORCE) tools/headerversions.o libccan.a tools/headerversions.o: ccan/config.h tools/check-bolt: tools/check-bolt.o $(TOOLS_COMMON_OBJS) -tools/hsmtool: tools/hsmtool.o $(TOOLS_COMMON_OBJS) $(BITCOIN_OBJS) common/amount.o common/autodata.o common/bech32.o common/bech32_util.o common/bigsize.o common/codex32.o common/configdir.o common/configvar.o common/derive_basepoints.o common/descriptor_checksum.o common/hsm_encryption.o common/node_id.o common/version.o wire/fromwire.o wire/towire.o +tools/hsmtool: tools/hsmtool.o $(TOOLS_COMMON_OBJS) $(BITCOIN_OBJS) common/amount.o common/autodata.o common/bech32.o common/bech32_util.o common/bigsize.o common/codex32.o common/configdir.o common/configvar.o common/derive_basepoints.o common/descriptor_checksum.o common/hsm_encryption.o common/key_derive.o common/node_id.o common/version.o wire/fromwire.o wire/towire.o tools/lightning-hsmtool: tools/hsmtool cp $< $@ diff --git a/tools/hsmtool.c b/tools/hsmtool.c index 94ee25fdb..7f2d30fb9 100644 --- a/tools/hsmtool.c +++ b/tools/hsmtool.c @@ -17,8 +17,10 @@ #include #include #include +#include #include #include +#include #include #include #include @@ -43,6 +45,8 @@ static void show_usage(const char *progname) "\n"); printf(" - guesstoremote " "\n"); + printf(" - derivetoremote [] " + "\n"); printf(" - generatehsm [ []]\n"); printf(" - checkhsm \n"); printf(" - dumponchaindescriptors [--show-secrets] [network]\n"); @@ -119,7 +123,7 @@ static void get_encrypted_hsm_secret(struct secret *hsm_secret, } /* Taken from hsmd. */ -static void get_channel_seed(struct secret *channel_seed, struct node_id *peer_id, +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; @@ -450,6 +454,32 @@ static int guess_to_remote(const char *address, struct node_id *node_id, return 1; } +static int 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); + + get_hsm_secret(&hsm_secret, hsm_secret_path); + 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)); + return 0; +} + struct wordlist_lang { char *abbr; char *name; @@ -794,6 +824,23 @@ int main(int argc, char *argv[]) argv[5]); } + if (streq(method, "derivetoremote")) { + /* 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; + } + return derive_to_remote(&info, argv[argc - 1]); + } + if (streq(method, "generatehsm")) { // argv[2] file, argv[3] lang_id, argv[4] word list, argv[5] passphrase if (argc < 3 || argc > 6 || argc == 4)