mirror of
https://github.com/ElementsProject/lightning.git
synced 2025-01-17 19:03:42 +01:00
tool/hsmtool: add a 'guesstoremote' command
This, in the case of data loss on a channel with `option_static_remotekey` negotiated, allows to likely (if the dbid is not unreasonable) recover the funds from a remote unilateral close just with the hsm_secret. Changelog-added: A new command, 'guesstoremote', is added to the hsmtool. It is meant to be used to recover funds after an unilateral close of a channel with `option_static_remotekey` enabled.
This commit is contained in:
parent
cd11c2050c
commit
e5e4958909
@ -14,7 +14,7 @@ tools/headerversions: FORCE tools/headerversions.o $(CCAN_OBJS)
|
||||
|
||||
tools/check-bolt: tools/check-bolt.o $(CCAN_OBJS) $(TOOLS_COMMON_OBJS)
|
||||
|
||||
tools/hsmtool: tools/hsmtool.o $(CCAN_OBJS) $(TOOLS_COMMON_OBJS) $(BITCOIN_OBJS) common/amount.o common/bigsize.o common/derive_basepoints.o common/node_id.o common/type_to_string.o wire/fromwire.o wire/towire.o
|
||||
tools/hsmtool: tools/hsmtool.o $(CCAN_OBJS) $(TOOLS_COMMON_OBJS) $(BITCOIN_OBJS) common/amount.o common/bech32.o common/bigsize.o common/derive_basepoints.o common/node_id.o common/type_to_string.o wire/fromwire.o wire/towire.o
|
||||
|
||||
clean: tools-clean
|
||||
|
||||
|
@ -5,6 +5,7 @@
|
||||
#include <ccan/read_write_all/read_write_all.h>
|
||||
#include <ccan/tal/path/path.h>
|
||||
#include <ccan/str/str.h>
|
||||
#include <common/bech32.h>
|
||||
#include <common/derive_basepoints.h>
|
||||
#include <common/node_id.h>
|
||||
#include <common/type_to_string.h>
|
||||
@ -30,6 +31,8 @@ static void show_usage(void)
|
||||
printf(" - encrypt <path/to/hsm_secret> <password>\n");
|
||||
printf(" - dumpcommitments <node id> <channel dbid> <depth> "
|
||||
"<path/to/hsm_secret> [hsm_secret password]\n");
|
||||
printf(" - guesstoremote <P2WPKH address> <node id> <tries> "
|
||||
"<path/to/hsm_secret> [hsm_secret password]\n");
|
||||
exit(0);
|
||||
}
|
||||
|
||||
@ -285,6 +288,76 @@ static int dump_commitments_infos(struct node_id *node_id, u64 channel_id,
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* In case of an unilateral close from the remote side while we suffered a
|
||||
* loss of data, this tries to recover the private key from the `to_remote`
|
||||
* output.
|
||||
* This basically iterates over every `dbid` to derive the channel_seed and
|
||||
* then derives the payment basepoint to compare to the pubkey hash specified
|
||||
* in the witness programm.
|
||||
* Note that since a node generates the key for the to_remote output from its
|
||||
* *local* per_commitment_point, there is nothing we can do if
|
||||
* `option_static_remotekey` was not negotiated.
|
||||
*
|
||||
* :param address: The bech32 address of the v0 P2WPKH witness programm
|
||||
* :param node_id: The id of the node with which the channel was established
|
||||
* :param tries: How many dbids to try.
|
||||
* :param hsm_secret_path: The path to the hsm_secret
|
||||
* :param passwd: The *optional* hsm_secret password
|
||||
*/
|
||||
static int guess_to_remote(const char *address, struct node_id *node_id,
|
||||
u64 tries, char *hsm_secret_path, char *passwd)
|
||||
{
|
||||
struct secret hsm_secret, channel_seed, basepoint_secret;
|
||||
struct pubkey basepoint;
|
||||
struct ripemd160 pubkeyhash;
|
||||
/* We only support P2WPKH, hence 20. */
|
||||
u8 goal_pubkeyhash[20];
|
||||
/* See common/bech32.h for buffer size. */
|
||||
char hrp[strlen(address) - 6];
|
||||
int witver;
|
||||
size_t witlen;
|
||||
|
||||
/* Get the hrp to accept addresses from any network. */
|
||||
if (bech32_decode(hrp, goal_pubkeyhash, &witlen, address, 90) != 1)
|
||||
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");
|
||||
|
||||
secp256k1_ctx = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY
|
||||
| SECP256K1_CONTEXT_SIGN);
|
||||
|
||||
if (passwd)
|
||||
get_encrypted_hsm_secret(&hsm_secret, hsm_secret_path, passwd);
|
||||
else
|
||||
get_hsm_secret(&hsm_secret, hsm_secret_path);
|
||||
|
||||
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,
|
||||
type_to_string(tmpctx,
|
||||
struct secret, &channel_seed));
|
||||
|
||||
pubkey_to_hash160(&basepoint, &pubkeyhash);
|
||||
if (memcmp(pubkeyhash.u.u8, goal_pubkeyhash, 20) == 0) {
|
||||
printf("bech32 : %s\n", address);
|
||||
printf("pubkey hash : %s\n",
|
||||
tal_hexstr(tmpctx, pubkeyhash.u.u8, 20));
|
||||
printf("pubkey : %s \n",
|
||||
type_to_string(tmpctx, struct pubkey, &basepoint));
|
||||
printf("privkey : %s \n",
|
||||
type_to_string(tmpctx, struct secret, &basepoint_secret));
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
printf("Could not find any basepoint matching the provided witness programm.\n"
|
||||
"Are you sure that the channel used `option_static_remotekey` ?\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
const char *method;
|
||||
@ -319,5 +392,16 @@ int main(int argc, char *argv[])
|
||||
argv[5], argv[6]);
|
||||
}
|
||||
|
||||
if (streq(method, "guesstoremote")) {
|
||||
/* address node_id depth hsm_secret ?password? */
|
||||
if (argc < 5)
|
||||
show_usage();
|
||||
struct node_id node_id;
|
||||
if (!node_id_from_hexstr(argv[3], strlen(argv[3]), &node_id))
|
||||
errx(ERROR_USAGE, "Bad node id");
|
||||
return guess_to_remote(argv[2], &node_id, atol(argv[4]),
|
||||
argv[5], argv[6]);
|
||||
}
|
||||
|
||||
show_usage();
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user