diff --git a/common/Makefile b/common/Makefile index a830c9c5a..8c2d6e87b 100644 --- a/common/Makefile +++ b/common/Makefile @@ -83,6 +83,7 @@ COMMON_SRC_NOGEN := \ ifeq ($(EXPERIMENTAL_FEATURES),1) COMMON_SRC_NOGEN += common/psbt_internal.c +COMMON_SRC_NOGEN += common/blindedpath.c endif COMMON_SRC_GEN := common/status_wiregen.c common/peer_status_wiregen.c diff --git a/common/blindedpath.c b/common/blindedpath.c new file mode 100644 index 000000000..d6c4c15e1 --- /dev/null +++ b/common/blindedpath.c @@ -0,0 +1,101 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* FIXME: Pad with dummy nodes, but make sure to check them on recv! */ +struct onionmsg_path **make_blindedpath(const tal_t *ctx, + const struct pubkey *route, + struct pubkey *initial_blinding, + struct pubkey *final_blinding) +{ + struct privkey e; + struct pubkey *pk_e, *b; + struct secret *rho; + struct onionmsg_path **path; + size_t num = tal_count(route); + + if (!num) + abort(); + + /* E(i) */ + pk_e = tal_arr(tmpctx, struct pubkey, num); + /* B(i) */ + b = tal_arr(tmpctx, struct pubkey, num); + /* rho(i) */ + rho = tal_arr(tmpctx, struct secret, num); + + randombytes_buf(&e, sizeof(e)); + if (!pubkey_from_privkey(&e, &pk_e[0])) + abort(); + + for (size_t i = 0; i < num; i++) { + struct secret ss; + struct secret hmac; + struct sha256 h; + + if (secp256k1_ecdh(secp256k1_ctx, ss.data, + &route[i].pubkey, e.secret.data, + NULL, NULL) != 1) + abort(); + + subkey_from_hmac("blinded_node_id", &ss, &hmac); + b[i] = route[i]; + if (i != 0) { + if (secp256k1_ec_pubkey_tweak_mul(secp256k1_ctx, + &b[i].pubkey, hmac.data) != 1) + abort(); + } + subkey_from_hmac("rho", &ss, &rho[i]); + blinding_hash_e_and_ss(&pk_e[i], &ss, &h); + if (i != num-1) + blinding_next_pubkey(&pk_e[i], &h, &pk_e[i+1]); + blinding_next_privkey(&e, &h, &e); + } + + *initial_blinding = pk_e[0]; + *final_blinding = pk_e[num-1]; + + path = tal_arr(ctx, struct onionmsg_path *, num); + for (size_t i = 0; i < num; i++) { + path[i] = tal(path, struct onionmsg_path); + path[i]->node_id = b[i]; + } + + for (size_t i = 0; i < num - 1; i++) { + const unsigned char npub[crypto_aead_chacha20poly1305_ietf_NPUBBYTES] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + struct tlv_onionmsg_payload *inner; + int ret; + + /* Inner is encrypted */ + inner = tlv_onionmsg_payload_new(tmpctx); + /* FIXME: We could support scids, too */ + inner->next_node_id = cast_const(struct pubkey *, &route[i+1]); + + path[i]->enctlv = tal_arr(path, u8, 0); + towire_encmsg_tlvs(&path[i]->enctlv, inner); + towire_pad(&path[i]->enctlv, + crypto_aead_chacha20poly1305_ietf_ABYTES); + + ret = crypto_aead_chacha20poly1305_ietf_encrypt(path[i]->enctlv, NULL, + path[i]->enctlv, + tal_bytelen(path[i]->enctlv) - crypto_aead_chacha20poly1305_ietf_ABYTES, + NULL, 0, + NULL, npub, + rho[i].data); + assert(ret == 0); + } + + /* Final one has no enctlv */ + path[num-1]->enctlv = NULL; + + return path; +} diff --git a/common/blindedpath.h b/common/blindedpath.h new file mode 100644 index 000000000..ae198132c --- /dev/null +++ b/common/blindedpath.h @@ -0,0 +1,17 @@ +#ifndef LIGHTNING_COMMON_BLINDEDPATH_H +#define LIGHTNING_COMMON_BLINDEDPATH_H +#include "config.h" +#include + +#if EXPERIMENTAL_FEATURES +struct route_info; +struct pubkey; + +/* Fills in *initial_blinding and *final_blinding and returns + * onionmsg_path array for this route */ +struct onionmsg_path **make_blindedpath(const tal_t *ctx, + const struct pubkey *route, + struct pubkey *initial_blinding, + struct pubkey *final_blinding); +#endif /* EXPERIMENTAL_FEATURES */ +#endif /* LIGHTNING_COMMON_BLINDEDPATH_H */