common/sphinx: make onionpacket.routinginfo a dynamic member.

Still asserts that it's the standard size, but makes it a dynamic
member.  For simpliciy, changes the parse_onionpacket API (it must be
a tal object now, so we might as well allocate it here to catch all
the callers).

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This commit is contained in:
Rusty Russell
2020-12-08 21:05:32 +10:30
committed by Christian Decker
parent 3776af4a35
commit 32c7c133f4
13 changed files with 118 additions and 95 deletions

View File

@@ -20,7 +20,6 @@
#define BLINDING_FACTOR_SIZE 32
#define NUM_STREAM_BYTES (2*ROUTING_INFO_SIZE)
#define ONION_REPLY_SIZE 256
#define RHO_KEYTYPE "rho"
@@ -131,7 +130,7 @@ u8 *serialize_onionpacket(
const tal_t *ctx,
const struct onionpacket *m)
{
u8 *dst = tal_arr(ctx, u8, TOTAL_PACKET_SIZE(ROUTING_INFO_SIZE));
u8 *dst = tal_arr(ctx, u8, TOTAL_PACKET_SIZE(tal_bytelen(m->routinginfo)));
u8 der[PUBKEY_CMPR_LEN];
int p = 0;
@@ -139,35 +138,48 @@ u8 *serialize_onionpacket(
pubkey_to_der(der, &m->ephemeralkey);
write_buffer(dst, &m->version, 1, &p);
write_buffer(dst, der, sizeof(der), &p);
write_buffer(dst, m->routinginfo, ROUTING_INFO_SIZE, &p);
write_buffer(dst, m->routinginfo, tal_bytelen(m->routinginfo), &p);
write_buffer(dst, m->hmac.bytes, sizeof(m->hmac.bytes), &p);
return dst;
}
enum onion_wire parse_onionpacket(const u8 *src,
const size_t srclen,
struct onionpacket *dest)
struct onionpacket *parse_onionpacket(const tal_t *ctx,
const u8 *src,
const size_t srclen,
enum onion_wire *failcode)
{
struct onionpacket *dest = tal(ctx, struct onionpacket);
const u8 *cursor = src;
size_t max = srclen;
/* FIXME: Allow parsing other sizes! */
assert(srclen == TOTAL_PACKET_SIZE(ROUTING_INFO_SIZE));
dest->version = fromwire_u8(&cursor, &max);
if (dest->version != 0x00) {
// FIXME add logging
return WIRE_INVALID_ONION_VERSION;
*failcode = WIRE_INVALID_ONION_VERSION;
return tal_free(dest);
}
fromwire_pubkey(&cursor, &max, &dest->ephemeralkey);
if (cursor == NULL) {
return WIRE_INVALID_ONION_KEY;
*failcode = WIRE_INVALID_ONION_KEY;
return tal_free(dest);
}
fromwire_u8_array(&cursor, &max, dest->routinginfo, ROUTING_INFO_SIZE);
/* If max underflows, this returns NULL and fromwire fails. */
dest->routinginfo = fromwire_tal_arrn(dest, &cursor, &max,
max - HMAC_SIZE);
fromwire_hmac(&cursor, &max, &dest->hmac);
assert(max == 0);
return 0;
if (cursor == NULL) {
*failcode = WIRE_INVALID_REALM;
return tal_free(dest);
}
return dest;
}
/*
@@ -248,12 +260,13 @@ static void compute_packet_hmac(const struct onionpacket *packet,
struct hmac *hmac)
{
compute_hmac(mukey,
packet->routinginfo, ROUTING_INFO_SIZE,
packet->routinginfo, tal_bytelen(packet->routinginfo),
assocdata, assocdatalen,
hmac);
}
static void generate_header_padding(void *dst, size_t dstlen,
size_t fixed_size,
const struct sphinx_path *path,
struct hop_params *params)
{
@@ -269,12 +282,12 @@ static void generate_header_padding(void *dst, size_t dstlen,
fillerSize = 0;
for (int j = 0; j < i; j++)
fillerSize += sphinx_hop_size(&path->hops[j]);
fillerStart = ROUTING_INFO_SIZE - fillerSize;
fillerStart = fixed_size - fillerSize;
/* The filler will dangle off of the end by the current
* hop-size, we'll make sure to copy it into the correct
* position in the next step. */
fillerEnd = ROUTING_INFO_SIZE + sphinx_hop_size(&path->hops[i]);
fillerEnd = fixed_size + sphinx_hop_size(&path->hops[i]);
/* Apply the cipher-stream to the part of the filler that'll
* be added by this hop */
@@ -284,6 +297,7 @@ static void generate_header_padding(void *dst, size_t dstlen,
}
static void generate_prefill(void *dst, size_t dstlen,
size_t fixed_size,
const struct sphinx_path *path,
struct hop_params *params)
{
@@ -299,7 +313,7 @@ static void generate_prefill(void *dst, size_t dstlen,
fillerSize = 0;
for (int j = 0; j < i; j++)
fillerSize += sphinx_hop_size(&path->hops[j]);
fillerStart = ROUTING_INFO_SIZE - fillerSize - dstlen;
fillerStart = fixed_size - fillerSize - dstlen;
/* Apply the cipher-stream to the part of the filler that'll
* be added by this hop */
@@ -444,21 +458,23 @@ static void sphinx_prefill_stream_xor(u8 *dst, size_t dstlen,
}
static void sphinx_prefill(u8 *routinginfo, const struct sphinx_path *sp,
size_t prefill_size, struct hop_params *params)
size_t prefill_size, struct hop_params *params,
size_t fixed_size)
{
int num_hops = tal_count(sp->hops);
size_t fillerSize = sphinx_path_payloads_size(sp) -
sphinx_hop_size(&sp->hops[num_hops - 1]);
size_t last_hop_size = sphinx_hop_size(&sp->hops[num_hops - 1]);
int prefill_offset =
ROUTING_INFO_SIZE - fillerSize - last_hop_size - prefill_size;
fixed_size - fillerSize - last_hop_size - prefill_size;
struct secret shared_secret;
/* Generate the prefill stream, which cancels out the layers of
* encryption that will be applied while wrapping the onion. This
* leaves the middle, unused, section with all 0x00 bytes after
* encrypting. */
generate_prefill(routinginfo + prefill_offset, prefill_size, sp, params);
generate_prefill(routinginfo + prefill_offset, prefill_size,
fixed_size, sp, params);
/* Now fill in the obfuscation stream, which can be regenerated by the
* node processing this onion. */
@@ -469,6 +485,7 @@ static void sphinx_prefill(u8 *routinginfo, const struct sphinx_path *sp,
struct onionpacket *create_onionpacket(
const tal_t *ctx,
struct sphinx_path *sp,
size_t fixed_size,
struct secret **path_secrets
)
{
@@ -476,20 +493,21 @@ struct onionpacket *create_onionpacket(
int i, num_hops = tal_count(sp->hops);
size_t fillerSize = sphinx_path_payloads_size(sp) -
sphinx_hop_size(&sp->hops[num_hops - 1]);
u8 filler[fillerSize];
u8 *filler;
struct keyset keys;
struct secret padkey;
struct hmac nexthmac;
struct hop_params *params;
struct secret *secrets = tal_arr(ctx, struct secret, num_hops);
size_t payloads_size = sphinx_path_payloads_size(sp);
size_t max_prefill = ROUTING_INFO_SIZE - payloads_size;
size_t max_prefill = fixed_size - payloads_size;
if (sphinx_path_payloads_size(sp) > ROUTING_INFO_SIZE) {
if (sphinx_path_payloads_size(sp) > fixed_size) {
tal_free(packet);
tal_free(secrets);
return NULL;
}
packet->routinginfo = tal_arr(packet, u8, fixed_size);
if (sp->session_key == NULL) {
sp->session_key = tal(sp, struct secret);
@@ -513,14 +531,16 @@ struct onionpacket *create_onionpacket(
/* Note that this is just hop_payloads: the rest of the packet is
* overwritten below or above anyway. */
subkey_from_hmac("pad", sp->session_key, &padkey);
generate_cipher_stream(packet->routinginfo, &padkey, ROUTING_INFO_SIZE);
generate_cipher_stream(packet->routinginfo, &padkey, fixed_size);
generate_header_padding(filler, sizeof(filler), sp, params);
filler = tal_arr(tmpctx, u8, fillerSize);
generate_header_padding(filler, tal_bytelen(filler), fixed_size, sp, params);
if (sp->rendezvous_id != NULL)
/* FIXME: Fuzz this or expose to the caller to hide encoded
* route length. */
sphinx_prefill(packet->routinginfo, sp, max_prefill, params);
sphinx_prefill(packet->routinginfo, sp, max_prefill, params,
fixed_size);
for (i = num_hops - 1; i >= 0; i--) {
sp->hops[i].hmac = nexthmac;
@@ -529,13 +549,13 @@ struct onionpacket *create_onionpacket(
/* Rightshift mix-header by FRAME_SIZE */
size_t shiftSize = sphinx_hop_size(&sp->hops[i]);
memmove(packet->routinginfo + shiftSize, packet->routinginfo,
ROUTING_INFO_SIZE-shiftSize);
fixed_size - shiftSize);
sphinx_write_frame(packet->routinginfo, &sp->hops[i]);
xor_cipher_stream(packet->routinginfo, &keys.rho,
ROUTING_INFO_SIZE);
fixed_size);
if (i == num_hops - 1) {
memcpy(packet->routinginfo + ROUTING_INFO_SIZE - fillerSize, filler, fillerSize);
memcpy(packet->routinginfo + fixed_size - fillerSize, filler, fillerSize);
}
compute_packet_hmac(packet, sp->associated_data, tal_bytelen(sp->associated_data), &keys.mu,
@@ -573,7 +593,7 @@ struct route_step *process_onionpacket(
struct hmac hmac;
struct keyset keys;
u8 blind[BLINDING_FACTOR_SIZE];
u8 paddedheader[2*ROUTING_INFO_SIZE];
u8 *paddedheader;
size_t payload_size;
bigsize_t shift_size;
bool valid;
@@ -591,15 +611,17 @@ struct route_step *process_onionpacket(
}
//FIXME:store seen secrets to avoid replay attacks
memset(paddedheader, 0, sizeof(paddedheader));
memcpy(paddedheader, msg->routinginfo, ROUTING_INFO_SIZE);
xor_cipher_stream(paddedheader, &keys.rho, sizeof(paddedheader));
paddedheader = tal_arrz(step, u8, tal_bytelen(msg->routinginfo)*2);
memcpy(paddedheader, msg->routinginfo, tal_bytelen(msg->routinginfo));
xor_cipher_stream(paddedheader, &keys.rho, tal_bytelen(paddedheader));
compute_blinding_factor(&msg->ephemeralkey, shared_secret, blind);
if (!blind_group_element(&step->next->ephemeralkey, &msg->ephemeralkey, blind))
return tal_free(step);
payload_size = onion_payload_length(paddedheader, ROUTING_INFO_SIZE, has_realm,
payload_size = onion_payload_length(paddedheader,
tal_bytelen(msg->routinginfo),
has_realm,
&valid, NULL);
/* Can't decode? Treat it as terminal. */
@@ -607,7 +629,7 @@ struct route_step *process_onionpacket(
shift_size = payload_size;
memset(step->next->hmac.bytes, 0, sizeof(step->next->hmac.bytes));
} else {
assert(payload_size <= ROUTING_INFO_SIZE - HMAC_SIZE);
assert(payload_size <= tal_bytelen(msg->routinginfo) - HMAC_SIZE);
/* Copy hmac */
shift_size = payload_size + HMAC_SIZE;
memcpy(step->next->hmac.bytes,
@@ -616,8 +638,10 @@ struct route_step *process_onionpacket(
step->raw_payload = tal_dup_arr(step, u8, paddedheader, payload_size, 0);
/* Left shift the current payload out and make the remainder the new onion */
memcpy(&step->next->routinginfo, paddedheader + shift_size,
ROUTING_INFO_SIZE);
step->next->routinginfo = tal_dup_arr(step->next,
u8,
paddedheader + shift_size,
tal_bytelen(msg->routinginfo), 0);
if (memeqzero(step->next->hmac.bytes, sizeof(step->next->hmac.bytes))) {
step->nextcase = ONION_END;
@@ -625,6 +649,7 @@ struct route_step *process_onionpacket(
step->nextcase = ONION_FORWARD;
}
tal_free(paddedheader);
return step;
}
@@ -775,7 +800,7 @@ struct onionpacket *sphinx_decompress(const tal_t *ctx,
/* Decompress routinginfo by copying the unmodified prefix, setting
* the compressed suffix to 0x00 bytes and then xoring the obfuscation
* stream in place. */
memset(res->routinginfo, 0, ROUTING_INFO_SIZE);
res->routinginfo = tal_arrz(res, u8, ROUTING_INFO_SIZE);
memcpy(res->routinginfo, src->routinginfo, srclen);
sphinx_prefill_stream_xor(res->routinginfo + srclen, prefill_size,
shared_secret);