From dd690553b8f0cd0986abe6aa235af0586a69a001 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 1 Apr 2020 14:23:09 +1030 Subject: [PATCH] channeld: handle onion messages. We do most of the decoding here, and just hand the results to lightningd. Signed-off-by: Rusty Russell --- channeld/channel_wire.csv | 17 ++++ channeld/channeld.c | 146 ++++++++++++++++++++++++++++++++++- lightningd/channel_control.c | 7 +- tools/generate-wire.py | 1 + 4 files changed, 169 insertions(+), 2 deletions(-) diff --git a/channeld/channel_wire.csv b/channeld/channel_wire.csv index 20e270661..c7505f5af 100644 --- a/channeld/channel_wire.csv +++ b/channeld/channel_wire.csv @@ -208,3 +208,20 @@ msgdata,channel_send_error,reason,wirestring, # Tell master channeld has sent the error message. msgtype,channel_send_error_reply,1108 + +# Tell lightningd we got a onion message (for us, or to fwd) +msgtype,got_onionmsg_to_us,1142 +msgdata,got_onionmsg_to_us,reply_blinding,?pubkey, +msgdata,got_onionmsg_to_us,reply_path_len,u16, +msgdata,got_onionmsg_to_us,reply_path,onionmsg_path,reply_path_len + +msgtype,got_onionmsg_forward,1143 +msgdata,got_onionmsg_forward,next_scid,?short_channel_id, +msgdata,got_onionmsg_forward,next_node_id,?node_id, +msgdata,got_onionmsg_forward,next_blinding,?pubkey, +msgdata,got_onionmsg_forward,next_onion,u8,1366 + +# Lightningd tells us to send a onion message. +msgtype,send_onionmsg,1040 +msgdata,send_onionmsg,onion,u8,1366 +msgdata,send_onionmsg,blinding,?pubkey, diff --git a/channeld/channeld.c b/channeld/channeld.c index e3cba8f45..cbb11862d 100644 --- a/channeld/channeld.c +++ b/channeld/channeld.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -1621,6 +1622,138 @@ static bool channeld_handle_custommsg(const u8 *msg) #endif } +#if EXPERIMENTAL_FEATURES +/* Peer sends onion msg. */ +static void handle_onion_message(struct peer *peer, const u8 *msg) +{ + enum onion_type badreason; + struct onionpacket op; + struct secret ss; + struct route_step *rs; + u8 onion[TOTAL_PACKET_SIZE]; + const u8 *cursor; + size_t max, maxlen; + struct tlv_onionmsg_payload *om; + const struct short_channel_id *next_scid; + struct node_id *next_node; + struct tlv_onion_message_tlvs *tlvs = tlv_onion_message_tlvs_new(msg); + + if (!fromwire_onion_message(msg, onion, tlvs)) + peer_failed(peer->pps, + &peer->channel_id, + "Bad onion_message %s", tal_hex(peer, msg)); + + if (tlvs->blinding) { + status_broken("FIXME: Handle blinding!"); + return; + } + + /* We unwrap the onion now. */ + badreason = parse_onionpacket(onion, TOTAL_PACKET_SIZE, &op); + if (badreason != 0) { + status_debug("onion msg: can't parse onionpacket: %s", + onion_type_name(badreason)); + return; + } + + /* Because wire takes struct pubkey. */ + msg = hsm_req(tmpctx, towire_hsm_ecdh_req(tmpctx, &op.ephemeralkey)); + if (!fromwire_hsm_ecdh_resp(msg, &ss)) + status_failed(STATUS_FAIL_HSM_IO, "Reading ecdh response"); + + /* We make sure we can parse onion packet, so we know if shared secret + * is actually valid (this checks hmac). */ + rs = process_onionpacket(tmpctx, &op, &ss, NULL, 0, false); + if (!rs) { + status_debug("onion msg: can't process onionpacket ss=%s", + type_to_string(tmpctx, struct secret, &ss)); + return; + } + + /* The raw payload is prepended with length in the TLV world. */ + cursor = rs->raw_payload; + max = tal_bytelen(rs->raw_payload); + maxlen = fromwire_bigsize(&cursor, &max); + if (!cursor) { + status_debug("onion msg: Invalid hop payload %s", + tal_hex(tmpctx, rs->raw_payload)); + return; + } + if (maxlen > max) { + status_debug("onion msg: overlong hop payload %s", + tal_hex(tmpctx, rs->raw_payload)); + return; + } + + om = tlv_onionmsg_payload_new(msg); + if (!fromwire_onionmsg_payload(&cursor, &maxlen, om)) { + status_debug("onion msg: invalid onionmsg_payload %s", + tal_hex(tmpctx, rs->raw_payload)); + return; + } + + if (om->next_short_channel_id) + next_scid = &om->next_short_channel_id->short_channel_id; + else + next_scid = NULL; + + if (om->next_node_id) { + next_node = tal(msg, struct node_id); + node_id_from_pubkey(next_node, &om->next_node_id->node_id); + } else + next_node = NULL; + + if (om->enctlv) { + status_broken("FIXME: Handle enctlv!"); + return; + } + + if (rs->nextcase == ONION_END) { + /* FIXME: Handle reply_path */ + /* payload may not be valid, so we hand it pre-decrypted to lightningd */ + wire_sync_write(MASTER_FD, + take(towire_got_onionmsg_to_us(NULL, + NULL, + NULL))); + } else { + /* This *MUST* have instructions on where to go next. */ + if (!next_scid && !next_node) { + status_debug("onion msg: no next field in %s", + tal_hex(tmpctx, rs->raw_payload)); + return; + } + + wire_sync_write(MASTER_FD, + take(towire_got_onionmsg_forward(NULL, + next_scid, + next_node, + NULL, + serialize_onionpacket(tmpctx, rs->next)))); + } +} + +/* We send onion msg. */ +static void send_onionmsg(struct peer *peer, const u8 *msg) +{ + u8 onion_routing_packet[TOTAL_PACKET_SIZE]; + struct pubkey *blinding; + struct tlv_onion_message_tlvs *tlvs = tlv_onion_message_tlvs_new(msg); + + if (!fromwire_send_onionmsg(msg, msg, onion_routing_packet, &blinding)) + master_badmsg(WIRE_SEND_ONIONMSG, msg); + + if (blinding) { + tlvs->blinding = tal(tlvs, + struct tlv_onion_message_tlvs_blinding); + tlvs->blinding->blinding = *blinding; + } + sync_crypto_write(peer->pps, + take(towire_onion_message(NULL, + onion_routing_packet, + tlvs))); +} +#endif /* EXPERIMENTAL_FEATURES */ + static void peer_in(struct peer *peer, const u8 *msg) { enum wire_type type = fromwire_peektype(msg); @@ -1694,7 +1827,8 @@ static void peer_in(struct peer *peer, const u8 *msg) return; #if EXPERIMENTAL_FEATURES case WIRE_ONION_MESSAGE: - break; + handle_onion_message(peer, msg); + return; #endif case WIRE_INIT: @@ -2684,6 +2818,14 @@ static void req_in(struct peer *peer, const u8 *msg) case WIRE_CHANNEL_SEND_ERROR: handle_send_error(peer, msg); return; +#if EXPERIMENTAL_FEATURES + case WIRE_SEND_ONIONMSG: + send_onionmsg(peer, msg); + return; +#else + case WIRE_SEND_ONIONMSG: + break; +#endif /* !EXPERIMENTAL_FEATURES */ #if DEVELOPER case WIRE_CHANNEL_DEV_REENABLE_COMMIT: handle_dev_reenable_commit(peer); @@ -2711,6 +2853,8 @@ static void req_in(struct peer *peer, const u8 *msg) case WIRE_CHANNEL_FAIL_FALLEN_BEHIND: case WIRE_CHANNEL_DEV_MEMLEAK_REPLY: case WIRE_CHANNEL_SEND_ERROR_REPLY: + case WIRE_GOT_ONIONMSG_TO_US: + case WIRE_GOT_ONIONMSG_FORWARD: break; } diff --git a/lightningd/channel_control.c b/lightningd/channel_control.c index aeac9f14a..b9d88c8c9 100644 --- a/lightningd/channel_control.c +++ b/lightningd/channel_control.c @@ -319,6 +319,10 @@ static unsigned channel_msg(struct subd *sd, const u8 *msg, const int *fds) case WIRE_CHANNEL_SEND_ERROR_REPLY: handle_error_channel(sd->channel, msg); break; + case WIRE_GOT_ONIONMSG_TO_US: + case WIRE_GOT_ONIONMSG_FORWARD: + /* FIXME */ + break; /* And we never get these from channeld. */ case WIRE_CHANNEL_INIT: @@ -334,7 +338,8 @@ static unsigned channel_msg(struct subd *sd, const u8 *msg, const int *fds) case WIRE_CHANNEL_FEERATES: case WIRE_CHANNEL_SPECIFIC_FEERATES: case WIRE_CHANNEL_DEV_MEMLEAK: - /* Replies go to requests. */ + case WIRE_SEND_ONIONMSG: + /* Replies go to requests. */ case WIRE_CHANNEL_OFFER_HTLC_REPLY: case WIRE_CHANNEL_DEV_REENABLE_COMMIT_REPLY: case WIRE_CHANNEL_DEV_MEMLEAK_REPLY: diff --git a/tools/generate-wire.py b/tools/generate-wire.py index 547c769ef..2c2958e19 100755 --- a/tools/generate-wire.py +++ b/tools/generate-wire.py @@ -226,6 +226,7 @@ class Type(FieldSet): 'onionreply', 'witscript', 'feature_set', + 'onionmsg_path', ] # Some BOLT types are re-typed based on their field name