diff --git a/common/sphinx.c b/common/sphinx.c index 6c29972ee..1b4441178 100644 --- a/common/sphinx.c +++ b/common/sphinx.c @@ -786,26 +786,96 @@ u8 *unwrap_onionreply(const tal_t *ctx, return final; } -u8 *sphinx_decompress(const tal_t *ctx, const u8 *compressed, - struct secret *shared_secret) +struct onionpacket *sphinx_decompress(const tal_t *ctx, + const struct sphinx_compressed_onion *src, + const struct secret *shared_secret) { - size_t compressedlen = tal_bytelen(compressed); - size_t prefill_size = TOTAL_PACKET_SIZE - compressedlen; - u8 *dst; + struct onionpacket *res = tal(ctx, struct onionpacket); + size_t srclen = tal_bytelen(src->routinginfo); + size_t prefill_size = ROUTING_INFO_SIZE - srclen; + + res->version = src->version; + res->ephemeralkey = src->ephemeralkey; + memcpy(res->mac, src->mac, HMAC_SIZE); + + /* 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); + memcpy(res->routinginfo, src->routinginfo, srclen); + sphinx_prefill_stream_xor(res->routinginfo + srclen, prefill_size, + shared_secret); + + return res; +} + +struct sphinx_compressed_onion * +sphinx_compress(const tal_t *ctx, const struct onionpacket *packet, + const struct sphinx_path *path) +{ + struct sphinx_compressed_onion *res; + size_t payloads_size = sphinx_path_payloads_size(path); + + /* We can't compress an onion that doesn't have a rendez-vous node. */ + if (path->rendezvous_id) + return NULL; + + res = tal(ctx, struct sphinx_compressed_onion); + res->version = packet->version; + res->ephemeralkey = packet->ephemeralkey; + memcpy(res->mac, packet->mac, HMAC_SIZE); + + res->routinginfo = tal_arr(res, u8, payloads_size); + memcpy(res->routinginfo, packet->routinginfo, payloads_size); + + return res; +} + +u8 *sphinx_compressed_onion_serialize(const tal_t *ctx, const struct sphinx_compressed_onion *onion) +{ + size_t routelen = tal_bytelen(onion->routinginfo); + size_t len = VERSION_SIZE + PUBKEY_SIZE + routelen + HMAC_SIZE; + u8 *dst = tal_arr(ctx, u8, len); + u8 der[PUBKEY_CMPR_LEN]; + int p = 0; + + pubkey_to_der(der, &onion->ephemeralkey); + + write_buffer(dst, &onion->version, VERSION_SIZE, &p); + write_buffer(dst, der, PUBKEY_SIZE, &p); + write_buffer(dst, onion->routinginfo, routelen, &p); + write_buffer(dst, onion->mac, HMAC_SIZE, &p); + + assert(p == len); + return dst; +} + +struct sphinx_compressed_onion * +sphinx_compressed_onion_deserialize(const tal_t *ctx, const u8 *src) +{ + size_t srclen = tal_bytelen(src); + size_t routelen = srclen - VERSION_SIZE - PUBKEY_SIZE - HMAC_SIZE; + struct sphinx_compressed_onion *dst = + tal(ctx, struct sphinx_compressed_onion); int p = 0; + u8 ephkey[PUBKEY_SIZE]; + + assert(srclen <= TOTAL_PACKET_SIZE); - assert(prefill_size >= 0); - assert(compressedlen >= VERSION_SIZE + PUBKEY_SIZE + HMAC_SIZE); - dst = tal_arrz(ctx, u8, TOTAL_PACKET_SIZE); - write_buffer( - dst, compressed, - VERSION_SIZE + PUBKEY_SIZE + ROUTING_INFO_SIZE - prefill_size, &p); + read_buffer(&dst->version, src, 1, &p); + if (dst->version != 0x00) + return tal_free(dst); + + read_buffer(ephkey, src, PUBKEY_SIZE, &p); + + if (!pubkey_from_der(ephkey, PUBKEY_SIZE, &dst->ephemeralkey)) { + return tal_free(dst); + } - /* We can just XOR here since we initialized the array with zeros. */ - sphinx_prefill_stream_xor(dst + p, prefill_size, shared_secret); - p += prefill_size; + dst->routinginfo = tal_arr(dst, u8, routelen); + read_buffer(dst->routinginfo, src, routelen, &p); + read_buffer(&dst->mac, src, HMAC_SIZE, &p); + assert(p == srclen); - write_buffer(dst, compressed + compressedlen - HMAC_SIZE, HMAC_SIZE, - &p); return dst; } diff --git a/common/sphinx.h b/common/sphinx.h index 63189dff4..2f359c123 100644 --- a/common/sphinx.h +++ b/common/sphinx.h @@ -30,6 +30,14 @@ struct onionpacket { u8 routinginfo[ROUTING_INFO_SIZE]; }; +struct sphinx_compressed_onion { + u8 version; + struct pubkey ephemeralkey; + u8 *routinginfo; + u8 mac[HMAC_SIZE]; +}; + + enum route_next_case { ONION_END = 0, ONION_FORWARD = 1, @@ -250,8 +258,9 @@ bool sphinx_path_set_rendezvous(struct sphinx_path *sp, * Given a compressed onion expand it by re-generating the prefiller and * inserting it in the appropriate place. */ -u8 *sphinx_decompress(const tal_t *ctx, const u8 *compressed, - struct secret *shared_secret); +struct onionpacket *sphinx_decompress(const tal_t *ctx, + const struct sphinx_compressed_onion *src, + const struct secret *shared_secret); /** * Use ECDH to generate a shared secret from a privkey and a pubkey. @@ -264,6 +273,20 @@ bool sphinx_create_shared_secret(struct secret *privkey, const struct pubkey *pubkey, const struct secret *secret); + +/** + * Given a compressible onionpacket, return the compressed version. + */ +struct sphinx_compressed_onion * +sphinx_compress(const tal_t *ctx, const struct onionpacket *packet, + const struct sphinx_path *path); + +u8 *sphinx_compressed_onion_serialize( + const tal_t *ctx, const struct sphinx_compressed_onion *onion); + +struct sphinx_compressed_onion * +sphinx_compressed_onion_deserialize(const tal_t *ctx, const u8 *src); + #if DEVELOPER /* Override to force us to reject valid onion packets */ extern bool dev_fail_process_onionpacket; diff --git a/devtools/onion.c b/devtools/onion.c index 75039fca2..49008e6ae 100644 --- a/devtools/onion.c +++ b/devtools/onion.c @@ -294,9 +294,11 @@ static void decompress(char *hexprivkey, char *hexonion) { struct privkey rendezvous_key; size_t onionlen = hex_data_size(strlen(hexonion)); - u8 *compressed, *decompressed; + u8 *compressed; struct pubkey ephkey; struct secret shared_secret; + struct onionpacket *onion; + struct sphinx_compressed_onion *tinyonion; if (!hex_decode(hexprivkey, strlen(hexprivkey), &rendezvous_key, sizeof(rendezvous_key))) errx(1, "Invalid private key hex '%s'", hexprivkey);