]> Cypherpunks.ru repositories - udpobfs.git/blobdiff - PROTOCOL
Unify copyright comment format
[udpobfs.git] / PROTOCOL
index 4cef2f15a80410a191ea86579a55b0c488eb45602111838ccfba0b6ba4a36e48..fe2574f031bcac3fc5b1312e0f5bcc40402fa407511de0f458995d49f5ead22d 100644 (file)
--- a/PROTOCOL
+++ b/PROTOCOL
@@ -1,29 +1,27 @@
-Protocol is trivial. Both peers has shared 256-bit key. SHA3 is used to
-derive four more keys from it:
+After TLS 1.3 handshake is finished, 512-bit keying material is
+exported. Various 256-bit keys are derived from it:
 
-    SHAKE128("go.cypherpunks.ru/udpobfs" || key) ->
-        256-bit InitiatorEncryptionKey ||
-        256-bit InitiatorObfuscationKey ||
-        256-bit ResponderEncryptionKey ||
-        256-bit ResponderObfuscationKey
+    InitEncKey  = BLAKE3-DeriveKey(seed, "go.cypherpunks.ru/udpobfs/v2 init enc")
+    InitMACKey  = BLAKE3-DeriveKey(seed, "go.cypherpunks.ru/udpobfs/v2 init mac")
+    InitObfsKey = BLAKE3-DeriveKey(seed, "go.cypherpunks.ru/udpobfs/v2 init obfs")
+    RespEncKey  = BLAKE3-DeriveKey(seed, "go.cypherpunks.ru/udpobfs/v2 resp enc")
+    RespMACKey  = BLAKE3-DeriveKey(seed, "go.cypherpunks.ru/udpobfs/v2 resp mac")
+    RespObfsKey = BLAKE3-DeriveKey(seed, "go.cypherpunks.ru/udpobfs/v2 resp obfs")
 
-Each side has 64-bit packet number counter, that is used as a nonce.
-That counter is kept in memory and only its lower 24 bits are sent.
-When remote side receives 24-bit counter with lower value, then it
-increments in-memory counter's remaining part. Completely the same
-as Extended Sequence Numbers are done in IPsec's ESP.
+Each peer has 64-bit packet sequence counter. It is fed to
+BLAKE3(len=256, key=*EncKey) and then its XOF output is XORed with
+plaintext packet. Then BLAKE3(len=48, key=*MACKey) is taken over the
+sequence counter concatenated with the ciphertext. That MAC, lower
+16-bits of the sequence counter are encrypted with Blowfish(key=*ObfsKey)
+and prepended to the ciphertext.
 
-ChaCha20 is initialised with corresponding EncryptionKey and nonce equal
-to the full sequence number value. Its first 256-bit of output will be
-Poly1305's one-time key. Next 256-bits are ignored. Further ones are
-XORed with the plaintext (UDP's payload). Poly1305 is calculated over
-the full 64-bit sequence number value and the whole ciphertext. Higher
-40-bits of the resulting tag with lower 24-bits of the sequence number
-are prepended to the ciphertext, encrypted with Blowfish:
+    ciphertext = BLAKE3(len=256, key=*EncKey)(SeqNum).XOF(len=len(plaintext))
+    ciphertext = ciphertext XOR plaintext
+    mac = BLAKE3(len=48, key=*MACKey)(SeqNum || ciphertext)
+    send(Blowfish(key=*ObfsKey)(mac || SeqNum[6:]) || ciphertext)
 
-    Blowfish(Seq || MAC) || Ciphertext
-
-Blowfish is initialized with ObfuscationKey. 40-bit MAC is rather weak,
-but enough for obfuscation purposes. 24-bit part of sequence number is
-enough for medium data-rate transmission where reordering or packets
-drops may occur.
+All of that gives only 8-byte overhead, providing at least some 48-bit
+authentication and invisibility of cleartext nonces/sequences. BLAKE3 is
+pretty fast algorithm, however ChaCha20-Poly1305 can be faster for small
+messages -- so performance depends on message sizes. Blowfish is just
+the fastest (serious) cipher with 64-bit blocksize in the Go's library.