Skip to content

Commit 00e0b99

Browse files
rustyrussellShahanaFarooqui
authored andcommitted
hsmtool: provide nodeid from hsm secret.
This allows tools to validate that it is accessing the correct hsm_secret for this node! This is extremely important for backups: if they are using VLS, they need to back *that* up instead, for example. Changelog-Added: `hsmtool`: `getnodeid` command derives the node id from the hsm_secret, to verify it's the correct secret. Signed-off-by: Rusty Russell <[email protected]>
1 parent e36b68a commit 00e0b99

File tree

3 files changed

+62
-12
lines changed

3 files changed

+62
-12
lines changed

doc/lightning-hsmtool.8.md

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -20,46 +20,46 @@ as well as derive secrets used in channel commitments.
2020
METHODS
2121
-------
2222

23-
**encrypt** *hsm\_secret* *password*
23+
**encrypt** *hsm\_secret\_path* *password*
2424

25-
Encrypt the `hsm_secret` file so that it can only be decrypted at
25+
Encrypt the `hsm_secret_path` file so that it can only be decrypted at
2626
**lightningd** startup.
2727
You must give the option **--encrypted-hsm** to **lightningd**.
28-
The password of the `hsm_secret` file will be asked whenever you
28+
The password of the `hsm_secret_path` file will be asked whenever you
2929
start **lightningd**.
3030

31-
**decrypt** *hsm\_secret* *password*
31+
**decrypt** *hsm\_secret\_path* *password*
3232

33-
Decrypt the `hsm_secret` file that was encrypted with the **encrypt**
33+
Decrypt the `hsm_secret_path` file that was encrypted with the **encrypt**
3434
method.
3535

36-
**dumpcommitments** *node\_id* *channel\_dbid* *depth* *hsm\_secret* \[*password*\]
36+
**dumpcommitments** *node\_id* *channel\_dbid* *depth* *hsm\_secret\_path* \[*password*\]
3737

3838
Show the per-commitment secret and point of up to *depth* commitments,
3939
of the specified channel with the specified peer,
4040
identified by the channel database index.
41-
Specify *password* if the `hsm_secret` is encrypted.
41+
Specify *password* if the `hsm_secret_path` is encrypted.
4242

43-
**guesstoremote** *p2wpkh* *node\_id* *max\_channel\_dbid* *hsm\_secret* \[*password*\]
43+
**guesstoremote** *p2wpkh* *node\_id* *max\_channel\_dbid* *hsm\_secret\_path* \[*password*\]
4444

4545
Brute-force the private key to our funds from a remote unilateral close
4646
of a channel, in a case where we have lost all database data except for
47-
our `hsm_secret`.
47+
our `hsm_secret_path`.
4848
The peer must be the one to close the channel (and the funds will remain
4949
unrecoverable until the channel is closed).
5050
*max\_channel\_dbid* is your own guess on what the *channel\_dbid* was,
5151
or at least the maximum possible value,
5252
and is usually no greater than the number of channels that the node has
5353
ever had.
54-
Specify *password* if the `hsm_secret` is encrypted.
54+
Specify *password* if the `hsm_secret_path` is encrypted.
5555

5656
**generatehsm** *hsm\_secret\_path*
5757
Generates a new hsm\_secret using BIP39.
5858

5959
**checkhsm** *hsm\_secret\_path*
6060
Checks that hsm\_secret matches a BIP39 passphrase.
6161

62-
**dumponchaindescriptors** \[*--show-secrets*\] *hsm\_secret* \[*network*\]
62+
**dumponchaindescriptors** \[*--show-secrets*\] *hsm\_secret\_path* \[*network*\]
6363
Dump output descriptors for our onchain wallet.
6464
This command requires the path to the hsm\_secret containing the wallet seed.
6565
If the flag *--show-secrets* is set the command will show the BIP32 extended private
@@ -74,7 +74,7 @@ password.
7474
To generate descriptors using testnet master keys, you may specify *testnet* as
7575
the last parameter. By default, mainnet-encoded keys are generated.
7676

77-
**makerune** *hsm\_secret*
77+
**makerune** *hsm\_secret\_path*
7878
Make a master rune for this node (with `uniqueid` 0)
7979
This produces the same results as lightning-commando-rune(7) on a fresh node.
8080
You will still need to create a rune once the node starts, if you want commando to work (as it is only activated once it has generated one).
@@ -85,6 +85,9 @@ You will still need to create a rune once the node starts, if you want commando
8585
**getemergencyrecover** *emergency.recover\_path*
8686
Print out the bech32 encoded emergency.recover file.
8787

88+
**getnodeid** *hsm\_secret\_path*
89+
Print out the node id that a node using this hsm secret would have: useful for verifying that you are accessing the correct secret!
90+
8891
BUGS
8992
----
9093

tests/test_wallet.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1701,3 +1701,11 @@ def test_hsmtool_makerune(node_factory):
17011701
# We have to generate a rune now, for commando to even start processing!
17021702
rune = l1.rpc.commando_rune()['rune']
17031703
assert rune == out
1704+
1705+
1706+
def test_hsmtool_getnodeid(node_factory):
1707+
l1 = node_factory.get_node()
1708+
1709+
cmd_line = ["tools/hsmtool", "getnodeid", os.path.join(l1.daemon.lightning_dir, TEST_NETWORK, "hsm_secret")]
1710+
out = subprocess.check_output(cmd_line).decode('utf-8').strip()
1711+
assert out == l1.info['id']

tools/hsmtool.c

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ static void show_usage(const char *progname)
4949
printf(" - makerune <path/to/hsm_secret>\n");
5050
printf(" - getcodexsecret <path/to/hsm_secret> <id>\n");
5151
printf(" - getemergencyrecover <path/to/emergency.recover>\n");
52+
printf(" - getnodeid <path/to/hsm_secret>\n");
5253
exit(0);
5354
}
5455

@@ -697,6 +698,38 @@ static int make_rune(const char *hsm_secret_path)
697698
return 0;
698699
}
699700

701+
static int get_node_id(const char *hsm_secret_path)
702+
{
703+
u32 salt = 0;
704+
struct secret hsm_secret;
705+
struct privkey node_privkey;
706+
struct pubkey node_id;
707+
708+
secp256k1_ctx = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY
709+
| SECP256K1_CONTEXT_SIGN);
710+
711+
/* Get hsm_secret */
712+
get_hsm_secret(&hsm_secret, hsm_secret_path);
713+
714+
/*~ So, there is apparently a 1 in 2^127 chance that a random value is
715+
* not a valid private key, so this never actually loops. */
716+
do {
717+
/*~ ccan/crypto/hkdf_sha256 implements RFC5869 "Hardened Key
718+
* Derivation Functions". That means that if a derived key
719+
* leaks somehow, the other keys are not compromised. */
720+
hkdf_sha256(&node_privkey, sizeof(node_privkey),
721+
&salt, sizeof(salt),
722+
&hsm_secret,
723+
sizeof(hsm_secret),
724+
"nodeid", 6);
725+
salt++;
726+
} while (!secp256k1_ec_pubkey_create(secp256k1_ctx, &node_id.pubkey,
727+
node_privkey.secret.data));
728+
729+
printf("%s\n", fmt_pubkey(tmpctx, &node_id));
730+
return 0;
731+
}
732+
700733
int main(int argc, char *argv[])
701734
{
702735
const char *method;
@@ -838,5 +871,11 @@ int main(int argc, char *argv[])
838871
return getemergencyrecover(argv[2]);
839872
}
840873

874+
if (streq(method, "getnodeid")) {
875+
if (argc < 3)
876+
show_usage(argv[0]);
877+
return get_node_id(argv[2]);
878+
}
879+
841880
show_usage(argv[0]);
842881
}

0 commit comments

Comments
 (0)