Browse Source

Add Sphinx article

master
t-bast 4 years ago
parent
commit
80d1f260f3
No known key found for this signature in database GPG Key ID: BE5D342AD368C13A
  1. 322
      sphinx.md

322
sphinx.md

@ -0,0 +1,322 @@
# Sphinx onion encryption: from Zero to Hero
Lightning uses an onion encryption scheme called [Sphinx](http://www.cypherpunks.ca/~iang/pubs/Sphinx_Oakland09.pdf)
to guarantee privacy along a payment path.
This article describes the Sphinx construction.
It can be particularly helpful for future implementers, curious users or people who want to tinker
with the crypto to provide new functionalities (such as rendezvous routing).
## Table of Contents
* [Notations](#notations)
* [Computing shared secrets](#computing-shared-secrets)
* [Generating a filler](#generating-a-filler)
* [Creating the payload](#creating-the-payload)
* [Decrypting the hop payload](#decrypting-the-hop-payload)
* [Full diagram](#full-diagram)
## Notations
```text
Alice -> N(1) -> N(2) -> ... -> N(r)
```
N(i)'s `node_id` is P(i) = k(i) * G.
The length of the encrypted payload sent to N(i) is l(i) (this includes the inner mac).
The total payload length is 1300 bytes.
## Computing shared secrets
* session_key <- {0;1}^256
* ek(1) = session_key
* Shared with N(1):
* E(1) = ek(1) * G (sent unencrypted in the onion header)
* ss(1) = H(ek(1) * P(1)) = H(k(1) * E(1))
* rho(1) = HMAC(0x72686F, ss(1))
* b(1) = H(E(1) || ss(1))
* ...
* ek(i) = b(i-1) * ek(i-1)
* Shared with N(i):
* E(i) = ek(i) * G (sent unencrypted in the onion header)
* ss(i) = H(ek(i) * P(i)) = H(k(i) * E(i))
* rho(i) = HMAC(0x72686F, ss(i))
* b(i) = H(E(i) || ss(i))
Every N(i) is able to compute E(i+1) = b(i) * E(i).
## Generating a filler
* Generate filler for payloads 1 to (r-1)
* Total of `l(1) + l(2) + ... + l(r-1)` bytes
* filler = []
* For i <- 1..(r-1):
* filler <- (filler + [0; l(i)]) xor stream(rho(i))[1300-l(i-1)-...-l(1):1300+l(i)]
Example filler for 3 nodes:
```text
<---l(1)--->
+----------+
| 00000000 |
+----------+
(+)
<-----------------1300-----------------><---l(1)--->
+--------------------------------------------------+
| stream(rho(1)) |
+--------------------------------------------------+
=
<---l(1)---><-----l(2)----->
+----------++--------------+
| xxxxxxxx || 000000000000 |
+----------++--------------+
(+)
<-----------------1300-------------><-----l(2)----->
+--------------------------------------------------+
| stream(rho(2)) |
+--------------------------------------------------+
=
<---l(1) + l(2)------------><----l(3)---->
+--------------------------++------------+
| xxxxxxxxxxxxxxxxxxxxxxxx || 0000000000 |
+--------------------------++------------+
(+)
<-----------------1300---------------><----l(3)---->
+--------------------------------------------------+
| stream(rho(3)) |
+--------------------------------------------------+
=
<---l(1) + l(2) + l(3)------------------->
+----------------------------------------+
| xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx |
+----------------------------------------+
```
## Creating the payload
The payload is encrypted hop by hop, starting from the recipient.
Every hop is authenticated.
I'm ignoring the mac handling for simplicity, but there's nothing complicated with it.
* Special case for the recipient:
* payload(r) = ((p(r) + random bytes) xor stream(rho(r))[0:1300-l(r-1)-...-l(1)]) + filler
* For i <- (r-1)..1:
* payload(i) = (p(i) + payload(i-1)[0:1300-l(i)]) xor stream(rho(i))
```text
<--------1300------------------------------------------------------------------->
<---l(r)---><-------------------------><----l(1) + l(2) + l(3)------------------>
+----------++-------------------------++----------------------------------------+
| p(r) || random initial bytes || xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx |
+----------++-------------------------++----------------------------------------+
(+)
<-------1300 - l(1) - l(2) - l(3)----->
+-------------------------------------+
| stream(rho(r)) |
+-------------------------------------+
=
<--------1300------------------------------------------------------------------->
+-------------------------------------------------------------------------------+
| encrypted payload for N(r) |
+-------------------------------------------------------------------------------+
<----l(3)----><-------1300 - l(3)----------------------------------------------->
+------------++-----------------------------------------------------------------+
| p(3) || encrypted payload for N(r) (truncated) |
+------------++-----------------------------------------------------------------+
(+)
+-------------------------------------------------------------------------------+
| stream(rho(3)) |
+-------------------------------------------------------------------------------+
=
<--------1300------------------------------------------------------------------->
+-------------------------------------------------------------------------------+
| encrypted payload for N(3) |
+-------------------------------------------------------------------------------+
<-----l(2)-----><-------1300 - l(2)--------------------------------------------->
+--------------++---------------------------------------------------------------+
| p(2) || encrypted payload for N(3) (truncated) |
+--------------++---------------------------------------------------------------+
(+)
+-------------------------------------------------------------------------------+
| stream(rho(2)) |
+-------------------------------------------------------------------------------+
=
<--------1300------------------------------------------------------------------->
+-------------------------------------------------------------------------------+
| encrypted payload for N(2) |
+-------------------------------------------------------------------------------+
<---l(1)---><--------1300 - l(1)------------------------------------------------>
+----------++-------------------------------------------------------------------+
| p(1) || encrypted payload for N(2) (truncated) |
+----------++-------------------------------------------------------------------+
(+)
+-------------------------------------------------------------------------------+
| stream(rho(1)) |
+-------------------------------------------------------------------------------+
=
<--------1300------------------------------------------------------------------->
+-------------------------------------------------------------------------------+
| encrypted payload for N(1) |
+-------------------------------------------------------------------------------+
```
## Decrypting the hop payload
This is where the filler matters: because it's generated with the same stream
cipher, decrypting re-creates it on-the-fly (and thus ensures macs are valid).
```text
<--------1300------------------------------------------------------------------->
+-------------------------------------------------------------------------------+
| encrypted payload for N(1) |
+-------------------------------------------------------------------------------+
(+) <---l(1)--->
+-------------------------------------------------------------------------------------------+
| stream(rho(1)) |
+-------------------------------------------------------------------------------------------+
=
<---l(1)---><--------1300------------------------------------------------------------------->
+----------++-------------------------------------------------------------------------------+
| p(1) || encrypted payload for N(2) |
+----------++-------------------------------------------------------------------------------+
(+) <-----l(2)----->
+-----------------------------------------------------------------------------------------------+
| stream(rho(2)) |
+-----------------------------------------------------------------------------------------------+
<-----l(2)-----> =
+--------------++-------------------------------------------------------------------------------+
| p(2) || encrypted payload for N(3) |
+--------------++-------------------------------------------------------------------------------+
(+) <----l(3)---->
+---------------------------------------------------------------------------------------------+
| stream(rho(3)) |
+---------------------------------------------------------------------------------------------+
<----l(3)----> =
+------------++-------------------------------------------------------------------------------+
| p(3) || encrypted payload for N(r) |
+------------++-------------------------------------------------------------------------------+
(+)
+-------------------------------------------------------------------------------+
| stream(rho(r)) |
+-------------------------------------------------------------------------------+
=
<--------1300------------------------------------------------------------------->
<---l(r)---><-------------------------><--- l(1) + l(2) + l(3) ----------------->
+----------++-------------------------++----------------------------------------+
| p(r) || random initial bytes || xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx |
+----------++-------------------------++----------------------------------------+
```
## Full diagram
```text
Alice -> Bob -> Carol -> Dave
```
```text
1. Filler Generation
<-----l(B)----->
+--------------+
| 000000000000 |
+--------------+
(+)
<-----------------1300------------------------------------------><-----l(B)----->
+-------------------------------------------------------------------------------+
| stream(ss(Bob)) |
+-------------------------------------------------------------------------------+
=
<-----l(B)-----><-l(C)->
+--------------++------+
| xxxxxxxxxxxx || 0000 |
+--------------++------+
(+)
<-----------------1300------------------------------------------><-l(C)->
+-----------------------------------------------------------------------+
| stream(ss(C)) |
+-----------------------------------------------------------------------+
=
<-----l(B) + l(C)------>
+----------------------+
| xxxxxxxxxxxxxxxxxxxx |
+----------------------+
2. Onion Encryption
<-----------------1300------------------------------------------>
<----l(D)----><-------------------------><------l(B) + l(C)----->
+------------++-------------------------++----------------------+
| p(D) || random init bytes || xxxxxxxxxxxxxxxxxxxx |
+------------++-------------------------++----------------------+
(+)
<---------1300 - l(B) - l(C)------------>
+---------------------------------------+
| stream(ss(D)) |
+---------------------------------------+
=
<---------1300 - l(B) - l(C)------------><------l(B) + l(C)----->
+---------------------------------------++----------------------+
| encrypted payload for Dave || xxxxxxxxxxxxxxxxxxxx |
+---------------------------------------++----------------------+
<-l(C)-><--------1300 - l(C)------------------------------------>
+------++-------------------------------------------------------+
| p(C) || encrypted payload for Dave (truncated) |
+------++-------------------------------------------------------+
(+)
+---------------------------------------------------------------+
| stream(ss(C)) |
+---------------------------------------------------------------+
=
<--------1300--------------------------------------------------->
+---------------------------------------------------------------+
| encrypted payload for Carol |
+---------------------------------------------------------------+
<-----l(B)-----><----------1300 - l(B)-------------------------->
+--------------++-----------------------------------------------+
| p(B) || encrypted payload for Carol (truncated) |
+--------------++-----------------------------------------------+
(+)
+---------------------------------------------------------------+
| stream(ss(B)) |
+---------------------------------------------------------------+
=
<--------1300--------------------------------------------------->
+---------------------------------------------------------------+
| encrypted payload for Bob |
+---------------------------------------------------------------+
3. Onion Decryption
<--------1300--------------------------------------------------->
+---------------------------------------------------------------+
| encrypted payload for Bob |
+---------------------------------------------------------------+
(+) <-----l(B)----->
+-------------------------------------------------------------------------------+
| stream(ss(B)) |
+-------------------------------------------------------------------------------+
=
<-----l(B)-----><------- 1300 -------------------------------------------------->
+--------------++---------------------------------------------------------------+
| p(B) || encrypted payload for Carol |
+--------------++---------------------------------------------------------------+
(+) <-l(C)->
+-----------------------------------------------------------------------+
| stream(ss(C)) |
+-----------------------------------------------------------------------+
=
<-l(C)-><------- 1300 -------------------------------------------------->
+------++---------------------------------------------------------------+
| p(C) || encrypted payload for Dave |
+------++---------------------------------------------------------------+
(+)
+---------------------------------------------------------------+
| stream(ss(D)) |
+---------------------------------------------------------------+
=
<-----------------1300------------------------------------------>
<----l(D)----><-------------------------><------l(B) + l(C)----->
+------------++-------------------------++----------------------+
| p(D) || random init bytes || xxxxxxxxxxxxxxxxxxxx |
+------------++-------------------------++----------------------+
```
Loading…
Cancel
Save