Skip to content

Commit

Permalink
common/onion_message_parse: generic routine for parsing onion messages.
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
rustyrussell committed Oct 4, 2022
1 parent 0e8414d commit b73647c
Show file tree
Hide file tree
Showing 5 changed files with 244 additions and 150 deletions.
1 change: 1 addition & 0 deletions common/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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 \
Expand Down
187 changes: 187 additions & 0 deletions common/onion_message_parse.c
Original file line number Diff line number Diff line change
@@ -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 changes: 37 additions & 0 deletions common/onion_message_parse.h
Original file line number Diff line number Diff line change
@@ -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 */
1 change: 1 addition & 0 deletions connectd/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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 \
Expand Down
Loading

0 comments on commit b73647c

Please sign in to comment.