2021-12-04 12:23:56 +01:00
|
|
|
#include "config.h"
|
2017-01-10 06:08:33 +01:00
|
|
|
#include <ccan/err/err.h>
|
2018-07-09 13:17:59 +02:00
|
|
|
#include <ccan/fdpass/fdpass.h>
|
2020-04-03 05:21:22 +02:00
|
|
|
#include <common/ecdh.h>
|
2021-12-14 22:57:21 +01:00
|
|
|
#include <common/errcode.h>
|
2024-01-18 22:19:55 +01:00
|
|
|
#include <common/hsm_capable.h>
|
2021-01-03 16:59:24 +01:00
|
|
|
#include <common/hsm_encryption.h>
|
2022-10-17 02:34:08 +02:00
|
|
|
#include <common/hsm_version.h>
|
2022-11-09 03:27:04 +01:00
|
|
|
#include <common/invoice_path_id.h>
|
2022-06-24 02:19:09 +02:00
|
|
|
#include <common/json_command.h>
|
2022-07-04 05:49:38 +02:00
|
|
|
#include <common/json_param.h>
|
|
|
|
#include <common/jsonrpc_errors.h>
|
2017-06-24 08:50:23 +02:00
|
|
|
#include <errno.h>
|
2020-08-25 03:55:38 +02:00
|
|
|
#include <hsmd/hsmd_wiregen.h>
|
2021-12-04 12:23:56 +01:00
|
|
|
#include <lightningd/hsm_control.h>
|
2020-02-04 06:53:17 +01:00
|
|
|
#include <lightningd/jsonrpc.h>
|
2021-12-04 12:23:56 +01:00
|
|
|
#include <lightningd/lightningd.h>
|
|
|
|
#include <lightningd/subd.h>
|
2018-07-09 13:17:59 +02:00
|
|
|
#include <sys/socket.h>
|
2019-10-03 23:08:43 +02:00
|
|
|
#include <sys/stat.h>
|
2017-02-21 05:45:29 +01:00
|
|
|
#include <wally_bip32.h>
|
2017-06-24 08:50:23 +02:00
|
|
|
#include <wire/wire_sync.h>
|
2017-01-10 06:08:33 +01:00
|
|
|
|
2018-09-20 05:07:41 +02:00
|
|
|
static int hsm_get_fd(struct lightningd *ld,
|
2019-04-08 11:58:32 +02:00
|
|
|
const struct node_id *id,
|
2018-07-09 13:17:59 +02:00
|
|
|
u64 dbid,
|
2023-08-07 07:51:40 +02:00
|
|
|
u64 permissions)
|
2018-07-09 13:17:59 +02:00
|
|
|
{
|
2023-04-10 02:12:56 +02:00
|
|
|
const u8 *msg;
|
2018-07-09 13:17:59 +02:00
|
|
|
|
2023-08-07 07:51:40 +02:00
|
|
|
msg = towire_hsmd_client_hsmfd(NULL, id, dbid, permissions);
|
2023-04-10 02:12:56 +02:00
|
|
|
msg = hsm_sync_req(tmpctx, ld, take(msg));
|
2020-08-25 03:55:38 +02:00
|
|
|
if (!fromwire_hsmd_client_hsmfd_reply(msg))
|
2018-07-09 13:17:59 +02:00
|
|
|
fatal("Bad reply from HSM: %s", tal_hex(tmpctx, msg));
|
|
|
|
|
2024-05-09 05:21:14 +02:00
|
|
|
return fdpass_recv(ld->hsm_fd);
|
2018-07-09 13:17:59 +02:00
|
|
|
}
|
|
|
|
|
2018-09-20 05:07:41 +02:00
|
|
|
int hsm_get_client_fd(struct lightningd *ld,
|
2019-04-08 11:58:32 +02:00
|
|
|
const struct node_id *id,
|
2018-09-20 05:07:41 +02:00
|
|
|
u64 dbid,
|
2023-08-07 07:51:40 +02:00
|
|
|
u64 permissions)
|
2018-09-20 05:07:41 +02:00
|
|
|
{
|
|
|
|
assert(dbid);
|
|
|
|
|
2023-08-07 07:51:40 +02:00
|
|
|
return hsm_get_fd(ld, id, dbid, permissions);
|
2018-09-20 05:07:41 +02:00
|
|
|
}
|
|
|
|
|
2023-08-07 07:51:40 +02:00
|
|
|
int hsm_get_global_fd(struct lightningd *ld, u64 permissions)
|
2018-09-20 05:07:41 +02:00
|
|
|
{
|
2024-07-18 03:25:55 +02:00
|
|
|
int fd = hsm_get_fd(ld, &ld->our_nodeid, 0, permissions);
|
2024-05-09 05:21:14 +02:00
|
|
|
|
|
|
|
if (fd < 0)
|
|
|
|
fatal("Could not read fd from HSM: %s", strerror(errno));
|
|
|
|
return fd;
|
2018-09-20 05:07:41 +02:00
|
|
|
}
|
|
|
|
|
2018-09-20 05:06:42 +02:00
|
|
|
static unsigned int hsm_msg(struct subd *hsmd,
|
|
|
|
const u8 *msg, const int *fds UNUSED)
|
|
|
|
{
|
|
|
|
/* We only expect one thing from the HSM that's not a STATUS message */
|
2019-04-08 11:58:32 +02:00
|
|
|
struct node_id client_id;
|
2018-09-20 05:06:42 +02:00
|
|
|
u8 *bad_msg;
|
2018-09-20 05:06:42 +02:00
|
|
|
char *desc;
|
2018-09-20 05:06:42 +02:00
|
|
|
|
|
|
|
if (!fromwire_hsmstatus_client_bad_request(tmpctx, msg, &client_id,
|
2018-09-20 05:06:42 +02:00
|
|
|
&desc, &bad_msg))
|
2018-09-20 05:06:42 +02:00
|
|
|
fatal("Bad status message from hsmd: %s", tal_hex(tmpctx, msg));
|
|
|
|
|
|
|
|
/* This should, of course, never happen. */
|
2018-09-20 05:06:42 +02:00
|
|
|
log_broken(hsmd->log, "client %s %s (request %s)",
|
2024-03-20 01:47:52 +01:00
|
|
|
fmt_node_id(tmpctx, &client_id),
|
2018-09-20 05:06:42 +02:00
|
|
|
desc, tal_hex(tmpctx, bad_msg));
|
2018-09-20 05:06:42 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2023-03-21 04:58:09 +01:00
|
|
|
/* Is this capability supported by the HSM? (So far, always a message
|
|
|
|
* number) */
|
|
|
|
bool hsm_capable(struct lightningd *ld, u32 msgtype)
|
|
|
|
{
|
2024-01-18 22:19:55 +01:00
|
|
|
return hsm_is_capable(ld->hsm_capabilities, msgtype);
|
2023-03-21 04:58:09 +01:00
|
|
|
}
|
|
|
|
|
2020-05-26 23:06:13 +02:00
|
|
|
struct ext_key *hsm_init(struct lightningd *ld)
|
2017-01-10 06:08:33 +01:00
|
|
|
{
|
2017-08-18 06:43:52 +02:00
|
|
|
u8 *msg;
|
2018-07-09 13:17:59 +02:00
|
|
|
int fds[2];
|
2020-05-26 23:06:13 +02:00
|
|
|
struct ext_key *bip32_base;
|
2023-03-21 04:58:09 +01:00
|
|
|
u32 hsm_version;
|
2018-07-09 13:17:59 +02:00
|
|
|
|
|
|
|
/* We actually send requests synchronously: only status is async. */
|
|
|
|
if (socketpair(AF_LOCAL, SOCK_STREAM, 0, fds) != 0)
|
2022-07-20 04:29:25 +02:00
|
|
|
err(EXITCODE_HSM_GENERIC_ERROR, "Could not create hsm socketpair");
|
2017-01-10 06:08:33 +01:00
|
|
|
|
2018-09-20 05:06:42 +02:00
|
|
|
ld->hsm = new_global_subd(ld, "lightning_hsmd",
|
2020-08-25 03:55:38 +02:00
|
|
|
hsmd_wire_name,
|
2018-09-20 05:06:42 +02:00
|
|
|
hsm_msg,
|
2018-07-09 13:17:59 +02:00
|
|
|
take(&fds[1]), NULL);
|
|
|
|
if (!ld->hsm)
|
2022-07-20 04:29:25 +02:00
|
|
|
err(EXITCODE_HSM_GENERIC_ERROR, "Could not subd hsm");
|
2017-01-10 06:08:33 +01:00
|
|
|
|
2019-10-03 23:08:43 +02:00
|
|
|
/* If hsm_secret is encrypted and the --encrypted-hsm startup option is
|
|
|
|
* not passed, don't let hsmd use the first 32 bytes of the cypher as the
|
|
|
|
* actual secret. */
|
|
|
|
if (!ld->config.keypass) {
|
2022-03-10 22:26:20 +01:00
|
|
|
if (is_hsm_secret_encrypted("hsm_secret") == 1)
|
2022-07-20 04:29:25 +02:00
|
|
|
errx(EXITCODE_HSM_ERROR_IS_ENCRYPT, "hsm_secret is encrypted, you need to pass the "
|
2022-03-10 22:26:20 +01:00
|
|
|
"--encrypted-hsm startup option.");
|
2019-10-03 23:08:43 +02:00
|
|
|
}
|
|
|
|
|
2018-07-09 13:17:59 +02:00
|
|
|
ld->hsm_fd = fds[0];
|
2024-04-04 05:36:13 +02:00
|
|
|
|
|
|
|
if (ld->developer) {
|
|
|
|
struct tlv_hsmd_dev_preinit_tlvs *tlv;
|
|
|
|
|
|
|
|
tlv = tlv_hsmd_dev_preinit_tlvs_new(tmpctx);
|
|
|
|
tlv->fail_preapprove = tal_dup(tlv, bool,
|
|
|
|
&ld->dev_hsmd_fail_preapprove);
|
|
|
|
tlv->no_preapprove_check = tal_dup(tlv, bool,
|
|
|
|
&ld->dev_hsmd_no_preapprove_check);
|
|
|
|
|
|
|
|
msg = towire_hsmd_dev_preinit(tmpctx, tlv);
|
|
|
|
if (!wire_sync_write(ld->hsm_fd, msg))
|
|
|
|
err(EXITCODE_HSM_GENERIC_ERROR, "Writing preinit msg to hsm");
|
|
|
|
}
|
|
|
|
|
2020-08-25 03:55:38 +02:00
|
|
|
if (!wire_sync_write(ld->hsm_fd, towire_hsmd_init(tmpctx,
|
2023-09-21 07:36:28 +02:00
|
|
|
&chainparams->bip32_key_version,
|
|
|
|
chainparams,
|
|
|
|
ld->config.keypass,
|
|
|
|
ld->dev_force_privkey,
|
|
|
|
ld->dev_force_bip32_seed,
|
|
|
|
ld->dev_force_channel_secrets,
|
|
|
|
ld->dev_force_channel_secrets_shaseed,
|
2023-03-20 21:26:02 +01:00
|
|
|
HSM_MIN_VERSION,
|
2022-10-17 02:34:08 +02:00
|
|
|
HSM_MAX_VERSION)))
|
2022-07-20 04:29:25 +02:00
|
|
|
err(EXITCODE_HSM_GENERIC_ERROR, "Writing init msg to hsm");
|
2017-01-10 06:08:33 +01:00
|
|
|
|
2020-05-26 23:06:13 +02:00
|
|
|
bip32_base = tal(ld, struct ext_key);
|
2018-07-09 13:17:59 +02:00
|
|
|
msg = wire_sync_read(tmpctx, ld->hsm_fd);
|
2023-03-21 04:58:09 +01:00
|
|
|
if (fromwire_hsmd_init_reply_v4(ld, msg,
|
|
|
|
&hsm_version,
|
|
|
|
&ld->hsm_capabilities,
|
2024-07-18 03:25:55 +02:00
|
|
|
&ld->our_nodeid, bip32_base,
|
2023-03-21 04:58:09 +01:00
|
|
|
&ld->bolt12_base)) {
|
|
|
|
/* nothing to do. */
|
|
|
|
} else {
|
2023-03-14 06:16:50 +01:00
|
|
|
if (ld->config.keypass)
|
|
|
|
errx(EXITCODE_HSM_BAD_PASSWORD, "Wrong password for encrypted hsm_secret.");
|
|
|
|
errx(EXITCODE_HSM_GENERIC_ERROR, "HSM did not give init reply");
|
2019-10-03 21:55:32 +02:00
|
|
|
}
|
2020-05-26 23:06:13 +02:00
|
|
|
|
2024-07-18 03:25:55 +02:00
|
|
|
if (!pubkey_from_node_id(&ld->our_pubkey, &ld->our_nodeid))
|
|
|
|
errx(EXITCODE_HSM_GENERIC_ERROR, "HSM gave invalid node id %s",
|
|
|
|
fmt_node_id(tmpctx, &ld->our_nodeid));
|
|
|
|
|
2023-03-21 04:58:09 +01:00
|
|
|
if (hsm_version < HSM_MIN_VERSION)
|
|
|
|
errx(EXITCODE_HSM_GENERIC_ERROR,
|
|
|
|
"HSM version %u below minimum %u",
|
|
|
|
hsm_version, HSM_MIN_VERSION);
|
|
|
|
if (hsm_version > HSM_MAX_VERSION)
|
|
|
|
errx(EXITCODE_HSM_GENERIC_ERROR,
|
|
|
|
"HSM version %u above maximum %u",
|
|
|
|
hsm_version, HSM_MAX_VERSION);
|
|
|
|
|
|
|
|
/* Debugging help */
|
|
|
|
for (size_t i = 0; i < tal_count(ld->hsm_capabilities); i++) {
|
|
|
|
log_debug(ld->hsm->log, "capability +%s",
|
|
|
|
hsmd_wire_name(ld->hsm_capabilities[i]));
|
|
|
|
}
|
|
|
|
|
2023-06-26 01:19:21 +02:00
|
|
|
if (feature_offered(ld->our_features->bits[INIT_FEATURE],
|
|
|
|
OPT_ANCHORS_ZERO_FEE_HTLC_TX)
|
|
|
|
&& !hsm_capable(ld, WIRE_HSMD_SIGN_ANCHORSPEND)) {
|
2024-02-06 05:13:28 +01:00
|
|
|
fatal("anchors needs HSM capable of signing anchors!");
|
2023-06-26 01:19:21 +02:00
|
|
|
}
|
|
|
|
|
2023-07-27 23:37:52 +02:00
|
|
|
if (feature_offered(ld->our_features->bits[INIT_FEATURE],
|
2023-11-14 01:16:31 +01:00
|
|
|
OPT_EXPERIMENTAL_SPLICE)
|
2023-07-27 23:37:52 +02:00
|
|
|
&& !hsm_capable(ld, WIRE_HSMD_SIGN_SPLICE_TX)) {
|
|
|
|
fatal("--experimental-splicing needs HSM capable of signing splices!");
|
|
|
|
}
|
|
|
|
|
2022-11-09 03:27:04 +01:00
|
|
|
/* This is equivalent to makesecret("bolt12-invoice-base") */
|
|
|
|
msg = towire_hsmd_derive_secret(NULL, tal_dup_arr(tmpctx, u8,
|
|
|
|
(const u8 *)INVOICE_PATH_BASE_STRING,
|
|
|
|
strlen(INVOICE_PATH_BASE_STRING), 0));
|
|
|
|
if (!wire_sync_write(ld->hsm_fd, take(msg)))
|
|
|
|
err(EXITCODE_HSM_GENERIC_ERROR, "Writing derive_secret msg to hsm");
|
|
|
|
|
|
|
|
msg = wire_sync_read(tmpctx, ld->hsm_fd);
|
|
|
|
if (!fromwire_hsmd_derive_secret_reply(msg, &ld->invoicesecret_base))
|
|
|
|
err(EXITCODE_HSM_GENERIC_ERROR, "Bad derive_secret_reply");
|
|
|
|
|
2020-05-26 23:06:13 +02:00
|
|
|
return bip32_base;
|
2017-06-24 08:50:23 +02:00
|
|
|
}
|
2020-02-04 06:53:17 +01:00
|
|
|
|
2023-03-21 04:58:15 +01:00
|
|
|
/*~ There was a nasty LND bug report where the user issued an address which it
|
|
|
|
* couldn't spend, presumably due to a bitflip. We check every address using our
|
|
|
|
* hsm, to be sure it's valid. Expensive, but not as expensive as losing BTC! */
|
|
|
|
void bip32_pubkey(struct lightningd *ld, struct pubkey *pubkey, u32 index)
|
|
|
|
{
|
|
|
|
const uint32_t flags = BIP32_FLAG_KEY_PUBLIC | BIP32_FLAG_SKIP_HASH;
|
|
|
|
struct ext_key ext;
|
|
|
|
|
|
|
|
if (index >= BIP32_INITIAL_HARDENED_CHILD)
|
|
|
|
fatal("Can't derive keu %u (too large!)", index);
|
|
|
|
|
|
|
|
if (bip32_key_from_parent(ld->bip32_base, index, flags, &ext) != WALLY_OK)
|
|
|
|
fatal("Can't derive key %u", index);
|
|
|
|
|
|
|
|
if (!secp256k1_ec_pubkey_parse(secp256k1_ctx, &pubkey->pubkey,
|
|
|
|
ext.pub_key, sizeof(ext.pub_key)))
|
|
|
|
fatal("Can't parse derived key %u", index);
|
|
|
|
|
|
|
|
/* Don't assume hsmd supports it! */
|
|
|
|
if (hsm_capable(ld, WIRE_HSMD_CHECK_PUBKEY)) {
|
|
|
|
bool ok;
|
2023-04-10 02:12:56 +02:00
|
|
|
const u8 *msg = towire_hsmd_check_pubkey(NULL, index, pubkey);
|
|
|
|
msg = hsm_sync_req(tmpctx, ld, take(msg));
|
2023-03-21 04:58:15 +01:00
|
|
|
if (!fromwire_hsmd_check_pubkey_reply(msg, &ok))
|
|
|
|
fatal("Invalid check_pubkey_reply from hsm");
|
|
|
|
if (!ok)
|
|
|
|
fatal("HSM said key derivation of %u != %s",
|
2024-03-20 01:47:52 +01:00
|
|
|
index, fmt_pubkey(tmpctx, pubkey));
|
2023-03-21 04:58:15 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-04-10 02:12:56 +02:00
|
|
|
const u8 *hsm_sync_req(const tal_t *ctx, struct lightningd *ld, const u8 *msg)
|
|
|
|
{
|
|
|
|
int type = fromwire_peektype(msg);
|
|
|
|
if (!wire_sync_write(ld->hsm_fd, msg))
|
|
|
|
fatal("Writing %s hsm", hsmd_wire_name(type));
|
|
|
|
msg = wire_sync_read(ctx, ld->hsm_fd);
|
|
|
|
if (!msg)
|
|
|
|
fatal("EOF reading from HSM after %s",
|
|
|
|
hsmd_wire_name(type));
|
|
|
|
return msg;
|
|
|
|
}
|
|
|
|
|
2022-06-24 02:19:09 +02:00
|
|
|
static struct command_result *json_makesecret(struct command *cmd,
|
|
|
|
const char *buffer,
|
|
|
|
const jsmntok_t *obj UNNEEDED,
|
|
|
|
const jsmntok_t *params)
|
|
|
|
{
|
2022-09-29 05:48:53 +02:00
|
|
|
u8 *data;
|
|
|
|
const char *strdata;
|
2022-06-24 02:19:09 +02:00
|
|
|
struct json_stream *response;
|
|
|
|
struct secret secret;
|
|
|
|
|
2023-10-24 23:09:04 +02:00
|
|
|
if (!param_check(cmd, buffer, params,
|
|
|
|
p_opt("hex", param_bin_from_hex, &data),
|
|
|
|
p_opt("string", param_string, &strdata),
|
|
|
|
NULL))
|
2022-06-24 02:19:09 +02:00
|
|
|
return command_param_failed();
|
|
|
|
|
2022-09-29 05:48:53 +02:00
|
|
|
if (strdata) {
|
|
|
|
if (data)
|
|
|
|
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
|
|
|
|
"Cannot have both hex and string");
|
|
|
|
data = tal_dup_arr(cmd, u8, (u8 *)strdata, strlen(strdata), 0);
|
|
|
|
} else {
|
|
|
|
if (!data)
|
|
|
|
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
|
|
|
|
"Must have either hex or string");
|
|
|
|
}
|
|
|
|
|
2023-10-24 23:09:04 +02:00
|
|
|
if (command_check_only(cmd))
|
|
|
|
return command_check_done(cmd);
|
2022-09-29 05:48:53 +02:00
|
|
|
|
|
|
|
u8 *msg = towire_hsmd_derive_secret(cmd, data);
|
2022-06-24 02:19:09 +02:00
|
|
|
if (!wire_sync_write(cmd->ld->hsm_fd, take(msg)))
|
|
|
|
return command_fail(cmd, LIGHTNINGD,
|
|
|
|
"Could not write to HSM: %s", strerror(errno));
|
|
|
|
|
|
|
|
|
|
|
|
msg = wire_sync_read(tmpctx, cmd->ld->hsm_fd);
|
|
|
|
if (!fromwire_hsmd_derive_secret_reply(msg, &secret))
|
|
|
|
return command_fail(cmd, LIGHTNINGD,
|
|
|
|
"Bad reply from HSM: %s", strerror(errno));
|
|
|
|
|
|
|
|
|
|
|
|
response = json_stream_success(cmd);
|
|
|
|
json_add_secret(response, "secret", &secret);
|
|
|
|
return command_success(cmd, response);
|
|
|
|
}
|
|
|
|
|
|
|
|
static const struct json_command makesecret_command = {
|
|
|
|
"makesecret",
|
|
|
|
&json_makesecret,
|
2022-07-15 06:27:07 +02:00
|
|
|
"Get a pseudorandom secret key, using some {hex} data."
|
2022-06-24 02:19:09 +02:00
|
|
|
};
|
|
|
|
AUTODATA(json_command, &makesecret_command);
|