Skip to content

Commit cc0f66f

Browse files
whitslackrustyrussell
authored andcommitted
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.
1 parent 6724db6 commit cc0f66f

File tree

3 files changed

+58
-2
lines changed

3 files changed

+58
-2
lines changed

doc/lightning-hsmtool.8.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,15 @@ and is usually no greater than the number of channels that the node has
5353
ever had.
5454
Specify *password* if the `hsm_secret_path` is encrypted.
5555

56+
**derivetoremote** *node\_id* *channel\_dbid* \[*commitment\_point*\] *hsm\_secret*
57+
Derive the private key to our funds from a remote unilateral close of a channel.
58+
The peer must be the one to close the channel (and the funds will remain
59+
unrecoverable until the channel is closed).
60+
*channel\_dbid* is the identifier of the channel in the CLN database.
61+
If *commitment\_point* is omitted, then the channel is assumed to have been
62+
negotiated with `option_static_remotekey`;
63+
otherwise, *commitment\_point* is the remote per-commitment point.
64+
5665
**generatehsm** *hsm\_secret\_path* \[*lang* *seed\_phrase* \[*passphrase*\]\]
5766
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.
5867

tools/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ tools/headerversions: $(FORCE) tools/headerversions.o libccan.a
1818
tools/headerversions.o: ccan/config.h
1919
tools/check-bolt: tools/check-bolt.o $(TOOLS_COMMON_OBJS)
2020

21-
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
21+
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
2222

2323
tools/lightning-hsmtool: tools/hsmtool
2424
cp $< $@

tools/hsmtool.c

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,10 @@
1717
#include <common/descriptor_checksum.h>
1818
#include <common/errcode.h>
1919
#include <common/hsm_encryption.h>
20+
#include <common/key_derive.h>
2021
#include <common/node_id.h>
2122
#include <common/utils.h>
23+
#include <common/utxo.h>
2224
#include <errno.h>
2325
#include <fcntl.h>
2426
#include <inttypes.h>
@@ -43,6 +45,8 @@ static void show_usage(const char *progname)
4345
"<path/to/hsm_secret>\n");
4446
printf(" - guesstoremote <P2WPKH address> <node id> <tries> "
4547
"<path/to/hsm_secret>\n");
48+
printf(" - derivetoremote <node id> <channel dbid> [<cmt pt>] "
49+
"<path/to/hsm_secret>\n");
4650
printf(" - generatehsm <path/to/new/hsm_secret> [<language_id> <word list> [<password>]]\n");
4751
printf(" - checkhsm <path/to/new/hsm_secret>\n");
4852
printf(" - dumponchaindescriptors [--show-secrets] <path/to/hsm_secret> [network]\n");
@@ -119,7 +123,7 @@ static void get_encrypted_hsm_secret(struct secret *hsm_secret,
119123
}
120124

121125
/* Taken from hsmd. */
122-
static void get_channel_seed(struct secret *channel_seed, struct node_id *peer_id,
126+
static void get_channel_seed(struct secret *channel_seed, const struct node_id *peer_id,
123127
u64 dbid, struct secret *hsm_secret)
124128
{
125129
struct secret channel_base;
@@ -450,6 +454,32 @@ static int guess_to_remote(const char *address, struct node_id *node_id,
450454
return 1;
451455
}
452456

457+
static int derive_to_remote(const struct unilateral_close_info *info, const char *hsm_secret_path)
458+
{
459+
struct secret hsm_secret, channel_seed, basepoint_secret;
460+
struct pubkey basepoint;
461+
struct privkey privkey;
462+
463+
secp256k1_ctx = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY
464+
| SECP256K1_CONTEXT_SIGN);
465+
466+
get_hsm_secret(&hsm_secret, hsm_secret_path);
467+
get_channel_seed(&channel_seed, &info->peer_id, info->channel_id, &hsm_secret);
468+
if (!derive_payment_basepoint(&channel_seed, &basepoint, &basepoint_secret))
469+
errx(ERROR_KEYDERIV, "Could not derive basepoints for dbid %"PRIu64
470+
" and channel seed %s.", info->channel_id,
471+
fmt_secret(tmpctx, &channel_seed));
472+
if (!info->commitment_point)
473+
privkey.secret = basepoint_secret;
474+
else if (!derive_simple_privkey(&basepoint_secret, &basepoint, info->commitment_point, &privkey))
475+
errx(ERROR_KEYDERIV, "Could not derive simple privkey for dbid %"PRIu64
476+
", channel seed %s, and commitment point %s.", info->channel_id,
477+
fmt_secret(tmpctx, &channel_seed),
478+
fmt_pubkey(tmpctx, info->commitment_point));
479+
printf("privkey : %s\n", fmt_secret(tmpctx, &privkey.secret));
480+
return 0;
481+
}
482+
453483
struct wordlist_lang {
454484
char *abbr;
455485
char *name;
@@ -794,6 +824,23 @@ int main(int argc, char *argv[])
794824
argv[5]);
795825
}
796826

827+
if (streq(method, "derivetoremote")) {
828+
/* node_id channel_id [commitment_point] hsm_secret */
829+
if (argc < 5 || argc > 6)
830+
show_usage(argv[0]);
831+
struct unilateral_close_info info = { };
832+
struct pubkey commitment_point;
833+
if (!node_id_from_hexstr(argv[2], strlen(argv[2]), &info.peer_id))
834+
errx(ERROR_USAGE, "Bad node id");
835+
info.channel_id = atoll(argv[3]);
836+
if (argc == 6) {
837+
if (!pubkey_from_hexstr(argv[4], strlen(argv[4]), &commitment_point))
838+
errx(ERROR_USAGE, "Bad commitment point");
839+
info.commitment_point = &commitment_point;
840+
}
841+
return derive_to_remote(&info, argv[argc - 1]);
842+
}
843+
797844
if (streq(method, "generatehsm")) {
798845
// argv[2] file, argv[3] lang_id, argv[4] word list, argv[5] passphrase
799846
if (argc < 3 || argc > 6 || argc == 4)

0 commit comments

Comments
 (0)