all repos — circe-vpn @ 12ec1e49175f80aa014a42d2a7a020d62ba42af0

Circe VPN

docs/PROTOCOL.md (view raw)

 1
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
# The Circe Protocol

## Cryptographic Primitives

- ChaCha20-Poly1305 for fast symmetric encryption and AEAD
- MLKEM-768 hybridized with x25519 for shared secret derivation
- KangarooTwelve128 and SHA2-512 for key and nonce derivation
- MLDSA-44 for cryptographic signatures

## Handshake (v1)

Three UDP messages, host database is pre-distributed (unknown hosts fall through to confirm-with-server state described in section 3).

**Transcript:** Running SHA2-512 hash `TH` over every handshake byte, in order, plaintext form, computed identically by both sides.

**Key Schedule:**
```
ek_p        = Polites' Curve25519 secret key
ek_e        = Eurylochus' Curve25519 secret key
ek_shared   = ECDH(ek_p, ek_e)
qk_shared   = MLKEM-768 shared secret
PRK         = HKDF-Extract(salt = "circe/v1", IKM = ek_shared | qk_shared)
k_hs_e      = HKDF-Expand(PRK, "hs eurylochus" | TH2, 32)
k_hs_p      = HKDF-Expand(PRK, "hs polites" | TH2, 32)
k_d_p2e     = HKDF-Expand(PRK, "data p2e" | TH3, 32)
k_d_e2p     = HKDF-Expand(PRK, "data e2p" | TH3, 32)
```

**Diagram:**
```mermaid
sequenceDiagram
    participant Polites
    participant Eurylochus
    Polites->>Eurylochus: OPENTUNNEL, x25519(ek_p, 9), qk_p=MLKEM768-pubkey()
    Eurylochus->>Polites: ACK-OPENTUNNEL, x25519(ek_e, 9), MLKEM768-encapsulated(qk_p), k_hs_e.encrypt(nonce=0, identity_e | sig_e)
    Note right of Polites: identity_e and sig_e match for host eurylochus.aeaea.circe
    Polites->>Eurylochus: IDENTIFY, k_hs_p.encrypt(nonce=0, identity_p | sig_p)
    Note right of Eurylochus: identity_p and sig_p match for host polites.aeaea.circe
    Eurylochus->>Polites: DATA, seqn0, k_d_e2p.encrypt(nonce=seqn0, packet length | packet contents), random padding bytes
    Polites->>Eurylochus: DATA, seqn1, k_d_p2e.encrypt(nonce=seqn1, packet length | packet contents), random padding bytes
    Eurylochus->>Polites: COMMAND, seqn2, k_d_e2p.encrypt(nonce=seqn2, CLOSE_TUNNEL | sig_e), random padding bytes
```

## State Machine

## Wire Format



## References

- [Curve25519](http://cr.yp.to/ecdh/curve25519-20051115.pdf)
- [ChaCha20-Poly1305](https://datatracker.ietf.org/doc/html/rfc7539)
- [MLKEM-768](https://nvlpubs.nist.gov/nistpubs/fips/nist.fips.203.pdf)
- [MLDSA-44](https://nvlpubs.nist.gov/nistpubs/fips/nist.fips.204.pdf)
- [KangarooTwelve128](https://eprint.iacr.org/2016/770.pdf)
- [SHA2](https://csrc.nist.gov/groups/STM/cavp/documents/shs/sha256-384-512.pdf)
- [Raft](https://raft.github.io/raft.pdf)