Handshake: key exchange and version negotiation

											┌───────────┐                                                ┌───────────┐
                      │ Initiator │                                                │ Responder │
                      └─────┬─────┘                                                └─────┬─────┘
                            │                                                            │
                            │                                                            │
┌───────────────────────────┤                                                            │
│                           │                                                            │
└──────────────────────────►│                                                            │
    x, ellswift_X =         │                                                            │
    ellswift_create()(1)    │                                                            │
                            │                                                            │
                            │    ellswift_X + initiator_garbage(up to 4095 bytes)(2)     │
                            ├───────────────────────────────────────────────────────────►│
                            │                                                            │
                            │                                                            │
                            │                                                            ├────────────────────────────┐
                            │                                                            │                            │
                            │                                                            │◄───────────────────────────┘
                            │                                                            │     y, ellswift_Y =
                            │                                                            │     ellswift_create()(3)
                            │                                                            │
                            │                                                            ├────────────────────────────┐
                            │                                                            │                            │
                            │                                                            │◄───────────────────────────┘
                            │                                                            │    ecdh_secret = v2_ecdh(y,
                            │                                                            │    ellswift_X, ellswift_Y,
                            │                                                            │    initiating=False)(4)
                            │                                                            │
                            │                                                            ├────────────────────────────┐
                            │                                                            │                            │
                            │                                                            │◄───────────────────────────┘
                            │                                                            │   v2_initialize(ecdh_secret,
                            │                                                            │   initiating=False)(5)
                            │                                                            │
                            │                                                            │
                            │   ellswift_Y + responder_garbage(up to 4095 bytes) +       │
                            │   responder_garbage_terminator + v2_enc_package(RESPONDER_ |
                            │   TRANSPORT_VERSION, aad=responder_garbage)(6)             │
                            │◄───────────────────────────────────────────────────────────┤
                            │                                                            │
                            │                                                            │
                            │                                                            │
┌───────────────────────────┤                                                            │
│                           │                                                            │
└──────────────────────────►│                                                            │
   ecdh_secret = v2_ecdh(X, │                                                            │
   ellswift_Y, ellswift_X,  │                                                            │
   initiating=True)(7)      │                                                            │
                            │                                                            │
┌───────────────────────────┤                                                            │
│                           │                                                            │
└──────────────────────────►│                                                            │
  v2_initialize(ecdh_secret,│                                                            │
  initiating=True)(8)       │                                                            │
                            │                                                            │
                            │                                                            │
                            │                                                            │
                            │                                                            │
                            │                                                            │
                            │  initiator_garbage_terminator + v2_enc_packet(INITIATOR_   │
                            │  TRANSPORT_VERSION, aad=initiator_garbage)(9)              │
                            ├───────────────────────────────────────────────────────────►│
                            │                                                            │
                            │                                                            │
                            │                                                            │
  1. The initiator generates a random secp256k1 private key x and computes the 64-byte ElligatorSwift-encoded public key ellswift_X. ElligatorSwift is used to achieve a random looking 512-bit stream that always corresponds to a valid public key, avoiding in this way fingerprinting based on the key encoding.
  2. The initiator sends ellswift_X alongside up to 4095 bytes of garbage to the responder. This provides a form of shapability (avoiding a recognizable pattern of sending exactly 64 bytes of data). The responder receives data from the initiator and parses it byte-by-byte, until one of the bytes does not match the 16 bytes consisting of the network magic followed by version\\x00\\x00\\x00\\x00\\x00. If the first 16 bytes match, the connection is treated as v1 instead. If any of the first 4 bytes does not match the network magic, but the 12 subsequent do match the version message this may be interpreted as a v1 from a different network, and the connection can be dropped. If the connection is not dropped, and flagged as v2, the rest remaining of the first 64 bytes can be parsed and decoded as ellswift_X.
  3. The responder also generates a random secp256k1 private key y and the corresponding ElligatorSwift-encoded public key ellswift_Y.
  4. The responder computes a shared_secret via X-only ECDH using his private key (y) and the exchanged public keys (ellswift_X and ellswift_Y).
  5. The responder deterministically derives 4 encryption keys from shared_secret (two for each direction: one for packet lengths, one for content encryption), alongside a session id, and two 16-byte garbage terminators (one per direction).
  6. The responder sends back ellswift_Y alongside up to 4095 bytes of garbage, his garbage terminator and the first encrypted message containing the version message of the responder (RESPONDER_TRANSPORT_MESSAGE) and authenticating the garbage. In the current state, the version message is empty (signaling version 0), but the flag is there to allow for protocol updates. Garbage is authenticated to prevent any attacker from being able to alter data, even if its garbage (the bar is set for the protocol to be stateful)
  7. Upon receiving ellswift_Y, the initiator can also derive the shared_secret.
  8. The initiator deterministically derives the same keys, session id and garbage terminators as the responder, and uses it to identify the garbage and authenticate it, ensuring, therefore, that the keys have been properly generated.
  9. Finally, the initiator sends back his garbage teminator, signaling in this way that no more garbage is being sent and that the secure channel has been stablished. Notice this is required given data is sent as a bitstream, so the responder could have send back his bit while the initiator was still sending garbage. It also serves as acknowledgment that the initiator has derived the shared_secret and all the data that is derived from it.

The specifics of how the ElligatorSwift keys are derived is not covered in these notes and is attributed to

Untitled