mirror of
https://github.com/ElementsProject/lightning.git
synced 2024-11-19 18:11:28 +01:00
8720bbedae
Some places (e.g. the pay plugin) only need to construct onions, not decode them. Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
395 lines
12 KiB
C
395 lines
12 KiB
C
/* Creates test vector bolt04/blinded-onion-message-onion-test.json. Run output through jq! */
|
|
static void maybe_print(const char *fmt, ...);
|
|
#define SUPERVERBOSE maybe_print
|
|
#include "config.h"
|
|
#include "../../wire/fromwire.c"
|
|
#include "../../wire/tlvstream.c"
|
|
#include "../../wire/towire.c"
|
|
#include "../amount.c"
|
|
#include "../bigsize.c"
|
|
#include "../blindedpath.c"
|
|
#include "../blindedpay.c"
|
|
#include "../blinding.c"
|
|
#include "../features.c"
|
|
#include "../hmac.c"
|
|
#include "../onion_encode.c"
|
|
#include "../onion_message_parse.c"
|
|
#include "../sphinx.c"
|
|
#include "../type_to_string.c"
|
|
#if EXPERIMENTAL_FEATURES
|
|
#include "../../wire/onion_exp_wiregen.c"
|
|
#include "../../wire/peer_exp_wiregen.c"
|
|
#else
|
|
#include "../../wire/onion_wiregen.c"
|
|
#include "../../wire/peer_wiregen.c"
|
|
#endif
|
|
#include <common/ecdh.h>
|
|
#include <common/setup.h>
|
|
#include <stdio.h>
|
|
|
|
/* AUTOGENERATED MOCKS START */
|
|
/* Generated stub for fromwire_channel_id */
|
|
bool fromwire_channel_id(const u8 **cursor UNNEEDED, size_t *max UNNEEDED,
|
|
struct channel_id *channel_id UNNEEDED)
|
|
{ fprintf(stderr, "fromwire_channel_id called!\n"); abort(); }
|
|
/* Generated stub for fromwire_node_id */
|
|
void fromwire_node_id(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct node_id *id UNNEEDED)
|
|
{ fprintf(stderr, "fromwire_node_id called!\n"); abort(); }
|
|
/* Generated stub for new_onionreply */
|
|
struct onionreply *new_onionreply(const tal_t *ctx UNNEEDED, const u8 *contents TAKES UNNEEDED)
|
|
{ fprintf(stderr, "new_onionreply called!\n"); abort(); }
|
|
/* Generated stub for pubkey_from_node_id */
|
|
bool pubkey_from_node_id(struct pubkey *key UNNEEDED, const struct node_id *id UNNEEDED)
|
|
{ fprintf(stderr, "pubkey_from_node_id called!\n"); abort(); }
|
|
/* Generated stub for status_fmt */
|
|
void status_fmt(enum log_level level UNNEEDED,
|
|
const struct node_id *peer UNNEEDED,
|
|
const char *fmt UNNEEDED, ...)
|
|
|
|
{ fprintf(stderr, "status_fmt called!\n"); abort(); }
|
|
/* Generated stub for towire_channel_id */
|
|
void towire_channel_id(u8 **pptr UNNEEDED, const struct channel_id *channel_id UNNEEDED)
|
|
{ fprintf(stderr, "towire_channel_id called!\n"); abort(); }
|
|
/* Generated stub for towire_node_id */
|
|
void towire_node_id(u8 **pptr UNNEEDED, const struct node_id *id UNNEEDED)
|
|
{ fprintf(stderr, "towire_node_id called!\n"); abort(); }
|
|
/* AUTOGENERATED MOCKS END */
|
|
|
|
static bool comma;
|
|
|
|
static void flush_comma(void)
|
|
{
|
|
if (comma)
|
|
printf(",\n");
|
|
comma = false;
|
|
}
|
|
|
|
static void maybe_comma(void)
|
|
{
|
|
comma = true;
|
|
}
|
|
|
|
static void json_start(const char *name, const char what)
|
|
{
|
|
flush_comma();
|
|
if (name)
|
|
printf("\"%s\":", name);
|
|
printf("%c\n", what);
|
|
}
|
|
|
|
static void json_end(const char what)
|
|
{
|
|
comma = false;
|
|
printf("%c\n", what);
|
|
}
|
|
|
|
static void json_strfield(const char *name, const char *val)
|
|
{
|
|
flush_comma();
|
|
printf("\t\"%s\": \"%s\"", name, val);
|
|
maybe_comma();
|
|
}
|
|
|
|
static void json_hexfield(const char *name, const u8 *val, size_t len)
|
|
{
|
|
json_strfield(name, tal_hexstr(tmpctx, val, len));
|
|
}
|
|
|
|
static void json_hex_talfield(const char *name, const u8 *val)
|
|
{
|
|
json_strfield(name, tal_hexstr(tmpctx, val, tal_bytelen(val)));
|
|
}
|
|
|
|
static void json_pubkey(const char *name, const struct pubkey *key)
|
|
{
|
|
json_strfield(name, pubkey_to_hexstr(tmpctx, key));
|
|
}
|
|
|
|
static bool enable_superverbose;
|
|
static void maybe_print(const char *fmt, ...)
|
|
{
|
|
if (enable_superverbose) {
|
|
va_list ap;
|
|
va_start(ap, fmt);
|
|
vfprintf(stdout, fmt, ap);
|
|
va_end(ap);
|
|
}
|
|
}
|
|
|
|
/* Updated each time, as we pretend to be Alice, Bob, Carol */
|
|
static const struct privkey *mykey;
|
|
|
|
void ecdh(const struct pubkey *point, struct secret *ss)
|
|
{
|
|
if (secp256k1_ecdh(secp256k1_ctx, ss->data, &point->pubkey,
|
|
mykey->secret.data, NULL, NULL) != 1)
|
|
abort();
|
|
}
|
|
|
|
/* This established by trial and error! */
|
|
#define LARGEST_TLV_SIZE 70
|
|
|
|
/* Generic, ugly, function to calc encrypted_recipient_data,
|
|
alias and next blinding, and print out JSON */
|
|
static u8 *add_hop(const char *name,
|
|
const char *comment,
|
|
const struct pubkey *me,
|
|
const struct pubkey *next,
|
|
const struct privkey *override_blinding,
|
|
const char *additional_field_hexstr,
|
|
const char *path_id_hexstr,
|
|
struct privkey *blinding, /* in and out */
|
|
struct pubkey *alias /* out */)
|
|
{
|
|
struct tlv_encrypted_data_tlv *tlv = tlv_encrypted_data_tlv_new(tmpctx);
|
|
u8 *enctlv, *encrypted_recipient_data;
|
|
const u8 *additional;
|
|
|
|
json_start(NULL, '{');
|
|
json_strfield("alias", name);
|
|
json_strfield("comment", comment);
|
|
json_hexfield("blinding_secret", blinding->secret.data, sizeof(*blinding));
|
|
|
|
/* We have to calc first, to make padding correct */
|
|
tlv->next_node_id = tal_dup_or_null(tlv, struct pubkey, next);
|
|
if (path_id_hexstr)
|
|
tlv->path_id = tal_hexdata(tlv, path_id_hexstr,
|
|
strlen(path_id_hexstr));
|
|
|
|
/* Normally we wouldn't know blinding privkey, and we'd just
|
|
* paste in the rest of the path as given, but here we're actually
|
|
* generating the lot. */
|
|
if (override_blinding) {
|
|
tlv->next_blinding_override = tal(tlv, struct pubkey);
|
|
assert(pubkey_from_privkey(override_blinding, tlv->next_blinding_override));
|
|
}
|
|
|
|
/* This is assumed to be a valid TLV tuple, and greater than
|
|
* any previous, so we simply append. */
|
|
if (additional_field_hexstr)
|
|
additional = tal_hexdata(tlv, additional_field_hexstr,
|
|
strlen(additional_field_hexstr));
|
|
else
|
|
additional = NULL;
|
|
|
|
enctlv = tal_arr(tmpctx, u8, 0);
|
|
towire_tlv_encrypted_data_tlv(&enctlv, tlv);
|
|
|
|
/* Now create padding, and reencode */
|
|
if (tal_bytelen(enctlv) + tal_bytelen(additional) != LARGEST_TLV_SIZE)
|
|
tlv->padding = tal_arrz(tlv, u8,
|
|
LARGEST_TLV_SIZE
|
|
- tal_bytelen(enctlv)
|
|
- tal_bytelen(additional)
|
|
- 2);
|
|
enctlv = tal_arr(tmpctx, u8, 0);
|
|
towire_tlv_encrypted_data_tlv(&enctlv, tlv);
|
|
towire(&enctlv, additional, tal_bytelen(additional));
|
|
assert(tal_bytelen(enctlv) == LARGEST_TLV_SIZE);
|
|
|
|
json_start("tlvs", '{');
|
|
if (tlv->padding)
|
|
json_hex_talfield("padding", tlv->padding);
|
|
if (tlv->next_node_id)
|
|
json_pubkey("next_node_id", tlv->next_node_id);
|
|
if (tlv->path_id)
|
|
json_hex_talfield("path_id", tlv->path_id);
|
|
if (tlv->next_blinding_override) {
|
|
json_pubkey("next_blinding_override",
|
|
tlv->next_blinding_override);
|
|
json_hexfield("blinding_override_secret",
|
|
override_blinding->secret.data,
|
|
sizeof(*override_blinding));
|
|
}
|
|
if (additional) {
|
|
/* Deconstruct into type, len, value */
|
|
size_t totlen = tal_bytelen(additional);
|
|
u64 type = fromwire_bigsize(&additional, &totlen);
|
|
u64 len = fromwire_bigsize(&additional, &totlen);
|
|
assert(len == totlen);
|
|
json_hexfield(tal_fmt(tmpctx, "unknown_tag_%"PRIu64, type),
|
|
additional, len);
|
|
}
|
|
json_end('}');
|
|
|
|
maybe_comma();
|
|
json_hex_talfield("encrypted_data_tlv", enctlv);
|
|
flush_comma();
|
|
enable_superverbose = true;
|
|
encrypted_recipient_data = enctlv_from_encmsg_raw(tmpctx,
|
|
blinding,
|
|
me,
|
|
enctlv,
|
|
blinding,
|
|
alias);
|
|
enable_superverbose = false;
|
|
json_hex_talfield("encrypted_recipient_data",
|
|
encrypted_recipient_data);
|
|
if (override_blinding)
|
|
*blinding = *override_blinding;
|
|
|
|
json_end('}');
|
|
maybe_comma();
|
|
return encrypted_recipient_data;
|
|
}
|
|
|
|
int main(int argc, char *argv[])
|
|
{
|
|
const char *alias[] = {"Alice", "Bob", "Carol", "Dave"};
|
|
struct privkey privkey[ARRAY_SIZE(alias)], blinding, override_blinding;
|
|
struct pubkey id[ARRAY_SIZE(alias)], blinding_pub;
|
|
struct secret session_key;
|
|
u8 *erd[ARRAY_SIZE(alias)]; /* encrypted_recipient_data */
|
|
u8 *onion_message_packet, *onion_message;
|
|
struct pubkey blinded[ARRAY_SIZE(alias)];
|
|
struct sphinx_path *sphinx_path;
|
|
struct onionpacket *op;
|
|
struct secret *path_secrets;
|
|
|
|
common_setup(argv[0]);
|
|
|
|
/* Alice is AAA... Bob is BBB... */
|
|
for (size_t i = 0; i < ARRAY_SIZE(alias); i++) {
|
|
memset(&privkey[i], alias[i][0], sizeof(privkey[i]));
|
|
assert(pubkey_from_privkey(&privkey[i], &id[i]));
|
|
}
|
|
|
|
memset(&session_key, 3, sizeof(session_key));
|
|
|
|
json_start(NULL, '{');
|
|
json_strfield("comment", "Test vector creating an onionmessage, including joining an existing one");
|
|
json_start("generate", '{');
|
|
json_strfield("comment", "This sections contains test data for Dave's blinded path Bob->Dave; sender has to prepend a hop to Alice to reach Bob");
|
|
json_hexfield("session_key", session_key.data, sizeof(session_key));
|
|
json_start("hops", '[');
|
|
|
|
memset(&blinding, 99, sizeof(blinding));
|
|
memset(&override_blinding, 1, sizeof(override_blinding));
|
|
erd[0] = add_hop("Alice", "Alice->Bob: note next_blinding_override to match that give by Dave for Bob",
|
|
&id[0], &id[1],
|
|
&override_blinding, NULL, NULL,
|
|
&blinding, &blinded[0]);
|
|
erd[1] = add_hop("Bob", "Bob->Carol",
|
|
&id[1], &id[2],
|
|
NULL, "fd023103123456", NULL,
|
|
&blinding, &blinded[1]);
|
|
erd[2] = add_hop("Carol", "Carol->Dave",
|
|
&id[2], &id[3],
|
|
NULL, NULL, NULL,
|
|
&blinding, &blinded[2]);
|
|
erd[3] = add_hop("Dave", "Dave is final node, hence path_id",
|
|
&id[3], NULL,
|
|
NULL, "fdffff0206c1",
|
|
"deadbeefbadc0ffeedeadbeefbadc0ffeedeadbeefbadc0ffeedeadbeefbadc0",
|
|
&blinding, &blinded[3]);
|
|
|
|
json_end(']');
|
|
json_end('}');
|
|
maybe_comma();
|
|
|
|
memset(&blinding, 99, sizeof(blinding));
|
|
assert(pubkey_from_privkey(&blinding, &blinding_pub));
|
|
json_start("route", '{');
|
|
json_strfield("comment", "The resulting blinded route Alice to Dave.");
|
|
json_pubkey("introduction_node_id", &id[0]);
|
|
json_pubkey("blinding", &blinding_pub);
|
|
|
|
json_start("hops", '[');
|
|
for (size_t i = 0; i < ARRAY_SIZE(erd); i++) {
|
|
json_start(NULL, '{');
|
|
if (i != 0)
|
|
json_pubkey("blinded_node_id", &blinded[i]);
|
|
json_hex_talfield("encrypted_recipient_data", erd[i]);
|
|
json_end('}');
|
|
maybe_comma();
|
|
}
|
|
json_end(']');
|
|
json_end('}');
|
|
maybe_comma();
|
|
|
|
json_start("onionmessage", '{');
|
|
json_strfield("comment", "An onion message which sends a 'hello' to Dave");
|
|
json_strfield("unknown_tag_1", "68656c6c6f");
|
|
|
|
/* Create the onionmessage */
|
|
sphinx_path = sphinx_path_new(tmpctx, NULL);
|
|
for (size_t i = 0; i < ARRAY_SIZE(erd); i++) {
|
|
struct tlv_onionmsg_tlv *tlv = tlv_onionmsg_tlv_new(tmpctx);
|
|
u8 *onionmsg_tlv;
|
|
tlv->encrypted_recipient_data = erd[i];
|
|
onionmsg_tlv = tal_arr(tmpctx, u8, 0);
|
|
/* For final hop, add unknown 'hello' field */
|
|
if (i == ARRAY_SIZE(erd) - 1) {
|
|
towire_bigsize(&onionmsg_tlv, 1); /* type */
|
|
towire_bigsize(&onionmsg_tlv, strlen("hello")); /* length */
|
|
towire(&onionmsg_tlv, "hello", strlen("hello"));
|
|
}
|
|
towire_tlv_onionmsg_tlv(&onionmsg_tlv, tlv);
|
|
sphinx_add_hop(sphinx_path, &blinded[i], onionmsg_tlv);
|
|
}
|
|
|
|
/* Make it use our session key! */
|
|
sphinx_path->session_key = &session_key;
|
|
|
|
/* BOLT-onion-message #4:
|
|
* - SHOULD set `len` to 1366 or 32834.
|
|
*/
|
|
op = create_onionpacket(tmpctx, sphinx_path, ROUTING_INFO_SIZE,
|
|
&path_secrets);
|
|
onion_message_packet = serialize_onionpacket(tmpctx, op);
|
|
json_hex_talfield("onion_message_packet", onion_message_packet);
|
|
json_end('}');
|
|
maybe_comma();
|
|
|
|
json_start("decrypt", '{');
|
|
json_strfield("comment", "This section contains the internal values generated by intermediate nodes when decrypting the onion.");
|
|
|
|
onion_message = towire_onion_message(tmpctx, &blinding_pub, onion_message_packet);
|
|
|
|
json_start("hops", '[');
|
|
for (size_t i = 0; i < ARRAY_SIZE(erd); i++) {
|
|
struct pubkey next_node_id;
|
|
struct tlv_onionmsg_tlv *final_om;
|
|
struct pubkey final_alias;
|
|
struct secret *final_path_id;
|
|
|
|
json_start(NULL, '{');
|
|
json_strfield("alias", alias[i]);
|
|
json_hexfield("privkey", privkey[i].secret.data, sizeof(privkey[i]));
|
|
json_hex_talfield("onion_message", onion_message);
|
|
|
|
/* Now, do full decrypt code, to check */
|
|
assert(fromwire_onion_message(tmpctx, onion_message,
|
|
&blinding_pub, &onion_message_packet));
|
|
|
|
/* For test_ecdh */
|
|
mykey = &privkey[i];
|
|
assert(onion_message_parse(tmpctx, onion_message_packet, &blinding_pub, NULL,
|
|
&id[i],
|
|
&onion_message, &next_node_id,
|
|
&final_om,
|
|
&final_alias,
|
|
&final_path_id));
|
|
if (onion_message) {
|
|
json_pubkey("next_node_id", &next_node_id);
|
|
} else {
|
|
const struct tlv_field *hello;
|
|
json_start("tlvs", '{');
|
|
json_hexfield("path_id", final_path_id->data, sizeof(*final_path_id));
|
|
hello = &final_om->fields[0];
|
|
json_hexfield(tal_fmt(tmpctx,
|
|
"unknown_tag_%"PRIu64,
|
|
hello->numtype),
|
|
hello->value,
|
|
hello->length);
|
|
json_end('}');
|
|
}
|
|
json_end('}');
|
|
maybe_comma();
|
|
}
|
|
json_end(']');
|
|
json_end('}');
|
|
json_end('}');
|
|
common_shutdown();
|
|
}
|