lightningd: handle modern onion termination.

This adds a new hook: onion_message_ourpath for when we know a message
came in via a blinded path we created.  The onion_message_blinded hook
is now called for all other messages, since all messages are now
blinded.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This commit is contained in:
Rusty Russell
2021-10-01 06:49:36 +09:30
committed by Christian Decker
parent 89d143bc63
commit f9a21d9fc9
3 changed files with 103 additions and 17 deletions

View File

@@ -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"} ],

View File

@@ -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,
1 #include <common/cryptomsg.h>
104 # Lightningd tells us to inject a gossip message (for addgossip RPC) msgdata,gossipd_send_onionmsg,blinding,pubkey,
105 msgtype,gossipd_addgossip,3044 # Lightningd tells us to inject a gossip message (for addgossip RPC)
106 msgdata,gossipd_addgossip,len,u16, msgtype,gossipd_addgossip,3044
107 msgdata,gossipd_addgossip,msg,u8,len msgdata,gossipd_addgossip,len,u16,
108 msgdata,gossipd_addgossip,msg,u8,len
109 # Empty string means no problem.
110 msgtype,gossipd_addgossip_reply,3144
111 msgdata,gossipd_addgossip_reply,err,wirestring,

View File

@@ -1,3 +1,4 @@
#include <ccan/mem/mem.h>
#include <common/blindedpath.h>
#include <common/json_command.h>
#include <common/json_helpers.h>
@@ -14,10 +15,15 @@
#include <sodium/randombytes.h>
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 {