diff --git a/common/blindedpath.c b/common/blindedpath.c index ae6ef2960..7ab13e303 100644 --- a/common/blindedpath.c +++ b/common/blindedpath.c @@ -204,6 +204,167 @@ static u8 *enctlv_from_encmsg(const tal_t *ctx, return ret; } +bool unblind_onion(const struct pubkey *blinding, + void (*ecdh)(const struct pubkey *point, struct secret *ss), + struct pubkey *onion_key, + struct secret *ss) +{ + struct secret hmac; + + /* E(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 */ + return secp256k1_ec_pubkey_tweak_mul(secp256k1_ctx, + &onion_key->pubkey, + hmac.data) == 1; +} + +static struct tlv_encmsg_tlvs *decrypt_encmsg(const tal_t *ctx, + const struct pubkey *blinding, + const struct secret *ss, + const u8 *enctlv) +{ + struct secret rho; + u8 *dec; + const u8 *cursor; + size_t maxlen; + struct tlv_encmsg_tlvs *encmsg; + /* All-zero npub */ + static const unsigned char npub[crypto_aead_chacha20poly1305_ietf_NPUBBYTES]; + + /* We need this to decrypt enctlv */ + subkey_from_hmac("rho", ss, &rho); + + /* BOLT-onion-message #4: + * - if `enctlv` is not present, or does not decrypt with the + * shared secret from the given `blinding` parameter: + * - MUST drop the message. + */ + /* Too short? */ + if (tal_bytelen(enctlv) < crypto_aead_chacha20poly1305_ietf_ABYTES) + return NULL; + + dec = tal_arr(tmpctx, u8, tal_bytelen(enctlv) + - crypto_aead_chacha20poly1305_ietf_ABYTES); + if (crypto_aead_chacha20poly1305_ietf_decrypt(dec, NULL, + NULL, + enctlv, tal_bytelen(enctlv), + NULL, 0, + npub, + rho.data) != 0) + return NULL; + + cursor = dec; + maxlen = tal_bytelen(dec); + + /* BOLT-onion-message #4: + * + * - if the `enctlv` is not a valid TLV... + * - MUST drop the message. + */ + encmsg = tlv_encmsg_tlvs_new(ctx); + if (!fromwire_encmsg_tlvs(&cursor, &maxlen, encmsg) + || !tlv_fields_valid(encmsg->fields, NULL, NULL)) + return tal_free(encmsg); + + return encmsg; +} + +bool decrypt_enctlv(const struct pubkey *blinding, + const struct secret *ss, + const u8 *enctlv, + struct pubkey *next_node, + struct pubkey *next_blinding) +{ + struct tlv_encmsg_tlvs *encmsg; + + 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. + */ + 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 `self_id`: + * - MUST drop the message. + */ + if (encmsg->self_id) + return false; + + /* 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)`). + */ + *next_node = *encmsg->next_node_id; + if (encmsg->next_blinding) + *next_blinding = *encmsg->next_blinding; + 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 **self_id) +{ + struct tlv_encmsg_tlvs *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->self_id) == sizeof(**self_id)) { + *self_id = tal(ctx, struct secret); + memcpy(*self_id, encmsg->self_id, sizeof(**self_id)); + } else + *self_id = NULL; + + return true; +} + u8 *create_enctlv(const tal_t *ctx, const struct privkey *blinding, const struct pubkey *node, diff --git a/common/blindedpath.h b/common/blindedpath.h index 6c2124450..d4adb7bec 100644 --- a/common/blindedpath.h +++ b/common/blindedpath.h @@ -59,4 +59,57 @@ u8 *create_final_enctlv(const tal_t *ctx, struct pubkey *node_alias) NON_NULL_ARGS(2, 3, 6); +/** + * unblind_onion - tweak onion epheremeral key so we can decode it with ours. + * @blinding: E(i), the blinding pubkey the previous peer gave us. + * @ecdh: the ecdh routine (usually ecdh from common/ecdh_hsmd). + * @onion_key: (in, out) the onionpacket->ephemeralkey to tweak. + * @ss: (out) the shared secret we gained from blinding pubkey. + * + * The shared secret is needed to decrypt the enctlv we expect to find, too. + */ +bool unblind_onion(const struct pubkey *blinding, + void (*ecdh)(const struct pubkey *point, struct secret *ss), + struct pubkey *onion_key, + struct secret *ss) + 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). + * + * Returns false if decryption failed or encmsg was malformed. + */ +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); + +/** + * decrypt_final_enctlv - Decrypt an encmsg to form an enctlv. + * @ctx: tal context for @self_id + * @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. + * @self_id: (out) the secret contained in the enctlv, if any. + * + * Returns false 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 **self_id) + NON_NULL_ARGS(1, 2, 4, 5); + #endif /* LIGHTNING_COMMON_BLINDEDPATH_H */ diff --git a/common/test/run-blindedpath_enctlv.c b/common/test/run-blindedpath_enctlv.c index 6b76b70e4..ec735ec10 100644 --- a/common/test/run-blindedpath_enctlv.c +++ b/common/test/run-blindedpath_enctlv.c @@ -60,10 +60,64 @@ static void json_strfield(const char *name, const char *val) printf("\t\"%s\": \"%s\",\n", name, val); } +/* Updated each time, as we pretend to be Alice, Bob, Carol */ +static const struct privkey *mykey; + +static void test_ecdh(const struct pubkey *point, struct secret *ss) +{ + if (secp256k1_ecdh(secp256k1_ctx, ss->data, &point->pubkey, + mykey->secret.data, NULL, NULL) != 1) + abort(); +} + +static void test_decrypt(const struct pubkey *blinding, + const u8 *enctlv, + const struct privkey *me, + const struct pubkey *expected_next_node, + const struct privkey *expected_next_blinding_priv) +{ + struct pubkey expected_next_blinding, dummy, next_node, next_blinding; + struct secret ss; + + /* 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)); + + pubkey_from_privkey(expected_next_blinding_priv, &expected_next_blinding); + assert(pubkey_eq(&next_blinding, &expected_next_blinding)); + assert(pubkey_eq(&next_node, expected_next_node)); +} + +static void test_final_decrypt(const struct pubkey *blinding, + const u8 *enctlv, + const struct privkey *me, + const struct pubkey *expected_alias, + const struct secret *expected_self_id) +{ + struct pubkey my_pubkey, dummy, alias; + struct secret ss, *self_id; + + /* We don't actually have an onion, so we put some dummy */ + pubkey_from_privkey(me, &dummy); + + 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)); + + assert(pubkey_eq(&alias, expected_alias)); + assert(secret_eq_consttime(self_id, expected_self_id)); +} + int main(int argc, char *argv[]) { struct privkey alice, bob, carol, dave, blinding, override_blinding; struct pubkey alice_id, bob_id, carol_id, dave_id, blinding_pub, override_blinding_pub, alias; + struct secret self_id; u8 *enctlv; common_setup(argv[0]); @@ -102,6 +156,8 @@ int main(int argc, char *argv[]) "},\n", tal_hex(tmpctx, enctlv)); + test_decrypt(&blinding_pub, enctlv, &alice, &bob_id, &blinding); + pubkey_from_privkey(&blinding, &blinding_pub); memset(&override_blinding, 7, sizeof(override_blinding)); pubkey_from_privkey(&override_blinding, &override_blinding_pub); @@ -130,6 +186,8 @@ int main(int argc, char *argv[]) "},\n", tal_hex(tmpctx, enctlv)); + test_decrypt(&blinding_pub, enctlv, &bob, &carol_id, &override_blinding); + /* That replaced the blinding */ blinding = override_blinding; blinding_pub = override_blinding_pub; @@ -157,5 +215,15 @@ int main(int argc, char *argv[]) "}]\n", tal_hex(tmpctx, enctlv)); + test_decrypt(&blinding_pub, enctlv, &carol, &dave_id, &blinding); + + /* FIXME: Add this to the test vectors! */ + fclose(stdout); + memset(&self_id, 0x77, sizeof(self_id)); + enctlv = create_final_enctlv(tmpctx, &blinding, &dave_id, + 0, &self_id, &alias); + + pubkey_from_privkey(&blinding, &blinding_pub); + test_final_decrypt(&blinding_pub, enctlv, &dave, &alias, &self_id); common_shutdown(); }