From 58d80473e0bc7a0f133f6da30c74a72ef4faea5c Mon Sep 17 00:00:00 2001 From: t-bast Date: Thu, 31 Mar 2022 17:03:56 +0200 Subject: [PATCH] Bolt 4: add route blinding construction Add specification requirements for creating and using blinded routes. This commit contains the low-level details of the route blinding scheme, decoupled from how it can be used by high-level components such as onion messages or payments. --- 04-onion-routing.md | 148 ++++++++++++++++++++++++++++++++ bolt04/route-blinding-test.json | 139 ++++++++++++++++++++++++++++++ 2 files changed, 287 insertions(+) create mode 100644 bolt04/route-blinding-test.json diff --git a/04-onion-routing.md b/04-onion-routing.md index b2242e052..4642d8178 100644 --- a/04-onion-routing.md +++ b/04-onion-routing.md @@ -50,6 +50,7 @@ A node: * [Packet Structure](#packet-structure) * [Payload Format](#payload-format) * [Basic Multi-Part Payments](#basic-multi-part-payments) + * [Route Blinding](#route-blinding) * [Accepting and Forwarding a Payment](#accepting-and-forwarding-a-payment) * [Payload for the Last Node](#payload-for-the-last-node) * [Non-strict Forwarding](#non-strict-forwarding) @@ -352,6 +353,153 @@ otherwise meets the amount criterion (eg. some other failure, or invoice timeout), however if it were to fulfill only some of them, intermediary nodes could simply claim the remaining ones. +### Route Blinding + +Nodes receiving onion packets may hide their identity from senders by +"blinding" an arbitrary amount of hops at the end of an onion path. + +When using route blinding, nodes find a route to themselves from a given +"introduction node". They then use ECDH with each node in that route to create +a "blinded" node ID and an encrypted blob (`encrypted_data`) for each one of +the blinded nodes. + +They communicate this blinded route and the encrypted blobs to the sender. +The sender finds a route to the introduction node and extends it with the +blinded route provided by the recipient. The sender includes the encrypted +blobs in the corresponding onion payloads: they allow nodes in the blinded +part of the route to "unblind" the next node and correctly forward the packet. + +The `encrypted_data` is a TLV stream, encrypted for a given blinded node, that +may contain the following TLV fields: + +1. `tlv_stream`: `encrypted_data_tlv` +2. types: + 1. type: 1 (`padding`) + 2. data: + * [`...*byte`:`padding`] + 1. type: 2 (`short_channel_id`) + 2. data: + * [`short_channel_id`:`short_channel_id`] + 1. type: 4 (`next_node_id`) + 2. data: + * [`point`:`node_id`] + 1. type: 6 (`path_id`) + 2. data: + * [`...*byte`:`data`] + 1. type: 8 (`next_blinding_override`) + 2. data: + * [`point`:`blinding`] + +#### Requirements + +A recipient N(r) creating a blinded route `N(0) -> N(1) -> ... -> N(r)` to itself: + +- MUST create a blinded node ID `B(i)` for each node using the following algorithm: + - `e(0) <- {0;1}^256` + - `E(0) = e(0) * G` + - For every node in the route: + - let `N(i) = k(i) * G` be the `node_id` (`k(i)` is `N(i)`'s private key) + - `ss(i) = SHA256(e(i) * N(i)) = SHA256(k(i) * E(i))` (ECDH shared secret known only by `N(r)` and `N(i)`) + - `B(i) = HMAC256("blinded_node_id", ss(i)) * N(i)` (blinded `node_id` for `N(i)`, private key known only by `N(i)`) + - `rho(i) = HMAC256("rho", ss(i))` (key used to encrypt the payload for `N(i)` by `N(r)`) + - `e(i+1) = SHA256(E(i) || ss(i)) * e(i)` (blinding ephemeral private key, only known by `N(r)`) + - `E(i+1) = SHA256(E(i) || ss(i)) * E(i)` (NB: `N(i)` MUST NOT learn `e(i)`) +- If it creates `encrypted_data` payloads for blinded nodes: + - MUST encrypt them with ChaCha20-Poly1305 using the `rho(i)` key and an all-zero nonce + - MAY store private data in the `path_id` of the payload to itself to verify + that the route is used in the right context and was created by them + - SHOULD add padding data to ensure all `encrypted_data` have the same length +- MUST communicate the blinded node IDs `B(i)` and `encrypted_data(i)` to the sender +- MUST communicate the real node ID of the introduction point `N(0)` to the sender +- MUST communicate the first blinding ephemeral key `E(0)` to the sender + +The sender: + +- MUST provide the `encrypted_data` field for each node in the blinded route + in their respective onion payloads +- MUST communicate `E(0)` to the introduction point (e.g. in the onion payload + for that node or in a tlv field of the lightning message) + +The introduction point: + +- MUST compute: + - `ss(0) = SHA256(k(0) * E(0))` (standard ECDH) + - `rho(0) = HMAC256("rho", ss(0))` + - `E(1) = SHA256(E(0) || ss(0)) * E(0)` +- If an `encrypted_data` field is provided: + - MUST decrypt it using `rho(0)` + - MUST use the decrypted fields to locate the next node +- 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 +- MUST forward the onion and include the next blinding point in the lightning + message for the next node + +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)` + - `rho(i) = HMAC256("rho", ss(i))` + - `E(i+1) = SHA256(E(i) || ss(i)) * E(i)` +- 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. +- If an `encrypted_data` field is provided: + - MUST decrypt it using `rho(i)` + - MUST use the decrypted fields to locate the next node +- If `encrypted_data` contains a `next_blinding_override`: + - MUST use it as the next blinding point instead of `E(i+1)` +- Otherwise: + - MUST use `E(i+1)` as the next blinding point +- MUST forward the onion and include the next blinding point in the lightning + message for the next node + +The final recipient: + +- MUST compute: + - `ss(r) = SHA256(k(r) * E(r))` (standard ECDH) + - `b(r) = HMAC256("blinded_node_id", ss(r)) * k(r)` + - `rho(r) = HMAC256("rho", ss(r))` +- If an `encrypted_data` field is provided: + - MUST decrypt it using `rho(r)` +- MUST ignore the message if the `path_id` does not match the blinded route it + created + +#### Rationale + +Route blinding is a lightweight technique to provide recipient anonymity. +It's more flexible than rendezvous routing because it simply replaces the public +keys of the nodes in the route with random public keys while letting senders +choose what data they put in the onion for each hop. Blinded routes are also +reusable in some cases (e.g. onion messages). + +Each node in the blinded route needs to receive `E(i)` to be able to decrypt +the onion and the `encrypted_data` payload. Protocols that use route blinding +must specify how this value is propagated between nodes. + +When concatenating two blinded routes generated by different nodes, the +last node of the first route needs to know the first `blinding_point` of the +second route: the `next_blinding_override` field must be used to transmit this +information. + +The final recipient must verify that the blinded route is used in the right +context (e.g. for a specific payment) and was created by them. Otherwise a +malicious sender could create different blinded routes to all the nodes that +they suspect could be the real recipient and try them until one accepts the +message. The recipient can protect against that by storing `E(r)` and the +context (e.g. a `payment_hash`), and verifying that they match when receiving +the onion. Otherwise, to avoid additional storage cost, it can put some private +context information in the `path_id` field (e.g. the `payment_preimage`) and +verify that when receiving the onion. Note that it's important to use private +information in that case, that senders cannot have access to. + +The `padding` field can be used to ensure that all `encrypted_data` have the +same length. It's particularly useful when adding dummy hops at the end of a +blinded route, to prevent the sender from figuring out which node is the final +recipient. + # Accepting and Forwarding a Payment Once a node has decoded the payload it either accepts the payment locally, or forwards it to the peer indicated as the next hop in the payload. diff --git a/bolt04/route-blinding-test.json b/bolt04/route-blinding-test.json new file mode 100644 index 000000000..0be3d7faa --- /dev/null +++ b/bolt04/route-blinding-test.json @@ -0,0 +1,139 @@ +{ + "comment": "test vector for using blinded routes", + "generate": { + "comment": "This section contains test data for creating a blinded route. This route is the concatenation of two blinded routes: one from Dave to Eve and one from Bob to Carol.", + "hops": [ + { + "comment": "Bob creates a Bob -> Carol route with the following session_key and concatenates it with the Dave -> Eve route.", + "session_key": "0202020202020202020202020202020202020202020202020202020202020202", + "alias": "Bob", + "node_id": "0324653eac434488002cc06bbfb7f10fe18991e35f9fe4302dbea6d2353dc0ab1c", + "tlvs": { + "padding": "00000000000000000000000000000000", + "short_channel_id": "0x0x42", + "next_node_id": "027f31ebc5462c1fdce1b737ecff52d37d75dea43ce11c74d25aa297165faa2007", + "unknown_tag_65001": "123456" + }, + "encoded_tlvs": "0110000000000000000000000000000000000208000000000000002a0421027f31ebc5462c1fdce1b737ecff52d37d75dea43ce11c74d25aa297165faa2007fdfde903123456", + "ephemeral_privkey": "0202020202020202020202020202020202020202020202020202020202020202", + "ephemeral_pubkey": "024d4b6cd1361032ca9bd2aeb9d900aa4d45d9ead80ac9423374c451a7254d0766", + "shared_secret": "76771bab0cc3d0de6e6f60147fd7c9c7249a5ced3d0612bdfaeec3b15452229d", + "rho": "ba217b23c0978d84c4a19be8a9ff64bc1b40ed0d7ecf59521567a5b3a9a1dd48", + "encrypted_data": "cd4b00ff9c09ed28102b210ac73aa12d63e90a5acebc496c49f57c639e098acbaec5b5ffb8592b07bdb6665ccb56f1258ab1857383f6542c8371dcee568a0a35a218288814849db13ce6f84a464fa517d9e1684333e3", + "blinded_node_id": "03da173ad2aee2f701f17e59fbd16cb708906d69838a5f088e8123fb36e89a2c25" + }, + { + "comment": "Notice the next_blinding_override tlv in Carol's payload, indicating that Bob concatenated his route with another blinded route starting at Dave.", + "alias": "Carol", + "node_id": "027f31ebc5462c1fdce1b737ecff52d37d75dea43ce11c74d25aa297165faa2007", + "tlvs": { + "next_node_id": "032c0b7cf95324a07d05398b240174dc0c2be444d96b159aa6c7f7b1e668680991", + "next_blinding_override": "031b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f" + }, + "encoded_tlvs": "0421032c0b7cf95324a07d05398b240174dc0c2be444d96b159aa6c7f7b1e6686809910821031b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f", + "ephemeral_privkey": "0a2aa791ac81265c139237b2b84564f6000b1d4d0e68d4b9cc97c5536c9b61c1", + "ephemeral_pubkey": "034e09f450a80c3d252b258aba0a61215bf60dda3b0dc78ffb0736ea1259dfd8a0", + "shared_secret": "dc91516ec6b530a3d641c01f29b36ed4dc29a74e063258278c0eeed50313d9b8", + "rho": "d1e62bae1a8e169da08e6204997b60b1a7971e0f246814c648125c35660f5416", + "encrypted_data": "ca26157e44ab01e82becf86497e1d05ad3e70903d22721210af41d791bf406873024d95b7a1ad128b2526932febfeeab237000563c1f33c78530b3880f8407326eef8bc004932b22323d13343ef740019c08e538e5c5", + "blinded_node_id": "02e466727716f044290abf91a14a6d90e87487da160c2a3cbd0d465d7a78eb83a7" + }, + { + "comment": "Eve creates a Dave -> Eve blinded route using the following session_key.", + "session_key": "0101010101010101010101010101010101010101010101010101010101010101", + "alias": "Dave", + "node_id": "032c0b7cf95324a07d05398b240174dc0c2be444d96b159aa6c7f7b1e668680991", + "tlvs": { + "padding": "0000000000000000000000000000000000000000000000", + "short_channel_id": "0x0x561", + "next_node_id": "02edabbd16b41c8371b92ef2f04c1185b4f03b6dcd52ba9b78d9d7c89c8f221145" + }, + "encoded_tlvs": "0117000000000000000000000000000000000000000000000002080000000000000231042102edabbd16b41c8371b92ef2f04c1185b4f03b6dcd52ba9b78d9d7c89c8f221145", + "ephemeral_privkey": "0101010101010101010101010101010101010101010101010101010101010101", + "ephemeral_pubkey": "031b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f", + "shared_secret": "dc46f3d1d99a536300f17bc0512376cc24b9502c5d30144674bfaa4b923d9057", + "rho": "393aa55d35c9e207a8f28180b81628a31dff558c84959cdc73130f8c321d6a06", + "encrypted_data": "0f94a72cff3b64a3d6e1e4903cf8c8b0a17144aeb249dcb86563a5ee1f679ee8db3c6719bd4364f469aa5fea76ffdc49543d568a707ab73a3e855b25ca585bf12c9d5c9cb6c5c10374a4a66d95aeeea4fe146d0c2754", + "blinded_node_id": "036861b366f284f0a11738ffbf7eda46241a8977592878fe3175ae1d1e4754eccf" + }, + { + "comment": "Eve is the final recipient, so she included a path_id in her own payload to verify that the route is used when she expects it.", + "alias": "Eve", + "node_id": "02edabbd16b41c8371b92ef2f04c1185b4f03b6dcd52ba9b78d9d7c89c8f221145", + "tlvs": { + "padding": "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "path_id": "00112233445566778899aabbccddeeff", + "unknown_tag_65535": "06c1" + }, + "encoded_tlvs": "012c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000061000112233445566778899aabbccddeefffdffff0206c1", + "ephemeral_privkey": "62e8bcd6b5f7affe29bec4f0515aab2eebd1ce848f4746a9638aa14e3024fb1b", + "ephemeral_pubkey": "03e09038ee76e50f444b19abf0a555e8697e035f62937168b80adf0931b31ce52a", + "shared_secret": "352a706b194c2b6d0a04ba1f617383fb816dc5f8f9ac0b60dd19c9ae3b517289", + "rho": "719d0307340b1c68b79865111f0de6e97b093a30bc603cebd1beb9eef116f2d8", + "encrypted_data": "da2c7e5f7881219884beae6ae68971de73bab4c3055d9865b1afb60722a63a7e4ea796de84fc9af674952e900ff518ed6b3640a7e47b5f3e4fbce5fab87e47a11d84c66d1234f1cec1da2f56b72b64896509aef9b754", + "blinded_node_id": "021982a48086cb8984427d3727fe35a03d396b234f0701f5249daa12e8105c8dae" + } + ] + }, + "route": { + "comment": "This section contains the resulting blinded route, which can then be used inside onion messages or payments.", + "introduction_node_id": "0324653eac434488002cc06bbfb7f10fe18991e35f9fe4302dbea6d2353dc0ab1c", + "blinding": "024d4b6cd1361032ca9bd2aeb9d900aa4d45d9ead80ac9423374c451a7254d0766", + "hops": [ + { + "blinded_node_id": "03da173ad2aee2f701f17e59fbd16cb708906d69838a5f088e8123fb36e89a2c25", + "encrypted_data": "cd4b00ff9c09ed28102b210ac73aa12d63e90a5acebc496c49f57c639e098acbaec5b5ffb8592b07bdb6665ccb56f1258ab1857383f6542c8371dcee568a0a35a218288814849db13ce6f84a464fa517d9e1684333e3" + }, + { + "blinded_node_id": "02e466727716f044290abf91a14a6d90e87487da160c2a3cbd0d465d7a78eb83a7", + "encrypted_data": "ca26157e44ab01e82becf86497e1d05ad3e70903d22721210af41d791bf406873024d95b7a1ad128b2526932febfeeab237000563c1f33c78530b3880f8407326eef8bc004932b22323d13343ef740019c08e538e5c5" + }, + { + "blinded_node_id": "036861b366f284f0a11738ffbf7eda46241a8977592878fe3175ae1d1e4754eccf", + "encrypted_data": "0f94a72cff3b64a3d6e1e4903cf8c8b0a17144aeb249dcb86563a5ee1f679ee8db3c6719bd4364f469aa5fea76ffdc49543d568a707ab73a3e855b25ca585bf12c9d5c9cb6c5c10374a4a66d95aeeea4fe146d0c2754" + }, + { + "blinded_node_id": "021982a48086cb8984427d3727fe35a03d396b234f0701f5249daa12e8105c8dae", + "encrypted_data": "da2c7e5f7881219884beae6ae68971de73bab4c3055d9865b1afb60722a63a7e4ea796de84fc9af674952e900ff518ed6b3640a7e47b5f3e4fbce5fab87e47a11d84c66d1234f1cec1da2f56b72b64896509aef9b754" + } + ] + }, + "unblind": { + "comment": "This section contains test data for unblinding the route at each intermediate hop.", + "hops": [ + { + "alias": "Bob", + "node_privkey": "4242424242424242424242424242424242424242424242424242424242424242", + "ephemeral_pubkey": "024d4b6cd1361032ca9bd2aeb9d900aa4d45d9ead80ac9423374c451a7254d0766", + "blinded_privkey": "d12fec0332c3e9d224789a17ebd93595f37d37bd8ef8bd3d2e6ce50acb9e554f", + "decrypted_data": "0110000000000000000000000000000000000208000000000000002a0421027f31ebc5462c1fdce1b737ecff52d37d75dea43ce11c74d25aa297165faa2007fdfde903123456", + "next_ephemeral_pubkey": "034e09f450a80c3d252b258aba0a61215bf60dda3b0dc78ffb0736ea1259dfd8a0" + }, + { + "alias": "Carol", + "node_privkey": "4343434343434343434343434343434343434343434343434343434343434343", + "ephemeral_pubkey": "034e09f450a80c3d252b258aba0a61215bf60dda3b0dc78ffb0736ea1259dfd8a0", + "blinded_privkey": "bfa697fbbc8bbc43ca076e6dd60d306038a32af216b9dc6fc4e59e5ae28823c1", + "decrypted_data": "0421032c0b7cf95324a07d05398b240174dc0c2be444d96b159aa6c7f7b1e6686809910821031b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f", + "next_ephemeral_pubkey": "03af5ccc91851cb294e3a364ce63347709a08cdffa58c672e9a5c587ddd1bbca60", + "next_ephemeral_pubkey_override": "031b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f" + }, + { + "alias": "Dave", + "node_privkey": "4444444444444444444444444444444444444444444444444444444444444444", + "ephemeral_pubkey": "031b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f", + "blinded_privkey": "cebc115c7fce4c295dc396dea6c79115b289b8ceeceea2ed61cf31428d88fc4e", + "decrypted_data": "0117000000000000000000000000000000000000000000000002080000000000000231042102edabbd16b41c8371b92ef2f04c1185b4f03b6dcd52ba9b78d9d7c89c8f221145", + "next_ephemeral_pubkey": "03e09038ee76e50f444b19abf0a555e8697e035f62937168b80adf0931b31ce52a" + }, + { + "alias": "Eve", + "node_privkey": "4545454545454545454545454545454545454545454545454545454545454545", + "ephemeral_pubkey": "03e09038ee76e50f444b19abf0a555e8697e035f62937168b80adf0931b31ce52a", + "blinded_privkey": "ff4e07da8d92838bedd019ce532eb990ed73b574e54a67862a1df81b40c0d2af", + "decrypted_data": "012c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000061000112233445566778899aabbccddeefffdffff0206c1", + "next_ephemeral_pubkey": "038fc6859a402b96ce4998c537c823d6ab94d1598fca02c788ba5dd79fbae83589" + } + ] + } +} \ No newline at end of file