diff --git a/common/sphinx.c b/common/sphinx.c index 972d84279..e43c01ba9 100644 --- a/common/sphinx.c +++ b/common/sphinx.c @@ -785,3 +785,27 @@ u8 *unwrap_onionreply(const tal_t *ctx, return tal_free(final); return final; } + +u8 *sphinx_decompress(const tal_t *ctx, const u8 *compressed, + struct secret *shared_secret) +{ + size_t compressedlen = tal_bytelen(compressed); + size_t prefill_size = TOTAL_PACKET_SIZE - compressedlen; + u8 *dst; + int p = 0; + + 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); + + /* 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; + + write_buffer(dst, compressed + compressedlen - HMAC_SIZE, HMAC_SIZE, + &p); + return dst; +} diff --git a/common/sphinx.h b/common/sphinx.h index 28fd588e1..fe5b946a9 100644 --- a/common/sphinx.h +++ b/common/sphinx.h @@ -246,6 +246,13 @@ u8 *serialize_compressed_onion(const tal_t *ctx, bool sphinx_path_set_rendezvous(struct sphinx_path *sp, const struct node_id *rendezvous_id); +/** + * 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); + #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 f58756394..b20e33a51 100644 --- a/devtools/onion.c +++ b/devtools/onion.c @@ -290,6 +290,30 @@ static void runtest(const char *filename) tal_free(ctx); } +static void decompress(char *hexprivkey, char *hexonion) +{ + struct privkey rendezvous_key; + size_t onionlen = hex_data_size(strlen(hexonion)); + u8 *compressed, *decompressed; + struct pubkey ephkey; + struct secret shared_secret; + + if (!hex_decode(hexprivkey, strlen(hexprivkey), &rendezvous_key, sizeof(rendezvous_key))) + errx(1, "Invalid private key hex '%s'", hexprivkey); + + compressed = tal_arr(NULL, u8, onionlen); + if (!hex_decode(hexonion, strlen(hexonion), compressed, onionlen)) + errx(1, "Invalid onion hex '%s'", hexonion); + + if (onionlen < HMAC_SIZE + 1 + PUBKEY_SIZE) + errx(1, "Onion is too short to contain the version, ephemeral key and HMAC"); + + pubkey_from_der(compressed + 1, PUBKEY_SIZE, &ephkey); + + decompressed = sphinx_decompress(NULL, compressed, &shared_secret); + printf("Decompressed Onion: %s\n", tal_hex(NULL, decompressed)); +} + /* Tal wrappers for opt. */ static void *opt_allocfn(size_t size) { @@ -353,6 +377,15 @@ int main(int argc, char **argv) do_generate(argc, argv, assocdata, NULL); else do_generate(argc, argv, assocdata, &rendezvous_id); + } else if (streq(method, "decompress")) { + if (argc != 4) { + errx(2, + "'%s decompress' requires a private key and a " + "compressed onion", + argv[0]); + } + + decompress(argv[2], argv[3]); } else if (streq(method, "decode")) { do_decode(argc, argv, assocdata); } else {