mirror of
https://github.com/ElementsProject/lightning.git
synced 2025-01-18 21:35:11 +01:00
common/onion_message_parse: generic routine for parsing onion messages.
Instead of open coding in connectd/onion_message, we move it to common with a nice API. This lets us process the BOLT test vectors. Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This commit is contained in:
parent
5cf86a1a2e
commit
159fc7d1a2
@ -59,6 +59,7 @@ COMMON_SRC_NOGEN := \
|
||||
common/node_id.c \
|
||||
common/onion.c \
|
||||
common/onionreply.c \
|
||||
common/onion_message_parse.c \
|
||||
common/peer_billboard.c \
|
||||
common/peer_failed.c \
|
||||
common/peer_io.c \
|
||||
|
187
common/onion_message_parse.c
Normal file
187
common/onion_message_parse.c
Normal file
@ -0,0 +1,187 @@
|
||||
/* Caller does fromwire_onion_message(), this does the rest. */
|
||||
#include "config.h"
|
||||
#include <assert.h>
|
||||
#include <common/blindedpath.h>
|
||||
#include <common/ecdh.h>
|
||||
#include <common/onion_message_parse.h>
|
||||
#include <common/sphinx.h>
|
||||
#include <common/status.h>
|
||||
#include <common/type_to_string.h>
|
||||
#include <common/utils.h>
|
||||
#include <wire/onion_wire.h>
|
||||
#include <wire/peer_wire.h>
|
||||
|
||||
static bool decrypt_final_onionmsg(const tal_t *ctx,
|
||||
const struct pubkey *blinding,
|
||||
const struct secret *ss,
|
||||
const u8 *enctlv,
|
||||
const struct pubkey *my_id,
|
||||
struct pubkey *alias,
|
||||
struct secret **path_id)
|
||||
{
|
||||
struct tlv_encrypted_data_tlv *encmsg;
|
||||
|
||||
if (!blindedpath_get_alias(ss, my_id, alias))
|
||||
return false;
|
||||
|
||||
encmsg = decrypt_encrypted_data(tmpctx, blinding, ss, enctlv);
|
||||
if (!encmsg)
|
||||
return false;
|
||||
|
||||
if (tal_bytelen(encmsg->path_id) == sizeof(**path_id)) {
|
||||
*path_id = tal(ctx, struct secret);
|
||||
memcpy(*path_id, encmsg->path_id, sizeof(**path_id));
|
||||
} else
|
||||
*path_id = NULL;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool decrypt_forwarding_onionmsg(const struct pubkey *blinding,
|
||||
const struct secret *ss,
|
||||
const u8 *enctlv,
|
||||
struct pubkey *next_node,
|
||||
struct pubkey *next_blinding)
|
||||
{
|
||||
struct tlv_encrypted_data_tlv *encmsg;
|
||||
|
||||
encmsg = decrypt_encrypted_data(tmpctx, blinding, ss, enctlv);
|
||||
if (!encmsg)
|
||||
return false;
|
||||
|
||||
/* BOLT-onion-message #4:
|
||||
*
|
||||
* The reader:
|
||||
* - if it is not the final node according to the onion encryption:
|
||||
*...
|
||||
* - if the `enctlv` ... does not contain
|
||||
* `next_node_id`:
|
||||
* - MUST drop the message.
|
||||
*/
|
||||
if (!encmsg->next_node_id)
|
||||
return false;
|
||||
|
||||
/* BOLT-onion-message #4:
|
||||
* The reader:
|
||||
* - if it is not the final node according to the onion encryption:
|
||||
*...
|
||||
* - if the `enctlv` contains `path_id`:
|
||||
* - MUST drop the message.
|
||||
*/
|
||||
if (encmsg->path_id)
|
||||
return false;
|
||||
|
||||
*next_node = *encmsg->next_node_id;
|
||||
blindedpath_next_blinding(encmsg, blinding, ss, next_blinding);
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Returns false on failure */
|
||||
bool onion_message_parse(const tal_t *ctx,
|
||||
const u8 *onion_message_packet,
|
||||
const struct pubkey *blinding,
|
||||
const struct node_id *peer,
|
||||
const struct pubkey *me,
|
||||
u8 **next_onion_msg,
|
||||
struct pubkey *next_node_id,
|
||||
struct tlv_onionmsg_tlv **final_om,
|
||||
struct pubkey *final_alias,
|
||||
struct secret **final_path_id)
|
||||
{
|
||||
enum onion_wire badreason;
|
||||
struct onionpacket *op;
|
||||
struct pubkey ephemeral;
|
||||
struct route_step *rs;
|
||||
struct tlv_onionmsg_tlv *om;
|
||||
struct secret ss, onion_ss;
|
||||
const u8 *cursor;
|
||||
size_t max, maxlen;
|
||||
|
||||
/* We unwrap the onion now. */
|
||||
op = parse_onionpacket(tmpctx,
|
||||
onion_message_packet,
|
||||
tal_bytelen(onion_message_packet),
|
||||
&badreason);
|
||||
if (!op) {
|
||||
status_peer_debug(peer, "onion_message_parse: can't parse onionpacket: %s",
|
||||
onion_wire_name(badreason));
|
||||
return false;
|
||||
}
|
||||
|
||||
ephemeral = op->ephemeralkey;
|
||||
if (!unblind_onion(blinding, ecdh, &ephemeral, &ss)) {
|
||||
status_peer_debug(peer, "onion_message_parse: can't unblind onionpacket");
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Now get onion shared secret and parse it. */
|
||||
ecdh(&ephemeral, &onion_ss);
|
||||
rs = process_onionpacket(tmpctx, op, &onion_ss, NULL, 0, false);
|
||||
if (!rs) {
|
||||
status_peer_debug(peer,
|
||||
"onion_message_parse: can't process onionpacket ss=%s",
|
||||
type_to_string(tmpctx, struct secret, &onion_ss));
|
||||
return false;
|
||||
}
|
||||
|
||||
/* The raw payload is prepended with length in the modern onion. */
|
||||
cursor = rs->raw_payload;
|
||||
max = tal_bytelen(rs->raw_payload);
|
||||
maxlen = fromwire_bigsize(&cursor, &max);
|
||||
if (!cursor) {
|
||||
status_peer_debug(peer, "onion_message_parse: Invalid hop payload %s",
|
||||
tal_hex(tmpctx, rs->raw_payload));
|
||||
return false;
|
||||
}
|
||||
if (maxlen > max) {
|
||||
status_peer_debug(peer, "onion_message_parse: overlong hop payload %s",
|
||||
tal_hex(tmpctx, rs->raw_payload));
|
||||
return false;
|
||||
}
|
||||
|
||||
om = fromwire_tlv_onionmsg_tlv(tmpctx, &cursor, &maxlen);
|
||||
if (!om) {
|
||||
status_peer_debug(peer, "onion_message_parse: invalid onionmsg_tlv %s",
|
||||
tal_hex(tmpctx, rs->raw_payload));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (rs->nextcase == ONION_END) {
|
||||
*next_onion_msg = NULL;
|
||||
*final_om = tal_steal(ctx, om);
|
||||
/* Final enctlv is actually optional */
|
||||
if (!om->encrypted_recipient_data) {
|
||||
*final_alias = *me;
|
||||
*final_path_id = NULL;
|
||||
} else if (!decrypt_final_onionmsg(ctx, blinding, &ss,
|
||||
om->encrypted_recipient_data, me,
|
||||
final_alias,
|
||||
final_path_id)) {
|
||||
status_peer_debug(peer,
|
||||
"onion_message_parse: failed to decrypt encrypted_recipient_data"
|
||||
" %s", tal_hex(tmpctx, om->encrypted_recipient_data));
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
struct pubkey next_blinding;
|
||||
|
||||
*final_om = NULL;
|
||||
|
||||
/* This fails as expected if no enctlv. */
|
||||
if (!decrypt_forwarding_onionmsg(blinding, &ss, om->encrypted_recipient_data, next_node_id,
|
||||
&next_blinding)) {
|
||||
status_peer_debug(peer,
|
||||
"onion_message_parse: invalid encrypted_recipient_data %s",
|
||||
tal_hex(tmpctx, om->encrypted_recipient_data));
|
||||
return false;
|
||||
}
|
||||
|
||||
*next_onion_msg = towire_onion_message(ctx,
|
||||
&next_blinding,
|
||||
serialize_onionpacket(tmpctx, rs->next));
|
||||
}
|
||||
|
||||
/* Exactly one is set */
|
||||
assert(!*next_onion_msg + !*final_om == 1);
|
||||
return true;
|
||||
}
|
37
common/onion_message_parse.h
Normal file
37
common/onion_message_parse.h
Normal file
@ -0,0 +1,37 @@
|
||||
#ifndef LIGHTNING_COMMON_ONION_MESSAGE_PARSE_H
|
||||
#define LIGHTNING_COMMON_ONION_MESSAGE_PARSE_H
|
||||
#include "config.h"
|
||||
#include <bitcoin/privkey.h>
|
||||
#include <common/amount.h>
|
||||
|
||||
struct tlv_onionmsg_tlv;
|
||||
struct node_id;
|
||||
struct pubkey;
|
||||
|
||||
/**
|
||||
* onion_message_parse: core routine to check onion_message
|
||||
* @ctx: context to allocate @next_onion_msg or @final_om/@path_id off
|
||||
* @onion_message_packet: Sphinx-encrypted onion
|
||||
* @blinding: Blinding we were given for @onion_message_packet
|
||||
* @peer: node_id of peer (for status_peer_debug msgs)
|
||||
* @me: my pubkey
|
||||
* @next_onion_msg (out): set if we should forward, otherwise NULL.
|
||||
* @next_node_id (out): set to node id to fwd to, iff *@next_onion_msg.
|
||||
* @final_om (out): set if we're the final hop, otherwise NULL.
|
||||
* @final_alias (out): our alias (if *@final_om), or our own ID
|
||||
* @final_path_id (out): secret enclosed, if any (iff *@final_om).
|
||||
*
|
||||
* Returns false if it wasn't valid.
|
||||
*/
|
||||
bool onion_message_parse(const tal_t *ctx,
|
||||
const u8 *onion_message_packet,
|
||||
const struct pubkey *blinding,
|
||||
const struct node_id *peer,
|
||||
const struct pubkey *me,
|
||||
u8 **next_onion_msg,
|
||||
struct pubkey *next_node_id,
|
||||
struct tlv_onionmsg_tlv **final_om,
|
||||
struct pubkey *final_alias,
|
||||
struct secret **final_path_id);
|
||||
|
||||
#endif /* LIGHTNING_COMMON_ONION_MESSAGE_PARSE_H */
|
@ -62,6 +62,7 @@ CONNECTD_COMMON_OBJS := \
|
||||
common/node_id.o \
|
||||
common/onion.o \
|
||||
common/onionreply.o \
|
||||
common/onion_message_parse.o \
|
||||
common/ping.o \
|
||||
common/per_peer_state.o \
|
||||
common/psbt_open.o \
|
||||
|
@ -6,6 +6,7 @@
|
||||
#include <common/daemon_conn.h>
|
||||
#include <common/ecdh_hsmd.h>
|
||||
#include <common/features.h>
|
||||
#include <common/onion_message_parse.h>
|
||||
#include <common/sphinx.h>
|
||||
#include <common/status.h>
|
||||
#include <common/type_to_string.h>
|
||||
@ -35,84 +36,17 @@ void onionmsg_req(struct daemon *daemon, const u8 *msg)
|
||||
}
|
||||
}
|
||||
|
||||
static bool decrypt_final_onionmsg(const tal_t *ctx,
|
||||
const struct pubkey *blinding,
|
||||
const struct secret *ss,
|
||||
const u8 *enctlv,
|
||||
const struct pubkey *my_id,
|
||||
struct pubkey *alias,
|
||||
struct secret **path_id)
|
||||
{
|
||||
struct tlv_encrypted_data_tlv *encmsg;
|
||||
|
||||
if (!blindedpath_get_alias(ss, my_id, alias))
|
||||
return false;
|
||||
|
||||
encmsg = decrypt_encrypted_data(tmpctx, blinding, ss, enctlv);
|
||||
if (!encmsg)
|
||||
return false;
|
||||
|
||||
if (tal_bytelen(encmsg->path_id) == sizeof(**path_id)) {
|
||||
*path_id = tal(ctx, struct secret);
|
||||
memcpy(*path_id, encmsg->path_id, sizeof(**path_id));
|
||||
} else
|
||||
*path_id = NULL;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool decrypt_forwarding_onionmsg(const struct pubkey *blinding,
|
||||
const struct secret *ss,
|
||||
const u8 *enctlv,
|
||||
struct pubkey *next_node,
|
||||
struct pubkey *next_blinding)
|
||||
{
|
||||
struct tlv_encrypted_data_tlv *encmsg;
|
||||
|
||||
encmsg = decrypt_encrypted_data(tmpctx, blinding, ss, enctlv);
|
||||
if (!encmsg)
|
||||
return false;
|
||||
|
||||
/* BOLT-onion-message #4:
|
||||
*
|
||||
* The reader:
|
||||
* - if it is not the final node according to the onion encryption:
|
||||
*...
|
||||
* - if the `enctlv` ... does not contain
|
||||
* `next_node_id`:
|
||||
* - MUST drop the message.
|
||||
*/
|
||||
if (!encmsg->next_node_id)
|
||||
return false;
|
||||
|
||||
/* BOLT-onion-message #4:
|
||||
* The reader:
|
||||
* - if it is not the final node according to the onion encryption:
|
||||
*...
|
||||
* - if the `enctlv` contains `path_id`:
|
||||
* - MUST drop the message.
|
||||
*/
|
||||
if (encmsg->path_id)
|
||||
return false;
|
||||
|
||||
*next_node = *encmsg->next_node_id;
|
||||
blindedpath_next_blinding(encmsg, blinding, ss, next_blinding);
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Peer sends an onion msg. */
|
||||
void handle_onion_message(struct daemon *daemon,
|
||||
struct peer *peer, const u8 *msg)
|
||||
{
|
||||
enum onion_wire badreason;
|
||||
struct onionpacket *op;
|
||||
struct pubkey blinding, ephemeral;
|
||||
struct route_step *rs;
|
||||
struct pubkey blinding;
|
||||
u8 *onion;
|
||||
struct tlv_onionmsg_tlv *om;
|
||||
struct secret ss, onion_ss;
|
||||
const u8 *cursor;
|
||||
size_t max, maxlen;
|
||||
u8 *next_onion_msg;
|
||||
struct pubkey next_node;
|
||||
struct tlv_onionmsg_tlv *final_om;
|
||||
struct pubkey final_alias;
|
||||
struct secret *final_path_id;
|
||||
|
||||
/* Ignore unless explicitly turned on. */
|
||||
if (!feature_offered(daemon->our_features->bits[NODE_ANNOUNCE_FEATURE],
|
||||
@ -127,91 +61,28 @@ void handle_onion_message(struct daemon *daemon,
|
||||
return;
|
||||
}
|
||||
|
||||
/* We unwrap the onion now. */
|
||||
op = parse_onionpacket(tmpctx, onion, tal_bytelen(onion), &badreason);
|
||||
if (!op) {
|
||||
status_peer_debug(&peer->id, "onion msg: can't parse onionpacket: %s",
|
||||
onion_wire_name(badreason));
|
||||
if (!onion_message_parse(tmpctx, onion, &blinding, &peer->id,
|
||||
&daemon->mykey,
|
||||
&next_onion_msg, &next_node,
|
||||
&final_om, &final_alias, &final_path_id))
|
||||
return;
|
||||
}
|
||||
|
||||
ephemeral = op->ephemeralkey;
|
||||
if (!unblind_onion(&blinding, ecdh, &ephemeral, &ss)) {
|
||||
status_peer_debug(&peer->id, "onion msg: can't unblind onionpacket");
|
||||
return;
|
||||
}
|
||||
|
||||
/* Now get onion shared secret and parse it. */
|
||||
ecdh(&ephemeral, &onion_ss);
|
||||
rs = process_onionpacket(tmpctx, op, &onion_ss, NULL, 0, false);
|
||||
if (!rs) {
|
||||
status_peer_debug(&peer->id,
|
||||
"onion msg: can't process onionpacket ss=%s",
|
||||
type_to_string(tmpctx, struct secret, &onion_ss));
|
||||
return;
|
||||
}
|
||||
|
||||
/* The raw payload is prepended with length in the modern onion. */
|
||||
cursor = rs->raw_payload;
|
||||
max = tal_bytelen(rs->raw_payload);
|
||||
maxlen = fromwire_bigsize(&cursor, &max);
|
||||
if (!cursor) {
|
||||
status_peer_debug(&peer->id, "onion msg: Invalid hop payload %s",
|
||||
tal_hex(tmpctx, rs->raw_payload));
|
||||
return;
|
||||
}
|
||||
if (maxlen > max) {
|
||||
status_peer_debug(&peer->id, "onion msg: overlong hop payload %s",
|
||||
tal_hex(tmpctx, rs->raw_payload));
|
||||
return;
|
||||
}
|
||||
|
||||
om = fromwire_tlv_onionmsg_tlv(msg, &cursor, &maxlen);
|
||||
if (!om) {
|
||||
status_peer_debug(&peer->id, "onion msg: invalid onionmsg_tlv %s",
|
||||
tal_hex(tmpctx, rs->raw_payload));
|
||||
return;
|
||||
}
|
||||
|
||||
if (rs->nextcase == ONION_END) {
|
||||
struct pubkey alias;
|
||||
struct secret *self_id;
|
||||
if (final_om) {
|
||||
u8 *omsg;
|
||||
|
||||
/* Final enctlv is actually optional */
|
||||
if (!om->encrypted_recipient_data) {
|
||||
alias = daemon->mykey;
|
||||
self_id = NULL;
|
||||
} else if (!decrypt_final_onionmsg(tmpctx, &blinding, &ss,
|
||||
om->encrypted_recipient_data, &daemon->mykey, &alias,
|
||||
&self_id)) {
|
||||
status_peer_debug(&peer->id,
|
||||
"onion msg: failed to decrypt encrypted_recipient_data"
|
||||
" %s", tal_hex(tmpctx, om->encrypted_recipient_data));
|
||||
return;
|
||||
}
|
||||
|
||||
/* We re-marshall here by policy, before handing to lightningd */
|
||||
omsg = tal_arr(tmpctx, u8, 0);
|
||||
towire_tlvstream_raw(&omsg, om->fields);
|
||||
towire_tlvstream_raw(&omsg, final_om->fields);
|
||||
daemon_conn_send(daemon->master,
|
||||
take(towire_connectd_got_onionmsg_to_us(NULL,
|
||||
&alias, self_id,
|
||||
om->reply_path,
|
||||
&final_alias, final_path_id,
|
||||
final_om->reply_path,
|
||||
omsg)));
|
||||
} else {
|
||||
struct pubkey next_node, next_blinding;
|
||||
struct peer *next_peer;
|
||||
struct node_id next_node_id;
|
||||
struct peer *next_peer;
|
||||
|
||||
/* This fails as expected if no enctlv. */
|
||||
if (!decrypt_forwarding_onionmsg(&blinding, &ss, om->encrypted_recipient_data, &next_node,
|
||||
&next_blinding)) {
|
||||
status_peer_debug(&peer->id,
|
||||
"onion msg: invalid encrypted_recipient_data %s",
|
||||
tal_hex(tmpctx, om->encrypted_recipient_data));
|
||||
return;
|
||||
}
|
||||
assert(next_onion_msg);
|
||||
|
||||
/* FIXME: Handle short_channel_id! */
|
||||
node_id_from_pubkey(&next_node_id, &next_node);
|
||||
@ -224,10 +95,7 @@ void handle_onion_message(struct daemon *daemon,
|
||||
&next_node));
|
||||
return;
|
||||
}
|
||||
inject_peer_msg(next_peer,
|
||||
take(towire_onion_message(NULL,
|
||||
&next_blinding,
|
||||
serialize_onionpacket(tmpctx, rs->next))));
|
||||
inject_peer_msg(next_peer, take(next_onion_msg));
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user