From 53e40c4380df362ac2fe6b7107d011898e26eca2 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 17 Oct 2022 11:13:07 +1030 Subject: [PATCH] common/blindedpath: generalize routines. We're going to share them for onion messages as well as for blinded payments. Signed-off-by: Rusty Russell --- common/blindedpath.c | 178 +++++++++++---------------- common/blindedpath.h | 51 ++++---- common/test/run-blindedpath_enctlv.c | 20 +-- common/test/run-blindedpath_onion.c | 8 +- connectd/onion_message.c | 75 ++++++++++- 5 files changed, 181 insertions(+), 151 deletions(-) diff --git a/common/blindedpath.c b/common/blindedpath.c index 1493e17a7..939be49a8 100644 --- a/common/blindedpath.c +++ b/common/blindedpath.c @@ -19,36 +19,27 @@ static bool blind_node(const struct privkey *blinding, struct pubkey *node_alias, struct privkey *next_blinding) { - struct secret node_id_blinding; struct pubkey blinding_pubkey; struct sha256 h; - /* - * Blinded node_id for N(i), private key known only by N(i): - * B(i) = HMAC256("blinded_node_id", ss(i)) * P(i) - */ - subkey_from_hmac("blinded_node_id", ss, &node_id_blinding); - SUPERVERBOSE("\t\"HMAC256('blinded_node_id', ss)\": \"%s\",\n", - type_to_string(tmpctx, struct secret, - &node_id_blinding)); - - *node_alias = *node; - if (secp256k1_ec_pubkey_tweak_mul(secp256k1_ctx, - &node_alias->pubkey, - node_id_blinding.data) != 1) + if (!blindedpath_get_alias(ss, node, node_alias)) return false; SUPERVERBOSE("\t\"blinded_node_id\": \"%s\",\n", type_to_string(tmpctx, struct pubkey, node_alias)); - /* - * Ephemeral private key, only known by N(r): - * e(i+1) = H(E(i) || ss(i)) * e(i) + /* BOLT-route-blinding #4: + * - `E(i+1) = SHA256(E(i) || ss(i)) * E(i)` + * (NB: `N(i)` MUST NOT learn `e(i)`) */ if (!pubkey_from_privkey(blinding, &blinding_pubkey)) return false; SUPERVERBOSE("\t\"E\": \"%s\",\n", type_to_string(tmpctx, struct pubkey, &blinding_pubkey)); + /* BOLT-route-blinding #4: + * - `e(i+1) = SHA256(E(i) || ss(i)) * e(i)` + * (blinding ephemeral private key, only known by `N(r)`) + */ blinding_hash_e_and_ss(&blinding_pubkey, ss, &h); SUPERVERBOSE("\t\"H(E || ss)\": \"%s\",\n", type_to_string(tmpctx, struct sha256, &h)); @@ -66,16 +57,15 @@ static u8 *enctlv_from_encmsg_raw(const tal_t *ctx, struct privkey *next_blinding, struct pubkey *node_alias) { - /* https://github.com/lightning/bolts/blob/route-blinding/proposals/route-blinding.md */ struct secret ss, rho; u8 *ret; int ok; /* All-zero npub */ static const unsigned char npub[crypto_aead_chacha20poly1305_ietf_NPUBBYTES]; - /* - * shared secret known only by N(r) and N(i): - * ss(i) = H(e(i) * P(i)) = H(k(i) * E(i)) + /* BOLT-route-blinding #4: + * - `ss(i) = SHA256(e(i) * N(i)) = SHA256(k(i) * E(i))` + * (ECDH shared secret known only by `N(r)` and `N(i)`) */ if (secp256k1_ecdh(secp256k1_ctx, ss.data, &node->pubkey, blinding->secret.data, @@ -91,17 +81,20 @@ static u8 *enctlv_from_encmsg_raw(const tal_t *ctx, ret = tal_dup_talarr(ctx, u8, raw_encmsg); SUPERVERBOSE("\t\"encmsg_hex\": \"%s\",\n", tal_hex(tmpctx, ret)); - /* - * Key used to encrypt payload for N(i) by N(r): - * rho(i) = HMAC256("rho", ss(i)) + /* BOLT-route-blinding #4: + * - `rho(i) = HMAC256("rho", ss(i))` + * (key used to encrypt the payload for `N(i)` by `N(r)`) */ subkey_from_hmac("rho", &ss, &rho); SUPERVERBOSE("\t\"rho\": \"%s\",\n", type_to_string(tmpctx, struct secret, &rho)); + /* BOLT-route-blinding #4: + * - MUST encrypt them with ChaCha20-Poly1305 using the `rho(i)` key + * and an all-zero nonce + */ /* Encrypt in place */ towire_pad(&ret, crypto_aead_chacha20poly1305_ietf_ABYTES); - ok = crypto_aead_chacha20poly1305_ietf_encrypt(ret, NULL, ret, tal_bytelen(ret) @@ -134,15 +127,24 @@ bool unblind_onion(const struct pubkey *blinding, { struct secret hmac; - /* E(i) */ + /* BOLT-route-blinding #4: + * An intermediate node in the blinded route: + * + * - MUST compute: + * - `ss(i) = SHA256(k(i) * E(i))` (standard ECDH) + * - `b(i) = HMAC256("blinded_node_id", ss(i)) * k(i)` + */ ecdh(blinding, ss); - - /* b(i) = HMAC256("blinded_node_id", ss(i)) * k(i) */ subkey_from_hmac("blinded_node_id", ss, &hmac); /* We instead tweak the *ephemeral* key from the onion and use * our normal privkey: since hsmd knows only how to ECDH with - * our real key */ + * our real key. IOW: */ + /* BOLT-route-blinding #4: + * - MUST use `b(i)` instead of its private key `k(i)` to decrypt the onion. Note + * that the node may instead tweak the onion ephemeral key with + * `HMAC256("blinded_node_id", ss(i))` which achieves the same result. + */ return secp256k1_ec_pubkey_tweak_mul(secp256k1_ctx, &onion_key->pubkey, hmac.data) == 1; @@ -158,7 +160,10 @@ static u8 *decrypt_encmsg_raw(const tal_t *ctx, /* All-zero npub */ static const unsigned char npub[crypto_aead_chacha20poly1305_ietf_NPUBBYTES]; - /* We need this to decrypt enctlv */ + /* BOLT-route-blinding #4: + * - If an `encrypted_data` field is provided: + * - MUST decrypt it using `rho(r)` + */ subkey_from_hmac("rho", ss, &rho); /* BOLT-onion-message #4: @@ -183,10 +188,10 @@ static u8 *decrypt_encmsg_raw(const tal_t *ctx, return dec; } -static struct tlv_encrypted_data_tlv *decrypt_encmsg(const tal_t *ctx, - const struct pubkey *blinding, - const struct secret *ss, - const u8 *enctlv) +struct tlv_encrypted_data_tlv *decrypt_encrypted_data(const tal_t *ctx, + const struct pubkey *blinding, + const struct secret *ss, + const u8 *enctlv) { const u8 *cursor = decrypt_encmsg_raw(tmpctx, blinding, ss, enctlv); size_t maxlen = tal_bytelen(cursor); @@ -203,93 +208,48 @@ static struct tlv_encrypted_data_tlv *decrypt_encmsg(const tal_t *ctx, return fromwire_tlv_encrypted_data_tlv(ctx, &cursor, &maxlen); } -bool decrypt_enctlv(const struct pubkey *blinding, - const struct secret *ss, - const u8 *enctlv, - struct pubkey *next_node, - struct pubkey *next_blinding) +bool blindedpath_get_alias(const struct secret *ss, + const struct pubkey *my_id, + struct pubkey *alias) { - struct tlv_encrypted_data_tlv *encmsg; + struct secret node_id_blinding; - encmsg = decrypt_encmsg(tmpctx, blinding, ss, enctlv); - if (!encmsg) - return false; - - /* BOLT-onion-message #4: - * - * The reader: - * - if it is not the final node according to the onion encryption: - *... - * - if the `enctlv` ... does not contain - * `next_node_id`: - * - MUST drop the message. + /* BOLT-route-blinding #4: + * - `B(i) = HMAC256("blinded_node_id", ss(i)) * N(i)` + * (blinded `node_id` for `N(i)`, private key known only by `N(i)`) */ - if (!encmsg->next_node_id) - return false; + subkey_from_hmac("blinded_node_id", ss, &node_id_blinding); + SUPERVERBOSE("\t\"HMAC256('blinded_node_id', ss)\": \"%s\",\n", + type_to_string(tmpctx, struct secret, + &node_id_blinding)); - /* BOLT-onion-message #4: - * The reader: - * - if it is not the final node according to the onion encryption: - *... - * - if the `enctlv` contains `path_id`: - * - MUST drop the message. - */ - if (encmsg->path_id) - return false; + *alias = *my_id; + return secp256k1_ec_pubkey_tweak_mul(secp256k1_ctx, + &alias->pubkey, + node_id_blinding.data) == 1; +} - /* BOLT-onion-message #4: - * The reader: - * - if it is not the final node according to the onion encryption: - *... - * - if `blinding` is specified in the `enctlv`: - * - MUST pass that as `blinding` in the `onion_message` - * - otherwise: - * - MUST pass `blinding` derived as in - * [Route Blinding][route-blinding] (i.e. - * `E(i+1) = H(E(i) || ss(i)) * E(i)`). +void blindedpath_next_blinding(const struct tlv_encrypted_data_tlv *enc, + const struct pubkey *blinding, + const struct secret *ss, + struct pubkey *next_blinding) +{ + /* BOLT-route + * - `E(1) = SHA256(E(0) || ss(0)) * E(0)` + * ... + * - If `encrypted_data` contains a `next_blinding_override`: + * - MUST use it as the next blinding point instead of `E(1)` + * - Otherwise: + * - MUST use `E(1)` as the next blinding point */ - *next_node = *encmsg->next_node_id; - if (encmsg->next_blinding_override) - *next_blinding = *encmsg->next_blinding_override; + if (enc->next_blinding_override) + *next_blinding = *enc->next_blinding_override; else { /* E(i-1) = H(E(i) || ss(i)) * E(i) */ struct sha256 h; blinding_hash_e_and_ss(blinding, ss, &h); blinding_next_pubkey(blinding, &h, next_blinding); } - return true; -} - -bool decrypt_final_enctlv(const tal_t *ctx, - const struct pubkey *blinding, - const struct secret *ss, - const u8 *enctlv, - const struct pubkey *my_id, - struct pubkey *alias, - struct secret **path_id) -{ - struct tlv_encrypted_data_tlv *encmsg; - struct secret node_id_blinding; - - /* Repeat the tweak to get the alias it was using for us */ - subkey_from_hmac("blinded_node_id", ss, &node_id_blinding); - *alias = *my_id; - if (secp256k1_ec_pubkey_tweak_mul(secp256k1_ctx, - &alias->pubkey, - node_id_blinding.data) != 1) - return false; - - encmsg = decrypt_encmsg(tmpctx, blinding, ss, enctlv); - if (!encmsg) - return false; - - if (tal_bytelen(encmsg->path_id) == sizeof(**path_id)) { - *path_id = tal(ctx, struct secret); - memcpy(*path_id, encmsg->path_id, sizeof(**path_id)); - } else - *path_id = NULL; - - return true; } u8 *create_enctlv(const tal_t *ctx, diff --git a/common/blindedpath.h b/common/blindedpath.h index 285ec189d..3bf092ba6 100644 --- a/common/blindedpath.h +++ b/common/blindedpath.h @@ -68,41 +68,38 @@ bool unblind_onion(const struct pubkey *blinding, NO_NULL_ARGS; /** - * decrypt_enctlv - Decrypt an encmsg to form an enctlv. - * @blinding: E(i), the blinding pubkey the previous peer gave us. - * @ss: the blinding secret from unblind_onion(). - * @enctlv: the enctlv from the onion (tal, may be NULL). - * @next_node: (out) the next node_id. - * @next_blinding: (out) the next blinding E(i+1). + * blindedpath_get_alias - tweak our id to see alias they used. + * @ss: the shared secret from unblind_onion + * @my_id: my node_id + * @alias: (out) the alias. * - * Returns false if decryption failed or encmsg was malformed. + * Returns false on ECDH fail. */ -bool decrypt_enctlv(const struct pubkey *blinding, - const struct secret *ss, - const u8 *enctlv, - struct pubkey *next_node, - struct pubkey *next_blinding) - NON_NULL_ARGS(1, 2, 4, 5); +bool blindedpath_get_alias(const struct secret *ss, + const struct pubkey *my_id, + struct pubkey *alias); /** - * decrypt_final_enctlv - Decrypt an encmsg to form an enctlv. - * @ctx: tal context for @path_id + * decrypt_encrypted_data - Decrypt an encmsg to form an tlv_encrypted_data_tlv. + * @ctx: the context to allocate off. * @blinding: E(i), the blinding pubkey the previous peer gave us. * @ss: the blinding secret from unblind_onion(). * @enctlv: the enctlv from the onion (tal, may be NULL). - * @my_id: the pubkey of this node. - * @alias: (out) the node_id this was addressed to. - * @path_id: (out) the secret contained in the enctlv, if any (NULL if invalid or unset) * - * Returns false if decryption failed or encmsg was malformed. + * Returns NULL if decryption failed or encmsg was malformed. */ -bool decrypt_final_enctlv(const tal_t *ctx, - const struct pubkey *blinding, - const struct secret *ss, - const u8 *enctlv, - const struct pubkey *my_id, - struct pubkey *alias, - struct secret **path_id) - NON_NULL_ARGS(1, 2, 4, 5); +struct tlv_encrypted_data_tlv *decrypt_encrypted_data(const tal_t *ctx, + const struct pubkey *blinding, + const struct secret *ss, + const u8 *enctlv) + NON_NULL_ARGS(2, 3); + +/** + * blindedpath_next_blinding - Calculate or extract next blinding pubkey + */ +void blindedpath_next_blinding(const struct tlv_encrypted_data_tlv *enc, + const struct pubkey *blinding, + const struct secret *ss, + struct pubkey *next_blinding); #endif /* LIGHTNING_COMMON_BLINDEDPATH_H */ diff --git a/common/test/run-blindedpath_enctlv.c b/common/test/run-blindedpath_enctlv.c index 9c619b7b3..5f30c9fd6 100644 --- a/common/test/run-blindedpath_enctlv.c +++ b/common/test/run-blindedpath_enctlv.c @@ -91,19 +91,22 @@ static void test_decrypt(const struct pubkey *blinding, const struct pubkey *expected_next_node, const struct privkey *expected_next_blinding_priv) { - struct pubkey expected_next_blinding, dummy, next_node, next_blinding; + struct pubkey expected_next_blinding, dummy, next_blinding; struct secret ss; + struct tlv_encrypted_data_tlv *enc; /* We don't actually have an onion, so we put some dummy */ pubkey_from_privkey(me, &dummy); mykey = me; assert(unblind_onion(blinding, test_ecdh, &dummy, &ss)); - assert(decrypt_enctlv(blinding, &ss, enctlv, &next_node, &next_blinding)); + enc = decrypt_encrypted_data(tmpctx, blinding, &ss, enctlv); + assert(enc); pubkey_from_privkey(expected_next_blinding_priv, &expected_next_blinding); + blindedpath_next_blinding(enc, blinding, &ss, &next_blinding); assert(pubkey_eq(&next_blinding, &expected_next_blinding)); - assert(pubkey_eq(&next_node, expected_next_node)); + assert(pubkey_eq(enc->next_node_id, expected_next_node)); } static void test_final_decrypt(const struct pubkey *blinding, @@ -113,7 +116,8 @@ static void test_final_decrypt(const struct pubkey *blinding, const struct secret *expected_self_id) { struct pubkey my_pubkey, dummy, alias; - struct secret ss, *self_id; + struct secret ss; + struct tlv_encrypted_data_tlv *enc; /* We don't actually have an onion, so we put some dummy */ pubkey_from_privkey(me, &dummy); @@ -121,11 +125,13 @@ static void test_final_decrypt(const struct pubkey *blinding, mykey = me; pubkey_from_privkey(me, &my_pubkey); assert(unblind_onion(blinding, test_ecdh, &dummy, &ss)); - assert(decrypt_final_enctlv(tmpctx, blinding, &ss, enctlv, &my_pubkey, - &alias, &self_id)); + enc = decrypt_encrypted_data(tmpctx, blinding, &ss, enctlv); + assert(enc); + assert(blindedpath_get_alias(&ss, &my_pubkey, &alias)); assert(pubkey_eq(&alias, expected_alias)); - assert(secret_eq_consttime(self_id, expected_self_id)); + assert(memeq(enc->path_id, tal_bytelen(enc->path_id), expected_self_id, + sizeof(*expected_self_id))); } int main(int argc, char *argv[]) diff --git a/common/test/run-blindedpath_onion.c b/common/test/run-blindedpath_onion.c index ef9711dca..db7f7fe53 100644 --- a/common/test/run-blindedpath_onion.c +++ b/common/test/run-blindedpath_onion.c @@ -108,12 +108,13 @@ static u8 *next_onion(const tal_t *ctx, u8 *omsg, { struct onionpacket *op; struct pubkey blinding, ephemeral; - struct pubkey next_node, next_blinding; + struct pubkey next_blinding; struct tlv_onionmsg_payload *om; struct secret ss, onion_ss; const u8 *cursor; size_t max, maxlen; struct route_step *rs; + struct tlv_encrypted_data_tlv *enc; assert(fromwire_onion_message(tmpctx, omsg, &blinding, &omsg)); assert(pubkey_eq(&blinding, expected_blinding)); @@ -137,8 +138,9 @@ static u8 *next_onion(const tal_t *ctx, u8 *omsg, if (rs->nextcase == ONION_END) return NULL; - assert(decrypt_enctlv(&blinding, &ss, om->encrypted_data_tlv, &next_node, - &next_blinding)); + enc = decrypt_encrypted_data(tmpctx, &blinding, &ss, om->encrypted_data_tlv); + assert(enc); + blindedpath_next_blinding(enc, &blinding, &ss, &next_blinding); return towire_onion_message(ctx, &next_blinding, serialize_onionpacket(tmpctx, rs->next)); } diff --git a/connectd/onion_message.c b/connectd/onion_message.c index d84349d53..ecc7bbacf 100644 --- a/connectd/onion_message.c +++ b/connectd/onion_message.c @@ -35,6 +35,71 @@ void onionmsg_req(struct daemon *daemon, const u8 *msg) } } +static bool decrypt_final_onionmsg(const tal_t *ctx, + const struct pubkey *blinding, + const struct secret *ss, + const u8 *enctlv, + const struct pubkey *my_id, + struct pubkey *alias, + struct secret **path_id) +{ + struct tlv_encrypted_data_tlv *encmsg; + + if (!blindedpath_get_alias(ss, my_id, alias)) + return false; + + encmsg = decrypt_encrypted_data(tmpctx, blinding, ss, enctlv); + if (!encmsg) + return false; + + if (tal_bytelen(encmsg->path_id) == sizeof(**path_id)) { + *path_id = tal(ctx, struct secret); + memcpy(*path_id, encmsg->path_id, sizeof(**path_id)); + } else + *path_id = NULL; + + return true; +} + +static bool decrypt_forwarding_onionmsg(const struct pubkey *blinding, + const struct secret *ss, + const u8 *enctlv, + struct pubkey *next_node, + struct pubkey *next_blinding) +{ + struct tlv_encrypted_data_tlv *encmsg; + + encmsg = decrypt_encrypted_data(tmpctx, blinding, ss, enctlv); + if (!encmsg) + return false; + + /* BOLT-onion-message #4: + * + * The reader: + * - if it is not the final node according to the onion encryption: + *... + * - if the `enctlv` ... does not contain + * `next_node_id`: + * - MUST drop the message. + */ + if (!encmsg->next_node_id) + return false; + + /* BOLT-onion-message #4: + * The reader: + * - if it is not the final node according to the onion encryption: + *... + * - if the `enctlv` contains `path_id`: + * - MUST drop the message. + */ + if (encmsg->path_id) + return false; + + *next_node = *encmsg->next_node_id; + blindedpath_next_blinding(encmsg, blinding, ss, next_blinding); + return true; +} + /* Peer sends an onion msg. */ void handle_onion_message(struct daemon *daemon, struct peer *peer, const u8 *msg) @@ -123,9 +188,9 @@ void handle_onion_message(struct daemon *daemon, if (!om->encrypted_data_tlv) { alias = me; self_id = NULL; - } else if (!decrypt_final_enctlv(tmpctx, &blinding, &ss, - om->encrypted_data_tlv, &me, &alias, - &self_id)) { + } else if (!decrypt_final_onionmsg(tmpctx, &blinding, &ss, + om->encrypted_data_tlv, &me, &alias, + &self_id)) { status_peer_debug(&peer->id, "onion msg: failed to decrypt enctlv" " %s", tal_hex(tmpctx, om->encrypted_data_tlv)); @@ -159,8 +224,8 @@ void handle_onion_message(struct daemon *daemon, struct node_id next_node_id; /* This fails as expected if no enctlv. */ - if (!decrypt_enctlv(&blinding, &ss, om->encrypted_data_tlv, &next_node, - &next_blinding)) { + if (!decrypt_forwarding_onionmsg(&blinding, &ss, om->encrypted_data_tlv, &next_node, + &next_blinding)) { status_peer_debug(&peer->id, "onion msg: invalid enctlv %s", tal_hex(tmpctx, om->encrypted_data_tlv));