Christian Decker [ARCHIVE] on Nostr: 📅 Original date posted:2018-11-18 📝 Original message: I finally got around to ...
📅 Original date posted:2018-11-18
📝 Original message:
I finally got around to amending my initial (broken) proposal for the
rendez-vous protocol using the ephemeral key switch at the rendez-vous
point. I'd like to try and keep a live document that describes the
entire proposal in the Wiki to make it easier for people to get an
overall view of the proposal, instead of having to stitch it together
from the ML posts. You can find the proposal here [1]. It makes heavy
use of the description in the onion routing bolt [2].
The initial proposal was to have the rendez-vous node `RV` swap in an
ephemeral key `ek_rv` instead of generating it from `ss_k` derived from
ECDH(`ek_{k-1}`, node_id), because that allows the recipient `R` to
generate the second half of the route by selecting that `ek_rv`.
The problem I mentioned in other mails arises from the fact that when
`RV` decrypts its payload to learn about its routing instructions and
learn about `ek_rv`, it was also encrypting the filler bytes appended to
the end. The decryption is done via XOR with a ChaCha20 bytestream whose
key is generated from `ss_k`, which is unknown to `R` (depends on the
ephemeal key selected by the sender and the intermediate hops). This is
important since `R` needs to know the exact contents of the packet
including the filler to compute valid HMACs.
The fix is relatively simple, and just adds a virtual hop at `RV`. I'll
describe the actions of `RV` here instead of the packet building since
this way is easier to follow:
- `RV` derives `ss_k` from `ek_k` which was given to it by the previous
hop, appends the `0x00`-vector to shift in when stripping its per-hop
payload (may need more than 65 bytes now since we shift more than one
per-hop field now), generates the ChaCha20 stream using `ss_k` and
XORs the packet with the stream.
- `RV` reads its per-hop payload notices that an ephemeral key switch
is desired and reads `ek_rv` from the per-hop payload. It overwrites
the, now encrypted, filler vector with `0x00`-bytes again (to
recreate a well-known state that `R` can use when generating its
partial onion).
- It then derives a new secret key `ss_rv` from `ek_rv` and its node
ID. `ss_rv` is then used to generate a new ChaCha20 stream which will
encrypt the packet again (obfuscating the filler) and it'll be used
to generate a new ephemeral key `ek_{rv+1}` which will be passed on
to the next hop.
At this point the normal operation continues as usual. IMHO the proposal
is clean and backwards compatible, but I'm open for suggestions. There
are a number of variants for this protocol, but I chose this one for its
symmetry with the existing scheme. I'll list a few alternatives here:
- `ek_{rv+1}` == `ek_rv`: it is not really required to generate a new
ephemeral key for the next hop, we could just reuse it. The reason
the switch is done in normal Sphinx is to avoid correlating hops, but
`ek_rv` is not really seen on the wire in cleartext right now so we
could just reuse it. I prefer not to simply because of symmetry.
- Overwrite the filler with `0x00`-bytes and don't obfuscate it. This
is the simple initial proposal, but it leaks the fact that `RV` is a
rendez-vous node to the next hop.
Please let me know if I missed something, I'll try to implement this
soon and see if something unexpected jumps at me :-)
Cheers,
Christian
[1] https://github.com/lightningnetwork/lightning-rfc/wiki/Rendez-vous-mechanism-on-top-of-Sphinx
[2] https://github.com/lightningnetwork/lightning-rfc/blob/master/04-onion-routing.md#shared-secret
📝 Original message:
I finally got around to amending my initial (broken) proposal for the
rendez-vous protocol using the ephemeral key switch at the rendez-vous
point. I'd like to try and keep a live document that describes the
entire proposal in the Wiki to make it easier for people to get an
overall view of the proposal, instead of having to stitch it together
from the ML posts. You can find the proposal here [1]. It makes heavy
use of the description in the onion routing bolt [2].
The initial proposal was to have the rendez-vous node `RV` swap in an
ephemeral key `ek_rv` instead of generating it from `ss_k` derived from
ECDH(`ek_{k-1}`, node_id), because that allows the recipient `R` to
generate the second half of the route by selecting that `ek_rv`.
The problem I mentioned in other mails arises from the fact that when
`RV` decrypts its payload to learn about its routing instructions and
learn about `ek_rv`, it was also encrypting the filler bytes appended to
the end. The decryption is done via XOR with a ChaCha20 bytestream whose
key is generated from `ss_k`, which is unknown to `R` (depends on the
ephemeal key selected by the sender and the intermediate hops). This is
important since `R` needs to know the exact contents of the packet
including the filler to compute valid HMACs.
The fix is relatively simple, and just adds a virtual hop at `RV`. I'll
describe the actions of `RV` here instead of the packet building since
this way is easier to follow:
- `RV` derives `ss_k` from `ek_k` which was given to it by the previous
hop, appends the `0x00`-vector to shift in when stripping its per-hop
payload (may need more than 65 bytes now since we shift more than one
per-hop field now), generates the ChaCha20 stream using `ss_k` and
XORs the packet with the stream.
- `RV` reads its per-hop payload notices that an ephemeral key switch
is desired and reads `ek_rv` from the per-hop payload. It overwrites
the, now encrypted, filler vector with `0x00`-bytes again (to
recreate a well-known state that `R` can use when generating its
partial onion).
- It then derives a new secret key `ss_rv` from `ek_rv` and its node
ID. `ss_rv` is then used to generate a new ChaCha20 stream which will
encrypt the packet again (obfuscating the filler) and it'll be used
to generate a new ephemeral key `ek_{rv+1}` which will be passed on
to the next hop.
At this point the normal operation continues as usual. IMHO the proposal
is clean and backwards compatible, but I'm open for suggestions. There
are a number of variants for this protocol, but I chose this one for its
symmetry with the existing scheme. I'll list a few alternatives here:
- `ek_{rv+1}` == `ek_rv`: it is not really required to generate a new
ephemeral key for the next hop, we could just reuse it. The reason
the switch is done in normal Sphinx is to avoid correlating hops, but
`ek_rv` is not really seen on the wire in cleartext right now so we
could just reuse it. I prefer not to simply because of symmetry.
- Overwrite the filler with `0x00`-bytes and don't obfuscate it. This
is the simple initial proposal, but it leaks the fact that `RV` is a
rendez-vous node to the next hop.
Please let me know if I missed something, I'll try to implement this
soon and see if something unexpected jumps at me :-)
Cheers,
Christian
[1] https://github.com/lightningnetwork/lightning-rfc/wiki/Rendez-vous-mechanism-on-top-of-Sphinx
[2] https://github.com/lightningnetwork/lightning-rfc/blob/master/04-onion-routing.md#shared-secret