-
Notifications
You must be signed in to change notification settings - Fork 912
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
common/blindedpath: create onion mesage test vectors.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
- Loading branch information
1 parent
b73647c
commit ef5a019
Showing
2 changed files
with
389 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,389 @@ | ||
/* 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.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); | ||
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)); | ||
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(); | ||
} |