libhsmd: Add dispatcher function

This commit is contained in:
Christian Decker 2021-04-20 22:41:33 +02:00 committed by Rusty Russell
parent ac836bbd1b
commit 3d959e128d
2 changed files with 165 additions and 49 deletions

View File

@ -1803,47 +1803,6 @@ static struct io_plan *handle_sign_node_announcement(struct io_conn *conn,
return req_reply(conn, c, take(reply)); return req_reply(conn, c, take(reply));
} }
/*~ 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 struct io_plan *handle_sign_message(struct io_conn *conn,
struct 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 bad_req(conn, 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 bad_req_fmt(conn, c, msg_in, "Failed to sign message");
}
return req_reply(conn, c,
take(towire_hsmd_sign_message_reply(NULL, &rsig)));
}
/*~ lightningd asks us to sign a bolt12 (e.g. offer). */ /*~ lightningd asks us to sign a bolt12 (e.g. offer). */
static struct io_plan *handle_sign_bolt12(struct io_conn *conn, static struct io_plan *handle_sign_bolt12(struct io_conn *conn,
@ -2050,7 +2009,10 @@ static struct io_plan *handle_client(struct io_conn *conn, struct client *c)
return handle_sign_mutual_close_tx(conn, c, c->msg_in); return handle_sign_mutual_close_tx(conn, c, c->msg_in);
case WIRE_HSMD_SIGN_MESSAGE: case WIRE_HSMD_SIGN_MESSAGE:
return handle_sign_message(conn, c, c->msg_in); /* Hand off to libhsmd for processing */
return req_reply(conn, c,
take(hsmd_handle_client_message(
tmpctx, c->hsmd_client, c->msg_in)));
case WIRE_HSMD_SIGN_BOLT12: case WIRE_HSMD_SIGN_BOLT12:
return handle_sign_bolt12(conn, c, c->msg_in); return handle_sign_bolt12(conn, c, c->msg_in);

View File

@ -1,3 +1,4 @@
#include <ccan/crypto/hkdf_sha256/hkdf_sha256.h>
#include <hsmd/capabilities.h> #include <hsmd/capabilities.h>
#include <hsmd/libhsmd.h> #include <hsmd/libhsmd.h>
@ -125,13 +126,12 @@ bool check_client_capabilities(struct hsmd_client *client, enum hsmd_wire t)
*/ */
/* This function is used to format an error message before passing it /* This function is used to format an error message before passing it
* to the library user specified hsmd_status_bad_request */ * to the library user specified hsmd_status_bad_request */
/* Temporarily pre-declare until we use the function, then we can make static u8 *hsmd_status_bad_request_fmt(struct hsmd_client *client,
* it static again. */ const u8 *msg, const char *fmt, ...)
u8 *hsmd_status_bad_request_fmt(struct hsmd_client *client, const u8 *msg, PRINTF_FMT(3, 4);
const char *fmt, ...) PRINTF_FMT(3, 4);
u8 *hsmd_status_bad_request_fmt(struct hsmd_client *client, const u8 *msg, static u8 *hsmd_status_bad_request_fmt(struct hsmd_client *client,
const char *fmt, ...) const u8 *msg, const char *fmt, ...)
{ {
va_list ap; va_list ap;
char *str; char *str;
@ -142,8 +142,162 @@ u8 *hsmd_status_bad_request_fmt(struct hsmd_client *client, const u8 *msg,
return hsmd_status_bad_request(client, msg, str); 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
}
/*~ 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);
}
u8 *hsmd_handle_client_message(const tal_t *ctx, struct hsmd_client *client, u8 *hsmd_handle_client_message(const tal_t *ctx, struct hsmd_client *client,
const u8 *msg) const u8 *msg)
{ {
return NULL; 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_INVOICE:
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:
case WIRE_HSMD_SIGN_BOLT12:
/* 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_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");
} }