From b8498b60f2e5d78bfec295d77a8f0c60b8a767c6 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 22 Sep 2021 06:47:30 +0930 Subject: [PATCH] lightningd: `blindedpath` helper to create a blinded path to ourselves. Currently it will be used for onion replies, but we can use it for offers and invoices in future, if we want to avoid revealing our node_id. Signed-off-by: Rusty Russell --- lightningd/Makefile | 1 + lightningd/onion_message.c | 122 +++++++++++++++++++++++++++++++++++++ 2 files changed, 123 insertions(+) diff --git a/lightningd/Makefile b/lightningd/Makefile index 63b6aa2be7d3..f04bcf183049 100644 --- a/lightningd/Makefile +++ b/lightningd/Makefile @@ -70,6 +70,7 @@ LIGHTNINGD_COMMON_OBJS := \ common/bech32_util.o \ common/bigsize.o \ common/bip32.o \ + common/blindedpath.o \ common/blinding.o \ common/blockheight_states.o \ common/bolt11.o \ diff --git a/lightningd/onion_message.c b/lightningd/onion_message.c index 98d0b7640d4a..c807b519f488 100644 --- a/lightningd/onion_message.c +++ b/lightningd/onion_message.c @@ -1,3 +1,4 @@ +#include #include #include #include @@ -10,6 +11,7 @@ #include #include #include +#include struct onion_message_hook_payload { /* Optional */ @@ -20,6 +22,26 @@ struct onion_message_hook_payload { struct tlv_onionmsg_payload *om; }; +static void json_add_blindedpath(struct json_stream *stream, + const char *fieldname, + const struct pubkey *blinding, + const struct pubkey *first_node_id, + struct onionmsg_path **path) +{ + json_object_start(stream, fieldname); + json_add_pubkey(stream, "blinding", blinding); + json_add_pubkey(stream, "first_node_id", first_node_id); + json_array_start(stream, "hops"); + for (size_t i = 0; i < tal_count(path); i++) { + json_object_start(stream, NULL); + json_add_pubkey(stream, "id", &path[i]->node_id); + json_add_hex_talarr(stream, "enctlv", path[i]->enctlv); + json_object_end(stream); + }; + json_array_end(stream); + json_object_end(stream); +} + static void onion_message_serialize(struct onion_message_hook_payload *payload, struct json_stream *stream, struct plugin *plugin) @@ -559,3 +581,103 @@ static const struct json_command sendonionmessage_command = { "Send message to {first_id}, using {blinding}, encoded over {hops} (id, tlv)" }; AUTODATA(json_command, &sendonionmessage_command); + +static struct command_result *param_pubkeys(struct command *cmd, + const char *name, + const char *buffer, + const jsmntok_t *tok, + struct pubkey **pubkeys) +{ + size_t i; + const jsmntok_t *t; + + if (tok->type != JSMN_ARRAY || tok->size == 0) + return command_fail(cmd, JSONRPC2_INVALID_PARAMS, + "%s must be an (non-empty) array", name); + + *pubkeys = tal_arr(cmd, struct pubkey, tok->size); + json_for_each_arr(i, t, tok) { + if (!json_to_pubkey(buffer, t, &(*pubkeys)[i])) + return command_fail_badparam(cmd, name, buffer, t, + "should be a compressed pubkey"); + } + return NULL; +} + +static struct command_result *json_blindedpath(struct command *cmd, + const char *buffer, + const jsmntok_t *obj UNNEEDED, + const jsmntok_t *params) +{ + struct pubkey *ids; + struct onionmsg_path **path; + struct privkey blinding_iter; + struct pubkey first_blinding, first_node, me; + size_t nhops; + struct json_stream *response; + + if (!param(cmd, buffer, params, + p_req("ids", param_pubkeys, &ids), + NULL)) + return command_param_failed(); + + nhops = tal_count(ids); + + /* Final id should be us! */ + if (!pubkey_from_node_id(&me, &cmd->ld->id)) + fatal("My id %s is invalid?", + type_to_string(tmpctx, struct node_id, &cmd->ld->id)); + + first_node = ids[0]; + if (!pubkey_eq(&ids[nhops-1], &me)) + return command_fail(cmd, LIGHTNINGD, + "Final of ids must be this node (%s), not %s", + type_to_string(tmpctx, struct pubkey, &me), + type_to_string(tmpctx, struct pubkey, + &ids[nhops-1])); + + randombytes_buf(&blinding_iter, sizeof(blinding_iter)); + if (!pubkey_from_privkey(&blinding_iter, &first_blinding)) + /* Should not happen! */ + return command_fail(cmd, LIGHTNINGD, + "Could not convert blinding to pubkey!"); + + /* We convert ids into aliases as we go. */ + path = tal_arr(cmd, struct onionmsg_path *, nhops); + + for (size_t i = 0; i < nhops - 1; i++) { + path[i] = tal(path, struct onionmsg_path); + path[i]->enctlv = create_enctlv(path[i], + &blinding_iter, + &ids[i], + &ids[i+1], + /* FIXME: Pad? */ + 0, + NULL, + &blinding_iter, + &path[i]->node_id); + } + + /* FIXME: Add padding! */ + path[nhops-1] = tal(path, struct onionmsg_path); + path[nhops-1]->enctlv = create_final_enctlv(path[nhops-1], + &blinding_iter, + &ids[nhops-1], + /* FIXME: Pad? */ + 0, + &cmd->ld->onion_reply_secret, + &path[nhops-1]->node_id); + + response = json_stream_success(cmd); + json_add_blindedpath(response, "blindedpath", + &first_blinding, &first_node, path); + return command_success(cmd, response); +} + +static const struct json_command blindedpath_command = { + "blindedpath", + "utility", + json_blindedpath, + "Create blinded path to us along {ids} (pubkey array ending in our id)" +}; +AUTODATA(json_command, &blindedpath_command);