mirror of
https://github.com/ElementsProject/lightning.git
synced 2024-11-20 02:27:51 +01:00
448 lines
15 KiB
C
448 lines
15 KiB
C
#include <ccan/crypto/hkdf_sha256/hkdf_sha256.h>
|
||
#include <common/bolt12_merkle.h>
|
||
#include <common/hash_u5.h>
|
||
#include <hsmd/capabilities.h>
|
||
#include <hsmd/libhsmd.h>
|
||
|
||
/* Version codes for BIP32 extended keys in libwally-core.
|
||
* It's not suitable to add this struct into client struct,
|
||
* so set it static.*/
|
||
struct bip32_key_version bip32_key_version;
|
||
|
||
#if DEVELOPER
|
||
/* If they specify --dev-force-privkey it ends up in here. */
|
||
struct privkey *dev_force_privkey;
|
||
/* If they specify --dev-force-bip32-seed it ends up in here. */
|
||
struct secret *dev_force_bip32_seed;
|
||
#endif
|
||
|
||
/* Have we initialized the secretstuff? */
|
||
bool initialized = false;
|
||
|
||
struct hsmd_client *hsmd_client_new_main(const tal_t *ctx, u64 capabilities,
|
||
void *extra)
|
||
{
|
||
struct hsmd_client *c = tal(ctx, struct hsmd_client);
|
||
c->dbid = 0;
|
||
c->capabilities = capabilities;
|
||
c->extra = extra;
|
||
return c;
|
||
}
|
||
|
||
struct hsmd_client *hsmd_client_new_peer(const tal_t *ctx, u64 capabilities,
|
||
u64 dbid,
|
||
const struct node_id *peer_id,
|
||
void *extra)
|
||
{
|
||
struct hsmd_client *c = tal(ctx, struct hsmd_client);
|
||
c->dbid = dbid;
|
||
c->capabilities = capabilities;
|
||
c->id = *peer_id;
|
||
c->extra = extra;
|
||
return c;
|
||
}
|
||
|
||
/*~ This routine checks that a client is allowed to call the handler. */
|
||
bool check_client_capabilities(struct hsmd_client *client, enum hsmd_wire t)
|
||
{
|
||
/*~ Here's a useful trick: enums in C are not real types, they're
|
||
* semantic sugar sprinkled over an int, bascally (in fact, older
|
||
* versions of gcc used to convert the values ints in the parser!).
|
||
*
|
||
* But GCC will do one thing for us: if we have a switch statement
|
||
* with a controlling expression which is an enum, it will warn us
|
||
* if a declared enum value is *not* handled in the switch, eg:
|
||
* enumeration value ‘FOOBAR’ not handled in switch [-Werror=switch]
|
||
*
|
||
* This only works if there's no 'default' label, which is sometimes
|
||
* hard, as we *can* have non-enum values in our enum. But the tradeoff
|
||
* is worth it so the compiler tells us everywhere we have to fix when
|
||
* we add a new enum identifier!
|
||
*/
|
||
switch (t) {
|
||
case WIRE_HSMD_ECDH_REQ:
|
||
return (client->capabilities & HSM_CAP_ECDH) != 0;
|
||
|
||
case WIRE_HSMD_CANNOUNCEMENT_SIG_REQ:
|
||
case WIRE_HSMD_CUPDATE_SIG_REQ:
|
||
case WIRE_HSMD_NODE_ANNOUNCEMENT_SIG_REQ:
|
||
return (client->capabilities & HSM_CAP_SIGN_GOSSIP) != 0;
|
||
|
||
case WIRE_HSMD_SIGN_DELAYED_PAYMENT_TO_US:
|
||
case WIRE_HSMD_SIGN_REMOTE_HTLC_TO_US:
|
||
case WIRE_HSMD_SIGN_PENALTY_TO_US:
|
||
case WIRE_HSMD_SIGN_LOCAL_HTLC_TX:
|
||
return (client->capabilities & HSM_CAP_SIGN_ONCHAIN_TX) != 0;
|
||
|
||
case WIRE_HSMD_GET_PER_COMMITMENT_POINT:
|
||
case WIRE_HSMD_CHECK_FUTURE_SECRET:
|
||
return (client->capabilities & HSM_CAP_COMMITMENT_POINT) != 0;
|
||
|
||
case WIRE_HSMD_SIGN_REMOTE_COMMITMENT_TX:
|
||
case WIRE_HSMD_SIGN_REMOTE_HTLC_TX:
|
||
return (client->capabilities & HSM_CAP_SIGN_REMOTE_TX) != 0;
|
||
|
||
case WIRE_HSMD_SIGN_MUTUAL_CLOSE_TX:
|
||
return (client->capabilities & HSM_CAP_SIGN_CLOSING_TX) != 0;
|
||
|
||
case WIRE_HSMD_INIT:
|
||
case WIRE_HSMD_CLIENT_HSMFD:
|
||
case WIRE_HSMD_SIGN_WITHDRAWAL:
|
||
case WIRE_HSMD_SIGN_INVOICE:
|
||
case WIRE_HSMD_SIGN_COMMITMENT_TX:
|
||
case WIRE_HSMD_GET_CHANNEL_BASEPOINTS:
|
||
case WIRE_HSMD_DEV_MEMLEAK:
|
||
case WIRE_HSMD_SIGN_MESSAGE:
|
||
case WIRE_HSMD_GET_OUTPUT_SCRIPTPUBKEY:
|
||
case WIRE_HSMD_SIGN_BOLT12:
|
||
return (client->capabilities & HSM_CAP_MASTER) != 0;
|
||
|
||
/*~ These are messages sent by the HSM so we should never receive them. */
|
||
/* FIXME: Since we autogenerate these, we should really generate separate
|
||
* enums for replies to avoid this kind of clutter! */
|
||
case WIRE_HSMD_ECDH_RESP:
|
||
case WIRE_HSMD_CANNOUNCEMENT_SIG_REPLY:
|
||
case WIRE_HSMD_CUPDATE_SIG_REPLY:
|
||
case WIRE_HSMD_CLIENT_HSMFD_REPLY:
|
||
case WIRE_HSMD_NODE_ANNOUNCEMENT_SIG_REPLY:
|
||
case WIRE_HSMD_SIGN_WITHDRAWAL_REPLY:
|
||
case WIRE_HSMD_SIGN_INVOICE_REPLY:
|
||
case WIRE_HSMD_INIT_REPLY:
|
||
case WIRE_HSMSTATUS_CLIENT_BAD_REQUEST:
|
||
case WIRE_HSMD_SIGN_COMMITMENT_TX_REPLY:
|
||
case WIRE_HSMD_SIGN_TX_REPLY:
|
||
case WIRE_HSMD_GET_PER_COMMITMENT_POINT_REPLY:
|
||
case WIRE_HSMD_CHECK_FUTURE_SECRET_REPLY:
|
||
case WIRE_HSMD_GET_CHANNEL_BASEPOINTS_REPLY:
|
||
case WIRE_HSMD_DEV_MEMLEAK_REPLY:
|
||
case WIRE_HSMD_SIGN_MESSAGE_REPLY:
|
||
case WIRE_HSMD_GET_OUTPUT_SCRIPTPUBKEY_REPLY:
|
||
case WIRE_HSMD_SIGN_BOLT12_REPLY:
|
||
break;
|
||
}
|
||
return false;
|
||
}
|
||
|
||
/*~ ccan/compiler.h defines PRINTF_FMT as the gcc compiler hint so it will
|
||
* check that fmt and other trailing arguments really are the correct type.
|
||
*/
|
||
/* This function is used to format an error message before passing it
|
||
* to the library user specified hsmd_status_bad_request */
|
||
static u8 *hsmd_status_bad_request_fmt(struct hsmd_client *client,
|
||
const u8 *msg, const char *fmt, ...)
|
||
PRINTF_FMT(3, 4);
|
||
|
||
static u8 *hsmd_status_bad_request_fmt(struct hsmd_client *client,
|
||
const u8 *msg, const char *fmt, ...)
|
||
{
|
||
va_list ap;
|
||
char *str;
|
||
|
||
va_start(ap, fmt);
|
||
str = tal_fmt(tmpctx, fmt, ap);
|
||
va_end(ap);
|
||
return hsmd_status_bad_request(client, msg, str);
|
||
}
|
||
|
||
/* Convenience wrapper for when we simply can't parse. */
|
||
static u8 *hsmd_status_malformed_request(struct hsmd_client *c, const u8 *msg_in)
|
||
{
|
||
return hsmd_status_bad_request(c, msg_in, "could not parse request");
|
||
}
|
||
|
||
/*~ This returns the secret and/or public key for this node. */
|
||
static void node_key(struct privkey *node_privkey, struct pubkey *node_id)
|
||
{
|
||
u32 salt = 0;
|
||
struct privkey unused_s;
|
||
struct pubkey unused_k;
|
||
|
||
/* If caller specifies NULL, they don't want the results. */
|
||
if (node_privkey == NULL)
|
||
node_privkey = &unused_s;
|
||
if (node_id == NULL)
|
||
node_id = &unused_k;
|
||
|
||
/*~ So, there is apparently a 1 in 2^127 chance that a random value is
|
||
* not a valid private key, so this never actually loops. */
|
||
do {
|
||
/*~ ccan/crypto/hkdf_sha256 implements RFC5869 "Hardened Key
|
||
* Derivation Functions". That means that if a derived key
|
||
* leaks somehow, the other keys are not compromised. */
|
||
hkdf_sha256(node_privkey, sizeof(*node_privkey),
|
||
&salt, sizeof(salt),
|
||
&secretstuff.hsm_secret,
|
||
sizeof(secretstuff.hsm_secret),
|
||
"nodeid", 6);
|
||
salt++;
|
||
} while (!secp256k1_ec_pubkey_create(secp256k1_ctx, &node_id->pubkey,
|
||
node_privkey->secret.data));
|
||
|
||
#if DEVELOPER
|
||
/* In DEVELOPER mode, we can override with --dev-force-privkey */
|
||
if (dev_force_privkey) {
|
||
*node_privkey = *dev_force_privkey;
|
||
if (!secp256k1_ec_pubkey_create(secp256k1_ctx, &node_id->pubkey,
|
||
node_privkey->secret.data))
|
||
hsmd_status_failed(STATUS_FAIL_INTERNAL_ERROR,
|
||
"Failed to derive pubkey for dev_force_privkey");
|
||
}
|
||
#endif
|
||
}
|
||
|
||
/*~ This returns the secret and/or public x-only key for this node. */
|
||
static void node_schnorrkey(secp256k1_keypair *node_keypair,
|
||
struct pubkey32 *node_id32)
|
||
{
|
||
secp256k1_keypair unused_kp;
|
||
struct privkey node_privkey;
|
||
|
||
if (!node_keypair)
|
||
node_keypair = &unused_kp;
|
||
|
||
node_key(&node_privkey, NULL);
|
||
if (secp256k1_keypair_create(secp256k1_ctx, node_keypair,
|
||
node_privkey.secret.data) != 1)
|
||
hsmd_status_failed(STATUS_FAIL_INTERNAL_ERROR,
|
||
"Failed to derive keypair");
|
||
|
||
if (node_id32) {
|
||
if (secp256k1_keypair_xonly_pub(secp256k1_ctx,
|
||
&node_id32->pubkey,
|
||
NULL, node_keypair) != 1)
|
||
hsmd_status_failed(STATUS_FAIL_INTERNAL_ERROR,
|
||
"Failed to derive xonly pub");
|
||
}
|
||
}
|
||
|
||
/*~ lightningd asks us to sign a message. I tweeted the spec
|
||
* in https://twitter.com/rusty_twit/status/1182102005914800128:
|
||
*
|
||
* @roasbeef & @bitconner point out that #lnd algo is:
|
||
* zbase32(SigRec(SHA256(SHA256("Lightning Signed Message:" + msg)))).
|
||
* zbase32 from https://philzimmermann.com/docs/human-oriented-base-32-encoding.txt
|
||
* and SigRec has first byte 31 + recovery id, followed by 64 byte sig. #specinatweet
|
||
*/
|
||
static u8 *handle_sign_message(struct hsmd_client *c, const u8 *msg_in)
|
||
{
|
||
u8 *msg;
|
||
struct sha256_ctx sctx = SHA256_INIT;
|
||
struct sha256_double shad;
|
||
secp256k1_ecdsa_recoverable_signature rsig;
|
||
struct privkey node_pkey;
|
||
|
||
if (!fromwire_hsmd_sign_message(tmpctx, msg_in, &msg))
|
||
return hsmd_status_malformed_request(c, msg_in);
|
||
|
||
/* Prefixing by a known string means we'll never be convinced
|
||
* to sign some gossip message, etc. */
|
||
sha256_update(&sctx, "Lightning Signed Message:",
|
||
strlen("Lightning Signed Message:"));
|
||
sha256_update(&sctx, msg, tal_count(msg));
|
||
sha256_double_done(&sctx, &shad);
|
||
|
||
node_key(&node_pkey, NULL);
|
||
/*~ By no small coincidence, this libsecp routine uses the exact
|
||
* recovery signature format mandated by BOLT 11. */
|
||
if (!secp256k1_ecdsa_sign_recoverable(secp256k1_ctx, &rsig,
|
||
shad.sha.u.u8,
|
||
node_pkey.secret.data,
|
||
NULL, NULL)) {
|
||
return hsmd_status_bad_request(c, msg_in, "Failed to sign message");
|
||
}
|
||
|
||
return towire_hsmd_sign_message_reply(NULL, &rsig);
|
||
}
|
||
|
||
/*~ lightningd asks us to sign a bolt12 (e.g. offer). */
|
||
static u8 *handle_sign_bolt12(struct hsmd_client *c, const u8 *msg_in)
|
||
{
|
||
char *messagename, *fieldname;
|
||
struct sha256 merkle, sha;
|
||
struct bip340sig sig;
|
||
secp256k1_keypair kp;
|
||
u8 *publictweak;
|
||
|
||
if (!fromwire_hsmd_sign_bolt12(tmpctx, msg_in,
|
||
&messagename, &fieldname, &merkle,
|
||
&publictweak))
|
||
return hsmd_status_malformed_request(c, msg_in);
|
||
|
||
sighash_from_merkle(messagename, fieldname, &merkle, &sha);
|
||
|
||
if (!publictweak) {
|
||
node_schnorrkey(&kp, NULL);
|
||
} else {
|
||
/* If we're tweaking key, we use bolt12 key */
|
||
struct pubkey32 bolt12;
|
||
struct sha256 tweak;
|
||
|
||
if (secp256k1_keypair_xonly_pub(secp256k1_ctx,
|
||
&bolt12.pubkey, NULL,
|
||
&secretstuff.bolt12) != 1)
|
||
hsmd_status_failed(
|
||
STATUS_FAIL_INTERNAL_ERROR,
|
||
"Could not derive bolt12 public key.");
|
||
payer_key_tweak(&bolt12, publictweak, tal_bytelen(publictweak),
|
||
&tweak);
|
||
|
||
kp = secretstuff.bolt12;
|
||
|
||
if (secp256k1_keypair_xonly_tweak_add(secp256k1_ctx,
|
||
&kp,
|
||
tweak.u.u8) != 1) {
|
||
return hsmd_status_bad_request_fmt(
|
||
c, msg_in, "Failed to get tweak key");
|
||
}
|
||
}
|
||
|
||
if (!secp256k1_schnorrsig_sign(secp256k1_ctx, sig.u8,
|
||
sha.u.u8,
|
||
&kp,
|
||
NULL, NULL)) {
|
||
return hsmd_status_bad_request_fmt(c, msg_in,
|
||
"Failed to sign bolt12");
|
||
}
|
||
|
||
return towire_hsmd_sign_bolt12_reply(NULL, &sig);
|
||
}
|
||
|
||
/*~ Lightning invoices, defined by BOLT 11, are signed. This has been
|
||
* surprisingly controversial; it means a node needs to be online to create
|
||
* invoices. However, it seems clear to me that in a world without
|
||
* intermedaries you need proof that you have received an offer (the
|
||
* signature), as well as proof that you've paid it (the preimage). */
|
||
static u8 *handle_sign_invoice(struct hsmd_client *c, const u8 *msg_in)
|
||
{
|
||
/*~ We make up a 'u5' type to represent BOLT11's 5-bits-per-byte
|
||
* format: it's only for human consumption, as typedefs are almost
|
||
* entirely transparent to the C compiler. */
|
||
u5 *u5bytes;
|
||
u8 *hrpu8;
|
||
char *hrp;
|
||
struct sha256 sha;
|
||
secp256k1_ecdsa_recoverable_signature rsig;
|
||
struct hash_u5 hu5;
|
||
struct privkey node_pkey;
|
||
|
||
if (!fromwire_hsmd_sign_invoice(tmpctx, msg_in, &u5bytes, &hrpu8))
|
||
return hsmd_status_malformed_request(c, msg_in);
|
||
|
||
/* BOLT #11:
|
||
*
|
||
* A writer... MUST set `signature` to a valid 512-bit
|
||
* secp256k1 signature of the SHA2 256-bit hash of the
|
||
* human-readable part, represented as UTF-8 bytes,
|
||
* concatenated with the data part (excluding the signature)
|
||
* with 0 bits appended to pad the data to the next byte
|
||
* boundary, with a trailing byte containing the recovery ID
|
||
* (0, 1, 2, or 3).
|
||
*/
|
||
|
||
/* FIXME: Check invoice! */
|
||
|
||
/*~ tal_dup_arr() does what you'd expect: allocate an array by copying
|
||
* another; the cast is needed because the hrp is a 'char' array, not
|
||
* a 'u8' (unsigned char) as it's the "human readable" part.
|
||
*
|
||
* The final arg of tal_dup_arr() is how many extra bytes to allocate:
|
||
* it's so often zero that I've thought about dropping the argument, but
|
||
* in cases like this (adding a NUL terminator) it's perfect. */
|
||
hrp = tal_dup_arr(tmpctx, char, (char *)hrpu8, tal_count(hrpu8), 1);
|
||
hrp[tal_count(hrpu8)] = '\0';
|
||
|
||
hash_u5_init(&hu5, hrp);
|
||
hash_u5(&hu5, u5bytes, tal_count(u5bytes));
|
||
hash_u5_done(&hu5, &sha);
|
||
|
||
node_key(&node_pkey, NULL);
|
||
/*~ By no small coincidence, this libsecp routine uses the exact
|
||
* recovery signature format mandated by BOLT 11. */
|
||
if (!secp256k1_ecdsa_sign_recoverable(secp256k1_ctx, &rsig,
|
||
(const u8 *)&sha,
|
||
node_pkey.secret.data,
|
||
NULL, NULL)) {
|
||
return hsmd_status_bad_request_fmt(c, msg_in,
|
||
"Failed to sign invoice");
|
||
}
|
||
|
||
return towire_hsmd_sign_invoice_reply(NULL, &rsig);
|
||
}
|
||
|
||
u8 *hsmd_handle_client_message(const tal_t *ctx, struct hsmd_client *client,
|
||
const u8 *msg)
|
||
{
|
||
enum hsmd_wire t = fromwire_peektype(msg);
|
||
|
||
hsmd_status_debug("Client: Received message %d from client", t);
|
||
|
||
/* Before we do anything else, is this client allowed to do
|
||
* what he asks for? */
|
||
if (!check_client_capabilities(client, t))
|
||
return hsmd_status_bad_request_fmt(
|
||
client, msg, "does not have capability to run %d", t);
|
||
|
||
/* If we aren't initialized yet we better get an init message
|
||
* first. Otherwise we don't load the secret and every
|
||
* signature we produce is just going to be junk. */
|
||
if (!initialized && t != WIRE_HSMD_INIT)
|
||
hsmd_status_failed(STATUS_FAIL_MASTER_IO,
|
||
"hsmd was not initialized correctly, expected "
|
||
"message type %d, got %d",
|
||
WIRE_HSMD_INIT, t);
|
||
|
||
/* Now actually go and do what the client asked for */
|
||
switch (t) {
|
||
case WIRE_HSMD_INIT:
|
||
case WIRE_HSMD_CLIENT_HSMFD:
|
||
case WIRE_HSMD_GET_CHANNEL_BASEPOINTS:
|
||
case WIRE_HSMD_GET_OUTPUT_SCRIPTPUBKEY:
|
||
case WIRE_HSMD_ECDH_REQ:
|
||
case WIRE_HSMD_CANNOUNCEMENT_SIG_REQ:
|
||
case WIRE_HSMD_CUPDATE_SIG_REQ:
|
||
case WIRE_HSMD_NODE_ANNOUNCEMENT_SIG_REQ:
|
||
case WIRE_HSMD_SIGN_WITHDRAWAL:
|
||
case WIRE_HSMD_SIGN_COMMITMENT_TX:
|
||
case WIRE_HSMD_SIGN_DELAYED_PAYMENT_TO_US:
|
||
case WIRE_HSMD_SIGN_REMOTE_HTLC_TO_US:
|
||
case WIRE_HSMD_SIGN_PENALTY_TO_US:
|
||
case WIRE_HSMD_SIGN_LOCAL_HTLC_TX:
|
||
case WIRE_HSMD_GET_PER_COMMITMENT_POINT:
|
||
case WIRE_HSMD_CHECK_FUTURE_SECRET:
|
||
case WIRE_HSMD_SIGN_REMOTE_COMMITMENT_TX:
|
||
case WIRE_HSMD_SIGN_REMOTE_HTLC_TX:
|
||
case WIRE_HSMD_SIGN_MUTUAL_CLOSE_TX:
|
||
/* Not implemented yet. Should not have been passed here yet. */
|
||
return hsmd_status_bad_request_fmt(client, msg, "Not implemented yet.");
|
||
|
||
case WIRE_HSMD_SIGN_INVOICE:
|
||
return handle_sign_invoice(client, msg);
|
||
case WIRE_HSMD_SIGN_BOLT12:
|
||
return handle_sign_bolt12(client, msg);
|
||
case WIRE_HSMD_SIGN_MESSAGE:
|
||
return handle_sign_message(client, msg);
|
||
|
||
case WIRE_HSMD_DEV_MEMLEAK:
|
||
case WIRE_HSMD_ECDH_RESP:
|
||
case WIRE_HSMD_CANNOUNCEMENT_SIG_REPLY:
|
||
case WIRE_HSMD_CUPDATE_SIG_REPLY:
|
||
case WIRE_HSMD_CLIENT_HSMFD_REPLY:
|
||
case WIRE_HSMD_NODE_ANNOUNCEMENT_SIG_REPLY:
|
||
case WIRE_HSMD_SIGN_WITHDRAWAL_REPLY:
|
||
case WIRE_HSMD_SIGN_INVOICE_REPLY:
|
||
case WIRE_HSMD_INIT_REPLY:
|
||
case WIRE_HSMSTATUS_CLIENT_BAD_REQUEST:
|
||
case WIRE_HSMD_SIGN_COMMITMENT_TX_REPLY:
|
||
case WIRE_HSMD_SIGN_TX_REPLY:
|
||
case WIRE_HSMD_GET_PER_COMMITMENT_POINT_REPLY:
|
||
case WIRE_HSMD_CHECK_FUTURE_SECRET_REPLY:
|
||
case WIRE_HSMD_GET_CHANNEL_BASEPOINTS_REPLY:
|
||
case WIRE_HSMD_DEV_MEMLEAK_REPLY:
|
||
case WIRE_HSMD_SIGN_MESSAGE_REPLY:
|
||
case WIRE_HSMD_GET_OUTPUT_SCRIPTPUBKEY_REPLY:
|
||
case WIRE_HSMD_SIGN_BOLT12_REPLY:
|
||
break;
|
||
}
|
||
return hsmd_status_bad_request(client, msg, "Unknown request");
|
||
}
|