diff --git a/doc/PLUGINS.md b/doc/PLUGINS.md index fc194f5ff..a6f94a77c 100644 --- a/doc/PLUGINS.md +++ b/doc/PLUGINS.md @@ -1508,28 +1508,34 @@ type prefix, since c-lightning does not know how to parse the message. Because this is a chained hook, the daemon expects the result to be `{'result': 'continue'}`. It will fail if something else is returned. -### `onion_message` and `onion_message_blinded` +### `onion_message`, `onion_message_blinded` and `onion_message_ourpath` **(WARNING: experimental-offers only)** -These two hooks are almost identical, in that they are called when an -onion message is received. The former is only used for unblinded -messages (where the source knows that it is sending to this node), and -the latter for blinded messages (where the source doesn't know that -this node is the destination). The latter hook will have a -"blinding_in" field, the former never will. +These three hooks are almost identical, in that they are called when +an onion message is received. The `onion_message` hook is only used +for obsolete unblinded messages, and can be ignored for modern usage. -These hooks are separate, because blinded messages must ensure the -sender used the correct "blinding_in", otherwise it should ignore the -message: this avoids the source trying to probe for responses without -using the designated delivery path. +`onion_message_blinded` is used for unsolicited messages (where the +source knows that it is sending to this node), and +`onion_message_ourpath` is used for messages which use a blinded path +we supplied (where the source doesn't know that this node is the +destination). The latter hook will have a `our_alias` field, the +former never will. + +These hooks are separate, because replies MUST be ignored unless they +use the correct path (i.e. `onion_message_ourpath`, with the expected +`our_alias`). This avoids the source trying to probe for responses +without using the designated delivery path. The payload for a call follows this format: ```json { "onion_message": { - "blinding_in": "02df5ffe895c778e10f7742a6c5b8a0cefbe9465df58b92fadeb883752c8107c8f", + "our_alias": "02df5ffe895c778e10f7742a6c5b8a0cefbe9465df58b92fadeb883752c8107c8f", + "reply_first_node": "02df5ffe895c778e10f7742a6c5b8a0cefbe9465df58b92fadeb883752c8107c8f", + "reply_blinding": "02df5ffe895c778e10f7742a6c5b8a0cefbe9465df58b92fadeb883752c8107c8f", "reply_path": [ {"id": "02df5ffe895c778e10f7742a6c5b8a0cefbe9465df58b92fadeb883752c8107c8f", "enctlv": "0a020d0d", "blinding": "02df5ffe895c778e10f7742a6c5b8a0cefbe9465df58b92fadeb883752c8107c8f"} ], diff --git a/gossipd/gossipd_wire.csv b/gossipd/gossipd_wire.csv index 636eac46a..bb8412776 100644 --- a/gossipd/gossipd_wire.csv +++ b/gossipd/gossipd_wire.csv @@ -104,7 +104,8 @@ msgdata,gossipd_got_obs_onionmsg_forward,next_onion_len,u16, msgdata,gossipd_got_obs_onionmsg_forward,next_onion,u8,next_onion_len msgtype,gossipd_got_onionmsg_to_us,3145 -msgdata,gossipd_got_onionmsg_to_us,blinding_in,pubkey, +msgdata,gossipd_got_onionmsg_to_us,node_alias,pubkey, +msgdata,gossipd_got_onionmsg_to_us,self_id,?secret, msgdata,gossipd_got_onionmsg_to_us,reply_blinding,?pubkey, msgdata,gossipd_got_onionmsg_to_us,reply_first_node,?pubkey, msgdata,gossipd_got_onionmsg_to_us,reply_path_len,u16, diff --git a/lightningd/onion_message.c b/lightningd/onion_message.c index c807b519f..1161d1c0d 100644 --- a/lightningd/onion_message.c +++ b/lightningd/onion_message.c @@ -1,3 +1,4 @@ +#include #include #include #include @@ -14,10 +15,15 @@ #include struct onion_message_hook_payload { + /* Pre-spec or modern? */ + bool obsolete; + /* Optional */ - struct pubkey *blinding_in; + struct pubkey *blinding_in; /* obsolete only */ struct pubkey *reply_blinding; struct onionmsg_path **reply_path; + struct pubkey *reply_first_node; /* non-obsolete only */ + struct pubkey *our_alias; /* non-obsolete only */ struct tlv_onionmsg_payload *om; }; @@ -47,9 +53,19 @@ static void onion_message_serialize(struct onion_message_hook_payload *payload, struct plugin *plugin) { json_object_start(stream, "onion_message"); + json_add_bool(stream, "obsolete", payload->obsolete); if (payload->blinding_in) json_add_pubkey(stream, "blinding_in", payload->blinding_in); - if (payload->reply_path) { + if (payload->our_alias) + json_add_pubkey(stream, "our_alias", payload->our_alias); + + /* Modern style. */ + if (payload->reply_first_node) { + json_add_blindedpath(stream, "reply_blindedpath", + payload->reply_blinding, + payload->reply_first_node, + payload->reply_path); + } else if (payload->reply_path) { json_array_start(stream, "reply_path"); for (size_t i = 0; i < tal_count(payload->reply_path); i++) { json_object_start(stream, NULL); @@ -113,6 +129,12 @@ REGISTER_PLUGIN_HOOK(onion_message_blinded, onion_message_serialize, struct onion_message_hook_payload *); +REGISTER_PLUGIN_HOOK(onion_message_ourpath, + plugin_hook_continue, + onion_message_hook_cb, + onion_message_serialize, + struct onion_message_hook_payload *); + void handle_obs_onionmsg_to_us(struct lightningd *ld, const u8 *msg) { struct onion_message_hook_payload *payload; @@ -121,7 +143,10 @@ void handle_obs_onionmsg_to_us(struct lightningd *ld, const u8 *msg) const u8 *subptr; payload = tal(ld, struct onion_message_hook_payload); + payload->obsolete = true; + payload->reply_first_node = NULL; payload->om = tlv_onionmsg_payload_new(payload); + payload->our_alias = NULL; if (!fromwire_gossipd_got_obs_onionmsg_to_us(payload, msg, &payload->blinding_in, @@ -197,8 +222,62 @@ void handle_obs_onionmsg_forward(struct lightningd *ld, const u8 *msg) void handle_onionmsg_to_us(struct lightningd *ld, const u8 *msg) { - /* FIXME! */ - return; + struct onion_message_hook_payload *payload; + u8 *submsg; + struct secret *self_id; + size_t submsglen; + const u8 *subptr; + + payload = tal(ld, struct onion_message_hook_payload); + payload->obsolete = false; + payload->om = tlv_onionmsg_payload_new(payload); + payload->blinding_in = NULL; + payload->our_alias = tal(payload, struct pubkey); + + if (!fromwire_gossipd_got_onionmsg_to_us(payload, msg, + payload->our_alias, + &self_id, + &payload->reply_blinding, + &payload->reply_first_node, + &payload->reply_path, + &submsg)) { + log_broken(ld->log, "bad got_onionmsg_tous: %s", + tal_hex(tmpctx, msg)); + return; + } + + /* If there's no self_id, or it's not correct, ignore alias: alias + * means we created the path it's using. */ + if (!self_id || !secret_eq_consttime(self_id, &ld->onion_reply_secret)) + payload->our_alias = tal_free(payload->our_alias); + + submsglen = tal_bytelen(submsg); + subptr = submsg; + if (!fromwire_onionmsg_payload(&subptr, + &submsglen, payload->om)) { + tal_free(payload); + log_broken(ld->log, "bad got_onionmsg_tous om: %s", + tal_hex(tmpctx, msg)); + return; + } + tal_free(submsg); + + /* Make sure gossipd gets this right. */ + if (payload->reply_path + && (!payload->reply_blinding || !payload->reply_first_node)) { + log_broken(ld->log, + "No reply blinding/first_node, ignoring reply path"); + payload->reply_path = tal_free(payload->reply_path); + } + + log_debug(ld->log, "Got onionmsg%s%s", + payload->our_alias ? " via-ourpath": "", + payload->reply_path ? " reply_path": ""); + + if (payload->our_alias) + plugin_hook_call_onion_message_ourpath(ld, payload); + else + plugin_hook_call_onion_message_blinded(ld, payload); } struct hop {