mirror of
https://github.com/ElementsProject/lightning.git
synced 2025-01-18 05:12:45 +01:00
libhsmd: Add dispatcher function
This commit is contained in:
parent
ac836bbd1b
commit
3d959e128d
46
hsmd/hsmd.c
46
hsmd/hsmd.c
@ -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);
|
||||||
|
168
hsmd/libhsmd.c
168
hsmd/libhsmd.c
@ -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");
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user