From 6c8c6a7c7d33e7e94dba401bc2a490ce8ef42d44 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Fri, 1 Oct 2021 06:49:36 +0930 Subject: [PATCH] gossipd: handle modern onion messages. Signed-off-by: Rusty Russell --- gossipd/Makefile | 1 + gossipd/gossipd.c | 144 +++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 144 insertions(+), 1 deletion(-) diff --git a/gossipd/Makefile b/gossipd/Makefile index 75b872447..465c4bb6f 100644 --- a/gossipd/Makefile +++ b/gossipd/Makefile @@ -35,6 +35,7 @@ GOSSIPD_COMMON_OBJS := \ common/bigsize.o \ common/bip32.o \ common/blinding.o \ + common/blindedpath.o \ common/channel_id.o \ common/crypto_state.o \ common/cryptomsg.o \ diff --git a/gossipd/gossipd.c b/gossipd/gossipd.c index 1af3977cc..e115ec384 100644 --- a/gossipd/gossipd.c +++ b/gossipd/gossipd.c @@ -12,6 +12,7 @@ */ #include #include +#include #include #include #include @@ -612,7 +613,148 @@ static u8 *handle_obs_onion_message(struct peer *peer, const u8 *msg) /* Peer sends onion msg. */ static u8 *handle_onion_message(struct peer *peer, const u8 *msg) { - /* FIXME */ + enum onion_wire badreason; + struct onionpacket *op; + struct pubkey blinding, ephemeral; + struct route_step *rs; + u8 *onion; + struct tlv_onionmsg_payload *om; + struct secret ss, onion_ss; + const u8 *cursor; + size_t max, maxlen; + + /* Ignore unless explicitly turned on. */ + if (!feature_offered(peer->daemon->our_features->bits[NODE_ANNOUNCE_FEATURE], + OPT_ONION_MESSAGES)) + return NULL; + + /* FIXME: ratelimit! */ + if (!fromwire_onion_message(msg, msg, &blinding, &onion)) + return towire_warningfmt(peer, NULL, "Bad onion_message"); + + /* 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)); + return NULL; + } + + ephemeral = op->ephemeralkey; + if (!unblind_onion(&blinding, ecdh, &ephemeral, &ss)) { + status_peer_debug(&peer->id, "onion msg: can't unblind onionpacket"); + return NULL; + } + + /* 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 NULL; + } + + /* 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 NULL; + } + if (maxlen > max) { + status_peer_debug(&peer->id, "onion msg: overlong hop payload %s", + tal_hex(tmpctx, rs->raw_payload)); + return NULL; + } + + om = tlv_onionmsg_payload_new(msg); + if (!fromwire_onionmsg_payload(&cursor, &maxlen, om)) { + status_peer_debug(&peer->id, "onion msg: invalid onionmsg_payload %s", + tal_hex(tmpctx, rs->raw_payload)); + return NULL; + } + + if (rs->nextcase == ONION_END) { + struct pubkey *reply_blinding, *first_node_id, me, alias; + const struct onionmsg_path **reply_path; + struct secret *self_id; + u8 *omsg; + + if (!pubkey_from_node_id(&me, &peer->daemon->id)) { + status_broken("Failed to convert own id"); + return NULL; + } + + /* Final enctlv is actually optional */ + if (!om->enctlv) { + alias = me; + self_id = NULL; + } else if (!decrypt_final_enctlv(tmpctx, &blinding, &ss, + om->enctlv, &me, &alias, + &self_id)) { + status_peer_debug(&peer->id, + "onion msg: failed to decrypt enctlv" + " %s", tal_hex(tmpctx, om->enctlv)); + return NULL; + } + + if (om->reply_path) { + first_node_id = &om->reply_path->first_node_id; + reply_blinding = &om->reply_path->blinding; + reply_path = cast_const2(const struct onionmsg_path **, + om->reply_path->path); + } else { + first_node_id = NULL; + reply_blinding = NULL; + reply_path = NULL; + } + + /* We re-marshall here by policy, before handing to lightningd */ + omsg = tal_arr(tmpctx, u8, 0); + towire_tlvstream_raw(&omsg, om->fields); + daemon_conn_send(peer->daemon->master, + take(towire_gossipd_got_onionmsg_to_us(NULL, + &alias, self_id, + reply_blinding, + first_node_id, + reply_path, + omsg))); + } else { + struct pubkey next_node, next_blinding; + struct peer *next_peer; + struct node_id next_node_id; + + /* This fails as expected if no enctlv. */ + if (!decrypt_enctlv(&blinding, &ss, om->enctlv, &next_node, + &next_blinding)) { + status_peer_debug(&peer->id, + "onion msg: invalid enctlv %s", + tal_hex(tmpctx, om->enctlv)); + return NULL; + } + + /* Even though lightningd checks for valid ids, there's a race + * where it might vanish before we read this command. */ + node_id_from_pubkey(&next_node_id, &next_node); + next_peer = find_peer(peer->daemon, &next_node_id); + if (!next_peer) { + status_peer_debug(&peer->id, + "onion msg: unknown next peer %s", + type_to_string(tmpctx, + struct pubkey, + &next_node)); + return NULL; + } + queue_peer_msg(next_peer, + take(towire_onion_message(NULL, + &next_blinding, + serialize_onionpacket(tmpctx, rs->next)))); + } + return NULL; }