Browse Source

sphinx: Working onion wrapping with filler cancellation

travis-debug
Christian Decker 5 years ago
committed by Rusty Russell
parent
commit
96dc0238ba
  1. 79
      common/sphinx.c

79
common/sphinx.c

@ -57,6 +57,14 @@ struct sphinx_path {
/* The individual hops on this route. */
struct sphinx_hop *hops;
/* If this is a rendez-vous onion, then the following node_id tells us
* which node will be processing this onion and decompressing the
* onion. It is used to generate the prefill obfuscation stream to
* hide the fact that the onion was compressed from the next
* node. NULL if this is not a rendez-vous onion, and shouldn't be
* compressible. */
struct pubkey *rendezvous_id;
};
struct sphinx_path *sphinx_path_new(const tal_t *ctx, const u8 *associated_data)
@ -64,6 +72,7 @@ struct sphinx_path *sphinx_path_new(const tal_t *ctx, const u8 *associated_data)
struct sphinx_path *sp = tal(ctx, struct sphinx_path);
sp->associated_data = tal_dup_talarr(sp, u8, associated_data);
sp->session_key = NULL;
sp->rendezvous_id = NULL;
sp->hops = tal_arr(sp, struct sphinx_hop, 0);
return sp;
}
@ -259,6 +268,36 @@ static bool generate_header_padding(void *dst, size_t dstlen,
return true;
}
static bool generate_prefill(void *dst, size_t dstlen,
const struct sphinx_path *path,
struct hop_params *params)
{
u8 stream[2 * ROUTING_INFO_SIZE];
u8 key[KEY_LEN];
size_t fillerStart, fillerSize;
memset(dst, 0, dstlen);
for (int i = 0; i < tal_count(path->hops); i++) {
if (!generate_key(&key, RHO_KEYTYPE, strlen(RHO_KEYTYPE),
&params[i].secret))
return false;
generate_cipher_stream(stream, key, sizeof(stream));
/* Sum up how many bytes have been used by previous hops,
* that gives us the start in the stream */
fillerSize = 0;
for (int j = 0; j < i; j++)
fillerSize += sphinx_hop_size(&path->hops[j]);
fillerStart = ROUTING_INFO_SIZE - fillerSize - dstlen;
/* Apply the cipher-stream to the part of the filler that'll
* be added by this hop */
xorbytes(dst, dst, stream + fillerStart, dstlen);
}
return true;
}
static void compute_blinding_factor(const struct pubkey *key,
const struct secret *sharedsecret,
u8 res[BLINDING_FACTOR_SIZE])
@ -386,6 +425,39 @@ static void sphinx_write_frame(u8 *dest, const struct sphinx_hop *hop)
memcpy(dest + tal_bytelen(hop->raw_payload), hop->hmac, HMAC_SIZE);
}
static void sphinx_prefill_stream_xor(u8 *dst, size_t dstlen,
const struct secret *shared_secret)
{
u8 padkey[KEY_LEN];
generate_key(padkey, "prefill", 7, shared_secret);
xor_cipher_stream(dst, padkey, dstlen);
}
static void sphinx_prefill(u8 *routinginfo, const struct sphinx_path *sp,
size_t prefill_size, struct hop_params *params)
{
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;
u8 prefill[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(prefill, prefill_size, sp, params);
memcpy(routinginfo + prefill_offset, prefill, prefill_size);
/* Now fill in the obfuscation stream, which can be regenerated by the
* node processing this onion. */
create_shared_secret(&shared_secret, sp->rendezvous_id, sp->session_key);
sphinx_prefill_stream_xor(routinginfo + prefill_offset, prefill_size, &shared_secret);
}
struct onionpacket *create_onionpacket(
const tal_t *ctx,
struct sphinx_path *sp,
@ -402,6 +474,8 @@ struct onionpacket *create_onionpacket(
u8 nexthmac[HMAC_SIZE];
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;
if (sphinx_path_payloads_size(sp) > ROUTING_INFO_SIZE) {
tal_free(packet);
@ -435,6 +509,11 @@ struct onionpacket *create_onionpacket(
generate_header_padding(filler, sizeof(filler), 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);
for (i = num_hops - 1; i >= 0; i--) {
memcpy(sp->hops[i].hmac, nexthmac, HMAC_SIZE);
generate_key_set(&params[i].secret, &keys);

Loading…
Cancel
Save