├── img ├── CUBIC.png ├── QUIC_Flow_control.png ├── QUIC_Client_Handshake.png ├── QUIC_Stream_Flow_Control.png └── QUIC_Connection_Flow_Control.png ├── protocol ├── quicerrorcode.go ├── protocol.go ├── quicentropyhash_test.go ├── README.md ├── quicfecpacket.go ├── quicfecpacket_test.go ├── parser_test.go ├── quicprivateheader_test.go ├── ringbuffer.go ├── ringbuffer_test.go ├── quicprivateheader.go ├── parser.go ├── quicpacket.go ├── quicpublicresetpacket.go ├── quicentropyhash.go ├── quicpublicresetpacket_test.go ├── message_test.go ├── quicpublicheader_test.go ├── quicpublicheader.go └── message.go ├── crypto ├── aead.go ├── p256_test.go ├── curve25519_test.go ├── curve25519.go ├── key_exchange.go ├── p256.go ├── aead_nullfnv1a128_test.go ├── fnv1a_test.go ├── aead_nullfnv1a128.go ├── hkdf.go ├── aead_chacha20poly1305.go ├── fnv1a.go ├── README.md ├── aead_aes128gcm12.go ├── aead_aes128gcm12_test.go ├── gcm.go └── chacha20.go ├── doc ├── TCPProportionalRateReduction.md ├── TCPRetransmissionTimer.md ├── QUIC_Event_Processing.md ├── TailLossProbe.md ├── HKDF.md └── CUBIC.md ├── README.md └── quic.go /img/CUBIC.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/romain-jacotin/quic/HEAD/img/CUBIC.png -------------------------------------------------------------------------------- /protocol/quicerrorcode.go: -------------------------------------------------------------------------------- 1 | package protocol 2 | 3 | type QuicErrorCode uint32 4 | -------------------------------------------------------------------------------- /img/QUIC_Flow_control.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/romain-jacotin/quic/HEAD/img/QUIC_Flow_control.png -------------------------------------------------------------------------------- /img/QUIC_Client_Handshake.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/romain-jacotin/quic/HEAD/img/QUIC_Client_Handshake.png -------------------------------------------------------------------------------- /img/QUIC_Stream_Flow_Control.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/romain-jacotin/quic/HEAD/img/QUIC_Stream_Flow_Control.png -------------------------------------------------------------------------------- /img/QUIC_Connection_Flow_Control.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/romain-jacotin/quic/HEAD/img/QUIC_Connection_Flow_Control.png -------------------------------------------------------------------------------- /protocol/protocol.go: -------------------------------------------------------------------------------- 1 | package protocol 2 | 3 | // DataParser interface 4 | type DataParser interface { 5 | ParseData(data []byte) (size int, err error) 6 | } 7 | 8 | // DataSerializer interface 9 | type DataSerialize interface { 10 | GetSerializedSize() int 11 | GetSerializedData() (data []byte, err error) 12 | } 13 | -------------------------------------------------------------------------------- /crypto/aead.go: -------------------------------------------------------------------------------- 1 | package crypto 2 | 3 | import "github.com/romain-jacotin/quic/protocol" 4 | 5 | type AEAD interface { 6 | // Open 7 | Open(sequencenumber protocol.QuicPacketSequenceNumber, plaintext, aad, ciphertext []byte) (bytescount int, err error) 8 | // Seal 9 | Seal(sequencenumber protocol.QuicPacketSequenceNumber, ciphertext, aad, plaintext []byte) (bytescount int, err error) 10 | // GetMacSize 11 | GetMacSize() int 12 | } 13 | -------------------------------------------------------------------------------- /protocol/quicentropyhash_test.go: -------------------------------------------------------------------------------- 1 | package protocol 2 | 3 | import "testing" 4 | import "fmt" 5 | 6 | func Test_GetEntropyHash(t *testing.T) { 7 | var seqnum QuicPacketSequenceNumber 8 | var entropy bool 9 | var hash QuicEntropyHash 10 | var err error 11 | 12 | rb, s := NewEntropyHashRingBuffer() 13 | fmt.Printf("Size = %v\n", s) 14 | entropy = true 15 | for i := 0; i < (65536 * 8); i++ { 16 | if seqnum, err = rb.GetNewPacket(entropy); err != nil { 17 | t.Errorf("GetNewPacket : error %v", err) 18 | return 19 | } 20 | fmt.Printf("get bit [%v] = %v (%v expected)\n", seqnum, rb.getEntropy(seqnum), entropy) 21 | if hash, err = rb.GetEntropyHash(seqnum); err != nil { 22 | t.Errorf("GetEntropyHash : error %v", err) 23 | return 24 | } 25 | fmt.Printf("get hash[%v] = %x\n", seqnum, hash) 26 | } 27 | for i := range rb.hashes { 28 | if rb.hashes[i] != 0xff { 29 | t.Error("bad hashes initialization with GetNewPacket") 30 | return 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /crypto/p256_test.go: -------------------------------------------------------------------------------- 1 | package crypto 2 | 3 | import "testing" 4 | import "bytes" 5 | 6 | func TestECDH_P256(test *testing.T) { 7 | errClient, keyExchangeClient := NewECDH_P256() 8 | if errClient != nil { 9 | test.Error("ECDH_P256: can't generate private/public key material at client side") 10 | return 11 | } 12 | errServer, keyExchangeServer := NewECDH_P256() 13 | 14 | if errClient != nil { 15 | test.Error("ECDH_P256: can't generate private/public key material at client side") 16 | return 17 | } 18 | 19 | pubKeyClient := keyExchangeClient.GetPublicKey() 20 | pubKeyServer := keyExchangeServer.GetPublicKey() 21 | 22 | errClient, sharedKeyClient := keyExchangeClient.ComputeSharedKey(pubKeyServer) 23 | if errClient != nil { 24 | test.Error("ECDH_P256: can't compute shared key at client side") 25 | return 26 | } 27 | 28 | errServer, sharedKeyServer := keyExchangeServer.ComputeSharedKey(pubKeyClient) 29 | if errServer != nil { 30 | test.Error("ECDH_P256: can't compute shared key at server side") 31 | return 32 | } 33 | 34 | if !bytes.Equal(sharedKeyClient, sharedKeyServer) { 35 | test.Error("ECDH_P256: different share keys evaluated by client and server !") 36 | return 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /crypto/curve25519_test.go: -------------------------------------------------------------------------------- 1 | package crypto 2 | 3 | import "testing" 4 | import "bytes" 5 | 6 | func TestECDH_Curve25519(test *testing.T) { 7 | errClient, keyExchangeClient := NewECDH_Curve25519() 8 | if errClient != nil { 9 | test.Error("ECDH_Curve25519: can't generate private/public key material at client side") 10 | return 11 | } 12 | errServer, keyExchangeServer := NewECDH_Curve25519() 13 | 14 | if errClient != nil { 15 | test.Error("ECDH_Curve25519: can't generate private/public key material at client side") 16 | return 17 | } 18 | 19 | pubKeyClient := keyExchangeClient.GetPublicKey() 20 | pubKeyServer := keyExchangeServer.GetPublicKey() 21 | 22 | errClient, sharedKeyClient := keyExchangeClient.ComputeSharedKey(pubKeyServer) 23 | if errClient != nil { 24 | test.Error("ECDH_Curve25519: can't compute shared key at client side") 25 | return 26 | } 27 | 28 | errServer, sharedKeyServer := keyExchangeServer.ComputeSharedKey(pubKeyClient) 29 | if errServer != nil { 30 | test.Error("ECDH_Curve25519: can't compute shared key at server side") 31 | return 32 | } 33 | 34 | if !bytes.Equal(sharedKeyClient, sharedKeyServer) { 35 | test.Error("ECDH_Curve25519: different share keys evaluated by client and server !") 36 | return 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /crypto/curve25519.go: -------------------------------------------------------------------------------- 1 | package crypto 2 | 3 | import "crypto/rand" 4 | import "golang.org/x/crypto/curve25519" 5 | import "io" 6 | import "errors" 7 | 8 | type c255 struct { 9 | publicKey [32]byte 10 | privateKey [32]byte 11 | } 12 | 13 | // NewECDH_Curve25519 returns an Elliptic Curve Diffie-Hellman Curve25519 KeyExchange algorithm. 14 | func NewECDH_Curve25519() (err error, keyexchange KeyExchange) { 15 | c := new(c255) 16 | _, err = io.ReadFull(rand.Reader, c.privateKey[:]) 17 | if err != nil { 18 | return 19 | } 20 | curve25519.ScalarBaseMult(&c.publicKey, &c.privateKey) 21 | return nil, c 22 | } 23 | 24 | // GetPublicKey generates local private/public keys pair and returns the local public key that should be sent to the remote host. 25 | func (this *c255) GetPublicKey() []byte { 26 | return this.publicKey[:] 27 | } 28 | 29 | // ComputeSharedKey computes and returns the shared key based on the local private key and the remote public key. 30 | func (this *c255) ComputeSharedKey(remotePublicKey []byte) (error, []byte) { 31 | var remote [32]byte 32 | if len(remotePublicKey) != 32 { 33 | return errors.New("ECDH : invalid Curve25519 KeyExchange"), nil 34 | } 35 | sharedKey := new([32]byte) 36 | copy(remote[:], remotePublicKey) 37 | curve25519.ScalarMult(sharedKey, &this.privateKey, &remote) 38 | return nil, sharedKey[:] 39 | } 40 | -------------------------------------------------------------------------------- /crypto/key_exchange.go: -------------------------------------------------------------------------------- 1 | package crypto 2 | 3 | import "github.com/romain-jacotin/quic/protocol" 4 | 5 | // A KeyExchange is a generic way to exchange a shared key between two hosts that own private/public key pairs. 6 | // 7 | // Supported Key Exchange algorithm in QUIC Crypto protocol are the following: 8 | // 9 | // Elliptic Curve Diffie-Hellman Curve25519: TagKEXS with value TagC255 10 | // Elliptic Curve Diffie-Hellman P-256: TagKEXS with value TagP256 11 | type KeyExchange interface { 12 | // GetPublicKey generates local private/public keys pair and returns the local public key that should be sent to the remote host. 13 | GetPublicKey() []byte 14 | // ComputeSharedKey computes and returns the shared key based on the local private key and the remote public key described in input. 15 | ComputeSharedKey([]byte) (error, []byte) 16 | } 17 | 18 | // NewKeyExchange is a KeyExchange factory that returns the KeyExchange algorithm corresponding to the MessageTag given in input. 19 | // 20 | // TagC255 = Elliptic Curve Diffie-Hellman Curve25519 21 | // TagP256 = Elliptic Curve Diffie-Hellman P-256 22 | func NewKeyExchange(kexs protocol.MessageTag) (error, KeyExchange) { 23 | switch kexs { 24 | case protocol.TagC255: // Elliptic Curve Diffie-Hellman Curve25519 25 | return NewECDH_Curve25519() 26 | case protocol.TagP256: // Elliptic Curve Diffie-Hellman P-256 27 | return NewECDH_P256() 28 | } 29 | return nil, nil 30 | } 31 | -------------------------------------------------------------------------------- /crypto/p256.go: -------------------------------------------------------------------------------- 1 | package crypto 2 | 3 | import "crypto/elliptic" 4 | import "crypto/rand" 5 | import "math/big" 6 | import "errors" 7 | 8 | type p256 struct { 9 | curve elliptic.Curve 10 | publicX *big.Int 11 | publicY *big.Int 12 | privateKey []byte 13 | } 14 | 15 | // NewECDH_P256 returns an Elliptic Curve Diffie-Hellman P-256 KeyExchange algorithm. 16 | func NewECDH_P256() (error, KeyExchange) { 17 | curve := elliptic.P256() 18 | priv, x, y, err := elliptic.GenerateKey(curve, rand.Reader) 19 | if err != nil { 20 | return err, nil 21 | } 22 | return nil, &p256{ 23 | curve: curve, 24 | publicX: x, 25 | publicY: y, 26 | privateKey: priv} 27 | } 28 | 29 | // GetPublicKey generates local private/public keys pair and returns the local public key that should be sent to the remote host. 30 | func (this *p256) GetPublicKey() []byte { 31 | return elliptic.Marshal(this.curve, this.publicX, this.publicY) 32 | } 33 | 34 | // ComputeSharedKey computes and returns the shared key based on the local private key and the remote public key. 35 | func (this *p256) ComputeSharedKey(remotePublicKey []byte) (error, []byte) { 36 | remotePublicX, remotePublicY := elliptic.Unmarshal(this.curve, remotePublicKey) 37 | if !this.curve.IsOnCurve(remotePublicX, remotePublicY) { 38 | return errors.New("ECDH : invalid P-256 KeyExchange"), nil 39 | } 40 | x, _ := this.curve.ScalarMult(remotePublicX, remotePublicY, this.privateKey) 41 | return nil, x.Bytes() 42 | } 43 | -------------------------------------------------------------------------------- /protocol/README.md: -------------------------------------------------------------------------------- 1 | [![GoDoc](https://godoc.org/github.com/romain-jacotin/quic/protocol?status.svg)](https://godoc.org/github.com/romain-jacotin/quic/protocol) 2 | 3 | # QUIC Protocol in Go language 4 | 5 | Work in progress on the QUIC protocol in Golang. 6 | 7 | **For official Google information about QUIC protocol, please consult the following website:** 8 | 9 | * Official QUIC information at chromium.org : 10 | * [https://www.chromium.org/quic](https://www.chromium.org/quic) 11 | * Chromium QUIC source code: 12 | * [https://code.google.com/p/chromium/codesearch#chromium/src/net/quic/](https://code.google.com/p/chromium/codesearch#chromium/src/net/quic/) 13 | * QUIC Forum: 14 | * [https://groups.google.com/a/chromium.org/forum/#!forum/proto-quic](https://groups.google.com/a/chromium.org/forum/#!forum/proto-quic) 15 | 16 | ---------------------- 17 | 18 | ## Table of Contents 19 | 20 | * [RingBuffer](#ringbuffer) 21 | 22 | ## RingBuffer 23 | 24 | __RingBuffer__ is a FIFO buffer with a fixed size in bytes (data copy is handled as a circular buffer): 25 | * Read() method extract data from the RingBuffer by copying them 26 | * Write() method copy new data into the RingBuffer 27 | 28 | It is safe to have concurrent Read() and Write(). But it is not safe to use it as is with more than one Reader, or more than one Writer on the same RingBuffer: in this case a synchronization mechanism is needed to serialize Readings and Writings. 29 | 30 | 31 | -------------------------------------------------------------------------------- /crypto/aead_nullfnv1a128_test.go: -------------------------------------------------------------------------------- 1 | package crypto 2 | 3 | import "testing" 4 | import "bytes" 5 | 6 | func Test_AEAD_NullFNV1A_128_Open(t *testing.T) { 7 | buffer := make([]byte, 1500) 8 | aad := []byte("All human beings are born free and equal in dignity and rights.") 9 | plainText := []byte("They are endowed with reason and conscience and should act towards one another in a spirit of brotherhood.") 10 | hash := []byte{0x98, 0x9b, 0x33, 0x3f, 0xe8, 0xde, 0x32, 0x5c, 0xa6, 0x7f, 0x9c, 0xf7} 11 | cipherText := append(hash, plainText...) 12 | aead := NewAEAD_NullFNV1A128() 13 | c, err := aead.Open(42, buffer, aad, cipherText) 14 | if err != nil { 15 | t.Error(err) 16 | } 17 | if c != len(plainText) { 18 | t.Error("AEAD_NullFNV1A_128.Open: bad decrypted length") 19 | } 20 | if !bytes.Equal(plainText, buffer[:c]) { 21 | t.Error("AEAD_NullFNV1A_128.Open: invalid decrypted plaintext") 22 | } 23 | } 24 | 25 | func Test_AEAD_NullFNV1A_128_Seal(t *testing.T) { 26 | buffer := make([]byte, 1500) 27 | aad := []byte("All human beings are born free and equal in dignity and rights.") 28 | plainText := []byte("They are endowed with reason and conscience and should act towards one another in a spirit of brotherhood.") 29 | hash := []byte{0x98, 0x9b, 0x33, 0x3f, 0xe8, 0xde, 0x32, 0x5c, 0xa6, 0x7f, 0x9c, 0xf7} 30 | aead := NewAEAD_NullFNV1A128() 31 | c, err := aead.Seal(42, buffer, aad, plainText) 32 | if err != nil { 33 | t.Error(err) 34 | } 35 | if !bytes.Equal(hash, buffer[:12]) { 36 | t.Errorf("AEAD_NullFNV1A_128.Seal: invalid AEAD Hash") 37 | } 38 | if !bytes.Equal(plainText, buffer[12:c]) { 39 | t.Error("AEAD_NullFNV1A_128.Seal: invalid encrypted plaintext") 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /crypto/fnv1a_test.go: -------------------------------------------------------------------------------- 1 | package crypto 2 | 3 | import "testing" 4 | 5 | func Test_FNV1A_64(t *testing.T) { 6 | /* 7 | https://tools.ietf.org/html/draft-eastlake-fnv-09 8 | 9 | FNV1A Test Vectors : 10 | 11 | String FNV32 FNV64 12 | "" 0x811c9dc5 0xcbf29ce484222325 13 | "a" 0xe40c292c 0xaf63dc4c8601ec8c 14 | "foobar" 0xbf9cf968 0x85944171f73967e8 15 | */ 16 | if ComputeHashFNV1A_64(nil) != 0xcbf29ce484222325 { 17 | t.Error("HashFNV1A_64: bad hash") 18 | } 19 | 20 | if ComputeHashFNV1A_64([]byte("a")) != 0xaf63dc4c8601ec8c { 21 | t.Error("HashFNV1A_64: bad hash") 22 | } 23 | 24 | if ComputeHashFNV1A_64([]byte("foobar")) != 0x85944171f73967e8 { 25 | t.Error("HashFNV1A_64: bad hash") 26 | } 27 | } 28 | 29 | func Test_FNV1A_128(t *testing.T) { 30 | /* 31 | http://find.fnvhash.com/ 32 | 33 | FNV1A Test Vectors : 34 | 35 | String FNV128 36 | "" 0x6c62272e07bb014262b821756295c58d 37 | "a" 0xd228cb696f1a8caf78912b704e4a8964 38 | "foobar" 0x343e1662793c64bf6f0d3597ba446f18 39 | */ 40 | if h, l := ComputeHashFNV1A_128(nil); (h != 0x6c62272e07bb0142) && (l != 0x62b821756295c58d) { 41 | t.Errorf("HashFNV1A_128: bad hash %x %x", h, l) 42 | } 43 | 44 | if h, l := ComputeHashFNV1A_128([]byte("a")); (h != 0xd228cb696f1a8caf) && (l != 0x78912b704e4a8964) { 45 | t.Error("HashFNV1A_128: bad hash") 46 | } 47 | 48 | if h, l := ComputeHashFNV1A_128([]byte("foobar")); (h != 0x343e1662793c64bf) && (l != 0x6f0d3597ba446f18) { 49 | t.Error("HashFNV1A_128: bad hash") 50 | } 51 | } 52 | 53 | func Test_ComputeAeadHashFNV1A_128(t *testing.T) { 54 | h, l := ComputeHashFNV1A_128([]byte("Carpe Diem")) 55 | h_aead, l_aead := ComputeAeadHashFNV1A_128([]byte("Carpe "), []byte("Diem")) 56 | if h != h_aead || l != l_aead { 57 | t.Error("ComputeAeadHashFNV1A_128: bad hash") 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /protocol/quicfecpacket.go: -------------------------------------------------------------------------------- 1 | package protocol 2 | 3 | import "errors" 4 | 5 | // QuicFECPacket 6 | type QuicFECPacket struct { 7 | seqNum QuicPacketSequenceNumber 8 | offset QuicFecGroupNumberOffset 9 | redundancy []byte 10 | } 11 | 12 | // Erase 13 | func (this *QuicFECPacket) Erase() { 14 | this.seqNum = 0 15 | this.offset = 0 16 | this.redundancy = nil 17 | } 18 | 19 | // Setup 20 | func (this *QuicFECPacket) Setup(seqnum QuicPacketSequenceNumber, offset QuicFecGroupNumberOffset) { 21 | this.seqNum = seqnum - QuicPacketSequenceNumber(offset) 22 | this.offset = offset 23 | } 24 | 25 | // ParseData 26 | func (this *QuicFECPacket) ParseData(data []byte) (size int, err error) { 27 | // All left data is the redundancy FEC Packet of the associated FEC Group 28 | size = len(data) 29 | if size > 0 { 30 | this.redundancy = data 31 | } else { 32 | size = 0 33 | err = errors.New("QuicFECPacket.ParseData : invalid FEC Redundancy data of size = 0") 34 | } 35 | return 36 | } 37 | 38 | // GetSerializedSize 39 | func (this *QuicFECPacket) GetSerializedSize() (size int) { 40 | return len(this.redundancy) 41 | } 42 | 43 | // GetSerializedData 44 | func (this *QuicFECPacket) GetSerializedData(data []byte) (size int, err error) { 45 | if len(this.redundancy) == 0 { 46 | err = errors.New("QuicFECPacket.GetSerializedData : invalid FEC Redundancy data of size = 0") 47 | return 48 | } 49 | if len(data) < len(this.redundancy) { 50 | err = errors.New("QuicFECPacket.GetSerializedData : data size too small to contain FEC Packet redundancy") 51 | return 52 | } 53 | size = copy(data, this.redundancy) 54 | return 55 | } 56 | 57 | // SetRedundancyData 58 | func (this *QuicFECPacket) SetRedundancyData(data []byte) { 59 | this.redundancy = data 60 | } 61 | 62 | // GetRedundancyData 63 | func (this *QuicFECPacket) GetRedundancyData() (data []byte) { 64 | data = this.redundancy 65 | return 66 | } 67 | -------------------------------------------------------------------------------- /crypto/aead_nullfnv1a128.go: -------------------------------------------------------------------------------- 1 | package crypto 2 | 3 | import "github.com/romain-jacotin/quic/protocol" 4 | import "errors" 5 | import "encoding/binary" 6 | import "fmt" 7 | 8 | type AEAD_NullFNV1A128 struct { 9 | } 10 | 11 | // NewAEAD_NullFNV1A128 returns a *AEAD_NullFNV1A128 that implements an AEAD interface with null encryption and FNV1A-128 hash truncated to 96-bit 12 | func NewAEAD_NullFNV1A128() AEAD { 13 | return new(AEAD_NullFNV1A128) 14 | } 15 | 16 | // Open 17 | func (this *AEAD_NullFNV1A128) Open(sequencenumber protocol.QuicPacketSequenceNumber, plaintext, aad, ciphertext []byte) (bytescount int, err error) { 18 | // Check the Hash 19 | l := len(ciphertext) - 12 20 | if l < 0 { 21 | err = errors.New("AEAD_NullFNV1A128.Open : Hash can't be less than 12 bytes") 22 | return 23 | } 24 | low := binary.LittleEndian.Uint64(ciphertext) 25 | high := binary.LittleEndian.Uint32(ciphertext[8:]) 26 | testhigh, testlow := ComputeAeadHashFNV1A_128(aad, ciphertext[12:]) 27 | if (low != testlow) || (high != uint32(testhigh&0xffffffff)) { 28 | err = fmt.Errorf("AEAD_NullFNV1A128.Open : invalid Hash verification %x:%x versus %x:%x", low, high, testlow, testhigh) 29 | return 30 | } 31 | // Then Copy (without decryption) 32 | copy(plaintext[:l], ciphertext[12:]) 33 | bytescount = l 34 | return 35 | } 36 | 37 | // Seal 38 | func (this *AEAD_NullFNV1A128) Seal(sequencenumber protocol.QuicPacketSequenceNumber, ciphertext, aad, plaintext []byte) (bytescount int, err error) { 39 | l := len(plaintext) 40 | if len(ciphertext) < (l + 12) { 41 | err = errors.New("AEAD_NullFNV1A128.Seal : ciphertext can't be less than plaintext + 12 bytes") 42 | return 43 | } 44 | // Hash 45 | high, low := ComputeAeadHashFNV1A_128(aad, plaintext) 46 | binary.LittleEndian.PutUint64(ciphertext, low) 47 | binary.LittleEndian.PutUint32(ciphertext[8:], uint32(high)) 48 | // Then Copy (without encryption) 49 | copy(ciphertext[12:], plaintext) 50 | bytescount = l + 12 51 | return 52 | } 53 | 54 | // GetMacSize 55 | func (this *AEAD_NullFNV1A128) GetMacSize() int { 56 | return 12 57 | } 58 | -------------------------------------------------------------------------------- /crypto/hkdf.go: -------------------------------------------------------------------------------- 1 | package crypto 2 | 3 | import "crypto/hmac" 4 | import "crypto/sha256" 5 | 6 | // HKDF contains the resulting AEAD Key and Initialization Vector for QUIC Client and QUIC Server 7 | type HKDF struct { 8 | clientWriteKey []byte 9 | clientWriteNonce []byte 10 | serverWriteKey []byte 11 | serverWriteNonce []byte 12 | } 13 | 14 | // NewHKDF is a factory HKDF that computes the output keys materials for AEAD by using HMAC-based Key Derivation Function using SHA-256 as Hash function. 15 | // 16 | // An error is return and a pointer to an HKDF structure that contains the resulting Output Keying Material. 17 | func NewHKDF(salt, ikm, info []byte, keysize, noncesize int) (error, *HKDF) { 18 | var t []byte 19 | var okm []byte 20 | var counter byte 21 | var i int 22 | 23 | counter = 1 24 | need := 2*keysize + 2*noncesize 25 | n := need / 32 26 | m := need % 32 27 | 28 | if salt == nil { 29 | salt = make([]byte, 32) // SHA-256 requires 32-bytes Key 30 | } 31 | 32 | extract := hmac.New(sha256.New, salt) 33 | extract.Write(ikm) 34 | prk := extract.Sum(nil) 35 | 36 | // We need 32 + 32 + 32 + 32 bytes of Output Keying Material 37 | expand := hmac.New(sha256.New, prk) 38 | 39 | // Fill the okm buffer 40 | for i = 0; i < n; i++ { 41 | expand.Reset() 42 | expand.Write(t) 43 | expand.Write(info) 44 | expand.Write([]byte{counter}) 45 | t = expand.Sum(nil) 46 | counter++ 47 | copy(okm[i*32:], t) 48 | } 49 | if m > 0 { 50 | expand.Reset() 51 | expand.Write(t) 52 | expand.Write(info) 53 | expand.Write([]byte{counter}) 54 | t = expand.Sum(nil) 55 | copy(okm[i*32:], t[0:m]) 56 | } 57 | 58 | return nil, &HKDF{ 59 | clientWriteKey: okm[0:keysize], 60 | clientWriteNonce: okm[keysize : 2*keysize], 61 | serverWriteKey: okm[2*keysize : 2*keysize+noncesize], 62 | serverWriteNonce: okm[2*keysize+noncesize : 2*keysize+2*noncesize]} 63 | } 64 | 65 | // GetClientWriteKey returns the Key used by the QUIC Client for AEAD when sending packet. 66 | func (this *HKDF) GetClientWriteKey() []byte { 67 | return this.clientWriteKey 68 | } 69 | 70 | // GetClientWriteNonce returns the Nonce used by the QUIC Client for AEAD when sending packet. 71 | func (this *HKDF) GetClientWriteNonce() []byte { 72 | return this.clientWriteNonce 73 | } 74 | 75 | // GetServerWriteKey returns the Key used by the QUIC Server for AEAD when sending packet. 76 | func (this *HKDF) GetServerWriteKey() []byte { 77 | return this.serverWriteKey 78 | } 79 | 80 | // GetServerWriteNonce returns the Nonce used by the QUIC Server for AEAD when sending packet. 81 | func (this *HKDF) GetServerWriteNonce() []byte { 82 | return this.serverWriteNonce 83 | } 84 | -------------------------------------------------------------------------------- /protocol/quicfecpacket_test.go: -------------------------------------------------------------------------------- 1 | package protocol 2 | 3 | import "testing" 4 | import "bytes" 5 | 6 | type testquicfecpacket struct { 7 | positiveTest bool 8 | data []byte 9 | seqnum QuicPacketSequenceNumber 10 | offset QuicFecGroupNumberOffset 11 | } 12 | 13 | var tests_quicfecpacket = []testquicfecpacket{ 14 | {false, []byte{}, 0x0102030405060708, 0x42}, 15 | {true, []byte{0x01}, 0x0102030405060708, 0x42}, 16 | {true, []byte{0x01, 0x02}, 0x0102030405060708, 0x42}, 17 | {true, []byte{0x01, 0x02, 0x03}, 0x0102030405060708, 0x42}, 18 | {true, []byte{0x01, 0x02, 0x03, 0x04}, 0x0102030405060708, 0x42}, 19 | } 20 | 21 | func Test_QuicFecPacket_ParseData(t *testing.T) { 22 | var fec QuicFECPacket 23 | 24 | for i, v := range tests_quicfecpacket { 25 | s, err := fec.ParseData(v.data) 26 | if v.positiveTest { 27 | if err != nil { 28 | t.Errorf("QuicFECPacket.ParseData : error %s in test %x with data[%v]%x", err, i, len(v.data), v.data) 29 | } 30 | if s != len(v.data) { 31 | t.Errorf("QuicFECPacket.ParseData : invalid size %v in test %x with data[%v]%x", s, i, len(v.data), v.data) 32 | } 33 | if !bytes.Equal(v.data, fec.redundancy) { 34 | t.Errorf("QuicFECPacket.ParseData : invalid redundancy data %x in test %x with data[%v]%x", fec.redundancy, i, len(v.data), v.data) 35 | } 36 | } else if err == nil { 37 | t.Errorf("QuicFECPacket.ParseData : missing error in test %x with data[%v]%x", i, len(v.data), v.data) 38 | } 39 | } 40 | } 41 | 42 | func Test_QuicFecPacket_GetSerializedData(t *testing.T) { 43 | var fec QuicFECPacket 44 | 45 | data := make([]byte, 4) 46 | for i, v := range tests_quicfecpacket { 47 | fec.Setup(v.seqnum, v.offset) 48 | fec.SetRedundancyData(v.data) 49 | s, err := fec.GetSerializedData(data) 50 | if v.positiveTest { 51 | if err != nil { 52 | t.Errorf("QuicFECPacket.GetSerializedData = error %s while serialized data in test n°%v", err, i) 53 | } 54 | if s != len(v.data) { 55 | t.Errorf("QuicFECPacket.GetSerializedData = invalid serialized size in test n°%v with data[%v]%x", i, s, data[:s]) 56 | } 57 | if !bytes.Equal(data[:s], v.data) { 58 | t.Errorf("QuicFECPacket.GetSerializedData = invalid serialized data in test n°%v with data[%v]%x", i, s, data[:s]) 59 | } 60 | if fec.seqNum != (v.seqnum - QuicPacketSequenceNumber(v.offset)) { 61 | t.Errorf("QuicFECPacket.GetSerializedData = invalid sequence number %x in test n°%v", fec.seqNum, i) 62 | } 63 | if fec.offset != v.offset { 64 | t.Errorf("QuicFECPacket.GetSerializedData = invalid FEC Group Number offset %x in test n°%v", fec.offset, i) 65 | } 66 | } 67 | fec.Erase() 68 | } 69 | 70 | } 71 | -------------------------------------------------------------------------------- /protocol/parser_test.go: -------------------------------------------------------------------------------- 1 | package protocol 2 | 3 | import "testing" 4 | import "bytes" 5 | 6 | func tests(in chan<- []byte) { 7 | in <- []byte{'C', 'H', 'L', 'O', 0, 0, 0, 0} 8 | in <- []byte{'C', 'H', 'L', 'O', 1, 0, 0, 0, 'S', 'N', 'I', 0, 1, 0, 0, 0, 1} 9 | in <- []byte{'C', 'H', 'L', 'O', 2, 0, 0, 0, 'S', 'N', 'I', 0, 1, 0, 0, 0, 'C', 'E', 'T', 'V', 3, 0, 0, 0, 1, 2, 3} 10 | in <- []byte{'C', 'H', 'L', 'O', 3, 0, 0, 0, 'S', 'N', 'I', 0, 1, 0, 0, 0, 'A', 'E', 'A', 'D', 4, 0, 0, 0, 'C', 'E', 'T', 'V', 6, 0, 0, 0, 1, 4, 5, 6, 2, 3} 11 | } 12 | 13 | func Test_Parser(t *testing.T) { 14 | var b bool 15 | var v []byte 16 | 17 | parser := NewParser() 18 | go tests(parser.GetInput()) 19 | parser.Start() 20 | out := parser.GetOutput() 21 | 22 | msg := <-out 23 | if msg.GetMessageTag() != TagCHLO { 24 | t.Error("Parser: invalid message tag") 25 | } 26 | if msg.GetNumEntries() != 0 { 27 | t.Error("Parser: invalid number of tag/value entries") 28 | } 29 | 30 | msg = <-out 31 | if msg.GetMessageTag() != TagCHLO { 32 | t.Error("Parser: invalid message tag") 33 | } 34 | if msg.GetNumEntries() != 1 { 35 | t.Error("Parser: invalid number of tag/value entries") 36 | } 37 | if b, v = msg.ContainsTag(TagSNI); !b { 38 | t.Error("Parser: tag is not exist in the Message") 39 | } 40 | if !bytes.Equal(v, []byte{1}) { 41 | t.Error("Parser: invalid tag value") 42 | } 43 | 44 | msg = <-out 45 | if msg.GetMessageTag() != TagCHLO { 46 | t.Error("Parser: invalid message tag") 47 | } 48 | if msg.GetNumEntries() != 2 { 49 | t.Error("Parser: invalid number of tag/value entries") 50 | } 51 | if b, v = msg.ContainsTag(TagSNI); !b { 52 | t.Error("Parser: tag is not exist in the Message") 53 | } 54 | if !bytes.Equal(v, []byte{1}) { 55 | t.Error("Parser: invalid tag value") 56 | } 57 | if b, v = msg.ContainsTag(TagCETV); !b { 58 | t.Error("Parser: tag is not exist in the Message") 59 | } 60 | if !bytes.Equal(v, []byte{2, 3}) { 61 | t.Error("Parser: invalid tag value") 62 | } 63 | 64 | msg = <-out 65 | if msg.GetMessageTag() != TagCHLO { 66 | t.Error("Parser: invalid message tag") 67 | } 68 | if msg.GetNumEntries() != 3 { 69 | t.Error("Parser: invalid number of tag/value entries") 70 | } 71 | if b, v = msg.ContainsTag(TagSNI); !b { 72 | t.Error("Parser: tag is not exist in the Message") 73 | } 74 | if !bytes.Equal(v, []byte{1}) { 75 | t.Error("Parser: invalid tag value") 76 | } 77 | if b, v = msg.ContainsTag(TagCETV); !b { 78 | t.Error("Parser: tag is not exist in the Message") 79 | } 80 | if !bytes.Equal(v, []byte{2, 3}) { 81 | t.Error("Parser: invalid tag value") 82 | } 83 | if b, v = msg.ContainsTag(TagAEAD); !b { 84 | t.Error("Parser: tag is not exist in the Message") 85 | } 86 | if !bytes.Equal(v, []byte{4, 5, 6}) { 87 | t.Error("Parser: invalid tag value") 88 | } 89 | parser.Stop() 90 | } 91 | -------------------------------------------------------------------------------- /crypto/aead_chacha20poly1305.go: -------------------------------------------------------------------------------- 1 | package crypto 2 | 3 | import "github.com/romain-jacotin/quic/protocol" 4 | import "encoding/binary" 5 | import "errors" 6 | import "fmt" 7 | 8 | type AEAD_ChaCha20Poly1305 struct { 9 | cipher *ChaCha20Cipher 10 | hasher *Poly1305 11 | } 12 | 13 | // NewAEAD_ChaCha20Poly1305 is an *AEAD_ChaCha20Poly1305 factory that implements AEAD interface 14 | func NewAEAD_ChaCha20Poly1305(key, nonceprefix []byte) (AEAD, error) { 15 | var buf [64]byte 16 | var err error 17 | 18 | if len(key) < 32 { 19 | return nil, errors.New("NewAEAD_ChaCha20Poly1305 : AEAD_CHACHA20_POLY1305_12 requires 256-bit key") 20 | } 21 | if len(nonceprefix) < 4 { 22 | return nil, errors.New("NewAEAD_ChaCha20Poly1305 : QUIC requires 32-bit nonce prefix") 23 | } 24 | 25 | aead := new(AEAD_ChaCha20Poly1305) 26 | if aead.cipher, err = NewChaCha20Cipher(key, nonceprefix, 0); err != nil { 27 | return nil, errors.New("NewAEAD_ChaCha20Poly1305 : error when calling NewChaCha20Cipher") 28 | } 29 | aead.cipher.GetNextKeystream(&buf) 30 | if aead.hasher, err = NewPoly1305(buf[0:32]); err != nil { 31 | return nil, errors.New("NewAEAD_ChaCha20Poly1305 : error when calling NewPoly1305") 32 | } 33 | return aead, nil 34 | } 35 | 36 | // Open 37 | func (this *AEAD_ChaCha20Poly1305) Open(seqnum protocol.QuicPacketSequenceNumber, plaintext, aad, ciphertext []byte) (bytescount int, err error) { 38 | // Authenticate: check the MAC 39 | l := len(ciphertext) - 12 40 | if l < 0 { 41 | err = errors.New("AEAD_ChaCha20Poly1305.Open : Message Authentication Code can't be less than 12 bytes") 42 | return 43 | } 44 | if len(plaintext) < l { 45 | err = errors.New("AEAD_ChaCha20Poly1305.Open : plaintext must same have length as ciphertext less 12 bytes at minimum") 46 | return 47 | } 48 | low := binary.LittleEndian.Uint64(ciphertext[l:]) 49 | high := binary.LittleEndian.Uint32(ciphertext[l+8:]) 50 | testhigh, testlow := this.hasher.ComputeAeadMAC(aad, ciphertext[:l]) 51 | if (low != testlow) || (high != uint32(testhigh&0xffffffff)) { 52 | err = fmt.Errorf("AEAD_ChaCha20Poly1305.Open : invalid Message Authentication Code verification %x:%x versus %x:%x", low, high, testlow, testhigh) 53 | return 54 | } 55 | // Then decrypt 56 | this.cipher.SetPacketSequenceNumber(seqnum) 57 | bytescount, err = this.cipher.Decrypt(plaintext, ciphertext[:l]) 58 | return 59 | } 60 | 61 | // Seal 62 | func (this *AEAD_ChaCha20Poly1305) Seal(seqnum protocol.QuicPacketSequenceNumber, ciphertext, aad, plaintext []byte) (bytescount int, err error) { 63 | // Encrypt 64 | l := len(plaintext) 65 | if len(ciphertext) < (l + 12) { 66 | err = errors.New("AEAD_ChaCha20Poly1305.Seal : ciphertext can't be less than plaintext + 12 bytes") 67 | return 68 | } 69 | this.cipher.SetPacketSequenceNumber(seqnum) 70 | if bytescount, err = this.cipher.Encrypt(ciphertext, plaintext); err != nil { 71 | return 72 | } 73 | if bytescount != l { 74 | err = errors.New("AEAD_ChaCha20Poly1305.Seal : truncated encryption by Chacha20Cipher.Encrypt") // Impossible normally 75 | } 76 | // Then MAC 77 | high, low := this.hasher.ComputeAeadMAC(aad, ciphertext[:l]) 78 | binary.LittleEndian.PutUint64(ciphertext[l:], low) 79 | binary.LittleEndian.PutUint32(ciphertext[l+8:], uint32(high)) 80 | bytescount += 12 81 | return 82 | } 83 | 84 | // GetMacSize 85 | func (this *AEAD_ChaCha20Poly1305) GetMacSize() int { 86 | return 12 87 | } 88 | -------------------------------------------------------------------------------- /doc/TCPProportionalRateReduction.md: -------------------------------------------------------------------------------- 1 | # Proportional Rate Reduction for TCP 2 | 3 | Extracts from __rfc6937__ : [http://tools.ietf.org/html/rfc6937](http://tools.ietf.org/html/rfc6937) 4 | 5 | ----------------------- 6 | 7 | ## Table of Contents: 8 | 9 | * [Overview](#overview) 10 | * [Definitions](#definitions) 11 | 12 | ## Overview 13 | 14 | Proportional Rate Reduction (PRR) algorithm is an experimental alternative to the widely deployed Fast Recovery and Rate-Halving algorithms. The goal is to improve the accuracy of the amount of data sent by TCP during loss recovery. 15 | 16 | These algorithms determine the amount of data sent by TCP during loss recovery. PRR minimizes excess window adjustments, and the actual window size at the end of recovery will be as close as possible to the __ssthresh__, as determined by the congestion control algorithm. 17 | 18 | ## Definitions 19 | 20 | * __SND.UNA__ : 21 | * Oldest unacknowledged sequence number 22 | * __duplicate ACK__ : 23 | * __FlightSize__ : 24 | * The amount of data that has been sent but not yet cumulatively acknowledged 25 | * __SMSS__ : 26 | * Sender Maximum Segment Size 27 | * __Voluntary window reductions__ : 28 | * Choosing not to send data in response to some ACKs, for the purpose of reducing the sending window size and data rate 29 | 30 | We define some additional variables: 31 | 32 | * __SACKd__ 33 | * The total number of bytes that the scoreboard indicates have been delivered to the receiver. This can be computed by scanning the scoreboard and counting the total number of bytes covered by all SACK blocks. If SACK is not in use, SACKd is not defined. 34 | * __DeliveredData__ : 35 | * is the total number of bytes that the current ACK indicates have been delivered to the receiver 36 | * When not in recovery: 37 | * __DeliveredData = SND.UNA + SACKd__ 38 | * In recovery without SACK: 39 | * On duplicate acknowledgements: 40 | * __DeliveredData = 1 * SMSS__ 41 | * On a subsequent partial or full ACK: 42 | * __DeliveredData = SND.UNA - 1 * SMSS__ for each preceding duplicate ACK 43 | 44 | Note that DeliveredData is robust; for TCP using SACK, DeliveredData can be precisely computed anywhere in the network just by inspecting the returning ACKs. The consequence of missing ACKs is that later ACKs will show a larger DeliveredData. Furthermore, for any TCP (with or without SACK), the sum of DeliveredData must agree with the forward progress over the same time interval. 45 | 46 | We introduce a local variable "sndcnt", which indicates exactly how many bytes should be sent in response to each ACK. Note that the decision of which data to send (e.g., retransmit missing data or send more new data) is out of scope for this document. 47 | 48 | ## Algorithms 49 | 50 | * At the beginning of recovery, initialize PRR state. This assumes a modern congestion control algorithm, CongCtrlAlg(), that might set __ssthresh__ to something other than __FlightSize/2__: 51 | 52 | ``` 53 | ssthresh = CongCtrlAlg() // Target cwnd after recovery 54 | prr_delivered = 0 // Total bytes delivered during recovery 55 | prr_out = 0 // Total bytes sent during recovery 56 | RecoverFS = snd.nxt-snd.una // FlightSize at the start of recovery 57 | ``` 58 | 59 | * On every ACK during recovery compute: 60 | 61 | ``` 62 | DeliveredData = change_in(snd.una) + change_in(SACKd) 63 | prr_delivered += DeliveredData 64 | pipe = (RFC 6675 pipe algorithm) 65 | if (pipe > ssthresh) { 66 | // Proportional Rate Reduction 67 | sndcnt = CEIL(prr_delivered * ssthresh / RecoverFS) - prr_out 68 | } else { 69 | // Two versions of the Reduction Bound 70 | if (conservative) { // PRR-CRB 71 | limit = prr_delivered - prr_out 72 | } else { // PRR-SSRB 73 | limit = MAX(prr_delivered - prr_out, DeliveredData) + MSS 74 | } 75 | // Attempt to catch up, as permitted by limit 76 | sndcnt = MIN(ssthresh - pipe, limit) 77 | } 78 | ``` 79 | 80 | * On any data transmission or retransmission: 81 | 82 | ``` 83 | prr_out += (data sent) // strictly less than or equal to sndcnt 84 | ``` 85 | 86 | 87 | 88 | -------------------------------------------------------------------------------- /protocol/quicprivateheader_test.go: -------------------------------------------------------------------------------- 1 | package protocol 2 | 3 | import "testing" 4 | import "bytes" 5 | 6 | /* 7 | 8 | 0 1 9 | +--------+--------+ 10 | |Private | FEC (8)| 11 | |Flags(8)| (opt) | 12 | +--------+--------+ 13 | 14 | 15 | Private flags: 16 | +---+---+---+---+---+---+---+---+ 17 | | 0 | 0 | 0 | 0 | 0 |FEC|GRP|ENT| 18 | +---+---+---+---+---+---+---+---+ 19 | 20 | */ 21 | 22 | type testquicprivateheader struct { 23 | positiveTest bool 24 | data []byte 25 | flagFecPacket bool 26 | flagFecGroup bool 27 | flagEntropy bool 28 | offset QuicFecGroupNumberOffset 29 | } 30 | 31 | var tests_quicprivateheader = []testquicprivateheader{ 32 | // Test[0-2] invalid data size 33 | {false, []byte{}, true, true, true, 0x66}, 34 | {false, []byte{0xf0}, false, false, false, 0x66}, 35 | {false, []byte{0x07}, true, true, true, 0x66}, 36 | // Test[3-6] various FEC Group and Entropy flags with FEC Packet flag set 37 | {true, []byte{QUICFLAG_FECPACKET | QUICFLAG_FECGROUP, 0x42}, true, true, false, 0x42}, 38 | {false, []byte{QUICFLAG_FECPACKET | QUICFLAG_ENTROPY}, true, false, true, 0}, 39 | {false, []byte{QUICFLAG_FECPACKET}, true, false, false, 0}, 40 | {true, []byte{QUICFLAG_FECPACKET | QUICFLAG_FECGROUP | QUICFLAG_ENTROPY, 0x42}, true, true, true, 0x42}, 41 | // Test[7-10] various FEC Group and Entropy flags with FEC Packet flag unset 42 | {true, []byte{QUICFLAG_FECGROUP | QUICFLAG_ENTROPY, 0x42}, false, true, true, 0x42}, 43 | {true, []byte{QUICFLAG_FECGROUP, 0x42}, false, true, false, 0x42}, 44 | {true, []byte{QUICFLAG_ENTROPY}, false, false, true, 0}, 45 | {true, []byte{0}, false, false, false, 0}, 46 | } 47 | 48 | func Test_QuicPrivateHeader_ParseData(t *testing.T) { 49 | var priv QuicPrivateHeader 50 | var fecgroupnum QuicFecGroupNumberOffset 51 | 52 | for i, v := range tests_quicprivateheader { 53 | s, err := priv.ParseData(v.data) 54 | if v.positiveTest { 55 | if err != nil { 56 | t.Errorf("ParseData = error %s in test n°%v", err, i) 57 | } 58 | if s != len(v.data) { 59 | t.Errorf("ParseData = invalid parsed size in test n°%v with data[%v]%x", i, len(v.data), v.data) 60 | } 61 | if priv.GetFecPacketFlag() != v.flagFecPacket { 62 | t.Errorf("ParseData = invalid FEC Packet flag in test n°%v with data[%v]%x", i, len(v.data), v.data) 63 | } 64 | if priv.GetFecGroupFlag() != v.flagFecGroup { 65 | t.Errorf("ParseData = invalid FEC Group presence flag in test n°%v with data[%v]%x", i, len(v.data), v.data) 66 | } 67 | if priv.GetEntropyFlag() != v.flagEntropy { 68 | t.Errorf("ParseData = invalid Entropy flag in test n°%v with data[%v]%x", i, len(v.data), v.data) 69 | } 70 | if v.flagFecGroup { 71 | if fecgroupnum, err = priv.GetFecGroupNumberOffset(); err != nil { 72 | t.Errorf("ParseData = error %s in test n°%v with data[%v]%x", err, i, len(v.data), v.data) 73 | } 74 | if fecgroupnum != v.offset { 75 | t.Errorf("ParseData = invalid FEC GRoup Offset value in test n°%v with data[%v]%x", i, len(v.data), v.data) 76 | } 77 | } 78 | } else if err == nil { 79 | t.Errorf("ParseData = missing error in test n°%v with data[%v]%x", i, len(v.data), v.data) 80 | } 81 | priv.Erase() 82 | } 83 | } 84 | 85 | func Test_QuicPrivateHeader_GetSerializedData(t *testing.T) { 86 | var priv QuicPrivateHeader 87 | 88 | data := make([]byte, 2) 89 | for i, v := range tests_quicprivateheader { 90 | if v.positiveTest { 91 | priv.SetEntropyFlag(v.flagEntropy) 92 | priv.SetFecGroupFlag(v.flagFecGroup) 93 | priv.SetFecPacketFlag(v.flagFecPacket) 94 | priv.SetFecGroupNumberOffset(v.offset) 95 | s, err := priv.GetSerializedData(data) 96 | if err != nil { 97 | t.Errorf("GetSerializedData = error %s while serialized data in test n°%v", err, i) 98 | } 99 | if s != len(v.data) { 100 | t.Errorf("GetSerializedData = invalid serialized size in test n°%v with data[%v]%x", i, s, data[:s]) 101 | } 102 | if !bytes.Equal(data[:s], v.data) { 103 | t.Errorf("GetSerializedData = invalid serialized data in test n°%v with data[%v]%x", i, s, data[:s]) 104 | } 105 | priv.Erase() 106 | } 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![GoDoc](https://godoc.org/github.com/romain-jacotin/quic?status.svg)](https://godoc.org/github.com/romain-jacotin/quic) 2 | 3 | # QUIC Protocol in Go language 4 | 5 | Work in progress on the High-Level API definition for QUIC programs in Golang. 6 | 7 | **For official Google information about QUIC protocol, please consult the following website:** 8 | 9 | * Official QUIC information at chromium.org : 10 | * [https://www.chromium.org/quic](https://www.chromium.org/quic) 11 | * Chromium QUIC source code: 12 | * [https://code.google.com/p/chromium/codesearch#chromium/src/net/quic/](https://code.google.com/p/chromium/codesearch#chromium/src/net/quic/) 13 | * QUIC Forum: 14 | * [https://groups.google.com/a/chromium.org/forum/#!forum/proto-quic](https://groups.google.com/a/chromium.org/forum/#!forum/proto-quic) 15 | 16 | ---------------------- 17 | 18 | ## Table of contents: 19 | 20 | * [Introduction](#introduction) 21 | * [QUIC Session management](#quicsessionmngt) 22 | * [Initialization](#sessioninitialization) 23 | * [Client side](#clientside) 24 | * [Server side](#serverside) 25 | * [Termination](#sessiontermination) 26 | * [Close](#sessionclose) 27 | * [GoAway](#sessiongoaway) 28 | * [Reset](#sessionreset) 29 | * [Keep Alive](#sessionkeepalive) 30 | * [Timeout](#sessiontimeout) 31 | * [Ping](#sessionping) 32 | * [Pacing](#pacing) 33 | * [Auto-pacing](#autopacing) 34 | * [Minimum pacing](#minimumpacing) 35 | * [Stream management](#streammngt) 36 | * [Creation](#streamcreation) 37 | * [Read](#streamread) 38 | * [Write](#streamwrite) 39 | * [Write (standard)](#standardwrite) 40 | * [Write with FEC](#fecwrite) 41 | * [Write with Duplicate QUIC packets](*duplicatewrite) 42 | * [Close (half)](#streamclose) 43 | * [Reset](#streamreset) 44 | * [ANNEX A: Extracts from RFC793 - TCP](./doc/TCP.md) 45 | * [ANNEX B: Extracts from RFC5681 _ TCP Congestion Control](./doc/TCPCongestionControl.md) 46 | * [ANNEX C: Extracts from RFC6298 - Computing TCP's Retransmission Timer](./doc/TCPRetransmissionTimer.md) 47 | * [ANNEX D: Extracts from draft-rhee-tcpm-cubic-02 - CUBIC Congestion Control for Fast Long-Distance Networks](./doc/CUBIC.md) 48 | * [ANNEX E: Extracts from RFC6937 - Proportional Rate Reduction for TCP](./doc/TCPProportionalRateReduction.md) 49 | 50 | ## Introduction 51 | 52 | ## QUIC Session management 53 | 54 | TBD 55 | 56 | ### Initialization 57 | 58 | TBD 59 | 60 | #### Client side 61 | 62 | TBD 63 | 64 | #### Server side 65 | 66 | TBD 67 | 68 | ### Termination 69 | 70 | TBD 71 | 72 | #### Close 73 | 74 | TBD 75 | 76 | #### GoAway 77 | 78 | TBD 79 | 80 | #### Reset 81 | 82 | TBD 83 | 84 | ## Pacing 85 | 86 | TBD 87 | 88 | ### Auto-pacing 89 | 90 | TBD 91 | 92 | ### Minimum pacing 93 | 94 | TBD 95 | 96 | ### Keep Alive 97 | 98 | TBD 99 | 100 | #### Timeout 101 | 102 | TBD 103 | 104 | #### Ping 105 | 106 | TBD 107 | 108 | ## Stream management 109 | 110 | TBD 111 | 112 | ### Creation 113 | 114 | TBD 115 | 116 | ### Read 117 | 118 | TBD 119 | 120 | ### Write 121 | 122 | TBD 123 | 124 | #### Write (standard) 125 | 126 | TBD 127 | 128 | #### Write with Forward Error Correction 129 | 130 | TBD 131 | 132 | #### Write with Duplicate QUIC packets 133 | 134 | TBD 135 | 136 | ### Close (half) 137 | 138 | TBD 139 | 140 | ### Reset 141 | 142 | TBD 143 | 144 | ### Priority 145 | 146 | TBD 147 | 148 | -------------------------------------------------------------------------------- /crypto/fnv1a.go: -------------------------------------------------------------------------------- 1 | package crypto 2 | 3 | func ComputeHashFNV1A_64(data []byte) uint64 { 4 | var val uint64 = 14695981039346656037 // offset_basis = 14695981039346656037 5 | const FNV_64_PRIME = 1099511628211 6 | 7 | for _, v := range data { 8 | // xor the bottom with the current octet 9 | val ^= uint64(v) 10 | 11 | // multiply by the 64 bit FNV magic prime mod 2^64 12 | // fnv_prime = 1099511628211 13 | val *= FNV_64_PRIME 14 | } 15 | return val 16 | } 17 | 18 | func ComputeAeadHashFNV1A_128(aad, text []byte) (high, low uint64) { 19 | high, low = ComputeHashFNV1A_128(aad) 20 | high, low = IncrementalHashFNV1A_128(high, low, text) 21 | return 22 | } 23 | 24 | func IncrementalHashFNV1A_128(high_prev, low_prev uint64, data []byte) (high, low uint64) { 25 | var val [4]uint64 26 | var tmp [4]uint64 // tmp 128 bit value 27 | 28 | const FNV_128_PRIME_LOW = 0x0000013B 29 | const FNV_128_PRIME_SHIFT = 24 30 | 31 | val[0] = low_prev & 0xffffffff 32 | val[1] = low_prev >> 32 33 | val[2] = high_prev & 0xffffffff 34 | val[3] = high_prev >> 32 35 | for _, v := range data { 36 | // xor the bottom with the current octet 37 | val[0] ^= uint64(v) 38 | 39 | // multiply by the 128 bit FNV magic prime mod 2^128 40 | // fnv_prime = 309485009821345068724781371 (decimal) 41 | // = 0x0000000001000000000000000000013B (hexadecimal) 42 | // = 0x00000000 0x01000000 0x00000000 0x0000013B (in 4*32 words) 43 | // = 0x0 1<> 32) 56 | tmp[2] += (tmp[1] >> 32) 57 | tmp[3] += (tmp[2] >> 32) 58 | 59 | val[0] = tmp[0] & 0xffffffff 60 | val[1] = tmp[1] & 0xffffffff 61 | val[2] = tmp[2] & 0xffffffff 62 | val[3] = tmp[3] // & 0xffffffff 63 | // Doing a val[3] &= 0xffffffff is not really needed since it simply 64 | // removes multiples of 2^128. We can discard these excess bits 65 | // outside of the loop when writing the hash in Little Endian. 66 | } 67 | return val[3]<<32 | val[2], val[1]<<32 | val[0] 68 | } 69 | 70 | func ComputeHashFNV1A_128(data []byte) (high, low uint64) { 71 | // offset_basis = 144066263297769815596495629667062367629 72 | // = 0x6C62272E 07BB0142 62B82175 6295C58D 73 | // Convert offset_basis into a base 2^32 array 74 | var val = [4]uint64{0x6295C58D, 0x62B82175, 0x07BB0142, 0x6C62272E} 75 | var tmp [4]uint64 // tmp 128 bit value 76 | 77 | const FNV_128_PRIME_LOW = 0x0000013B 78 | const FNV_128_PRIME_SHIFT = 24 79 | 80 | for _, v := range data { 81 | // xor the bottom with the current octet 82 | val[0] ^= uint64(v) 83 | 84 | // multiply by the 128 bit FNV magic prime mod 2^128 85 | // fnv_prime = 309485009821345068724781371 (decimal) 86 | // = 0x0000000001000000000000000000013B (hexadecimal) 87 | // = 0x00000000 0x01000000 0x00000000 0x0000013B (in 4*32 words) 88 | // = 0x0 1<> 32) 101 | tmp[2] += (tmp[1] >> 32) 102 | tmp[3] += (tmp[2] >> 32) 103 | 104 | val[0] = tmp[0] & 0xffffffff 105 | val[1] = tmp[1] & 0xffffffff 106 | val[2] = tmp[2] & 0xffffffff 107 | val[3] = tmp[3] // & 0xffffffff 108 | // Doing a val[3] &= 0xffffffff is not really needed since it simply 109 | // removes multiples of 2^128. We can discard these excess bits 110 | // outside of the loop when writing the hash in Little Endian. 111 | } 112 | return val[3]<<32 | val[2], val[1]<<32 | val[0] 113 | } 114 | -------------------------------------------------------------------------------- /protocol/ringbuffer.go: -------------------------------------------------------------------------------- 1 | package protocol 2 | 3 | import "errors" 4 | 5 | // RingBuffer implements io.Reader interface with an internal ring buffer. 6 | // RingBuffer makes copy on Read(), but not on Write() where it returns a slice pointing to the ring buffer on Write call (it is not a io.Writer interface). 7 | // 8 | // 9 | // It is safe to have only one Reader and only one concurrent Writer. 10 | // But it is totally unsafe to use it as is with multiple readers or multiple writers without an external synchronization mechanism. 11 | type RingBuffer struct { 12 | buffer []byte 13 | size int 14 | writeOffset uint64 15 | readOffset uint64 16 | ch chan int 17 | } 18 | 19 | // NewRingBuffer is a factory for RingBuffer, from various size in bytes. 20 | func NewRingBuffer(size int) (error, *RingBuffer) { 21 | b := make([]byte, size, size) 22 | if len(b) != size { 23 | return errors.New("RingBuffer: Failed to allocated memory buffer"), nil 24 | } 25 | return nil, &RingBuffer{ 26 | buffer: b, 27 | size: size, 28 | ch: make(chan int)} 29 | } 30 | 31 | // GetBufferSize returns the size of the buffer. 32 | func (this *RingBuffer) GetBufferSize() int { 33 | return this.size 34 | } 35 | 36 | // CanRead returns the current number of bytes in the RingBuffer that can be reads. 37 | func (this *RingBuffer) CanRead() int { 38 | return int(this.writeOffset - this.readOffset) 39 | } 40 | 41 | // CanWrite returns the current number of bytes in the RingBuffer that can be writes. 42 | func (this *RingBuffer) CanWrite() int { 43 | return len(this.buffer) - int(this.writeOffset-this.readOffset) 44 | } 45 | 46 | // Resize function grows or diminishes the buffer as soon as it is possible. 47 | // 48 | // Depending on current RingBuffer's state, growth can be done immediatly or when enough Reading & Writing have occured so the RingBuffer is not half cut on the end and the beginning of the buffer. 49 | // 50 | // Depending on current RingBuffer's state, diminish can be done immedialtly or when enough Reading have occured. 51 | func (this *RingBuffer) Resize(newsize int) error { 52 | return errors.New("RingBuffer: Resize FUNC NOT YET IMPLEMENTED !") 53 | } 54 | 55 | // Read reads up to len(p) bytes into p. It returns the number of bytes read (0 <= n <= len(p)) and any error encountered. 56 | // Even if Read returns n < len(p), it may use all of p as scratch space during the call. 57 | // If some data is available but not len(p) bytes, Read conventionally returns what is available instead of waiting for more. 58 | // 59 | // Implementations does not retain p. 60 | func (this *RingBuffer) Read(p []byte) (n int, err error) { 61 | lenp := len(p) 62 | if lenp == 0 { // no data to read 63 | return 64 | } 65 | max := len(this.buffer) 66 | maxread := int(this.writeOffset - this.readOffset) 67 | if maxread == 0 { 68 | // buffer is empty 69 | return 70 | } 71 | // Can't read more than what we have in buffer 72 | if lenp > max { 73 | n = max 74 | } else { 75 | n = lenp 76 | } 77 | if n > maxread { 78 | n = maxread 79 | } 80 | // Translate to RingBuffer index 81 | rp := int(this.readOffset % uint64(max)) 82 | // Copy from the ring buffer 83 | a := max - rp 84 | if a > 0 { 85 | if a > n { 86 | a = n 87 | } 88 | copy(p[:a], this.buffer[rp:rp+a]) // first part of write buffer 89 | } 90 | b := n - a 91 | if b > 0 { 92 | copy(p[a:], this.buffer[:b]) // second part of write buffer 93 | } 94 | this.readOffset += uint64(n) 95 | return n, nil 96 | } 97 | 98 | // Write writes len(p) bytes from p to the underlying data stream. 99 | // It returns the number of bytes written from p (0 <= n <= len(p)) and any error encountered that caused the write to stop early. 100 | // Write returns a non-nil error if it returns n < len(p). 101 | // Write does not modify the slice data. 102 | // 103 | // Implementations does not retain p. 104 | func (this *RingBuffer) Write(p []byte) (n int, err error) { 105 | lenp := len(p) 106 | if lenp == 0 { // no data to write 107 | return 108 | } 109 | max := len(this.buffer) 110 | maxwrite := max - int(this.writeOffset-this.readOffset) 111 | if maxwrite == 0 { 112 | // current buffer is full 113 | return maxwrite, nil 114 | } 115 | // Can't write more than what we have in buffer 116 | if lenp > max { 117 | n = max 118 | } else { 119 | n = lenp 120 | } 121 | if n > maxwrite { 122 | n = maxwrite 123 | } 124 | // Translate to RingBuffer index 125 | wp := int(this.writeOffset % uint64(max)) 126 | // Copy to the ring buffer 127 | a := max - wp 128 | if a > 0 { 129 | if a > n { 130 | a = n 131 | } 132 | copy(this.buffer[wp:wp+a], p[:a]) // first part of write buffer 133 | } 134 | b := n - a 135 | if b > 0 { 136 | copy(this.buffer[:b], p[a:]) // second part of write buffer 137 | } 138 | this.writeOffset += uint64(n) 139 | return n, nil 140 | } 141 | -------------------------------------------------------------------------------- /protocol/ringbuffer_test.go: -------------------------------------------------------------------------------- 1 | package protocol 2 | 3 | import "testing" 4 | import "bytes" 5 | 6 | func TestRingBuffer(t *testing.T) { 7 | var n int 8 | readData := make([]byte, 52) 9 | writeData := []byte("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ") 10 | 11 | // Create a RingBuffer 12 | err, rb := NewRingBuffer(6) 13 | if err != nil { 14 | t.Error(err) 15 | } 16 | 17 | // Check initial RingBuffer values 18 | n = rb.GetBufferSize() 19 | if n != 6 { 20 | t.Errorf("Bad GetBufferSize value : %v", n) 21 | } 22 | 23 | n = rb.CanRead() 24 | if n != 0 { 25 | t.Errorf("Bad CanRead value = %v", n) 26 | } 27 | 28 | n = rb.CanWrite() 29 | if n != 6 { 30 | t.Errorf("Bad CanWrite value = %v", n) 31 | } 32 | 33 | // Write 2 bytes and check RingBuffer values 34 | n, _ = rb.Write(writeData[:2]) 35 | if n != 2 { 36 | t.Errorf("Bad Write value = %v", n) 37 | } 38 | 39 | n = rb.CanRead() 40 | if n != 2 { 41 | t.Errorf("Bad CanRead value = %v", n) 42 | } 43 | 44 | n = rb.CanWrite() 45 | if n != 4 { 46 | t.Errorf("Bad CanWrite value = %v", n) 47 | } 48 | 49 | // Read only 2 bytes out of 4 50 | n, _ = rb.Read(readData[0:4]) 51 | if n != 2 { 52 | t.Errorf("Bad Read value = %v", n) 53 | } 54 | 55 | n = rb.CanRead() 56 | if n != 0 { 57 | t.Errorf("Bad CanRead value = %v", n) 58 | } 59 | 60 | n = rb.CanWrite() 61 | if n != 6 { 62 | t.Errorf("Bad CanWrite value = %v", n) 63 | } 64 | 65 | // Write 4 bytes 66 | n, _ = rb.Write(writeData[2:6]) 67 | if n != 4 { 68 | t.Errorf("Bad Write value = %v", n) 69 | } 70 | 71 | n = rb.CanRead() 72 | if n != 4 { 73 | t.Errorf("Bad CanRead value = %v", n) 74 | } 75 | n = rb.CanWrite() 76 | if n != 2 { 77 | t.Errorf("Bad CanWrite value = %v", n) 78 | } 79 | 80 | // Read 4 writes 81 | n, _ = rb.Read(readData[2:6]) 82 | if n != 4 { 83 | t.Errorf("Bad Read value = %v", n) 84 | } 85 | 86 | n = rb.CanRead() 87 | if n != 0 { 88 | t.Errorf("Bad CanRead value = %v", n) 89 | } 90 | 91 | n = rb.CanWrite() 92 | if n != 6 { 93 | t.Errorf("Bad CanWrite value = %v", n) 94 | } 95 | 96 | // Write 4 bytes 97 | n, _ = rb.Write(writeData[6:10]) 98 | if n != 4 { 99 | t.Errorf("Bad Write value = %v", n) 100 | } 101 | 102 | n = rb.CanRead() 103 | if n != 4 { 104 | t.Errorf("Bad CanRead value = %v", n) 105 | } 106 | n = rb.CanWrite() 107 | if n != 2 { 108 | t.Errorf("Bad CanWrite value = %v", n) 109 | } 110 | 111 | // Read 4 bytes 112 | n, _ = rb.Read(readData[6:10]) 113 | if n != 4 { 114 | t.Errorf("Bad Read value = %v", n) 115 | } 116 | 117 | n = rb.CanRead() 118 | if n != 0 { 119 | t.Errorf("Bad CanRead value = %v", n) 120 | } 121 | 122 | n = rb.CanWrite() 123 | if n != 6 { 124 | t.Errorf("Bad CanWrite value = %v", n) 125 | } 126 | 127 | // Write 4 bytes 128 | n, _ = rb.Write(writeData[10:14]) 129 | if n != 4 { 130 | t.Errorf("Bad Write value = %v", n) 131 | } 132 | 133 | n = rb.CanRead() 134 | if n != 4 { 135 | t.Errorf("Bad CanRead value = %v", n) 136 | } 137 | n = rb.CanWrite() 138 | if n != 2 { 139 | t.Errorf("Bad CanWrite value = %v", n) 140 | } 141 | 142 | // Read 4 bytes 143 | n, _ = rb.Read(readData[10:14]) 144 | if n != 4 { 145 | t.Errorf("Bad Read value = %v", n) 146 | } 147 | 148 | n = rb.CanRead() 149 | if n != 0 { 150 | t.Errorf("Bad CanRead value = %v", n) 151 | } 152 | 153 | n = rb.CanWrite() 154 | if n != 6 { 155 | t.Errorf("Bad CanWrite value = %v", n) 156 | } 157 | 158 | // Write 5 bytes 159 | n, _ = rb.Write(writeData[14:19]) 160 | if n != 5 { 161 | t.Errorf("Bad Write value = %v", n) 162 | } 163 | 164 | n = rb.CanRead() 165 | if n != 5 { 166 | t.Errorf("Bad CanRead value = %v", n) 167 | } 168 | n = rb.CanWrite() 169 | if n != 1 { 170 | t.Errorf("Bad CanWrite value = %v", n) 171 | } 172 | 173 | // Read 4 bytes 174 | n, _ = rb.Read(readData[14:18]) 175 | if n != 4 { 176 | t.Errorf("Bad Read value = %v", n) 177 | } 178 | 179 | n = rb.CanRead() 180 | if n != 1 { 181 | t.Errorf("Bad CanRead value = %v", n) 182 | } 183 | 184 | n = rb.CanWrite() 185 | if n != 5 { 186 | t.Errorf("Bad CanWrite value = %v", n) 187 | } 188 | 189 | // Write 5 bytes over 6 190 | n, _ = rb.Write(writeData[19:25]) 191 | if n != 5 { 192 | t.Errorf("Bad Write value = %v", n) 193 | } 194 | 195 | n = rb.CanRead() 196 | if n != 6 { 197 | t.Errorf("Bad CanRead value = %v", n) 198 | } 199 | n = rb.CanWrite() 200 | if n != 0 { 201 | t.Errorf("Bad CanWrite value = %v", n) 202 | } 203 | 204 | // Read 6 bytes 205 | n, _ = rb.Read(readData[18:24]) 206 | if n != 6 { 207 | t.Errorf("Bad Read value = %v", n) 208 | } 209 | 210 | n = rb.CanRead() 211 | if n != 0 { 212 | t.Errorf("Bad CanRead value = %v", n) 213 | } 214 | 215 | n = rb.CanWrite() 216 | if n != 6 { 217 | t.Errorf("Bad CanWrite value = %v", n) 218 | } 219 | 220 | // Verify 221 | if !bytes.Equal(readData[:24], writeData[:24]) { 222 | t.Errorf("Read data %v different from Write data %v", string(readData[:24]), string(writeData[:24])) 223 | } 224 | } 225 | -------------------------------------------------------------------------------- /crypto/README.md: -------------------------------------------------------------------------------- 1 | [![GoDoc](https://godoc.org/github.com/romain-jacotin/quic/crypto?status.svg)](https://godoc.org/github.com/romain-jacotin/quic/crypto) 2 | 3 | # QUIC Crypto Protocol in Go language 4 | 5 | Work in progress on the crypto protocol and crypto handshake in Golang. 6 | 7 | **For official Google information about QUIC protocol, please consult the following website:** 8 | 9 | * Official QUIC information at chromium.org : 10 | * [https://www.chromium.org/quic](https://www.chromium.org/quic) 11 | * Chromium QUIC source code: 12 | * [https://code.google.com/p/chromium/codesearch#chromium/src/net/quic/](https://code.google.com/p/chromium/codesearch#chromium/src/net/quic/) 13 | * QUIC Forum: 14 | * [https://groups.google.com/a/chromium.org/forum/#!forum/proto-quic](https://groups.google.com/a/chromium.org/forum/#!forum/proto-quic) 15 | 16 | ---------------------- 17 | 18 | ## Table of Contents 19 | 20 | * [Overview](#overview) 21 | * [Parsing crypto message](#parsing) 22 | * [The Crypto Handshake](#handshake) 23 | * [Key Derivation](#keyderivation) 24 | * [ANNEX A: Extracts from QUIC Crypto Protocol](../doc/QUIC_crypto_protocol.md) 25 | * [ANNEX B: Extracts from RFC5869 - HMAC-based Key Derivation Function (HKDF)](../doc/HKDF.md) 26 | 27 | ## Overview 28 | 29 | ## Parsing crypto message 30 | 31 | ## The Crypto handshake 32 | 33 | The crypto protocol on Stream Id=1 must be handle with the following constraints: 34 | 35 | * During the crypto handshake phase: 36 | * __QUIC Client__ 37 | * must only send one message type, and only once 38 | * __CHLO message__ : must fit into a single QUIC packet 39 | * __QUIC Server__ 40 | * must respond only to unique __CHLO__ message with the following crypto message: 41 | * __REJ message__ : this is a stateless answer from the server, so QUIC Client must retry a new QUIC Connection with the new informations provided in the REJ response to move forward. 42 | * Following checks causes a REJ response from the QUIC server: 43 | * Message received from the client is not a Client Hello (__CHLO__) 44 | * Client Hello is too small (__CHLO__) 45 | * Client Hello does not fit into a single QUIC Frame/Packet (__CHLO__) 46 | * QUIC version is missing (__VERS__) 47 | * QUIC version not supported (__VERS__) 48 | * Bad Server Name Indication (__SNI__) 49 | * Server config ID is missing (__SCID__) 50 | * Bad Server config ID (__SCID__) 51 | * Source-address Token is missing (__STK__) 52 | * Bad Source-address Token (__STK__) 53 | * Client nonce is missing (__NONC__) 54 | * Bad client nonce, size not equal to 32 bytes (__NONC__) 55 | * Authenticated encryption algorithms is missing (__AEAD__) 56 | * Authenticated encryption algorithms not supported (__AEAD__) 57 | * Key Exchange algorithms is missing (__KEXS__) 58 | * Key Exchange algorithms not supported (__KEXS__) 59 | * Client’s public value is missing (__PUBS__) 60 | * Bad client’s public value (__PUBS__) 61 | * Bad client encrypted tag-values, if provided (__CETV__) 62 | * __SHLO message__ : 63 | * Following tags must be included: 64 | * __VERS__ 65 | * __PUBS__ 66 | * Server has the oppotunity to send a new __STK__ (optional) 67 | 68 | * After crypto handshahe complete: 69 | * __QUIC Client__ 70 | * CAN'T send any crypto message: any single byte of data on Stream ID=1 cause QUIC Connection termination 71 | * __QUIC Server__ 72 | * can only send one message type 73 | * __SCUP message__ : to notify the Client about the Server config update 74 | 75 | ## Key Derivation 76 | 77 | __Step 1__: Extract a master secret key of 32 bytes by using HMAC function SHA-256 78 | 79 | ``` 80 | // inputs 81 | salt = CNON + SNO 82 | IKM = KEXS(PUBS) 83 | 84 | // output 85 | PRK = HDKF_Extract(salt, IKM) 86 | ``` 87 | 88 | __Step 2__: Expand output key material by using HMAC function SHA-256 89 | 90 | ``` 91 | // inputs 92 | PRK // obtain from Step 1 93 | info = []byte{"QUIC key expansion"} + []byte{0x00} + byte[]{CID} + []byte{CHLO_message} + []byte{SCFG_message} 94 | L = 2*AEAD_key_size + 2* AEAD_nonce_size 95 | 96 | // output: 97 | OKM = HDKF_Expand(PRK, info, L) 98 | 99 | // OKM, the Output Keying Material is a []byte used like this: 100 | clientWriteKey = OKM[ 0 : AEAD_key_size ] 101 | serverWriteKey = OKM[ AEAD_key_size : 2*AEAD_key_size ] 102 | clientWriteIV = OKM[ 2*AEAD_key_size : 2*AEAD_key_size + AEAD_nonce_size ] 103 | serverWriteIV = OKM[ 2*AEAD_key_size + AEAD_nonce_size : 2*AEAD_key_size + 2*AEAD_nonce_size ] 104 | ``` 105 | -------------------------------------------------------------------------------- /protocol/quicprivateheader.go: -------------------------------------------------------------------------------- 1 | package protocol 2 | 3 | import "errors" 4 | 5 | /* 6 | 7 | 0 1 8 | +--------+--------+ 9 | |Private | FEC (8)| 10 | |Flags(8)| (opt) | 11 | +--------+--------+ 12 | 13 | 14 | Private flags: 15 | +---+---+---+---+---+---+---+---+ 16 | | 0 | 0 | 0 | 0 | 0 |FEC|GRP|ENT| 17 | +---+---+---+---+---+---+---+---+ 18 | 19 | */ 20 | 21 | const ( 22 | // Mask and flag for Entropy bit in QuicPrivateHeader 23 | QUICFLAG_ENTROPY = 0x01 24 | // Mask and flag for FEC Group offset presence in QuicPrivateHeader 25 | QUICFLAG_FECGROUP = 0x02 26 | // Mask and flag for FEC Packet in QuicPrivateHeader 27 | QUICFLAG_FECPACKET = 0x04 28 | ) 29 | 30 | // QuicFecGroupNumberOffset 31 | type QuicFecGroupNumberOffset byte 32 | 33 | // QuicPrivateHeader 34 | type QuicPrivateHeader struct { 35 | flagFecPacket bool 36 | flagFecGroup bool 37 | flagEntropy bool 38 | fecGroupNumberOffset QuicFecGroupNumberOffset 39 | } 40 | 41 | // Erase 42 | func (this *QuicPrivateHeader) Erase() { 43 | this.flagFecPacket = false 44 | this.flagFecGroup = false 45 | this.flagEntropy = false 46 | this.fecGroupNumberOffset = 0 47 | } 48 | 49 | // ParseData 50 | func (this *QuicPrivateHeader) ParseData(data []byte) (size int, err error) { 51 | var pv byte 52 | 53 | l := len(data) 54 | if l == 0 { 55 | err = errors.New("QuicPrivateHeader.ParseData : no data (= 0 byte)") 56 | return 57 | } 58 | // Parse Private Flags 59 | pv = data[0] 60 | // Parse unused bits 61 | if (pv & 0xf8) != 0 { 62 | err = errors.New("QuicPrivateHeader.ParseData : unused bits must be set to 0") 63 | return 64 | } 65 | // Parse FEC Group presence flag 66 | if (pv & QUICFLAG_FECGROUP) == QUICFLAG_FECGROUP { 67 | this.flagFecGroup = true 68 | if l < 2 { 69 | err = errors.New("QuicPrivateHeader.ParseData : too little data FEC Group (< 2 bytes)") 70 | return 71 | } 72 | this.fecGroupNumberOffset = QuicFecGroupNumberOffset(data[1]) 73 | size = 2 74 | } else { 75 | size = 1 76 | } 77 | // Parse Entropy bit 78 | if (pv & QUICFLAG_ENTROPY) == QUICFLAG_ENTROPY { 79 | this.flagEntropy = true 80 | } 81 | // Parse FEC Packet flag 82 | if (pv & QUICFLAG_FECPACKET) == QUICFLAG_FECPACKET { 83 | this.flagFecPacket = true 84 | if !this.flagFecGroup { 85 | err = errors.New("QuicPrivateHeader.ParseData : internal error FEC packet must have FEC Group Number offset") // Must be impossible 86 | return 87 | } 88 | } 89 | return 90 | } 91 | 92 | // GetSerializedSize 93 | func (this *QuicPrivateHeader) GetSerializedSize() (size int) { 94 | if this.flagFecGroup { 95 | return 2 96 | } 97 | return 1 98 | } 99 | 100 | // GetSerializedData 101 | func (this *QuicPrivateHeader) GetSerializedData(data []byte) (size int, err error) { 102 | var pv QuicFecGroupNumberOffset 103 | 104 | if this.flagFecPacket && (!this.flagFecGroup) { 105 | err = errors.New("QuicPrivateHeader.GetSerializedData : internal error FEC packet must have FEC Group Number offset") // Must be impossible 106 | return 107 | } 108 | if this.flagFecGroup { 109 | size = 2 110 | } else { 111 | size = 1 112 | } 113 | // Check minimum data size 114 | if len(data) < size { 115 | size = 0 116 | err = errors.New("QuicPrivateHeader.GetSerializedData : data size too small to contain Private Header") 117 | return 118 | } 119 | // Serialized Entropy flag 120 | if this.flagEntropy { 121 | pv |= QUICFLAG_ENTROPY 122 | } 123 | // Serialized FEC Group presence flag 124 | if this.flagFecGroup { 125 | pv |= QUICFLAG_FECGROUP 126 | } 127 | // Serialized FEC Packet flag 128 | if this.flagFecPacket { 129 | pv |= QUICFLAG_FECPACKET 130 | } 131 | data[0] = byte(pv) 132 | // Serialized FEC Group Number Offset 133 | if this.flagFecGroup { 134 | data[1] = byte(this.fecGroupNumberOffset) 135 | } 136 | return 137 | } 138 | 139 | // GetFecPacketFlag 140 | func (this *QuicPrivateHeader) GetFecPacketFlag() bool { 141 | return this.flagFecPacket 142 | } 143 | 144 | // SetFecPacketFlag 145 | func (this *QuicPrivateHeader) SetFecPacketFlag(state bool) { 146 | this.flagFecPacket = state 147 | return 148 | } 149 | 150 | // GetFecGroupFlag 151 | func (this *QuicPrivateHeader) GetFecGroupFlag() bool { 152 | return this.flagFecGroup 153 | } 154 | 155 | // SetFecGroupFlag 156 | func (this *QuicPrivateHeader) SetFecGroupFlag(state bool) { 157 | this.flagFecGroup = state 158 | return 159 | } 160 | 161 | // GetEntropyFlag 162 | func (this *QuicPrivateHeader) GetEntropyFlag() bool { 163 | return this.flagEntropy 164 | } 165 | 166 | // SetEntropyFlag 167 | func (this *QuicPrivateHeader) SetEntropyFlag(state bool) { 168 | this.flagEntropy = state 169 | return 170 | } 171 | 172 | // GetFecGroupNumberOffset 173 | func (this *QuicPrivateHeader) GetFecGroupNumberOffset() (offset QuicFecGroupNumberOffset, err error) { 174 | if this.flagFecGroup { 175 | offset = this.fecGroupNumberOffset 176 | return 177 | } 178 | err = errors.New("QuicPrivateHeader.GetFecGroupNumberOffset : no FEC Group Number offset in this Private Header") 179 | return 180 | } 181 | 182 | // SetFecGroupNumberOffset 183 | func (this *QuicPrivateHeader) SetFecGroupNumberOffset(offset QuicFecGroupNumberOffset) { 184 | this.fecGroupNumberOffset = offset 185 | return 186 | } 187 | -------------------------------------------------------------------------------- /protocol/parser.go: -------------------------------------------------------------------------------- 1 | package protocol 2 | 3 | import "encoding/binary" 4 | 5 | // internal Parser state's type. 6 | type parserState uint32 7 | 8 | // Constants use to describe current Parser's state. 9 | const ( 10 | sREADMESSAGETAG = iota 11 | sREADNUMBERENTRIES 12 | sREADTAGSANDOFFSETS 13 | sREADVALUES 14 | ) 15 | 16 | // Parser is a Message parser that takes slices of bytes on its input channel and sends out Message(s) on its output channel. 17 | // 18 | // It handles the case where a message is on multiple slices of bytes. 19 | // 20 | // When the Parser encounters a non valid Message a nil value is sends on its output channel. 21 | type Parser struct { 22 | // input channel containing bytes to parse 23 | input chan []byte 24 | // output channel for sending parsed Message 25 | output chan *Message 26 | // internal Parser state variables that need to be keep when Parser is Start/Stop/Start/ ... 27 | state parserState 28 | off bool 29 | needMoreData uint32 30 | data []byte 31 | msgTag MessageTag 32 | numEntries uint16 33 | tags []MessageTag 34 | endOffsets []uint32 35 | values [][]byte 36 | } 37 | 38 | // NewParser is a Parser factory. 39 | func NewParser() *Parser { 40 | return &Parser{ 41 | input: make(chan []byte), 42 | output: make(chan *Message), 43 | state: sREADMESSAGETAG, 44 | off: true, 45 | needMoreData: 4} 46 | } 47 | 48 | // GetInput returns the send only []byte input channel. 49 | func (this *Parser) GetInput() chan<- []byte { 50 | return this.input 51 | } 52 | 53 | // GetOutput returns the receveive only Message output channel. 54 | func (this *Parser) GetOutput() <-chan *Message { 55 | return this.output 56 | } 57 | 58 | // Stop method stops the Parser. 59 | // The boolean value return is not an error and just in fact an indication about the state of the Parser before the call. 60 | func (this *Parser) Stop() bool { 61 | if this.off { 62 | return false 63 | } 64 | this.off = true 65 | return true 66 | } 67 | 68 | // Start method starts the Parser if it is in 'Stop' state and return 'true', otherwise do nothing and return 'false'. 69 | // The boolean value return is not an error and just in fact an indication about the state of the Parser before the call. 70 | func (this *Parser) Start() bool { 71 | if this.off { 72 | this.off = false 73 | go this.runParser() 74 | return true 75 | } 76 | return false 77 | } 78 | 79 | // RunParser is the core function of the parsing process. It must only be launch as a Go routine by the Start function. 80 | func (this *Parser) runParser() { 81 | var i, j int 82 | 83 | for { 84 | // Do we need to Stop parsing ? 85 | if this.off { 86 | return 87 | } 88 | 89 | // While not enough data in ringbuffer, append from input channel 90 | for this.needMoreData > uint32(len(this.data)) { 91 | this.data = append(this.data, <-this.input...) 92 | } 93 | 94 | // Read messages fields according to current Parser's state 95 | switch this.state { 96 | case sREADMESSAGETAG: // Read 4 bytes 97 | // Read uint32 message tag 98 | this.msgTag = MessageTag(binary.LittleEndian.Uint32(this.data)) 99 | // Advance reading slice of []byte 100 | this.data = this.data[4:] 101 | // Advance state 102 | this.state = sREADNUMBERENTRIES 103 | // Ask for next data size 104 | this.needMoreData = 4 105 | break 106 | case sREADNUMBERENTRIES: // Read 2+2 bytes 107 | // Read uint16 number of entries and ignore next uint16 of padding 108 | this.numEntries = uint16(binary.LittleEndian.Uint16(this.data)) 109 | if this.numEntries > MaxMessageTagNumEntries { 110 | this.off = true 111 | this.output <- nil 112 | } 113 | // Advance reading slice of []byte 114 | this.data = this.data[4:] 115 | // Advance state 116 | this.state = sREADTAGSANDOFFSETS 117 | // Ask for next data size 118 | this.needMoreData = uint32(8 * this.numEntries) 119 | break 120 | case sREADTAGSANDOFFSETS: // Read numentries*(4+4) 121 | if this.numEntries > 0 { 122 | // Allocate ressources for tag-offset pairs 123 | this.tags = make([]MessageTag, this.numEntries) 124 | this.endOffsets = make([]uint32, this.numEntries) 125 | for i = 0; i < int(this.numEntries); i++ { 126 | // Read uint32 tag 127 | this.tags[i] = MessageTag(binary.LittleEndian.Uint32(this.data)) 128 | this.data = this.data[4:] 129 | // Read uint32 offset 130 | this.endOffsets[i] = uint32(binary.LittleEndian.Uint32(this.data)) 131 | this.data = this.data[4:] 132 | } 133 | // Ask for next data size 134 | this.needMoreData = this.endOffsets[this.numEntries-1] 135 | } 136 | // Advance state 137 | this.state = sREADVALUES 138 | break 139 | case sREADVALUES: 140 | if this.numEntries > 0 { 141 | // Allocate ressources for tag-value pairs 142 | this.values = make([][]byte, this.numEntries) 143 | // Read values 144 | j = 0 145 | for i = 0; i < int(this.numEntries); i++ { 146 | this.values[i] = this.data[j:this.endOffsets[i]] 147 | j = int(this.endOffsets[i]) 148 | } 149 | // Advance reading slice of []byte 150 | this.data = this.data[this.endOffsets[this.numEntries-1]:] 151 | } 152 | // Advance state 153 | this.state = sREADMESSAGETAG 154 | // Ask for next data size 155 | this.needMoreData = 4 156 | // Put Message on the output channel 157 | this.output <- &Message{ 158 | msgTag: this.msgTag, 159 | tags: this.tags, 160 | values: this.values} 161 | break 162 | } 163 | } 164 | } 165 | -------------------------------------------------------------------------------- /doc/TCPRetransmissionTimer.md: -------------------------------------------------------------------------------- 1 | # Computing TCP's Retransmission Timer 2 | 3 | Extracts from __RFC6298__: [https://tools.ietf.org/html/rfc6298](https://tools.ietf.org/html/rfc6298) 4 | 5 | --------------------- 6 | 7 | ## Table of Content 8 | 9 | * [The Basic Algorithm](#basicalgo) 10 | * [Taking RTT Samples](#rttsamples) 11 | * [Clock Granularity](#clock) 12 | * [Managing the RTO Timer](#rtotimer) 13 | 14 | ## The Basic Algorithm 15 | 16 | To compute the current __RTO__, a TCP sender maintains two state variables: 17 | 18 | * __SRTT__ (smoothed round-trip time) 19 | * __RTTVAR__ (round-trip time variation). 20 | 21 | In addition, we assume a clock granularity of G seconds. 22 | 23 | The rules governing the computation of __SRTT__, __RTTVAR__, and __RTO__ are as follows: 24 | 25 | 1. Until a round-trip time (RTT) measurement has been made for a segment sent between the sender and receiver, the sender SHOULD set __RTO = 1 second__, though the "backing off" on repeated retransmission discussed in the ["Managing the RTO Timer"](#rtotimer) section still applies. 26 | 2. When the first RTT measurement __R__ is made (where __K = 4__): 27 | * __SRTT = R__ 28 | * __RTTVAR = R/2__ 29 | * __RTO = SRTT + max( G, K * RTTVAR )__ 30 | 3. When a subsequent RTT measurement __R'__ is made (using __alpha=1/8__ and __beta=1/4__) : 31 | * __RTTVAR = (1 - beta) * RTTVAR + beta * |SRTT - R'|__ 32 | * __SRTT = (1 - alpha) * SRTT + alpha * R'__ 33 | * __RTO = SRTT + max( G, K * RTTVAR )__ 34 | 4. __if RTO is less than 1 second, then the RTO SHOULD be rounded up to 1 second__. 35 | 5. __A maximum value MAY be placed on RTO provided it is at least 60 seconds__. 36 | 37 | ## Taking RTT Samples 38 | 39 | TCP MUST use Karn's algorithm for taking RTT samples. 40 | That is, RTT samples MUST NOT be made using segments that were retransmitted (and thus for which it is ambiguous whether the reply was for the first instance of the packet or a later instance). 41 | The only case when TCP can safely take RTT samples from retransmitted segments is when the TCP timestamp option is employed, since the timestamp option removes the ambiguity regarding which instance of the data segment triggered the acknowledgment. 42 | 43 | Traditionally, TCP implementations have taken one RTT measurement at a time (typically, once per RTT). 44 | However, when using the timestamp option, each ACK can be used as an RTT sample. 45 | RFC 1323 suggests that TCP connections utilizing large congestion windows should take many RTT samples per window of data to avoid aliasing effects in the estimated RTT. 46 | A TCP implementation MUST take at least one RTT measurement per RTT (unless that is not possible per Karn's algorithm). 47 | 48 | For fairly modest congestion window sizes, research suggests that timing each segment does not lead to a better RTT estimator. 49 | Additionally, when multiple samples are taken per RTT, the alpha and beta defined may keep an inadequate RTT history. 50 | 51 | ## Clock Granularity 52 | 53 | There is no requirement for the clock granularity G used for computing RTT measurements and the different state variables. However, if the K * RTTVAR term in the RTO calculation equals zero, the variance term MUST be rounded to G seconds. 54 | 55 | * __RTO = SRTT + max( G, K * RTTVAR )__ 56 | 57 | Experience has shown that finer clock granularities (<= 100 msec) perform somewhat better than coarser granularities. 58 | 59 | ## Managing the RTO Timer 60 | 61 | An implementation MUST manage the retransmission timer(s) in such a way that a segment is never retransmitted too early, i.e., less than one RTO after the previous transmission of that segment. 62 | 63 | The following is the RECOMMENDED algorithm for managing the retransmission timer: 64 | 65 | 1. Every time a packet containing data is sent (including a retransmission), if the timer is not running, start it running so that it will expire after RTO seconds (for the current value of RTO). 66 | 2. When all outstanding data has been acknowledged, turn off the retransmission timer. 67 | 3. When an ACK is received that acknowledges new data, restart the retransmission timer so that it will expire after RTO seconds (for the current value of RTO). 68 | 69 | When the retransmission timer expires, do the following: 70 | 71 | 1. Retransmit the earliest segment that has not been acknowledged by the TCP receiver. 72 | 2. __RTO = RTO * 2__ ("back off the timer"), a maximum value MAY be placed on RTO provided it is at least 60 seconds. 73 | 3. Start the retransmission timer, such that it expires after RTO seconds. 74 | 4. If the timer expires awaiting the ACK of a SYN segment and the TCP implementation is using an RTO less than 3 seconds, the RTO MUST be re-initialized to 3 seconds when data transmission begins (i.e., after the three-way handshake completes). 75 | 76 | Note that after retransmitting, once a new RTT measurement is obtained (which can only happen when new data has been sent and acknowledged), the computations outlined in ["The Basic Algorithm"](#basicalgo) section are performed, including the computation of RTO, which may result in "collapsing" RTO back down after it has been subject to exponential back off (RTO = RTO*2). 77 | 78 | Note that a TCP implementation MAY clear SRTT and RTTVAR after backing off the timer multiple times as it is likely that the current SRTT and RTTVAR are bogus in this situation. Once SRTT and RTTVAR are cleared, they should be initialized with the next RTT sample taken like step 2 of ["The Basic Algorithm"](#basicalgo): 79 | 80 | * __SRTT = R__ 81 | * __RTTVAR = R/2__ 82 | * __RTO = SRTT + max( G, K * RTTVAR )__ 83 | -------------------------------------------------------------------------------- /crypto/aead_aes128gcm12.go: -------------------------------------------------------------------------------- 1 | package crypto 2 | 3 | import "github.com/romain-jacotin/quic/protocol" 4 | import "crypto/aes" 5 | import "crypto/cipher" 6 | import "errors" 7 | import "fmt" 8 | 9 | type AEAD_AES128GCM12 struct { 10 | cipher cipher.Block 11 | h0 uint64 12 | h1 uint64 13 | ghash [16]byte 14 | y [16]byte 15 | nonce [16]byte 16 | } 17 | 18 | // NewAEAD_AES128GCM12 returns a *AEAD_AES128GCM12 that implements crypto.AEAD interface 19 | func NewAEAD_AES128GCM12(key, nonce []byte) (AEAD, error) { 20 | var err error 21 | var i uint 22 | 23 | if len(key) < 16 { 24 | return nil, errors.New("NewAEAD_AES128GCM12 : key must be 16 bytes at minimum") 25 | } 26 | if len(nonce) < 4 { 27 | return nil, errors.New("NewAEAD_AES128GCM12 : nonce must be 4 bytes at minimum") 28 | } 29 | aead := new(AEAD_AES128GCM12) 30 | if aead.cipher, err = aes.NewCipher(key[:16]); err != nil { 31 | return nil, err 32 | } 33 | aead.cipher.Encrypt(aead.y[:], aead.nonce[:]) 34 | for i = 0; i < 8; i++ { 35 | aead.h1 += uint64(aead.y[i]) << (56 - (i << 3)) 36 | aead.h0 += uint64(aead.y[i+8]) << (56 - (i << 3)) 37 | } 38 | for i = 0; i < 4; i++ { 39 | aead.nonce[i] = nonce[i] 40 | } 41 | return aead, nil 42 | } 43 | 44 | // Open 45 | func (this *AEAD_AES128GCM12) Open(seqnum protocol.QuicPacketSequenceNumber, plaintext, aad, ciphertext []byte) (bytescount int, err error) { 46 | var c, i, j, k, n, modn uint32 47 | 48 | l := len(ciphertext) - 12 49 | if l < 0 { 50 | err = errors.New("AEAD_AES128GCM12.Open : Message Authentication Code can't be less than 12 bytes") 51 | return 52 | } 53 | if len(plaintext) < l { 54 | err = errors.New("AEAD_AES128GCM12.Open : plaintext must same have length as ciphertext less 12 bytes at minimum") 55 | return 56 | } 57 | 58 | // Authenticate: check the MAC 59 | 60 | // Compute nonce prefix 61 | for i = 0; i < 8; i++ { 62 | this.nonce[4+i] = byte(seqnum >> (i << 3)) 63 | } 64 | this.nonce[12] = 0 65 | this.nonce[13] = 0 66 | this.nonce[14] = 0 67 | this.nonce[15] = 1 68 | c = 1 69 | this.computeGHash(aad, ciphertext[:l]) 70 | // Compute Y0 71 | // Compute E(K,Y0) 72 | this.cipher.Encrypt(this.y[:], this.nonce[:]) 73 | // Compute and compare GHASH^E(K,Y0) 74 | k = uint32(l) 75 | for i = 0; i < 12; i++ { 76 | this.ghash[i] ^= this.y[i] 77 | if ciphertext[k] != this.ghash[i] { 78 | err = fmt.Errorf("AEAD_AEAD_AES128GCM12.Open : invalid Message Authentication Code verification %x versus %x", ciphertext[l:], this.ghash[:12]) 79 | return 80 | } 81 | k++ 82 | } 83 | 84 | // Then decrypt 85 | n = uint32(l) 86 | modn = n & 0xf 87 | n >>= 4 88 | for i = 0; i < n; i++ { 89 | // Compute Yi = incr(Yi−1) 90 | c++ 91 | this.nonce[15] = byte(c & 0xff) 92 | this.nonce[14] = byte((c >> 8) & 0xff) 93 | this.nonce[13] = byte((c >> 16) & 0xff) 94 | this.nonce[12] = byte((c >> 24) & 0xff) 95 | // Compute E(K,Yi) 96 | this.cipher.Encrypt(this.y[:], this.nonce[:]) 97 | // Compute Ci = Pi xor E(K,Yi) 98 | k = i << 4 99 | for j = 0; j < 16; j++ { 100 | plaintext[k] = ciphertext[k] ^ this.y[j] 101 | k++ 102 | } 103 | } 104 | if modn > 0 { 105 | // Compute Yn = incr(Yn−1) 106 | c++ 107 | this.nonce[15] = byte(c & 0xff) 108 | this.nonce[14] = byte((c >> 8) & 0xff) 109 | this.nonce[13] = byte((c >> 16) & 0xff) 110 | this.nonce[12] = byte((c >> 24) & 0xff) 111 | // Compute E(K,Yn) 112 | this.cipher.Encrypt(this.y[:], this.nonce[:]) 113 | // Compute Cn = Pn xor MSBv( E(K,Yn) ) 114 | k = n << 4 115 | for j = 0; j < modn; j++ { 116 | plaintext[k] = ciphertext[k] ^ this.y[j] 117 | k++ 118 | } 119 | } 120 | bytescount = l 121 | return 122 | } 123 | 124 | // Seal 125 | func (this *AEAD_AES128GCM12) Seal(seqnum protocol.QuicPacketSequenceNumber, ciphertext, aad, plaintext []byte) (bytescount int, err error) { 126 | var c, i, j, n, modn uint32 127 | 128 | l := len(plaintext) 129 | if len(ciphertext) < (l + 12) { 130 | err = errors.New("AEAD_AES128GCM12.Seal : ciphertext can't be less than plaintext + 12 bytes") 131 | return 132 | } 133 | 134 | // Encrypt 135 | 136 | for i = 0; i < 8; i++ { 137 | this.nonce[4+i] = byte(seqnum >> (i << 3)) 138 | } 139 | c = 1 140 | // Encryption of the plain text 141 | n = uint32(len(plaintext)) 142 | modn = n & 0xf 143 | n >>= 4 144 | for i = 0; i < n; i++ { 145 | // Compute Yi = incr(Yi−1) 146 | c++ 147 | this.nonce[15] = byte(c & 0xff) 148 | this.nonce[14] = byte((c >> 8) & 0xff) 149 | this.nonce[13] = byte((c >> 16) & 0xff) 150 | this.nonce[12] = byte((c >> 24) & 0xff) 151 | // Compute E(K,Yi) 152 | this.cipher.Encrypt(this.y[:], this.nonce[:]) 153 | // Compute Ci = Pi xor E(K,Yi) 154 | for j = 0; j < 16; j++ { 155 | ciphertext[(i<<4)+j] = plaintext[(i<<4)+j] ^ this.y[j] 156 | } 157 | } 158 | if modn > 0 { 159 | // Compute Yn = incr(Yn−1) 160 | c++ 161 | this.nonce[15] = byte(c & 0xff) 162 | this.nonce[14] = byte((c >> 8) & 0xff) 163 | this.nonce[13] = byte((c >> 16) & 0xff) 164 | this.nonce[12] = byte((c >> 24) & 0xff) 165 | // Compute E(K,Yn) 166 | this.cipher.Encrypt(this.y[:], this.nonce[:]) 167 | // Compute Cn = Pn xor MSBv( E(K,Yn) ) 168 | for j = 0; j < modn; j++ { 169 | ciphertext[(n<<4)+j] = plaintext[(n<<4)+j] ^ this.y[j] 170 | } 171 | } 172 | 173 | // Then MAC 174 | 175 | this.computeGHash(aad, ciphertext[:l]) 176 | // Compute Y0 177 | this.nonce[12] = 0 178 | this.nonce[13] = 0 179 | this.nonce[14] = 0 180 | this.nonce[15] = 1 181 | // Compute E(K,Y0) 182 | this.cipher.Encrypt(this.y[:], this.nonce[:]) 183 | for i := 0; i < 12; i++ { 184 | ciphertext[l+i] = this.ghash[i] ^ this.y[i] 185 | } 186 | bytescount = l + 12 187 | return 188 | } 189 | 190 | // GetMacSize 191 | func (this *AEAD_AES128GCM12) GetMacSize() int { 192 | return 12 193 | } 194 | -------------------------------------------------------------------------------- /crypto/aead_aes128gcm12_test.go: -------------------------------------------------------------------------------- 1 | package crypto 2 | 3 | import "testing" 4 | import "bytes" 5 | import "strings" 6 | import "encoding/binary" 7 | import "github.com/romain-jacotin/quic/protocol" 8 | 9 | type testVector struct { 10 | key string 11 | nonce string 12 | aad string 13 | plaintext string 14 | ciphertext string 15 | tag string 16 | } 17 | 18 | var testsAES128GCM12 = []testVector{ 19 | { 20 | // Test Case 1 from http://csrc.nist.gov/groups/ST/toolkit/BCM/documents/proposedmodes/gcm/gcm-revised-spec.pdf 21 | "00000000000000000000000000000000", // key 22 | "000000000000000000000000", // nonce 23 | "", // aad 24 | "", // plain text 25 | "", // waiting cipher 26 | "58e2fccefa7e3061367f1d57a4e7455a"}, // waiting tag 27 | { 28 | // Test Case 2 from http://csrc.nist.gov/groups/ST/toolkit/BCM/documents/proposedmodes/gcm/gcm-revised-spec.pdf 29 | "00000000000000000000000000000000", // key 30 | "000000000000000000000000", // nonce 31 | "", // aad 32 | "00000000000000000000000000000000", // plain text 33 | "0388dace60b6a392f328c2b971b2fe78", // waiting cipher 34 | "ab6e47d42cec13bdf53a67b21257bddf"}, // waiting tag 35 | { 36 | // Test Case 3 from http://csrc.nist.gov/groups/ST/toolkit/BCM/documents/proposedmodes/gcm/gcm-revised-spec.pdf 37 | "feffe9928665731c6d6a8f9467308308", // key 38 | "cafebabefacedbaddecaf888", // nonce 39 | "", // aad 40 | "d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b391aafd255", // plain text 41 | "42831ec2217774244b7221b784d0d49ce3aa212f2c02a4e035c17e2329aca12e21d514b25466931c7d8f6a5aac84aa051ba30b396a0aac973d58e091473f5985", // waiting cipher 42 | "4d5c2af327cd64a62cf35abd2ba6fab4"}, // waiting tag 43 | { 44 | // Test Case 4 from http://csrc.nist.gov/groups/ST/toolkit/BCM/documents/proposedmodes/gcm/gcm-revised-spec.pdf 45 | "feffe9928665731c6d6a8f9467308308", // key 46 | "cafebabefacedbaddecaf888", // nonce 47 | "feedfacedeadbeeffeedfacedeadbeefabaddad2", // aad 48 | "d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b39", // plain text 49 | "42831ec2217774244b7221b784d0d49ce3aa212f2c02a4e035c17e2329aca12e21d514b25466931c7d8f6a5aac84aa051ba30b396a0aac973d58e091", // waiting cipher 50 | "5bc94fbc3221a5db94fae95ae7121a47"}, // waiting tag 51 | } 52 | 53 | func toByte(s string) []byte { 54 | bs := []byte(strings.ToLower(s)) 55 | b := make([]byte, len(bs)/2) 56 | 57 | if len(bs) == 0 { 58 | return []byte{} 59 | } 60 | for i := 0; i < len(s)/2; i++ { 61 | switch bs[i*2] { 62 | case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': 63 | b[i] = (bs[i*2] - '0') << 4 64 | case 'a', 'b', 'c', 'd', 'e', 'f': 65 | b[i] = (bs[i*2] - 'a' + 10) << 4 66 | } 67 | switch bs[i*2+1] { 68 | case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': 69 | b[i] += (bs[i*2+1] - '0') 70 | case 'a', 'b', 'c', 'd', 'e', 'f': 71 | b[i] += (bs[i*2+1] - 'a' + 10) 72 | } 73 | } 74 | return b 75 | } 76 | 77 | func Test_AEAD_AES128GCM12_Open(t *testing.T) { 78 | var key, nonce, aad, plaintext, ciphertext, tag []byte 79 | 80 | for _, i := range testsAES128GCM12 { 81 | 82 | key = toByte(i.key) 83 | nonce = toByte(i.nonce) 84 | aad = toByte(i.aad) 85 | plaintext = toByte(i.plaintext) 86 | ciphertext = toByte(i.ciphertext) 87 | tag = toByte(i.tag) 88 | 89 | // fmt.Printf("\n~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~\n\n") 90 | open(key, nonce, aad, plaintext, ciphertext, tag, t) 91 | } 92 | 93 | } 94 | 95 | func Test_AEAD_AES128GCM12_Seal(t *testing.T) { 96 | var key, nonce, aad, plaintext, ciphertext, tag []byte 97 | 98 | for _, i := range testsAES128GCM12 { 99 | 100 | key = toByte(i.key) 101 | nonce = toByte(i.nonce) 102 | aad = toByte(i.aad) 103 | plaintext = toByte(i.plaintext) 104 | ciphertext = toByte(i.ciphertext) 105 | tag = toByte(i.tag) 106 | 107 | //fmt.Printf("\n~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~\n\n") 108 | seal(key, nonce, aad, plaintext, ciphertext, tag, t) 109 | } 110 | 111 | } 112 | 113 | func seal(key, nonce, aad, plaintext, ciphertext, tag []byte, test *testing.T) { 114 | var bc int 115 | 116 | buffer := make([]byte, 1500) 117 | aead, err := NewAEAD_AES128GCM12(key, nonce) 118 | if bc, err = aead.Seal(protocol.QuicPacketSequenceNumber(binary.LittleEndian.Uint64(nonce[4:])), buffer, aad, plaintext); err != nil { 119 | test.Errorf("AEAD_AES128GCM12.Seal : Error return = %v", err) 120 | } 121 | if !bytes.Equal(ciphertext, buffer[:bc-12]) || !bytes.Equal(tag[:12], buffer[bc-12:bc]) { 122 | test.Errorf("----------------\nENCRYPTION TEST:\n----------------\nKey : [%d] %x\nNonce : [%d] %x\nAssociated data : [%d] %x\nPlain text : [%d] %x\n\nCipher text : [%d] %x\nTag : [%d] %x\n\n TEST STATUS = [ FAIL ]\n\n", 123 | len(key), key, len(nonce), nonce, len(aad), aad, len(plaintext), plaintext, bc-12, buffer[:bc-12], 12, buffer[bc-12:bc]) 124 | } 125 | } 126 | 127 | func open(key, nonce, aad, plaintext, ciphertext, tag []byte, test *testing.T) { 128 | var bc int 129 | 130 | buffer := make([]byte, 1500) 131 | ct := append(ciphertext, tag[:12]...) 132 | aead, err := NewAEAD_AES128GCM12(key, nonce) 133 | if bc, err = aead.Open(protocol.QuicPacketSequenceNumber(binary.LittleEndian.Uint64(nonce[4:])), buffer, aad, ct); err != nil { 134 | test.Errorf("AEAD_AES128GCM12.Open : Error return = %v", err) 135 | } 136 | if !bytes.Equal(plaintext, buffer[:bc]) { 137 | test.Errorf("----------------\nDECRYPTION TEST:\n----------------\nKey : [%d] %x\nNonce : [%d] %x\nAssociated data : [%d] %x\nCipher text : [%d] %x\nTag : [%d] %x\n\nPlain text : [%d] %x\n\n TEST STATUS = [ FAIL ]\n\n", 138 | len(key), key, len(nonce), nonce, len(aad), aad, len(ciphertext), ciphertext, 12, buffer[bc-12:], bc, buffer[:bc-12]) 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /quic.go: -------------------------------------------------------------------------------- 1 | // Package quic provides a portable interface for network I/O with QUIC, a multiplexed stream transport over UDP. 2 | // 3 | // See https://www.chromium.org/quic 4 | package quic 5 | 6 | import "net" 7 | import "time" 8 | 9 | type QUICListener struct { 10 | } 11 | 12 | type QUICSession struct { 13 | } 14 | 15 | type StreamConn struct { 16 | } 17 | 18 | // ListenUDP listens for incoming UDP packets addressed to the local address laddr. 19 | // Net must be "udp", "udp4", or "udp6". 20 | // If laddr has a port of 0, ListenUDP will choose an available port. 21 | // The LocalAddr method of the returned QUICSession can be used to discover the port. 22 | // The returned connection's ReadFrom and WriteTo methods can be used to receive and send UDP packets with per-packet addressing. 23 | func ListenQUIC(net string, laddr *net.UDPAddr) (*QUICListener, error) { 24 | return nil, nil 25 | } 26 | 27 | // AcceptQUIC accepts the next incoming new QUIC call and returns the new session. 28 | func (l *QUICListener) AcceptQUIC() (*QUICSession, error) { 29 | return nil, nil 30 | } 31 | 32 | // Addr returns the listener's network address, a *UDPAddr. 33 | func (l *QUICListener) Addr() (a net.UDPAddr) { 34 | return 35 | } 36 | 37 | // Close stops listening on the QUIC address. 38 | // Already Accepted sessions are not closed. 39 | func (l *QUICListener) Close() error { 40 | return nil 41 | } 42 | 43 | // SetDeadline sets the deadline associated with the listener. 44 | // A zero time value disables the deadline. 45 | func (l *QUICListener) SetDeadline(t time.Time) error { 46 | return nil 47 | } 48 | 49 | // DialQUIC connects to the remote address raddr on the network net, which must be "udp", "udp4", or "udp6". 50 | // If laddr is not nil, it is used as the local address for the connection. 51 | func DialQUIC(net string, laddr, raddr *net.UDPAddr) (*QUICSession, error) { 52 | return nil, nil 53 | } 54 | 55 | // Close closes the session. 56 | func (s *QUICSession) Close() error { 57 | return nil 58 | } 59 | 60 | // PublicReset closes immediatly the session. 61 | func (s *QUICSession) PublicReset() error { 62 | return nil 63 | } 64 | 65 | // LocalAddr returns the local network address. 66 | func (s *QUICSession) LocalAddr() (l net.UDPAddr) { 67 | return 68 | } 69 | 70 | // RemoteAddr returns the remote network address. 71 | func (s *QUICSession) RemoteAddr() (r net.UDPAddr) { 72 | return 73 | } 74 | 75 | // SetKeepAlive sets whether the QUIC session should send PING frames on the connection. 76 | func (s *QUICSession) SetKeepAlive(keepalive bool) error { 77 | return nil 78 | } 79 | 80 | // SetKeepAlivePeriod sets period between QUIC PING frames. 81 | func (s *QUICSession) SetKeepAlivePeriod(d time.Duration) error { 82 | return nil 83 | } 84 | 85 | // PING is a blocking function that send a PING frame and waits for the associated ACK 86 | func (s *QUICSession) Ping(keepalive bool) error { 87 | return nil 88 | } 89 | 90 | // NewStrem creates and add a new Stream connection on the QUIC session. 91 | func (s *QUICSession) NewStream() (*StreamConn, error) { 92 | return nil, nil 93 | } 94 | 95 | // AcceptStream accepts the next incoming stream and returns the new connection. 96 | func (s *QUICSession) AcceptStream() (*StreamConn, error) { 97 | return nil, nil 98 | } 99 | 100 | // Close closes the connection. 101 | func (c *StreamConn) Close() error { 102 | return nil 103 | } 104 | 105 | // CloseRead shuts down the reading side of the Stream connection. 106 | // Most callers should just use Close. 107 | func (c *StreamConn) CloseRead() error { 108 | return nil 109 | } 110 | 111 | // CloseWrite shuts down the writing side of the Stream connection. 112 | // Most callers should just use Close. 113 | func (c *StreamConn) CloseWrite() error { 114 | return nil 115 | } 116 | 117 | // Read implements the net.Conn Read method. 118 | // Read reads data from the Stream connection. 119 | // Read can be made to time out and return a Error with Timeout() == true 120 | // after a fixed time limit; see SetDeadline and SetReadDeadline. 121 | func (c *StreamConn) Read(b []byte) (int, error) { 122 | return 0, nil 123 | } 124 | 125 | // Write implements the net.Conn Write method. 126 | // Write writes data to the Stream connection. 127 | // Write can be made to time out and return a Error with Timeout() == true 128 | // after a fixed time limit; see SetDeadline and SetWriteDeadline. 129 | func (c *StreamConn) Write(b []byte) (int, error) { 130 | return 0, nil 131 | } 132 | 133 | // Write writes data to the Stream connection with Forward Error Correction (FEC). 134 | // Write can be made to time out and return a Error with Timeout() == true 135 | // after a fixed time limit; see SetDeadline and SetWriteDeadline. 136 | func (c *StreamConn) WriteFEC(b []byte) (int, error) { 137 | return 0, nil 138 | } 139 | 140 | // Write writes important data to the Stream connection by sending duplicate QUIC packet with pacing. 141 | // Write can be made to time out and return a Error with Timeout() == true 142 | // after a fixed time limit; see SetDeadline and SetWriteDeadline. 143 | func (c *StreamConn) WriteDuplicate(b []byte) (int, error) { 144 | return 0, nil 145 | } 146 | 147 | // SetDeadline implements the net.Conn SetDeadline method. 148 | func (c *StreamConn) SetDeadline(t time.Time) error { 149 | return nil 150 | } 151 | 152 | // SetLinger sets the behavior of Close on a connection which still has data waiting to be sent or to be acknowledged. 153 | // 154 | // If sec < 0 (the default), the operating system finishes sending the data in the background. 155 | // 156 | // If sec == 0, the operating system discards any unsent or unacknowledged data. 157 | // 158 | // If sec > 0, the data is sent in the background as with sec < 0. 159 | // On some operating systems after sec seconds have elapsed any remaining unsent data may be discarded. 160 | func (c *StreamConn) SetLinger(sec int) error { 161 | return nil 162 | } 163 | 164 | // SetNoDelay controls whether the operating system should delay packet transmission in hopes of sending fewer packets (like TCP Nagle's algorithm). 165 | // The default is true (no delay), meaning that data is sent as soon as possible after a Write. 166 | func (c *StreamConn) SetNoDelay(noDelay bool) error { 167 | return nil 168 | } 169 | 170 | // SetReadDeadline implements the net.Conn SetReadDeadline method. 171 | func (c *StreamConn) SetReadDeadline(t time.Time) error { 172 | return nil 173 | } 174 | 175 | // SetWriteDeadline implements the net.Conn SetWriteDeadline method. 176 | func (c *StreamConn) SetWriteDeadline(t time.Time) error { 177 | return nil 178 | } 179 | -------------------------------------------------------------------------------- /doc/QUIC_Event_Processing.md: -------------------------------------------------------------------------------- 1 | # QUIC Event Processing 2 | 3 | --------------------- 4 | 5 | ## Table of Contents 6 | 7 | * [Overview](#overview) 8 | * [Packet Events](#packetevents) 9 | * [Packet Received](#) 10 | * [DATA Packet](#) 11 | * [FEC Protected DATA Packet](#) 12 | * [FEC Packet](#) 13 | * [Frame Events](#frameevents) 14 | * [Connection Management Event](#) 15 | * [CONNECTION_CLOSE_FRAME](#) 16 | * [GO_AWAY_FRAME Event](#) 17 | * [PING_FRAME Event](#) 18 | * [Stream Management Event](#) 19 | * [New Stream Event (=first STREAM_FRAME)](#) 20 | * [Half-Close Stream Event (=last STREAM_FRAME with FIN bit)](#) 21 | * [RESET_STREAM_FRAME Event](#) 22 | * [Data Event](#) 23 | * [STREAM_FRAME Event](#) 24 | * [PADDING_FRAME Event](#) 25 | * [Data Management Event](#) 26 | * [ACK_FRAME Event](#) 27 | * [STOP_WAITING_FRAME Event](#) 28 | * [WINDOW_UPDATE Event](#) 29 | * [BLOCKED_FRAME Event](#) 30 | * [Timeout Events](#timeoutevents) 31 | * [Ack Delay](#ackdelay) 32 | * [Retransmission Timeout (RTO) Event](#retransmissiontimeout) 33 | * [Crypto Hanshake Timeout Event ](#cryptohandshaketimeout) 34 | * [Ping Timeout](#pingtimeout) 35 | * [Send Timeout & Resume Write Timeout (pacing & resume write)](#sendtimer) 36 | * [FEC Timer (?)](#fectimer) 37 | 38 | ## Overview 39 | 40 | ## Packet Events 41 | 42 | ## Frame Events 43 | 44 | ## Timeout Events 45 | 46 | ### Ack Delay Event 47 | 48 | Ack delay before sending ACK_FRAME depends on the retransmission mode: 49 | 50 | * __MaximumDelayedAckTime = 25 ms__ 51 | * __MinimumRetransmissionTime = 200 ms__ 52 | * _HANDSHAKE MODE_ : 53 | * __Ack delay = 0 ms__ (no delay before sending Ack !) 54 | * Otherwise : 55 | * __Ack delay = Min( MaximumDelayedAckTime, MinimumRetransmissionTime/2 ) = 25 ms__ 56 | 57 | ### Retransmission Timeout (RTO) Event 58 | 59 | Always reset the retransmission alarm when an ack comes in, since we now have a better estimate of the current RTT than when it was set. Calcul of the Retransmission timeout (__RTO__) depends on the retransmission mode: 60 | 61 | * _HANDSHAKE MODE_ : 62 | * The Crypto Retransmission Delay (ms) is equivalent to the Tail Loss Probe Delay, but slightly more aggressive because crypto handshake messages don't incur a delayed ack time. 63 | * __SRTT__ = smoothed RTT in ms 64 | * __MinimumHandshakeTimeout = 10 ms__ 65 | * __ConsecutiveCryptoRetransmissionCount__ = number of consecutive crypto packet needed to retransmit 66 | * __CryptoRetransmissionDelay = max( MinimumHandshakeTimeout , 1.5*SRTT ) * (2^ConsecutiveCryptoRetransmissionCount)__ 67 | * __RTO = CryptoRetransmissionDelay__ 68 | * ( RTO += CurrentTime in discrete time ) 69 | * _LOSS MODE_ 70 | * _TCP Loss algorithm_ 71 | * Set the timeout for the earliest retransmittable packet where early retransmit applies 72 | * __NumberOfNacksBeforeRetransmission = 3__ 73 | * __MinimumLossDelay = 5 ms__ 74 | * __SRTT__ = smoothed RTT in ms 75 | * __EarlyRetransmitDelay = Max( MinimumLossDelay, 1.25*SRTT )__ 76 | * __RTO = EarlyRetransmitDelay__ 77 | * ( RTO += LastUnackedPacketSentTime in discrete time ) 78 | * _Time Loss algorithm_ 79 | * Packet is consider lost with a LossDelay timeout after their sending 80 | * __MinimumLossDelay = 5 ms__ 81 | * __SRTT__ = smoothed RTT in ms 82 | * __LatestRTT__ = the latest estimated RTT value in ms (no smoothed) 83 | * __LossDelay = 1.25 * Max( MinimumLossDelay, SRTT, LatestRTT )__ 84 | * __RTO = LossDelay__ 85 | * ( RTO += LastUnackedPacketSentTime in discrete time ) 86 | * _TAIL LOSS PROBE_ 87 | * __MinimumTailLossProbeTimeout = 10 ms__ 88 | * __MinimumRetransmissionTime= 200 ms__ 89 | * if more than one packet in flight: 90 | * __TailLossProbeDelay = Max( MinimumTailLossProbeTimeout, 2*SRTT )__ 91 | * otherwise 92 | * __TailLossProbeDelay = Max( 2 * SRTT, 1.5 * SRTT + MinimumRetransmissionTime/2 )__ 93 | * __RTO = TailLossProbeDelay__ 94 | * ( RTO += LastUnackedPacketSentTime in discrete time ) 95 | * _RTO_ (the first outstanding packet) 96 | * Must wait at minimum for Tail Loss Probe packets to be acked 97 | * __DefaultRetransmissionTime = 500 ms__ 98 | * __MinimumRetransmissionTime = 200 ms__ 99 | * __MaximumRetransmissionTime = 60.000 ms__ 100 | * __MaximumRetransmissions = 10__ 101 | * __ConsecutiveRTOCount__ = consecutive number RTO events before receiving any ACK_FRAME 102 | * __RetransmissionDelay = SRTT + 4*MeanDeviationRTT__ 103 | * __RetransmissionDelay * = (2 ^ Min( ConsecutiveRTOCount , MaximumRetransmissions ))__ 104 | * __RetransmissionDelay = Min( RetransmissionDelay, MaximumRetransmissionTime )__ 105 | * __RTO = Max( RetransmissionDelay, TailLossProbeDelay, MinimumRetransmissionTime )__ 106 | * ( RTO += LastUnackedPacketSentTime in discrete time ) 107 | 108 | ### Crypto Handshake Timeout Event 109 | 110 | * __Max time before crypto handshake = 10 seconds__ 111 | * __Max idle time before crypto handshake = 5 seconds__ (no network activity) 112 | 113 | ### Ping Timeout Event 114 | 115 | * If there is open streams: 116 | * a QUIC PING_FRAME must be send if no QUIC packet is send or received for a "ping timeout" duration 117 | * __Ping timeout = 15 seconds__ 118 | 119 | ### Send Timeout & Resume Write Timeout Event 120 | 121 | * Variable delay to wait before sending packets ... 122 | 123 | ### FEC Timer Event 124 | 125 | We want to put some space between a protected packet and the FEC packet to avoid losing them both within the same loss episode. On the other hand, we expect to be able to recover from any loss in about an RTT. We resolve this tradeoff by sending an FEC packet at most half an RTT, or equivalently, half the max number of in-flight packets, the first protected packet. Since we don't want to delay a FEC packet past half a RTT, we set the max FEC group size to be half the current congestion window. 126 | 127 | * When a FEC Timer event is received then: 128 | * Flush QUIC packets in queue, 129 | * Send FEC protected data Frames 130 | * Send the (last) FEC packet that closes the FEC group 131 | * __FEC Timer = RTT/2__ 132 | 133 | -------------------------------------------------------------------------------- /protocol/quicpacket.go: -------------------------------------------------------------------------------- 1 | package protocol 2 | 3 | import "errors" 4 | import "encoding/binary" 5 | 6 | const cFRAMEBUFFERSIZE = 4 7 | 8 | const ( 9 | QUICPACKETTYPE_UNKNOW = 0 10 | QUICPACKETTYPE_PUBLICRESET = 1 11 | QUICPACKETTYPE_VERSION = 2 12 | QUICPACKETTYPE_FEC = 3 13 | QUICPACKETTYPE_FRAME = 4 14 | QUICPACKETTYPE_PROTECTEDFRAME = 5 15 | ) 16 | 17 | type QuicPacketType int 18 | 19 | // QuicPacket 20 | type QuicPacket struct { 21 | packetType QuicPacketType 22 | publicHeader QuicPublicHeader 23 | privateHeader QuicPrivateHeader 24 | // If FEC Packet only 25 | fecPacket QuicFECPacket 26 | // If PublicResetPacket only 27 | publicReset QuicPublicResetPacket 28 | // If Frames Packet only 29 | framesSet []QuicFrame // framesSet uses frameBuffer array for the first cFRAMEBUFFERSIZE frames and after need making bigger slices 30 | // internal buffers 31 | buffer [1472]byte // internal buffer to store serialized QUIC Packet at reception or before encryption and transmit 32 | frameBuffer [cFRAMEBUFFERSIZE]QuicFrame // array used by framesSet for the first cFRAMEBUFFERSIZE frames only 33 | } 34 | 35 | // Erase 36 | func (this *QuicPacket) Erase() { 37 | this.publicHeader.Erase() 38 | this.privateHeader.Erase() 39 | this.fecPacket.Erase() 40 | this.publicReset.Erase() 41 | this.packetType = QUICPACKETTYPE_UNKNOW 42 | this.framesSet = nil 43 | for i, _ := range this.buffer { 44 | this.buffer[i] = 0 45 | } 46 | } 47 | 48 | // ParseData 49 | func (this *QuicPacket) ParseData(data []byte) (size int, err error) { 50 | var s int 51 | var fecgroupnum QuicFecGroupNumberOffset 52 | 53 | l := len(data) 54 | // Parse QuicPublicHeader 55 | if s, err = this.publicHeader.ParseData(data); err != nil { 56 | // Error while parsing QuicPublicHeader 57 | return 58 | } 59 | size += s 60 | if this.publicHeader.GetPublicResetFlag() { 61 | // This is a Public Reset packet type 62 | this.packetType = QUICPACKETTYPE_PUBLICRESET 63 | // Parse PublicResetPacket 64 | if s, err = this.publicReset.ParseData(data[size:]); err != nil { 65 | // Error while parsing QuiPublicResetPacket 66 | return 67 | } 68 | size += s 69 | } else { 70 | // Parse QuicPrivateHeader 71 | if s, err = this.privateHeader.ParseData(data[size:]); err != nil { 72 | // Error while parsing QuicPrivateHeader 73 | return 74 | } 75 | size += s 76 | if this.privateHeader.GetFecPacketFlag() { 77 | // This is a FEC packet type 78 | this.packetType = QUICPACKETTYPE_FEC 79 | // Setup the FEC Packet based on Sequence Number and FEC Group Offset 80 | if fecgroupnum, err = this.privateHeader.GetFecGroupNumberOffset(); err != nil { 81 | return 82 | } 83 | this.fecPacket.Setup(this.publicHeader.GetSequenceNumber(), fecgroupnum) 84 | // Parse Fec redundancy payload 85 | if s, err = this.fecPacket.ParseData(data[size:]); err != nil { 86 | // Error while parsing QuicFECPacket 87 | return 88 | } 89 | size += s 90 | } else { 91 | if this.publicHeader.GetVersionFlag() { 92 | // This is a Version Negotiation packet type 93 | this.packetType = QUICPACKETTYPE_VERSION 94 | } else if this.privateHeader.GetFecGroupFlag() { 95 | // This is a Protected Frame packet type 96 | this.packetType = QUICPACKETTYPE_PROTECTEDFRAME 97 | } else { 98 | // This is a Frame packet type 99 | this.packetType = QUICPACKETTYPE_FRAME 100 | } 101 | // Parse Frames vector 102 | i := 0 103 | for left := l - size; left > 0; i++ { 104 | if i == 0 { // initialize the frames set to use frameBuffer array 105 | this.framesSet = this.frameBuffer[:1] 106 | } else if (i % cFRAMEBUFFERSIZE) > 0 { // grow the frame set by using existing slice capacity (+1) 107 | this.framesSet = this.framesSet[:i] 108 | } else { // grow the frame set with make (+ccFRAMEBUFFERSIZE) and copy 109 | fs := make([]QuicFrame, i, ((i/cFRAMEBUFFERSIZE)+1)*cFRAMEBUFFERSIZE) 110 | copy(fs, this.framesSet) 111 | } 112 | // Parse next QuicFrame 113 | if s, err = this.framesSet[i].ParseData(data[size:]); err != nil { 114 | return 115 | } 116 | size += s 117 | left -= s 118 | } 119 | } 120 | } 121 | if size != l { 122 | err = errors.New("QuicPacket.ParseData : internal error parsed bytes count different from data size") // Must be impossible 123 | } 124 | return 125 | } 126 | 127 | // GetSerializedSize 128 | func (this *QuicPacket) GetSerializedSize() (size int) { 129 | switch this.packetType { 130 | case QUICPACKETTYPE_PUBLICRESET: 131 | size = 9 + this.publicReset.GetSerializedSize() 132 | return 133 | case QUICPACKETTYPE_VERSION, QUICPACKETTYPE_FRAME, QUICPACKETTYPE_PROTECTEDFRAME: 134 | size = this.publicHeader.GetSerializedSize() + this.privateHeader.GetSerializedSize() // + this.framesSet.GetSerializedSize() 135 | return 136 | case QUICPACKETTYPE_FEC: 137 | size = this.publicHeader.GetSerializedSize() + this.publicReset.GetSerializedSize() 138 | return 139 | } 140 | return 141 | } 142 | 143 | // GetSerializedData 144 | func (this *QuicPacket) GetSerializedData() (data []byte, err error) { 145 | var s, size int 146 | 147 | switch this.packetType { 148 | case QUICPACKETTYPE_PUBLICRESET: 149 | // Serialize public flags 150 | this.buffer[0] = QUICFLAG_PUBLICRESET | QUICFLAG_CONNID_64bit 151 | // Serialize Connection ID (64-bit) 152 | binary.LittleEndian.PutUint64(this.buffer[1:], uint64(this.publicHeader.connId)) 153 | size, err = this.publicReset.GetSerializedData(this.buffer[9:]) 154 | data = this.buffer[:size+9] 155 | return 156 | case QUICPACKETTYPE_VERSION, QUICPACKETTYPE_FRAME, QUICPACKETTYPE_PROTECTEDFRAME: 157 | size = this.publicHeader.GetSerializedSize() + this.privateHeader.GetSerializedSize() // + this.framesSet.GetSerializedSize() 158 | data = this.buffer[:size] 159 | return 160 | case QUICPACKETTYPE_FEC: 161 | if size, err = this.publicHeader.GetSerializedData(this.buffer[:]); err != nil { 162 | return 163 | } 164 | if s, err = this.privateHeader.GetSerializedData(this.buffer[size:]); err != nil { 165 | return 166 | } 167 | size += s 168 | if s, err = this.fecPacket.GetSerializedData(this.buffer[size:]); err != nil { 169 | return 170 | } 171 | size += s 172 | data = this.buffer[:size] 173 | return 174 | } 175 | err = errors.New("QuicPacket.GetSerializedData : can't serialized unknown packet type") 176 | return 177 | } 178 | 179 | // GetPacketType 180 | func (this *QuicPacket) GetPacketType() (packettype QuicPacketType) { 181 | return this.packetType 182 | } 183 | 184 | // SetPacketType 185 | func (this *QuicPacket) SetPacketType(packettype QuicPacketType) { 186 | this.packetType = packettype 187 | } 188 | -------------------------------------------------------------------------------- /protocol/quicpublicresetpacket.go: -------------------------------------------------------------------------------- 1 | package protocol 2 | 3 | import "encoding/binary" 4 | import "errors" 5 | 6 | /* 7 | 8 | 0 1 2 3 4 8 9 | +--------+--------+--------+--------+--------+-- --+ 10 | | Public | Connection ID (64) ... | -> 11 | |Flags(8)| | 12 | +--------+--------+--------+--------+--------+-- --+ 13 | 14 | 9 10 11 12 13 14 15 | +--------+--------+--------+--------+--------+--------+--- 16 | | Quic Tag (32) | Tag value map ... -> 17 | | (PRST) | (variable length) 18 | +--------+--------+--------+--------+--------+--------+--- 19 | 20 | 21 | Public flags: 22 | +---+---+---+---+---+---+---+---+ 23 | | 0 | 0 | SeqNum| ConnID|Rst|Ver| 24 | +---+---+---+---+---+---+---+---+ 25 | 26 | */ 27 | 28 | type QuicPublicResetNonceProof uint64 29 | 30 | type QuicPublicResetPacket struct { 31 | msg Message 32 | nonceProof QuicPublicResetNonceProof 33 | rejectedSeqNum QuicPacketSequenceNumber 34 | buffer [16]byte 35 | } 36 | 37 | // Erase 38 | func (this *QuicPublicResetPacket) Erase() { 39 | this.msg.tags = nil 40 | this.msg.values = nil 41 | this.nonceProof = 0 42 | this.rejectedSeqNum = 0 43 | } 44 | 45 | // ParseData 46 | func (this *QuicPublicResetPacket) ParseData(data []byte) (size int, err error) { 47 | var b bool 48 | var buffer []byte 49 | 50 | // Check minimum Public Reset packet size 51 | l := len(data) 52 | if l < (40) { 53 | err = errors.New("QuicPublicResetPacket.ParseData : data size too small to contain Public Reset packet") 54 | return 55 | } 56 | // Read uint32 message tag 57 | this.msg.msgTag = MessageTag(binary.LittleEndian.Uint32(data)) 58 | size = 4 59 | if this.msg.msgTag != TagPRST { 60 | err = errors.New("QuicPublicResetPacket.ParseData : invalid Public Reset packet, PRST message tag required") 61 | return 62 | } 63 | // Read uint16 number of entries and ignore next uint16 of padding 64 | numEntries := uint16(binary.LittleEndian.Uint16(data[size:])) 65 | if numEntries < 2 { 66 | err = errors.New("QuicPublicResetPacket.ParseData : invalid Public Reset packet, RNON and RSEG tags required") 67 | return 68 | } 69 | if numEntries > MaxMessageTagNumEntries { 70 | err = errors.New("QuicPublicResetPacket.ParseData : invalid number of Tag entries in Public Reset packet (>128)") 71 | return 72 | } 73 | size = 8 74 | // Ask for next data size 75 | needMoreData := int(8*numEntries) + size 76 | if l < needMoreData { 77 | err = errors.New("QuicPublicResetPacket.ParseData : data size too small to contain Public Reset packet") 78 | return 79 | } 80 | // Allocate ressources for tag-offset pairs 81 | this.msg.tags = make([]MessageTag, numEntries) 82 | endOffsets := make([]uint32, numEntries) 83 | for i := 0; i < int(numEntries); i++ { 84 | // Read uint32 tag 85 | this.msg.tags[i] = MessageTag(binary.LittleEndian.Uint32(data[size:])) 86 | size += 4 87 | // Read uint32 offset 88 | endOffsets[i] = uint32(binary.LittleEndian.Uint32(data[size:])) 89 | size += 4 90 | } 91 | // Ask for next data size 92 | needMoreData += int(endOffsets[numEntries-1]) 93 | if l < needMoreData { 94 | err = errors.New("QuicPublicResetPacket.ParseData : data size too small to contain Public Reset packet") 95 | return 96 | } 97 | // Allocate ressources for tag-value pairs 98 | this.msg.values = make([][]byte, numEntries) 99 | // Read values 100 | offset := 0 101 | for i := 0; i < int(numEntries); i++ { 102 | this.msg.values[i] = data[size+offset : uint32(size)+endOffsets[i]] 103 | offset = int(endOffsets[i]) 104 | } 105 | size += offset 106 | // Do we have mandatory RNON tag/value pair ? 107 | if b, buffer = this.msg.ContainsTag(TagRNON); !b { 108 | err = errors.New("QuicPublicResetPacket.ParseData : invalid Public Reset packet, RNON tag required") 109 | return 110 | } 111 | // Parse Nonce Proof 112 | if len(buffer) != 8 { 113 | err = errors.New("QuicPublicResetPacket.ParseData : invalid Public Reset packet, invalid RNON value size (no 64bit value size)") 114 | return 115 | } 116 | this.nonceProof = QuicPublicResetNonceProof(binary.LittleEndian.Uint64(buffer)) 117 | // Do we have mandatory RSEQ tag/value pair ? 118 | if b, buffer = this.msg.ContainsTag(TagRSEQ); !b { 119 | err = errors.New("QuicPublicResetPacket.ParseData : invalid Public Reset packet, RSEQ tag required") 120 | return 121 | } 122 | // Parse Rejected Sequence Number 123 | if len(buffer) != 8 { 124 | err = errors.New("QuicPublicResetPacket.ParseData : invalid Public Reset packet, invalid RSEQ value size (no 64bit value size)") 125 | return 126 | } 127 | this.rejectedSeqNum = QuicPacketSequenceNumber(binary.LittleEndian.Uint64(buffer)) 128 | return 129 | } 130 | 131 | // GetSerializedSize 132 | func (this *QuicPublicResetPacket) GetSerializedSize() (size int) { 133 | size = int(this.msg.GetSerializeSize()) 134 | return 135 | } 136 | 137 | // GetSerializedData 138 | func (this *QuicPublicResetPacket) GetSerializedData(data []byte) (size int, err error) { 139 | 140 | this.msg.msgTag = TagPRST 141 | size = int(this.msg.GetSerializeSize()) 142 | if len(data) < size { 143 | err = errors.New("QuicPublicResetPacket.GetSerializedData : data size too small to contain Public Reset packet") 144 | size = 0 145 | } 146 | copy(data, this.msg.GetSerialize()) 147 | return 148 | } 149 | 150 | // GetNonceProof 151 | func (this *QuicPublicResetPacket) GetNonceProof() QuicPublicResetNonceProof { 152 | return this.nonceProof 153 | } 154 | 155 | // SetNonceProof 156 | func (this *QuicPublicResetPacket) SetNonceProof(nonce QuicPublicResetNonceProof) { 157 | this.nonceProof = nonce 158 | // Add 'RNON' tag/value pair 159 | binary.LittleEndian.PutUint64(this.buffer[0:8], uint64(this.nonceProof)) 160 | if b, _ := this.msg.ContainsTag(TagRNON); b { 161 | this.msg.UpdateTagValue(TagRNON, this.buffer[0:8]) 162 | } else { 163 | this.msg.AddTagValue(TagRNON, this.buffer[0:8]) 164 | } 165 | } 166 | 167 | // GetRejectedSequenceNumber 168 | func (this *QuicPublicResetPacket) GetRejectedSequenceNumber() QuicPacketSequenceNumber { 169 | return this.rejectedSeqNum 170 | } 171 | 172 | // SetRejectedSequenceNumber 173 | func (this *QuicPublicResetPacket) SetRejectedSequenceNumber(seqnum QuicPacketSequenceNumber) { 174 | this.rejectedSeqNum = seqnum 175 | // Add 'RSEQ' tag/value pair 176 | binary.LittleEndian.PutUint64(this.buffer[8:16], uint64(this.rejectedSeqNum)) 177 | if b, _ := this.msg.ContainsTag(TagRSEQ); b { 178 | this.msg.UpdateTagValue(TagRSEQ, this.buffer[8:16]) 179 | } else { 180 | this.msg.AddTagValue(TagRSEQ, this.buffer[8:16]) 181 | } 182 | } 183 | -------------------------------------------------------------------------------- /protocol/quicentropyhash.go: -------------------------------------------------------------------------------- 1 | package protocol 2 | 3 | import "errors" 4 | 5 | const MAXIMUM_ENTROPY_CONSECUTIVE_SEQNUM = 0x80000 // = 524288 6 | 7 | type QuicEntropyHash byte 8 | 9 | type EntropyHashRingBuffer struct { 10 | largestKnownSeqNum QuicPacketSequenceNumber // start of ring (=read) 11 | largestKnownEntropyHash QuicEntropyHash // Cumulative entropy hash since first packet 12 | nextSeqNum QuicPacketSequenceNumber // end of ring (=write) 13 | hashes [0x10000]byte // = 65536 bytes 14 | } 15 | 16 | // NewEntropyHashRingBuffer is a factory that returns an EntropyHashRingBuffer and its associated size. 17 | // The size of the EntropyHashRingBuffer represents the total number of consecutive packet sequence number for which entropy bits can be stored. 18 | // Ring buffer left capacity is decreasing each time 'GetNewPacket()' is called. 19 | // Ring buffer left capacity is increasing each time 'SetLargestKnownPacket()' is called. 20 | // It is possible to generate and manage 2^46 packets sequence number and associated entropy bit, 21 | // but the current limitation is that there must be no more than MAXIMUM_ENTROPY_CONSECUTIVE_SEQNUM packets in flight. 22 | // At this time, MAXIMUM_ENTROPY_CONSECUTIVE_SEQNUM = 524.288 consecutive sequence numbers with a memory footprint of only 64KB, 23 | // if considering a conservative mean stream frame size of 512 bytes, then a theoretical maximum congestion window of 268.435.456 bytes can be managed. 24 | func NewEntropyHashRingBuffer() (entropymanager *EntropyHashRingBuffer, size int) { 25 | entropymanager = &EntropyHashRingBuffer{largestKnownSeqNum: 1, nextSeqNum: 1} 26 | size = MAXIMUM_ENTROPY_CONSECUTIVE_SEQNUM 27 | return 28 | } 29 | 30 | // getEntropy returns the entropy bit status for a given Sequence Number. 31 | // Note: no boundary check is make on the sequence number: for debugging and 'go test' purpose only. 32 | func (this *EntropyHashRingBuffer) getEntropy(seqnum QuicPacketSequenceNumber) bool { 33 | if QuicEntropyHash(this.hashes[(seqnum>>3)&0xffff]&(1<<(seqnum&0x7))) == 0 { 34 | return false 35 | } 36 | return true 37 | } 38 | 39 | // setEntropy sets the entropy bit status for a given Sequence Number. 40 | // Note: no boundary check is make on the sequence number: for debugging and 'go test' purpose only. 41 | func (this *EntropyHashRingBuffer) setEntropy(seqnum QuicPacketSequenceNumber, entropy bool) { 42 | if entropy { 43 | this.hashes[(seqnum>>3)&0xffff] |= (1 << (seqnum & 0x7)) 44 | } else { 45 | this.hashes[(seqnum>>3)&0xffff] &= (0xff ^ (1 << (seqnum & 0x7))) 46 | } 47 | return 48 | } 49 | 50 | // GetEntropyHash returns the non cumulative entropy hash for the requested sequence number. 51 | // Example : if packet 42 as entropy flag set, then GetEntropyHash(42) returns the absolute hash value 0x04 52 | func (this *EntropyHashRingBuffer) GetEntropyHash(seqnum QuicPacketSequenceNumber) (hash QuicEntropyHash, err error) { 53 | if (seqnum >= this.nextSeqNum) || (seqnum < this.largestKnownSeqNum) { 54 | err = errors.New("EntropyHashRingBuffer.GetEntropyHash : invalid Packet Sequence Number") 55 | return 56 | } 57 | hash = QuicEntropyHash(this.hashes[(seqnum>>3)&0xffff] & (1 << (seqnum & 0x7))) 58 | return 59 | } 60 | 61 | // GetCumulativeEntropyHash returns the cumulative entropy hash for the requested sequence number since the very first packet. 62 | func (this *EntropyHashRingBuffer) GetCumulativeEntropyHash(seqnum QuicPacketSequenceNumber) (hash QuicEntropyHash, err error) { 63 | if (seqnum < this.largestKnownSeqNum) || (seqnum >= this.nextSeqNum) { 64 | err = errors.New("EntropyHashRingBuffer.GetCumulativeEntropyHash : invalid Packet Sequence Number") 65 | } 66 | hash = this.largestKnownEntropyHash 67 | if seqnum > this.largestKnownSeqNum { 68 | for i := this.largestKnownSeqNum; i < seqnum; i++ { 69 | hash ^= QuicEntropyHash(this.hashes[(i>>3)&0xffff] & (1 << (i & 0x7))) 70 | } 71 | } 72 | return 73 | } 74 | 75 | // GetCumulativeEntropyHashFromTo returns the cumulative entropy hash 'from' a starting sequence number 'to' a ending sequence number, 76 | // and returns an error if the given sequence numbers are out of scope of the ring buffer. 77 | // Note that the 'from' sequence number must be less than or equal to the 'to' sequence number. 78 | func (this *EntropyHashRingBuffer) GetCumulativeEntropyHashFromTo(from, to QuicPacketSequenceNumber) (hash QuicEntropyHash, err error) { 79 | if (from > to) || (from < this.largestKnownSeqNum) || (to >= this.nextSeqNum) { 80 | err = errors.New("EntropyHashRingBuffer.GetCumulativeEntropyHashFromTo : invalid 'from' and 'to' Packet Sequence Number") 81 | } 82 | hash = QuicEntropyHash(this.hashes[(from>>3)&0xffff] & (1 << (from & 0x7))) 83 | for i := from; i <= to; i++ { 84 | hash ^= QuicEntropyHash(this.hashes[(i>>3)&0xffff] & (1 << (i & 0x7))) 85 | } 86 | return 87 | } 88 | 89 | // GetNewPacket returns a monotonic increasing packet sequence number for which the given entropy bit is stored in the ring buffer. 90 | // GetNewPacket is typically called for creating/sending a new QUIC packet. 91 | func (this *EntropyHashRingBuffer) GetNewPacket(entropy bool) (seqnum QuicPacketSequenceNumber, err error) { 92 | // Check if ring buffer is full 93 | if int(this.nextSeqNum-this.largestKnownSeqNum) >= MAXIMUM_ENTROPY_CONSECUTIVE_SEQNUM { 94 | err = errors.New("EntropyHashRingBuffer.GetNewPacket : ring buffer full, can't store new packet entropy") 95 | return 96 | } 97 | seqnum = this.nextSeqNum 98 | if entropy { 99 | // Set the correct bit in the correct byte of the ring buffer 100 | this.hashes[(seqnum>>3)&0xffff] |= (1 << (seqnum & 0x7)) 101 | } else { 102 | // Clear the correct bit in the correct byte of the ring buffer 103 | this.hashes[(seqnum>>3)&0xffff] &= (0xff ^ (1 << (seqnum & 0x7))) 104 | } 105 | this.nextSeqNum++ 106 | return 107 | } 108 | 109 | // SetLargestKnownPacket removes the begining hashes from the ring buffer up to and including the given Sequence Number, 110 | // it returns the cumulative entropy hash for this new largest known sequence number and an error if the given sequence number is out of scope of the ring buffer. 111 | func (this *EntropyHashRingBuffer) SetLargestKnownPacket(seqnum QuicPacketSequenceNumber) (hash QuicEntropyHash, err error) { 112 | // Check sequence number validity towards current bounds of the ring buffer 113 | if (seqnum < this.largestKnownSeqNum) || (seqnum >= this.nextSeqNum) { 114 | err = errors.New("EntropyHashRingBuffer.SetLargestKnownPacket : invalid Packet Sequence Number") 115 | } 116 | // Compute and return the hash for this new largest known sequence number 117 | hash = this.largestKnownEntropyHash 118 | if seqnum > this.largestKnownSeqNum { 119 | for i := this.largestKnownSeqNum; i < seqnum; i++ { 120 | hash ^= QuicEntropyHash(this.hashes[(i>>3)&0xffff] & (1 << (i & 0x7))) 121 | } 122 | // Update Largest Known Entropy Hash 123 | this.largestKnownEntropyHash = hash 124 | // Update Largest Known Sequence Number 125 | this.largestKnownSeqNum = seqnum 126 | } 127 | return 128 | } 129 | -------------------------------------------------------------------------------- /protocol/quicpublicresetpacket_test.go: -------------------------------------------------------------------------------- 1 | package protocol 2 | 3 | import "testing" 4 | import "bytes" 5 | 6 | /* 7 | 8 | 0 1 2 3 4 8 9 | +--------+--------+--------+--------+--------+-- --+ 10 | | Public | Connection ID (64) ... | -> 11 | |Flags(8)| | 12 | +--------+--------+--------+--------+--------+-- --+ 13 | 14 | 9 10 11 12 13 14 15 | +--------+--------+--------+--------+--------+--------+--- 16 | | Quic Tag (32) | Tag value map ... -> 17 | | (PRST) | (variable length) 18 | +--------+--------+--------+--------+--------+--------+--- 19 | 20 | 21 | Public flags: 22 | +---+---+---+---+---+---+---+---+ 23 | | 0 | 0 | SeqNum| ConnID|Rst|Ver| 24 | +---+---+---+---+---+---+---+---+ 25 | 26 | TagPRST = 0x54535250 27 | TagRNON = 0x4e4f4e52 28 | TagRSEQ = 0x51455352 29 | 30 | */ 31 | 32 | type testquicpublicresetpacket struct { 33 | positiveTest bool 34 | data []byte 35 | nonceProof QuicPublicResetNonceProof 36 | rejectedseqnum QuicPacketSequenceNumber 37 | } 38 | 39 | var tests_quicpublicresetpacket = []testquicpublicresetpacket{ 40 | {true, []byte{ 41 | 0x50, 0x52, 0x53, 0x54, // Tag 'PRST' 42 | 0x02, 0x00, 0x00, 0x00, // Num entries (uint16) + 2 bytes of padding 43 | 0x52, 0x4e, 0x4f, 0x4e, // Tag 'RNON' 44 | 0x08, 0x00, 0x00, 0x00, // 'RNON' offset 45 | 0x52, 0x53, 0x45, 0x51, // Tag 'RSEQ' 46 | 0x10, 0x00, 0x00, 0x00, // 'RSEQ' offset 47 | 0xde, 0xda, 0xfe, 0xce, 0xbe, 0xba, 0xfe, 0xca, // RNON value 48 | 0xdd, 0xcc, 0xbb, 0xaa, 0x0d, 0x0c, 0x0b, 0x0a}, // Rejected Sequence Number 49 | 0xcafebabecefedade, 0x0a0b0c0daabbccdd}, 50 | // Bad message tag 'QRST' 51 | {false, []byte{ 52 | 0x51, 0x52, 0x53, 0x54, // Tag 'QRST' 53 | 0x02, 0x00, 0x00, 0x00, // Num entries (uint16) + 2 bytes of padding 54 | 0x52, 0x4e, 0x4f, 0x4e, // Tag 'RNON' 55 | 0x08, 0x00, 0x00, 0x00, // 'RNON' offset 56 | 0x52, 0x53, 0x45, 0x51, // Tag 'RSEQ' 57 | 0x10, 0x00, 0x00, 0x00, // 'RSEQ' offset 58 | 0xde, 0xda, 0xfe, 0xce, 0xbe, 0xba, 0xfe, 0xca, // RNON value 59 | 0xdd, 0xcc, 0xbb, 0xaa, 0x0d, 0x0c, 0x0b, 0x0a}, // Rejected Sequence Number 60 | 0xcafebabecefedade, 0x0a0b0c0daabbccdd}, 61 | // Missing 'RNON' tag 62 | {false, []byte{ 63 | 0x50, 0x52, 0x53, 0x54, // Tag 'PRST' 64 | 0x02, 0x00, 0x00, 0x00, // Num entries (uint16) + 2 bytes of padding 65 | 0x52, 0x4e, 0x4f, 0x4f, // Tag 'RNOO' 66 | 0x08, 0x00, 0x00, 0x00, // 'RNOO' offset 67 | 0x52, 0x53, 0x45, 0x51, // Tag 'RSEQ' 68 | 0x10, 0x00, 0x00, 0x00, // 'RSEQ' offset 69 | 0xde, 0xda, 0xfe, 0xce, 0xbe, 0xba, 0xfe, 0xca, // RNOO value 70 | 0xdd, 0xcc, 0xbb, 0xaa, 0x0d, 0x0c, 0x0b, 0x0a}, // Rejected Sequence Number 71 | 0xcafebabecefedade, 0x0a0b0c0daabbccdd}, 72 | // Bad 'RNON' value size (< 64bit) 73 | {false, []byte{ 74 | 0x50, 0x52, 0x53, 0x54, // Tag 'PRST' 75 | 0x02, 0x00, 0x00, 0x00, // Num entries (uint16) + 2 bytes of padding 76 | 0x52, 0x4e, 0x4f, 0x4e, // Tag 'RNON' 77 | 0x07, 0x00, 0x00, 0x00, // 'RNON' offset 78 | 0x52, 0x53, 0x45, 0x51, // Tag 'RSEQ' 79 | 0x0f, 0x00, 0x00, 0x00, // 'RSEQ' offset 80 | 0xde, 0xda, 0xfe, 0xce, 0xbe, 0xba, 0xfe, // RNON value 81 | 0xdd, 0xcc, 0xbb, 0xaa, 0x0d, 0x0c, 0x0b, 0x0a}, // Rejected Sequence Number 82 | 0xfebabecefedade, 0x0a0b0c0daabbccdd}, 83 | // Bad 'RNON' value size (> 64bit) 84 | {false, []byte{ 85 | 0x50, 0x52, 0x53, 0x54, // Tag 'PRST' 86 | 0x02, 0x00, 0x00, 0x00, // Num entries (uint16) + 2 bytes of padding 87 | 0x52, 0x4e, 0x4f, 0x4e, // Tag 'RNON' 88 | 0x09, 0x00, 0x00, 0x00, // 'RNON' offset 89 | 0x52, 0x53, 0x45, 0x51, // Tag 'RSEQ' 90 | 0x11, 0x00, 0x00, 0x00, // 'RSEQ' offset 91 | 0xde, 0xda, 0xfe, 0xce, 0xbe, 0xba, 0xfe, 0xca, 0x00, // RNON value 92 | 0xdd, 0xcc, 0xbb, 0xaa, 0x0d, 0x0c, 0x0b, 0x0a}, // Rejected Sequence Number 93 | 0xcafebabecefedade, 0x0a0b0c0daabbccdd}, 94 | // Missing 'RSEQ' tag 95 | {false, []byte{ 96 | 0x50, 0x52, 0x53, 0x54, // Tag 'PRST' 97 | 0x02, 0x00, 0x00, 0x00, // Num entries (uint16) + 2 bytes of padding 98 | 0x52, 0x4e, 0x4f, 0x4e, // Tag 'RNON' 99 | 0x08, 0x00, 0x00, 0x00, // 'RNON' offset 100 | 0x52, 0x53, 0x46, 0x51, // Tag 'RSFQ' 101 | 0x10, 0x00, 0x00, 0x00, // 'RSFQ' offset 102 | 0xde, 0xda, 0xfe, 0xce, 0xbe, 0xba, 0xfe, 0xca, // RNON value 103 | 0xdd, 0xcc, 0xbb, 0xaa, 0x0d, 0x0c, 0x0b, 0x0a}, // Rejected Sequence Number 104 | 0xcafebabecefedade, 0x0a0b0c0daabbccdd}, 105 | // Bad 'RSEQ' value size (< 64bit) 106 | {false, []byte{ 107 | 0x50, 0x52, 0x53, 0x54, // Tag 'PRST' 108 | 0x02, 0x00, 0x00, 0x00, // Num entries (uint16) + 2 bytes of padding 109 | 0x52, 0x4e, 0x4f, 0x4e, // Tag 'RNON' 110 | 0x08, 0x00, 0x00, 0x00, // 'RNON' offset 111 | 0x52, 0x53, 0x45, 0x51, // Tag 'RSEQ' 112 | 0x0f, 0x00, 0x00, 0x00, // 'RSEQ' offset 113 | 0xde, 0xda, 0xfe, 0xce, 0xbe, 0xba, 0xfe, 0xca, // RNON value 114 | 0xdd, 0xcc, 0xbb, 0xaa, 0x0d, 0x0c, 0x0b}, // Rejected Sequence Number 115 | 0xcafebabecefedade, 0x0b0c0daabbccdd}, 116 | // Bad 'RSEQ' value size (> 64bit) 117 | {false, []byte{ 118 | 0x50, 0x52, 0x53, 0x54, // Tag 'PRST' 119 | 0x02, 0x00, 0x00, 0x00, // Num entries (uint16) + 2 bytes of padding 120 | 0x52, 0x4e, 0x4f, 0x4e, // Tag 'RNON' 121 | 0x08, 0x00, 0x00, 0x00, // 'RNON' offset 122 | 0x52, 0x53, 0x45, 0x51, // Tag 'RSEQ' 123 | 0x11, 0x00, 0x00, 0x00, // 'RSEQ' offset 124 | 0xde, 0xda, 0xfe, 0xce, 0xbe, 0xba, 0xfe, 0xca, // RNON value 125 | 0xdd, 0xcc, 0xbb, 0xaa, 0x0d, 0x0c, 0x0b, 0x0a, 0x00}, // Rejected Sequence Number 126 | 0xcafebabecefedade, 0x0a0b0c0daabbccdd}, 127 | } 128 | 129 | func Test_QuicPublicRestPacket_ParseData(t *testing.T) { 130 | var reset QuicPublicResetPacket 131 | 132 | for i, v := range tests_quicpublicresetpacket { 133 | s, err := reset.ParseData(v.data) 134 | if v.positiveTest { 135 | if err != nil { 136 | t.Errorf("QuicPublicRestPacket.ParseData : error %s in test %x with data[%v]%x", err, i, len(v.data), v.data) 137 | } 138 | if s != len(v.data) { 139 | t.Errorf("QuicPublicRestPacket.ParseData : invalid size %v in test %x with data[%v]%x", s, i, len(v.data), v.data) 140 | } 141 | if v.nonceProof != reset.GetNonceProof() { 142 | t.Errorf("QuicPublicRestPacket.ParseData : invalid proof %x in test %x with data[%v]%x", reset.GetNonceProof(), i, len(v.data), v.data) 143 | } 144 | if v.rejectedseqnum != reset.GetRejectedSequenceNumber() { 145 | t.Errorf("QuicPublicRestPacket.ParseData : invalid rejected sequence number %x in test %x with data[%v]%x", reset.GetRejectedSequenceNumber(), i, len(v.data), v.data) 146 | } 147 | } else if err == nil { 148 | t.Error("QuicPublicRestPacket.ParseData : missing error in test %x with data[%v]%x", i, len(v.data), v.data) 149 | } 150 | } 151 | } 152 | 153 | func Test_QuicPublicRestPacket_GetSerializedData(t *testing.T) { 154 | var reset QuicPublicResetPacket 155 | 156 | data := make([]byte, 40) 157 | for i, v := range tests_quicpublicresetpacket { 158 | if v.positiveTest { 159 | reset.SetNonceProof(v.nonceProof) 160 | reset.SetRejectedSequenceNumber(v.rejectedseqnum) 161 | s, err := reset.GetSerializedData(data) 162 | if err != nil { 163 | t.Errorf("QuicPublicRestPacket.GetSerializedData = error %s while serialized data in test n°%v", err, i) 164 | } 165 | if s != len(v.data) { 166 | t.Errorf("QuicPublicRestPacket.GetSerializedData = invalid serialized size in test n°%v with data[%v]%x", i, s, data[:s]) 167 | } 168 | if !bytes.Equal(data[:s], v.data) { 169 | t.Errorf("QuicPublicRestPacket.GetSerializedData = invalid serialized data in test n°%v with data[%v]%x", i, s, data[:s]) 170 | } 171 | reset.Erase() 172 | } 173 | } 174 | 175 | } 176 | -------------------------------------------------------------------------------- /protocol/message_test.go: -------------------------------------------------------------------------------- 1 | package protocol 2 | 3 | import "testing" 4 | import "bytes" 5 | 6 | func Test_NewMessage(t *testing.T) { 7 | var msg *Message 8 | 9 | if msg = NewMessage(TagCHLO); msg == nil { 10 | t.Error("NewMessage: return nil on CHLO") 11 | } 12 | if msg.GetMessageTag() != TagCHLO { 13 | t.Error("NewMessage: invalid new CHLO message") 14 | } 15 | if msg = NewMessage(TagREJ); msg == nil { 16 | t.Error("NewMessage: return nil on REJ") 17 | } 18 | if msg.GetMessageTag() != TagREJ { 19 | t.Error("NewMessage: invalid new REJ message") 20 | } 21 | if msg = NewMessage(TagSHLO); msg == nil { 22 | t.Error("NewMessage: return nil on SHLO") 23 | } 24 | if msg.GetMessageTag() != TagSHLO { 25 | t.Error("NewMessage: invalid new SHLO message") 26 | } 27 | if msg = NewMessage(TagSCUP); msg == nil { 28 | t.Error("NewMessage: return nil on SCUP") 29 | } 30 | if msg.GetMessageTag() != TagSCUP { 31 | t.Error("NewMessage: invalid new SCUP message") 32 | } 33 | if msg = NewMessage(666); msg != nil { 34 | t.Error("NewMessage: not returning nil on bad message tag") 35 | } 36 | msg = NewMessage(TagCHLO) 37 | msg.AddTagValue(TagSTK, []byte{0, 1}) 38 | 39 | if msg.GetMessageTag() != TagCHLO { 40 | t.Error("NewMessage: bad message tag initialization") 41 | } 42 | if msg.GetNumEntries() != 1 { 43 | t.Error("NewMessage: bad tag/value pairs initialization") 44 | } 45 | if b, v := msg.ContainsTag(TagSTK); !b { 46 | t.Error("NewMessage: bad tag/value pairs initialization") 47 | } else if !bytes.Equal(v, []byte{0, 1}) { 48 | t.Error("NewMessage: bad tag/value pairs initialization") 49 | } 50 | } 51 | 52 | func Test_GetMessageTag(t *testing.T) { 53 | msg := NewMessage(TagCHLO) 54 | if msg.GetMessageTag() != TagCHLO { 55 | t.Error("GetMessageTag: bad message tag") 56 | } 57 | } 58 | 59 | func Test_IsMessageTag(t *testing.T) { 60 | msg := NewMessage(TagSCUP) 61 | if msg.IsMessageTag(TagCHLO) { 62 | t.Error("IsMessageTag: bad message tag") 63 | } 64 | if !msg.IsMessageTag(TagSCUP) { 65 | t.Error("IsMessageTag: bad message tag") 66 | } 67 | } 68 | 69 | func Test_GetNumEntries(t *testing.T) { 70 | msg := NewMessage(TagREJ) 71 | if msg.GetNumEntries() != 0 { 72 | t.Error("GetNumEntries: bad value, must be zero") 73 | } 74 | msg = NewMessage(TagSHLO) 75 | msg.AddTagValue(TagAEAD, []byte{0, 1}) 76 | msg.AddTagValue(TagCETV, []byte{2, 3}) 77 | if msg.GetNumEntries() != 2 { 78 | t.Error("GetNumEntries: bad value, must be 2") 79 | } 80 | } 81 | 82 | func Test_ContainsTag(t *testing.T) { 83 | var b bool 84 | var v []byte 85 | 86 | msg := NewMessage(TagREJ) 87 | if b, v = msg.ContainsTag(TagCETV); b { 88 | t.Error("ContainsTag: tag can't be in the Message") 89 | } 90 | msg = NewMessage(TagSHLO) 91 | msg.AddTagValue(TagAEAD, []byte{0, 1}) 92 | msg.AddTagValue(TagCETV, []byte{2, 3}) 93 | msg.AddTagValue(TagSCFG, []byte{4, 5}) 94 | if b, v = msg.ContainsTag(TagKEXS); b { 95 | t.Error("ContainsTag: tag can't be in the Message") 96 | } 97 | if v != nil { 98 | t.Error("ContainsTag: unknow tag can't have a value in the Message") 99 | } 100 | if b, v = msg.ContainsTag(TagAEAD); !b { 101 | t.Error("ContainsTag: tag must be in the Message") 102 | } 103 | if !bytes.Equal(v, []byte{0, 1}) { 104 | t.Error("ContainsTag: invalid associated value for this tag") 105 | } 106 | if b, v = msg.ContainsTag(TagCETV); !b { 107 | t.Error("ContainsTag: tag must be in the Message") 108 | } 109 | if !bytes.Equal(v, []byte{2, 3}) { 110 | t.Error("ContainsTag: invalid associated value for this tag") 111 | } 112 | if b, v = msg.ContainsTag(TagSCFG); !b { 113 | t.Error("ContainsTag: tag must be in the Message") 114 | } 115 | if !bytes.Equal(v, []byte{4, 5}) { 116 | t.Error("ContainsTag: invalid associated value for this tag") 117 | } 118 | } 119 | 120 | func Test_UpdateTagValue(t *testing.T) { 121 | var b bool 122 | var v []byte 123 | 124 | msg := NewMessage(TagSHLO) 125 | msg.AddTagValue(TagAEAD, []byte{0, 1}) 126 | msg.AddTagValue(TagCETV, []byte{2, 3}) 127 | msg.AddTagValue(TagSCFG, []byte{4, 5}) 128 | if msg.UpdateTagValue(TagKEXS, []byte{6, 7}) { 129 | t.Error("UpdateTagValue: can update a tag value that is not already exist") 130 | } 131 | if !msg.UpdateTagValue(TagAEAD, []byte{8, 9}) { 132 | t.Error("UpdateTagValue: can't update a tag value that is already exist") 133 | } 134 | if b, v = msg.ContainsTag(TagAEAD); !b { 135 | t.Error("UpdateTagValue: can't update a tag value that is already exist") 136 | } 137 | if !bytes.Equal(v, []byte{8, 9}) { 138 | t.Error("UpdateTagValue: can't update a tag value that is already exist") 139 | } 140 | if !msg.UpdateTagValue(TagCETV, []byte{10, 11}) { 141 | t.Error("UpdateTagValue: can't update a tag value that is already exist") 142 | } 143 | if b, v = msg.ContainsTag(TagCETV); !b { 144 | t.Error("UpdateTagValue: can't update a tag value that is already exist") 145 | } 146 | if !bytes.Equal(v, []byte{10, 11}) { 147 | t.Error("UpdateTagValue: can't update a tag value that is already exist") 148 | } 149 | if !msg.UpdateTagValue(TagSCFG, []byte{12, 13}) { 150 | t.Error("UpdateTagValue: can't update a tag value that is already exist") 151 | } 152 | if b, v = msg.ContainsTag(TagSCFG); !b { 153 | t.Error("UpdateTagValue: can't update a tag value that is already exist") 154 | } 155 | if !bytes.Equal(v, []byte{12, 13}) { 156 | t.Error("UpdateTagValue: can't update a tag value that is already exist") 157 | } 158 | } 159 | 160 | func Test_AddTagValue(t *testing.T) { 161 | var b bool 162 | var v []byte 163 | 164 | msg := NewMessage(TagREJ) 165 | msg.AddTagValue(TagAESG, []byte{0, 1}) 166 | if msg.GetNumEntries() != 1 { 167 | t.Error("AddTagValue: invalid number of entries") 168 | } 169 | if b, v = msg.ContainsTag(TagAESG); !b { 170 | t.Error("AddTagValue: can'f find added tag") 171 | } 172 | if !bytes.Equal(v, []byte{0, 1}) { 173 | t.Error("AddTagValue: can'f find added tag/value") 174 | } 175 | msg.AddTagValue(TagSCFG, []byte{2, 3}) 176 | if msg.GetNumEntries() != 2 { 177 | t.Error("AddTagValue: invalid number of entries") 178 | } 179 | if b, v = msg.ContainsTag(TagSCFG); !b { 180 | t.Error("AddTagValue: can'f find added tag") 181 | } 182 | if !bytes.Equal(v, []byte{2, 3}) { 183 | t.Error("AddTagValue: can'f find added tag/value") 184 | } 185 | msg.AddTagValue(TagKEXS, []byte{4, 5}) 186 | if msg.GetNumEntries() != 3 { 187 | t.Error("AddTagValue: invalid number of entries") 188 | } 189 | if b, v = msg.ContainsTag(TagKEXS); !b { 190 | t.Error("AddTagValue: can'f find added tag") 191 | } 192 | if !bytes.Equal(v, []byte{4, 5}) { 193 | t.Error("AddTagValue: can'f find added tag/value") 194 | } 195 | msg.AddTagValue(TagAEAD, []byte{6, 7}) 196 | if msg.GetNumEntries() != 4 { 197 | t.Error("AddTagValue: invalid number of entries") 198 | } 199 | if b, v = msg.ContainsTag(TagAEAD); !b { 200 | t.Error("AddTagValue: can'f find added tag") 201 | } 202 | if !bytes.Equal(v, []byte{6, 7}) { 203 | t.Error("AddTagValue: can'f find added tag/value") 204 | } 205 | } 206 | 207 | func Test_GetSerializeSize(t *testing.T) { 208 | msg := NewMessage(TagCHLO) 209 | if msg.GetSerializeSize() != 8 { 210 | t.Error("GetSerializeSize: bad size") 211 | } 212 | msg.AddTagValue(TagSNI, []byte{1}) 213 | if msg.GetSerializeSize() != (8 + 1*8 + 1) { 214 | t.Error("GetSerializeSize: bad size") 215 | } 216 | msg.AddTagValue(TagCETV, []byte{2, 3}) 217 | if msg.GetSerializeSize() != (8 + 2*8 + 3) { 218 | t.Error("GetSerializeSize: bad size") 219 | } 220 | msg.AddTagValue(TagAEAD, []byte{4, 5, 6}) 221 | if msg.GetSerializeSize() != (8 + 3*8 + 6) { 222 | t.Error("GetSerializeSize: bad size") 223 | } 224 | } 225 | 226 | func Test_GetSerialize(t *testing.T) { 227 | msg := NewMessage(TagCHLO) 228 | s := msg.GetSerialize() 229 | r := []byte{'C', 'H', 'L', 'O', 0, 0, 0, 0} 230 | if !bytes.Equal(s, r) { 231 | t.Error("GetSerialize: bad binary string") 232 | } 233 | 234 | msg.AddTagValue(TagSNI, []byte{1}) 235 | s = msg.GetSerialize() 236 | r = []byte{'C', 'H', 'L', 'O', 1, 0, 0, 0, 'S', 'N', 'I', 0, 1, 0, 0, 0, 1} 237 | if !bytes.Equal(s, r) { 238 | t.Error("GetSerialize: bad binary string") 239 | } 240 | 241 | msg.AddTagValue(TagCETV, []byte{2, 3}) 242 | s = msg.GetSerialize() 243 | r = []byte{'C', 'H', 'L', 'O', 2, 0, 0, 0, 'S', 'N', 'I', 0, 1, 0, 0, 0, 'C', 'E', 'T', 'V', 3, 0, 0, 0, 1, 2, 3} 244 | if !bytes.Equal(s, r) { 245 | t.Error("GetSerialize: bad binary string") 246 | } 247 | 248 | msg.AddTagValue(TagAEAD, []byte{4, 5, 6}) 249 | s = msg.GetSerialize() 250 | r = []byte{'C', 'H', 'L', 'O', 3, 0, 0, 0, 'S', 'N', 'I', 0, 1, 0, 0, 0, 'A', 'E', 'A', 'D', 4, 0, 0, 0, 'C', 'E', 'T', 'V', 6, 0, 0, 0, 1, 4, 5, 6, 2, 3} 251 | if !bytes.Equal(s, r) { 252 | t.Error("GetSerialize: bad binary string") 253 | } 254 | } 255 | -------------------------------------------------------------------------------- /protocol/quicpublicheader_test.go: -------------------------------------------------------------------------------- 1 | package protocol 2 | 3 | import "testing" 4 | import "bytes" 5 | 6 | /* 7 | 8 | 0 1 2 3 4 8 9 | +--------+--------+--------+--------+--------+--- ---+ 10 | | Public | Connection ID (0, 8, 32, or 64) ... | -> 11 | |Flags(8)| (variable length) | 12 | +--------+--------+--------+--------+--------+--- ---+ 13 | 14 | 9 10 11 12 15 | +--------+--------+--------+--------+ 16 | | Quic Version (32) | -> 17 | | (optional) | 18 | +--------+--------+--------+--------+ 19 | 20 | 13 14 15 16 17 18 21 | +--------+--------+--------+--------+--------+--------+ 22 | | Sequence Number (8, 16, 32, or 48) | 23 | | (variable length) | 24 | +--------+--------+--------+--------+--------+--------+ 25 | 26 | 27 | Public flags: 28 | +---+---+---+---+---+---+---+---+ 29 | | 0 | 0 | SeqNum| ConnID|Rst|Ver| 30 | +---+---+---+---+---+---+---+---+ 31 | 32 | */ 33 | 34 | type testquicpublicheader struct { 35 | data []byte 36 | flagPublicReset bool 37 | flagVersion bool 38 | version QuicVersion 39 | connId QuicConnectionID 40 | seqNum QuicPacketSequenceNumber 41 | positiveTest bool 42 | } 43 | 44 | var tests_quicpublicheader = []testquicpublicheader{ 45 | 46 | // Tests[0-1] not enough data 47 | 48 | {[]byte{}, true, true, 0x01020304, 0x1122334455667788, 0xaabbccdd0a0b0c0d, false}, 49 | 50 | {[]byte{0x66}, true, true, 0x01020304, 0x1122334455667788, 0xaabbccdd0a0b0c0d, false}, 51 | 52 | // Tests[2-5] with mixed flags of Version and Public reset 53 | 54 | {[]byte{QUICFLAG_CONNID_0bit | QUICFLAG_SEQNUM_8bit, 0x0d}, 55 | false, false, 0, 0, 0x0d, true}, 56 | 57 | {[]byte{QUICFLAG_VERSION | QUICFLAG_CONNID_0bit | QUICFLAG_SEQNUM_8bit, 0x04, 0x03, 0x02, 0x01, 0x0d}, 58 | false, true, 0x01020304, 0, 0x0d, true}, 59 | 60 | {[]byte{QUICFLAG_CONNID_0bit | QUICFLAG_SEQNUM_8bit, 0x0d}, 61 | false, false, 0, 0, 0x0d, true}, 62 | 63 | {[]byte{QUICFLAG_VERSION | QUICFLAG_CONNID_0bit | QUICFLAG_SEQNUM_8bit, 0x04, 0x03, 0x02, 0x01, 0x0d}, 64 | false, true, 0x01020304, 0, 0x0d, true}, 65 | 66 | // Tests[6-13] with various Connection ID size and Version flags 67 | 68 | {[]byte{QUICFLAG_CONNID_0bit | QUICFLAG_SEQNUM_8bit, 0x0d}, 69 | false, false, 0, 0, 0x0d, true}, 70 | 71 | {[]byte{QUICFLAG_CONNID_8bit | QUICFLAG_SEQNUM_8bit, 0x88, 0x0d}, 72 | 73 | false, false, 0, 0x88, 0x0d, true}, 74 | {[]byte{QUICFLAG_CONNID_32bit | QUICFLAG_SEQNUM_8bit, 0x88, 0x77, 0x66, 0x55, 0x0d}, 75 | false, false, 0, 0x55667788, 0x0d, true}, 76 | 77 | {[]byte{QUICFLAG_CONNID_64bit | QUICFLAG_SEQNUM_8bit, 0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, 0x0d}, 78 | false, false, 0, 0x1122334455667788, 0x0d, true}, 79 | 80 | {[]byte{QUICFLAG_VERSION | QUICFLAG_CONNID_0bit | QUICFLAG_SEQNUM_8bit, 0x04, 0x03, 0x02, 0x01, 0x0d}, 81 | false, true, 0x01020304, 0, 0x0d, true}, 82 | 83 | {[]byte{QUICFLAG_VERSION | QUICFLAG_CONNID_8bit | QUICFLAG_SEQNUM_8bit, 0x88, 0x04, 0x03, 0x02, 0x01, 0x0d}, 84 | false, true, 0x01020304, 0x88, 0x0d, true}, 85 | 86 | {[]byte{QUICFLAG_VERSION | QUICFLAG_CONNID_32bit | QUICFLAG_SEQNUM_8bit, 0x88, 0x77, 0x66, 0x55, 0x04, 0x03, 0x02, 0x01, 0x0d}, 87 | false, true, 0x01020304, 0x55667788, 0x0d, true}, 88 | 89 | {[]byte{QUICFLAG_VERSION | QUICFLAG_CONNID_64bit | QUICFLAG_SEQNUM_8bit, 0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, 0x04, 0x03, 0x02, 0x01, 0x0d}, 90 | false, true, 0x01020304, 0x1122334455667788, 0x0d, true}, 91 | 92 | // Tests[14-21] with various Sequence Number size and Version flags 93 | 94 | {[]byte{QUICFLAG_CONNID_0bit | QUICFLAG_SEQNUM_8bit, 0x0d}, 95 | false, false, 0, 0, 0x0d, true}, 96 | 97 | {[]byte{QUICFLAG_CONNID_0bit | QUICFLAG_SEQNUM_16bit, 0x0d, 0x0c}, 98 | false, false, 0, 0, 0x0c0d, true}, 99 | 100 | {[]byte{QUICFLAG_CONNID_0bit | QUICFLAG_SEQNUM_32bit, 0x0d, 0x0c, 0x0b, 0x0a}, 101 | false, false, 0, 0, 0x0a0b0c0d, true}, 102 | 103 | {[]byte{QUICFLAG_CONNID_0bit | QUICFLAG_SEQNUM_48bit, 0x0d, 0x0c, 0x0b, 0x0a, 0xdd, 0xcc}, 104 | false, false, 0, 0, 0xccdd0a0b0c0d, true}, 105 | 106 | {[]byte{QUICFLAG_VERSION | QUICFLAG_CONNID_0bit | QUICFLAG_SEQNUM_8bit, 0x04, 0x03, 0x02, 0x01, 0x0d}, 107 | false, true, 0x01020304, 0, 0x0d, true}, 108 | 109 | {[]byte{QUICFLAG_VERSION | QUICFLAG_CONNID_0bit | QUICFLAG_SEQNUM_16bit, 0x04, 0x03, 0x02, 0x01, 0x0d, 0x0c}, 110 | false, true, 0x01020304, 0, 0x0c0d, true}, 111 | 112 | {[]byte{QUICFLAG_VERSION | QUICFLAG_CONNID_0bit | QUICFLAG_SEQNUM_32bit, 0x04, 0x03, 0x02, 0x01, 0x0d, 0x0c, 0x0b, 0x0a}, 113 | false, true, 0x01020304, 0, 0x0a0b0c0d, true}, 114 | 115 | {[]byte{QUICFLAG_VERSION | QUICFLAG_CONNID_0bit | QUICFLAG_SEQNUM_48bit, 0x04, 0x03, 0x02, 0x01, 0x0d, 0x0c, 0x0b, 0x0a, 0xdd, 0xcc}, 116 | false, true, 0x01020304, 0, 0xccdd0a0b0c0d, true}, 117 | 118 | // Tests [22-26] with various Connection ID size, Sequence Number size and Version flags 119 | 120 | {[]byte{QUICFLAG_VERSION | QUICFLAG_CONNID_8bit | QUICFLAG_SEQNUM_16bit, 0x88, 0x04, 0x03, 0x02, 0x01, 0x0d, 0x0c}, 121 | false, true, 0x01020304, 0x88, 0x0c0d, true}, 122 | 123 | {[]byte{QUICFLAG_VERSION | QUICFLAG_CONNID_32bit | QUICFLAG_SEQNUM_16bit, 0x88, 0x77, 0x66, 0x55, 0x04, 0x03, 0x02, 0x01, 0x0d, 0x0c}, 124 | false, true, 0x01020304, 0x55667788, 0x0c0d, true}, 125 | 126 | {[]byte{QUICFLAG_VERSION | QUICFLAG_CONNID_32bit | QUICFLAG_SEQNUM_32bit, 0x88, 0x77, 0x66, 0x55, 0x04, 0x03, 0x02, 0x01, 0x0d, 0x0c, 0x0b, 0x0a}, 127 | false, true, 0x01020304, 0x55667788, 0x0a0b0c0d, true}, 128 | 129 | {[]byte{QUICFLAG_VERSION | QUICFLAG_CONNID_32bit | QUICFLAG_SEQNUM_48bit, 0x88, 0x77, 0x66, 0x55, 0x04, 0x03, 0x02, 0x01, 0x0d, 0x0c, 0x0b, 0x0a, 0xdd, 0xcc}, 130 | false, true, 0x01020304, 0x55667788, 0xccdd0a0b0c0d, true}, 131 | 132 | {[]byte{QUICFLAG_VERSION | QUICFLAG_CONNID_64bit | QUICFLAG_SEQNUM_48bit, 0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, 0x04, 0x03, 0x02, 0x01, 0x0d, 0x0c, 0x0b, 0x0a, 0xdd, 0xcc}, 133 | false, true, 0x01020304, 0x1122334455667788, 0xccdd0a0b0c0d, true}, 134 | 135 | // Tests [27-30] various Public Reset 136 | 137 | {[]byte{QUICFLAG_PUBLICRESET | QUICFLAG_CONNID_64bit | QUICFLAG_SEQNUM_8bit, 0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11}, 138 | true, false, 0, 0x1122334455667788, 0, true}, 139 | } 140 | 141 | func Test_QuicPublicHeader_ParseData(t *testing.T) { 142 | var pub QuicPublicHeader 143 | 144 | for i, v := range tests_quicpublicheader { 145 | s, err := pub.ParseData(v.data) 146 | if v.positiveTest { 147 | if err != nil { 148 | t.Errorf("ParseData = error %s in test n°%v", err, i) 149 | } 150 | if s != len(v.data) { 151 | t.Errorf("ParseData = invalid parsed size in test n°%v with data[%v]%x", i, len(v.data), v.data) 152 | } 153 | if pub.GetPublicResetFlag() != v.flagPublicReset { 154 | t.Errorf("ParseData = invalid Public reset flag in test n°%v with data[%v]%x", i, len(v.data), v.data) 155 | } 156 | if pub.GetVersionFlag() != v.flagVersion { 157 | t.Errorf("ParseData = invalid Version flag in test n°%v with data[%v]%x", i, len(v.data), v.data) 158 | } 159 | if v.flagVersion { 160 | if pub.GetVersion() != v.version { 161 | t.Errorf("ParseData = invalid Version %x in test n°%v with data[%v]%x", pub.GetVersion(), i, len(v.data), v.data) 162 | } 163 | } 164 | if pub.GetConnectionID() != v.connId { 165 | t.Errorf("ParseData = invalid Connection ID %x in test n°%v with data[%v]%x", pub.GetConnectionID(), i, len(v.data), v.data) 166 | } 167 | if pub.GetSequenceNumber() != v.seqNum { 168 | t.Errorf("ParseData = invalid Sequence Number %x in test n°%v with data[%v]%x", pub.GetSequenceNumber(), i, len(v.data), v.data) 169 | } 170 | } else if err == nil { 171 | t.Errorf("ParseData = missing error in test n°%v with data[%v]%x", i, len(v.data), v.data) 172 | } 173 | pub.Erase() 174 | } 175 | } 176 | 177 | func Test_QuicPublicHeader_GetSerializedData(t *testing.T) { 178 | var pub QuicPublicHeader 179 | 180 | data := make([]byte, 19) 181 | for i, v := range tests_quicpublicheader { 182 | if v.positiveTest { 183 | pub.SetPublicResetFlag(v.flagPublicReset) 184 | pub.SetVersionFlag(v.flagVersion) 185 | pub.SetVersion(v.version) 186 | pub.SetConnectionID(v.connId) 187 | pub.SetConnectionIdSize(parsePublicheaderConnectionIdSize[(v.data[0]>>2)&0x0f]) 188 | pub.SetSequenceNumber(v.seqNum) 189 | pub.SetSequenceNumberSize(parsePublicheaderSequenceNumberSize[(v.data[0]>>2)&0x0f]) 190 | s, err := pub.GetSerializedData(data) 191 | if err != nil { 192 | t.Errorf("GetSerializedData = error %s while serialized data in test n°%v", err, i) 193 | } 194 | if s != len(v.data) { 195 | t.Errorf("GetSerializedData = invalid serialized size in test n°%v with data[%v]%x", i, s, data[:s]) 196 | } 197 | if !bytes.Equal(data[:s], v.data) { 198 | t.Errorf("GetSerializedData = invalid serialized data in test n°%v with data[%v]%x", i, s, data[:s]) 199 | } 200 | pub.Erase() 201 | } 202 | } 203 | } 204 | -------------------------------------------------------------------------------- /crypto/gcm.go: -------------------------------------------------------------------------------- 1 | package crypto 2 | 3 | // computeGHash 4 | func (this *AEAD_AES128GCM12) computeGHash(aad, ciphertext []byte) { 5 | 6 | // GHASH(H, A, C) = Xm+n+1 where the variables Xi for i = 0,...,m+n+1 are defined as: 7 | // 8 | // STEP 1: 9 | // X0 = 0 10 | // 11 | // STEP 2: 12 | // for i = 1,...,m−1 13 | // Xi = (Xi-1 xor Ai) * H 14 | // end for 15 | // 16 | // STEP 3: 17 | // Xm = (Xm-1 xor (Am || 0^(128−v)) * H 18 | // 19 | // STEP 4: 20 | // for i = m+1,...,m+n−1 21 | // Xi = (Xi−1 xor Ci−m) * H 22 | // end for 23 | // 24 | // STEP 5: 25 | // Xm+n = (Xm+n-1 xor (Cn||0^(128−u)) * H 26 | // 27 | // STEP 6: 28 | // Xm+n+1 = (Xm+n xor (len(A) || len(C))) * H 29 | 30 | var x0, x1, a0, a1, v0, v1, y0, y1 uint64 31 | var i, j, k, l uint32 32 | 33 | m := uint32(len(aad)) 34 | modm := m & 0xf 35 | v := uint64(m) << 3 36 | m >>= 4 37 | 38 | n := uint32(len(ciphertext)) 39 | modn := n & 0xf 40 | u := uint64(n) << 3 41 | n >>= 4 42 | 43 | // STEP 1: Compute X0 = 0 44 | // --> x0 = x1 = 0 (default uint64 value) 45 | 46 | // STEP 2: Compute X1 to Xm-1 47 | for i = 0; i < m; i++ { 48 | // Compute Ai (= little endian uint128 in two uint64) 49 | a0 = 0 50 | a1 = 0 51 | l = i << 4 52 | for j = 0; j < 8; j++ { 53 | a1 += uint64(aad[l]) << (56 - (j << 3)) 54 | l++ 55 | } 56 | for j = 8; j < 16; j++ { 57 | a0 += uint64(aad[l]) << (56 - ((j - 8) << 3)) 58 | l++ 59 | } 60 | // Compute Xi = (Xi−1 xor Ai) * H 61 | 62 | //x0, x1 = this.multH(x0^a0, x1^a1) 63 | v0 = x0 ^ a0 64 | v1 = x1 ^ a1 65 | x0 = 0 66 | x1 = 0 67 | y0 = this.h0 68 | y1 = this.h1 69 | for l = 0; l < 64; l++ { 70 | // if Yi = 1 then Z = Z xor V 71 | if (y1 & (1 << (63 - l))) > 0 { 72 | x0 ^= v0 73 | x1 ^= v1 74 | } 75 | // if V127 = 0 then V = righshift( V ) else V = rightshift( V ) xor R 76 | if (v0 & 1) == 0 { 77 | v0 >>= 1 78 | if (v1 & 1) == 1 { 79 | v0 |= 0x8000000000000000 80 | } 81 | v1 >>= 1 82 | } else { 83 | v0 >>= 1 84 | if (v1 & 1) == 1 { 85 | v0 |= 0x8000000000000000 86 | } 87 | v1 >>= 1 88 | v1 ^= 0xe100000000000000 89 | v0 ^= 0 90 | } 91 | } 92 | for l = 0; l < 64; l++ { 93 | // if Yi = 1 then Z = Z xor V 94 | if (y0 & (1 << (63 - l))) > 0 { 95 | x0 ^= v0 96 | x1 ^= v1 97 | } 98 | // if V127 = 0 then V = righshift( V ) else V = rightshift( V ) xor R 99 | if (v0 & 1) == 0 { 100 | v0 >>= 1 101 | if (v1 & 1) == 1 { 102 | v0 |= 0x8000000000000000 103 | } 104 | v1 >>= 1 105 | } else { 106 | v0 >>= 1 107 | if (v1 & 1) == 1 { 108 | v0 |= 0x8000000000000000 109 | } 110 | v1 >>= 1 111 | v1 ^= 0xe100000000000000 112 | v0 ^= 0 113 | } 114 | } 115 | } 116 | 117 | // STEP 3: Compute Xm = (Xm-1 xor (Am || 0^(128−v)) * H 118 | // Compute Am (= little endian uint128 in two uint64) 119 | if modm > 0 { 120 | a0 = 0 121 | a1 = 0 122 | if modm < 8 { 123 | k = modm 124 | } else { 125 | k = 8 126 | } 127 | l = m << 4 128 | for j = 0; j < k; j++ { 129 | a1 += uint64(aad[l]) << (56 - (j << 3)) 130 | l++ 131 | } 132 | if modm > 8 { 133 | k = modm 134 | for j = 8; j < k; j++ { 135 | a0 += uint64(aad[l]) << (56 - ((j - 8) << 3)) 136 | l++ 137 | } 138 | } 139 | // Compute Xm = (Xm-1 xor (Am || 0^(128−v)) * H 140 | 141 | // x0, x1 = this.multH(x0^a0, x1^a1) 142 | v0 = x0 ^ a0 143 | v1 = x1 ^ a1 144 | x0 = 0 145 | x1 = 0 146 | y0 = this.h0 147 | y1 = this.h1 148 | for l = 0; l < 64; l++ { 149 | // if Yi = 1 then Z = Z xor V 150 | if (y1 & (1 << (63 - l))) > 0 { 151 | x0 ^= v0 152 | x1 ^= v1 153 | } 154 | // if V127 = 0 then V = righshift( V ) else V = rightshift( V ) xor R 155 | if (v0 & 1) == 0 { 156 | v0 >>= 1 157 | if (v1 & 1) == 1 { 158 | v0 |= 0x8000000000000000 159 | } 160 | v1 >>= 1 161 | } else { 162 | v0 >>= 1 163 | if (v1 & 1) == 1 { 164 | v0 |= 0x8000000000000000 165 | } 166 | v1 >>= 1 167 | v1 ^= 0xe100000000000000 168 | v0 ^= 0 169 | } 170 | } 171 | for l = 0; l < 64; l++ { 172 | // if Yi = 1 then Z = Z xor V 173 | if (y0 & (1 << (63 - l))) > 0 { 174 | x0 ^= v0 175 | x1 ^= v1 176 | } 177 | // if V127 = 0 then V = righshift( V ) else V = rightshift( V ) xor R 178 | if (v0 & 1) == 0 { 179 | v0 >>= 1 180 | if (v1 & 1) == 1 { 181 | v0 |= 0x8000000000000000 182 | } 183 | v1 >>= 1 184 | } else { 185 | v0 >>= 1 186 | if (v1 & 1) == 1 { 187 | v0 |= 0x8000000000000000 188 | } 189 | v1 >>= 1 190 | v1 ^= 0xe100000000000000 191 | v0 ^= 0 192 | } 193 | } 194 | } 195 | 196 | // STEP 4: Compute Xm+1 to Xm+n-1 197 | for i = 0; i < n; i++ { 198 | // Compute Ci (= little endian uint128 in two uint64) 199 | a0 = 0 200 | a1 = 0 201 | l = i << 4 202 | for j = 0; j < 8; j++ { 203 | a1 += uint64(ciphertext[l]) << (56 - (j << 3)) 204 | l++ 205 | } 206 | for j = 8; j < 16; j++ { 207 | a0 += uint64(ciphertext[l]) << (56 - ((j - 8) << 3)) 208 | l++ 209 | } 210 | // Compute Xi = (Xi−1 xor Ci−m) * H 211 | 212 | // x0, x1 = this.multH(x0^a0, x1^a1) 213 | v0 = x0 ^ a0 214 | v1 = x1 ^ a1 215 | x0 = 0 216 | x1 = 0 217 | y0 = this.h0 218 | y1 = this.h1 219 | for l = 0; l < 64; l++ { 220 | // if Yi = 1 then Z = Z xor V 221 | if (y1 & (1 << (63 - l))) > 0 { 222 | x0 ^= v0 223 | x1 ^= v1 224 | } 225 | // if V127 = 0 then V = righshift( V ) else V = rightshift( V ) xor R 226 | if (v0 & 1) == 0 { 227 | v0 >>= 1 228 | if (v1 & 1) == 1 { 229 | v0 |= 0x8000000000000000 230 | } 231 | v1 >>= 1 232 | } else { 233 | v0 >>= 1 234 | if (v1 & 1) == 1 { 235 | v0 |= 0x8000000000000000 236 | } 237 | v1 >>= 1 238 | v1 ^= 0xe100000000000000 239 | v0 ^= 0 240 | } 241 | } 242 | for l = 0; l < 64; l++ { 243 | // if Yi = 1 then Z = Z xor V 244 | if (y0 & (1 << (63 - l))) > 0 { 245 | x0 ^= v0 246 | x1 ^= v1 247 | } 248 | // if V127 = 0 then V = righshift( V ) else V = rightshift( V ) xor R 249 | if (v0 & 1) == 0 { 250 | v0 >>= 1 251 | if (v1 & 1) == 1 { 252 | v0 |= 0x8000000000000000 253 | } 254 | v1 >>= 1 255 | } else { 256 | v0 >>= 1 257 | if (v1 & 1) == 1 { 258 | v0 |= 0x8000000000000000 259 | } 260 | v1 >>= 1 261 | v1 ^= 0xe100000000000000 262 | v0 ^= 0 263 | } 264 | } 265 | } 266 | 267 | // STEP 5: Compute Xm+n = (Xm+n-1 xor (Cn||0^(128−u)) * H 268 | if modn > 0 { 269 | a0 = 0 270 | a1 = 0 271 | // Compute Cn (= little endian uint128 in two uint64) 272 | if modn < 8 { 273 | k = modn 274 | } else { 275 | k = 8 276 | } 277 | l = n << 4 278 | for j = 0; j < k; j++ { 279 | a1 += uint64(ciphertext[l]) << (56 - (j << 3)) 280 | l++ 281 | } 282 | if modn > 8 { 283 | k = modn 284 | for j = 8; j < k; j++ { 285 | a0 += uint64(ciphertext[l]) << (56 - ((j - 8) << 3)) 286 | l++ 287 | } 288 | } 289 | // Compute Xm+n = (Xm+n-1 xor (Cn||0^(128−u)) * H 290 | 291 | // x0, x1 = this.multH(x0^a0, x1^a1) 292 | v0 = x0 ^ a0 293 | v1 = x1 ^ a1 294 | x0 = 0 295 | x1 = 0 296 | y0 = this.h0 297 | y1 = this.h1 298 | for l = 0; l < 64; l++ { 299 | // if Yi = 1 then Z = Z xor V 300 | if (y1 & (1 << (63 - l))) > 0 { 301 | x0 ^= v0 302 | x1 ^= v1 303 | } 304 | // if V127 = 0 then V = righshift( V ) else V = rightshift( V ) xor R 305 | if (v0 & 1) == 0 { 306 | v0 >>= 1 307 | if (v1 & 1) == 1 { 308 | v0 |= 0x8000000000000000 309 | } 310 | v1 >>= 1 311 | } else { 312 | v0 >>= 1 313 | if (v1 & 1) == 1 { 314 | v0 |= 0x8000000000000000 315 | } 316 | v1 >>= 1 317 | v1 ^= 0xe100000000000000 318 | v0 ^= 0 319 | } 320 | } 321 | for l = 0; l < 64; l++ { 322 | // if Yi = 1 then Z = Z xor V 323 | if (y0 & (1 << (63 - l))) > 0 { 324 | x0 ^= v0 325 | x1 ^= v1 326 | } 327 | // if V127 = 0 then V = righshift( V ) else V = rightshift( V ) xor R 328 | if (v0 & 1) == 0 { 329 | v0 >>= 1 330 | if (v1 & 1) == 1 { 331 | v0 |= 0x8000000000000000 332 | } 333 | v1 >>= 1 334 | } else { 335 | v0 >>= 1 336 | if (v1 & 1) == 1 { 337 | v0 |= 0x8000000000000000 338 | } 339 | v1 >>= 1 340 | v1 ^= 0xe100000000000000 341 | v0 ^= 0 342 | } 343 | } 344 | 345 | } 346 | 347 | // STEP 6: Xm+n+1 = (Xm+n xor (len(A) || len(C))) * H 348 | 349 | //x0, x1 = this.multH(x0^u, x1^v) 350 | v0 = x0 ^ u 351 | v1 = x1 ^ v 352 | x0 = 0 353 | x1 = 0 354 | y0 = this.h0 355 | y1 = this.h1 356 | for l = 0; l < 64; l++ { 357 | // if Yi = 1 then Z = Z xor V 358 | if (y1 & (1 << (63 - l))) > 0 { 359 | x0 ^= v0 360 | x1 ^= v1 361 | } 362 | // if V127 = 0 then V = righshift( V ) else V = rightshift( V ) xor R 363 | if (v0 & 1) == 0 { 364 | v0 >>= 1 365 | if (v1 & 1) == 1 { 366 | v0 |= 0x8000000000000000 367 | } 368 | v1 >>= 1 369 | } else { 370 | v0 >>= 1 371 | if (v1 & 1) == 1 { 372 | v0 |= 0x8000000000000000 373 | } 374 | v1 >>= 1 375 | v1 ^= 0xe100000000000000 376 | v0 ^= 0 377 | } 378 | } 379 | for l = 0; l < 64; l++ { 380 | // if Yi = 1 then Z = Z xor V 381 | if (y0 & (1 << (63 - l))) > 0 { 382 | x0 ^= v0 383 | x1 ^= v1 384 | } 385 | // if V127 = 0 then V = righshift( V ) else V = rightshift( V ) xor R 386 | if (v0 & 1) == 0 { 387 | v0 >>= 1 388 | if (v1 & 1) == 1 { 389 | v0 |= 0x8000000000000000 390 | } 391 | v1 >>= 1 392 | } else { 393 | v0 >>= 1 394 | if (v1 & 1) == 1 { 395 | v0 |= 0x8000000000000000 396 | } 397 | v1 >>= 1 398 | v1 ^= 0xe100000000000000 399 | v0 ^= 0 400 | } 401 | } 402 | 403 | // Write GHASH as Little Endian 404 | for i = 0; i < 8; i++ { 405 | this.ghash[i] = byte(x1 >> (56 - (i << 3))) 406 | this.ghash[i+8] = byte(x0 >> (56 - (i << 3))) 407 | } 408 | } 409 | -------------------------------------------------------------------------------- /doc/TailLossProbe.md: -------------------------------------------------------------------------------- 1 | # Tail Loss Probe 2 | 3 | Extracts from __draft-dukkipati-tcpm-tcp-loss-probe-01__ : [https://tools.ietf.org/html/draft-dukkipati-tcpm-tcp-loss-probe-01](https://tools.ietf.org/html/draft-dukkipati-tcpm-tcp-loss-probe-01) 4 | 5 | ----------------------- 6 | 7 | ## Table of Contents 8 | 9 | * [Overview](#overview) 10 | * [Loss Probe algorithm](#algo) 11 | * [Pseudocode](#pseudocode) 12 | * [Example](#example) 13 | * [FACK threshold based recovery](#fack) 14 | 15 | ## Overview 16 | 17 | Retransmission timeouts are detrimental to application latency, especially for short transfers such as Web transactions where timeouts can often take longer than all of the rest of a transaction. 18 | 19 | __The primary cause of retransmission timeouts are lost segments at the tail of transactions.__ 20 | 21 | This document describes an experimental algorithm for TCP to quickly recover lost segments at the end of transactions or when an entire window of data or acknowledgments are lost. 22 | 23 | __Tail Loss Probe (TLP) is a sender-only algorithm that allows the transport to recover tail losses through fast recovery as opposed to lengthy retransmission timeouts:__ 24 | 25 | * If a connection is not receiving any acknowledgments for a certain period of time, TLP transmits the last unacknowledged segment (loss probe). 26 | * In the event of a tail loss in the original transmissions, the acknowledgment from the loss probe triggers SACK/FACK based fast recovery. 27 | 28 | TLP effectively avoids long timeouts and thereby improves TCP performance. 29 | 30 | ## Loss probe algorithm 31 | 32 | __The Loss probe algorithm is designed for a sender to quickly detect tail losses without waiting for an RTO.__ 33 | 34 | We will henceforth use tail loss to generally refer to either drops at the tail end of transactions or a loss of an entire window of data/ACKs. 35 | 36 | * TLP works for senders with SACK enabled and in Open state, i.e. the sender has so far received in-sequence ACKs with no SACK blocks. 37 | 38 | The risk of a sender incurring a timeout is high when the sender has not received any ACKs for a certain portion of time but is unable to transmit any further data either because it is application limited (out of new data to send), receiver window (rwnd) limited, or congestion window (cwnd) limited. For these circumstances, the basic idea of TLP is to transmit probe segments for the specific purpose of eliciting additional ACKs from the receiver. 39 | 40 | The initial idea was to send some form of zero window probe (ZWP) with one byte of new or old data. 41 | The ACK from the ZWP would provide an additional opportunity for a SACK block to detect loss without an RTO. 42 | Additional losses can be detected subsequently and repaired as SACK based fast recovery proceeds. 43 | However, in practice sending a single byte of data turned out to be problematic to implement and more fragile than necessary. 44 | Instead we use a full segment to probe but have to add complexity to compensate for the probe itself masking losses. 45 | 46 | Define probe timeout (__PTO__) to be a timer event indicating that an ACK is overdue on a connection: 47 | 48 | * __SRTT__ = smoothed RTT 49 | * __PTO = max(2 * SRTT, 10ms)__ 50 | * __PTO__ value is adjusted to account for delayed ACK timer when there is only one outstanding segment. 51 | 52 | The basic version of the TLP algorithm transmits one probe segment after a probe timeout if the connection has outstanding unacknowledged data but is otherwise idle, i.e. not receiving any ACKs or is cwnd/rwnd/application limited. 53 | 54 | * The transmitted segment, aka loss probe, can be either a new segment if available and the receive window permits, or a retransmission of the most recently sent segment, i.e., the segment with the highest sequence number. 55 | * When there is tail loss, the ACK from the probe triggers fast recovery. 56 | * In the absence of loss, there is no change in the congestion control or loss recovery state of the connection, apart from any state related to TLP itself. 57 | 58 | __TLP MUST NOT be used for non-SACK connections:__ 59 | 60 | * SACK feedback allows senders to use the algorithm described in section 3 to infer whether any segments were lost. 61 | 62 | ### Pseudocode 63 | 64 | * __FlightSize__: amount of outstanding data in the network as defined in [RFC5681](#https://tools.ietf.org/html/rfc5681) 65 | * __RTO__: The transport's retransmission timeout ( RTO ) is based on measured round-trip times ( RTT ) between the sender and receiver, as specified in [RFC6298](https://tools.ietf.org/html/rfc6298) for TCP 66 | * __PTO__: Probe timeout is a timer event indicating that an ACK is overdue ( PTO <= RTO ) 67 | * __SRTT__: smoothed round-trip time computed like in [RFC6298](https://tools.ietf.org/html/rfc6298) 68 | * __Open state__: the sender has so far received in-sequence ACKs with no SACK blocks, and no other indications (such as retransmission timeout) that a loss may have occurred 69 | * __Consecutive PTOs__: back-to-back PTOs all scheduled for the same tail packets in a flight 70 | * The ( N+1 )st PTO is scheduled after transmitting the probe segment for Nth PTO 71 | 72 | The TLP algorithm works as follows: 73 | 74 | * __(1)__ Schedule PTO after transmission of new data in Open state: 75 | * Check for conditions to schedule PTO outlined in step __2__ below. 76 | * If __FlightSize > 1__ 77 | * then schedule __PTO__ in __max( 2*SRTT, 10ms )__ 78 | * If __FlightSize == 1__ 79 | * __WCDelAckT = 200 ms__, the worst case delayed ACK timer 80 | * then schedule __PTO__ in __max( 2*SRTT, 1.5 * SRTT + WCDelAckT )__ 81 | * If __RTO__ is earlier 82 | * then schedule __PTO__ in __min( RTO, PTO )__ 83 | 84 | A PTO value of 2*SRTT allows a sender to wait long enough to know that an ACK is overdue. Under normal circumstances, i.e. no losses, an ACK typically arrives in one RTT. But choosing PTO to be exactly an RTT is likely to generate spurious probes given that even end-system timings can easily push an ACK to be above an RTT. We chose PTO to be the next integral value of RTT. If RTO is smaller than the computed value for PTO, then a probe is scheduled to be sent at the RTO time. The RTO timer is rearmed at the time of sending the probe, as is shown in Step (__3__) below. This ensures that a PTO is always sent prior to a connection experiencing an RTO. 85 | 86 | * __(2)__ Conditions for scheduling PTO: 87 | * Connection is in Open state. 88 | * Connection is either cwnd limited or application limited. 89 | * Number of consecutive PTOs <= 2. 90 | * Connection is SACK enabled. 91 | 92 | Implementations MAY use one or two consecutive PTOs. 93 | 94 | * __(3)__ When PTO fires: 95 | 96 | * If a new previously unsent segment exists: 97 | * Transmit new segment. 98 | * __FlightSize += SMSS__ ( __cwnd__ remains unchanged ) 99 | * If no new segment exists: 100 | * Retransmit the last segment. 101 | * Increment statistics counter for loss probes. 102 | * If conditions in (__2__) are satisfied: 103 | * Reschedule next __PTO__ 104 | * Else: 105 | * Rearm __RTO__ to fire at epoch __now+RTO__ 106 | 107 | The reason for retransmitting the last segment is so that the ACK will carry SACK blocks and trigger either SACK-based loss recovery [RFC6675](https://tools.ietf.org/html/rfc6675) or FACK threshold based fast recovery [FACK](http://conferences.sigcomm.org/sigcomm/1996/papers/mathis.pdf). On transmission of a TLP, a MIB counter is incremented to keep track of the total number of loss probes sent. 108 | 109 | * __(4)__ During ACK processing: 110 | * Cancel any existing __PTO__ 111 | * If conditions in (__2__) allow: 112 | * Reschedule __PTO__ relative to the ACK receipt time. 113 | 114 | ### Example 115 | 116 | Following is an example of TLP. All events listed are at a TCP sender. 117 | 118 | 1. Sender transmits segments 1-10: 1, 2, 3, ..., 8, 9, 10 119 | * There is no more new data to transmit. 120 | * A PTO is scheduled to fire in 2 RTTs, after the transmission of the 10th segment. 121 | 2. Receives acknowledgements (ACKs) for segments 1-5; segments 6-10 are lost and no ACKs are received 122 | * Note that the sender (re)schedules its PTO timer relative to the last received ACK, which is the ACK for segment 5 in this case. 123 | * The sender sets the PTO interval using the calculation described in step (__1__) of the algorithm. 124 | 3. When PTO fires, sender retransmits segment 10 125 | 4. After an RTT, SACK for packet 10 arrives 126 | * The ACK also carries SACK holes for segments 6, 7, 8 and 9. 127 | * This triggers FACK threshold based recovery. 128 | 5. Connection enters fast recovery and retransmits remaining lost segments. 129 | 130 | ### FACK threshold based recovery 131 | 132 | At the core of TLP is its reliance on FACK threshold based algorithm to invoke Fast Recovery. 133 | 134 | Section 3.1 of the Forward Acknowledgement (FACK) Paper [FACK](http://conferences.sigcomm.org/sigcomm/1996/papers/mathis.pdf) describes an alternate algorithm for triggering fast retransmit, based on the extent of the SACK scoreboard. Its goal is to trigger fast retransmit as soon as the receiver's reassembly queue is larger than the dupack threshold, as indicated by the difference between the forward most SACK block edge and SND.UNA. This algorithm quickly and reliably triggers fast retransmit in the presence of burst losses -- often on the first SACK following such a loss. Such a threshold based algorithm also triggers fast retransmit immediately in the presence of any reordering with extent greater than the dupack threshold. 135 | 136 | FACK threshold based recovery works by introducing a new TCP state variable at the sender called __SND.FACK__. 137 | 138 | * __SND.FACK__ reflects the forward-most data held by the receiver and is updated when a SACK block is received acknowledging data with a higher sequence number than the current value of __SND.FACK__. 139 | * __SND.FACK__ reflects the highest sequence number known to have been received plus one. Note that in non-recovery states, __SND.FACK__ is the same as __SND.UNA__. 140 | 141 | The following snippet is the pseudocode for FACK threshold based recovery. 142 | 143 | * If ( __SND.FACK - SND.UNA__ ) > dupack threshold: 144 | * Invoke Fast Retransmit and Fast Recovery 145 | 146 | -------------------------------------------------------------------------------- /protocol/quicpublicheader.go: -------------------------------------------------------------------------------- 1 | package protocol 2 | 3 | import "errors" 4 | import "encoding/binary" 5 | 6 | /* 7 | 8 | 0 1 2 3 4 8 9 | +--------+--------+--------+--------+--------+--- ---+ 10 | | Public | Connection ID (0, 8, 32, or 64) ... | -> 11 | |Flags(8)| (variable length) | 12 | +--------+--------+--------+--------+--------+--- ---+ 13 | 14 | 9 10 11 12 15 | +--------+--------+--------+--------+ 16 | | Quic Version (32) | -> 17 | | (optional) | 18 | +--------+--------+--------+--------+ 19 | 20 | 13 14 15 16 17 18 19 20 21 | +--------+--------+--------+--------+--------+--------+--------+--------+ 22 | | Sequence Number (8, 16, 32, or 48) |Private | FEC (8)| 23 | | (variable length) |Flags(8)| (opt) | 24 | +--------+--------+--------+--------+--------+--------+--------+--------+ 25 | 26 | 27 | Public flags: 28 | +---+---+---+---+---+---+---+---+ 29 | | 0 | 0 | SeqNum| ConnID|Rst|Ver| 30 | +---+---+---+---+---+---+---+---+ 31 | 32 | FLAGS SeqNum size ConnID size 33 | 34 | 00 00 1 0 35 | 00 01 1 1 36 | 00 10 1 4 37 | 00 11 1 8 38 | 01 00 2 0 39 | 01 01 2 1 40 | 01 10 2 4 41 | 01 11 2 8 42 | 10 00 4 0 43 | 10 01 4 1 44 | 10 10 4 4 45 | 10 11 4 8 46 | 11 00 6 0 47 | 11 01 6 1 48 | 11 10 6 4 49 | 11 11 6 8 50 | 51 | */ 52 | 53 | var parsePublicheaderConnectionIdSize = []int{0, 1, 4, 8, 0, 1, 4, 8, 0, 1, 4, 8, 0, 1, 4, 8} 54 | var parsePublicheaderSequenceNumberSize = []int{1, 1, 1, 1, 2, 2, 2, 2, 4, 4, 4, 4, 6, 6, 6, 6} 55 | 56 | const ( 57 | // Mask and flag for Version in QuicPublicHeader 58 | QUICFLAG_VERSION = 0x01 59 | // Mask and flag for Public reset in QuicPublicHeader 60 | QUICFLAG_PUBLICRESET = 0x02 61 | // Mask and flags for Connection ID size in QuicPublicHeader 62 | QUICMASK_CONNID_SIZE = 0x0C 63 | QUICFLAG_CONNID_64bit = 0x0C 64 | QUICFLAG_CONNID_32bit = 0x08 65 | QUICFLAG_CONNID_8bit = 0x04 66 | QUICFLAG_CONNID_0bit = 0x00 67 | // Mask and flags for Sequence Number size in QuicPublicHeader 68 | QUICMASK_SEQNUM_SIZE = 0x30 69 | QUICFLAG_SEQNUM_48bit = 0x30 70 | QUICFLAG_SEQNUM_32bit = 0x20 71 | QUICFLAG_SEQNUM_16bit = 0x10 72 | QUICFLAG_SEQNUM_8bit = 0x00 73 | ) 74 | 75 | // Public Header field types 76 | type QuicVersion uint32 77 | type QuicConnectionID uint64 78 | type QuicPacketSequenceNumber uint64 79 | 80 | // QuicPublicHeader 81 | type QuicPublicHeader struct { 82 | // Public flags 83 | flagVersion bool 84 | flagPublicReset bool 85 | connIDByteSize int 86 | seqNumByteSize int 87 | // Public Fields 88 | connId QuicConnectionID 89 | version QuicVersion 90 | seqNum QuicPacketSequenceNumber 91 | } 92 | 93 | // Erase 94 | func (this *QuicPublicHeader) Erase() { 95 | this.flagVersion = false 96 | this.flagPublicReset = false 97 | this.connIDByteSize = 0 98 | this.connId = 0 99 | this.version = 0 100 | this.seqNumByteSize = 0 101 | this.seqNum = 0 102 | } 103 | 104 | // ParseData 105 | func (this *QuicPublicHeader) ParseData(data []byte) (size int, err error) { 106 | // Check minimum Public Header size 107 | l := len(data) 108 | if l < 2 { 109 | err = errors.New("QuicPublicHeader.ParseData : too little data (< 2 bytes)") 110 | return 111 | } 112 | // Parse public flags 113 | pf := data[0] 114 | // Parse unused bits 115 | if (pf & 0xc0) != 0 { 116 | err = errors.New("QuicPublicHeader.ParseData : unused bits must be set to 0") 117 | return 118 | } 119 | // Parse Public reset flag 120 | if (pf & QUICFLAG_PUBLICRESET) == QUICFLAG_PUBLICRESET { 121 | this.flagPublicReset = true 122 | // Check minimum Public header size for 64-bit Connection ID 123 | if l < 9 { 124 | err = errors.New("QuicPublicHeader.ParseData : too little data for Public Reset (< 9 bytes)") 125 | return 126 | } 127 | // Parse the 64-bit Connection ID 128 | this.connIDByteSize = 8 129 | this.connId = QuicConnectionID(binary.LittleEndian.Uint64(data[1:])) 130 | size = 9 131 | return 132 | } else { 133 | this.flagPublicReset = false 134 | } 135 | // Parse version flag 136 | if (pf & QUICFLAG_VERSION) == QUICFLAG_VERSION { 137 | this.flagVersion = true 138 | } else { 139 | this.flagVersion = false 140 | } 141 | // Parse Connection ID size in bytes 142 | pf = (pf >> 2) & 0x0f 143 | this.connIDByteSize = parsePublicheaderConnectionIdSize[pf] 144 | // Parse Sequence Number size in bytes 145 | this.seqNumByteSize = parsePublicheaderSequenceNumberSize[pf] 146 | // Check minimum data left based on theoric Public Header size 147 | s := (1 + this.connIDByteSize + this.seqNumByteSize) 148 | if this.flagVersion { 149 | s += 4 150 | } 151 | if l < s { 152 | err = errors.New("QuicPublicHeader.ParseData : too little data left based on public flags") 153 | return 154 | } 155 | // Parse Connection ID 156 | size = 1 157 | switch this.connIDByteSize { 158 | case 0: 159 | this.connId = 0 160 | break 161 | case 1: 162 | this.connId = QuicConnectionID(data[size]) 163 | break 164 | case 4: 165 | this.connId = QuicConnectionID(binary.LittleEndian.Uint32(data[size:])) 166 | break 167 | case 8: 168 | this.connId = QuicConnectionID(binary.LittleEndian.Uint64(data[size:])) 169 | break 170 | } 171 | size += this.connIDByteSize 172 | // Parse QUIC version if needed 173 | if this.flagVersion { 174 | this.version = QuicVersion(binary.LittleEndian.Uint32(data[size:])) 175 | size += 4 176 | } 177 | // Parse Sequence Number 178 | switch this.seqNumByteSize { 179 | case 1: 180 | this.seqNum = QuicPacketSequenceNumber(data[size]) 181 | break 182 | case 2: 183 | this.seqNum = QuicPacketSequenceNumber(binary.LittleEndian.Uint16(data[size:])) 184 | break 185 | case 4: 186 | this.seqNum = QuicPacketSequenceNumber(binary.LittleEndian.Uint32(data[size:])) 187 | break 188 | case 6: 189 | this.seqNum = QuicPacketSequenceNumber(binary.LittleEndian.Uint32(data[size:])) + 190 | (QuicPacketSequenceNumber(binary.LittleEndian.Uint16(data[size+4:])) << 32) 191 | break 192 | } 193 | size += this.seqNumByteSize 194 | if size != s { // This test should be removed at first official release 1.0 195 | err = errors.New("QuicPublicHeader.ParseData : internal error parsed bytes count different from calculated size") // Must be impossible 196 | } 197 | return 198 | } 199 | 200 | // GetSerializedSize 201 | func (this *QuicPublicHeader) GetSerializedSize() (size int) { 202 | size = (1 + this.connIDByteSize + this.seqNumByteSize) 203 | if this.flagVersion { 204 | size += 4 205 | } 206 | return 207 | } 208 | 209 | // GetSerializedData 210 | func (this *QuicPublicHeader) GetSerializedData(data []byte) (size int, err error) { 211 | var pf byte 212 | 213 | // Serialize Public Reset public header 214 | if this.flagPublicReset { 215 | if len(data) < 9 { 216 | err = errors.New("QuicPublicHeader.GetSerializedData : data size too small to contain Public Reset packet") 217 | return 218 | } 219 | // Serialize Public flags 220 | data[0] = QUICFLAG_PUBLICRESET | QUICFLAG_CONNID_64bit 221 | // Serialize Connection ID 222 | binary.LittleEndian.PutUint64(data[1:], uint64(this.connId)) 223 | size = 9 224 | return 225 | } 226 | // Check minimum data size 227 | s := (1 + this.connIDByteSize + this.seqNumByteSize) 228 | if this.flagVersion { 229 | s += 4 230 | } 231 | if s > len(data) { 232 | err = errors.New("QuicPublicHeader.GetSerializedData : data size too small to contain the serialized data") 233 | return 234 | } 235 | // Serialized Public flags, Connection ID, Version and Sequence Number 236 | switch this.connIDByteSize { 237 | case 0: 238 | pf = QUICFLAG_CONNID_0bit 239 | break 240 | case 1: 241 | pf = QUICFLAG_CONNID_8bit 242 | data[1] = byte(this.connId) 243 | break 244 | case 4: 245 | pf = QUICFLAG_CONNID_32bit 246 | binary.LittleEndian.PutUint32(data[1:], uint32(this.connId)) 247 | break 248 | case 8: 249 | pf = QUICFLAG_CONNID_64bit 250 | binary.LittleEndian.PutUint64(data[1:], uint64(this.connId)) 251 | break 252 | } 253 | size = this.connIDByteSize + 1 254 | if this.flagVersion { 255 | pf |= 0x01 256 | binary.LittleEndian.PutUint32(data[size:], uint32(this.version)) 257 | size += 4 258 | } 259 | switch this.seqNumByteSize { 260 | case 1: 261 | pf |= QUICFLAG_SEQNUM_8bit 262 | data[size] = byte(this.seqNum) 263 | break 264 | case 2: 265 | pf |= QUICFLAG_SEQNUM_16bit 266 | binary.LittleEndian.PutUint16(data[size:], uint16(this.seqNum)) 267 | break 268 | case 4: 269 | pf |= QUICFLAG_SEQNUM_32bit 270 | binary.LittleEndian.PutUint32(data[size:], uint32(this.seqNum)) 271 | break 272 | case 6: 273 | pf |= QUICFLAG_SEQNUM_48bit 274 | binary.LittleEndian.PutUint32(data[size:], uint32(this.seqNum)) 275 | binary.LittleEndian.PutUint16(data[size+4:], uint16(this.seqNum>>32)) 276 | break 277 | } 278 | data[0] = pf 279 | size += this.seqNumByteSize 280 | if size != s { // This test should be removed at first official release 1.0 281 | err = errors.New("QuicPublicHeader.GetSerializedData : internal error serialized bytes count different from calculated size") // Must be impossible 282 | } 283 | return 284 | } 285 | 286 | // GetVersionFlag 287 | func (this *QuicPublicHeader) GetVersionFlag() bool { 288 | return this.flagVersion 289 | } 290 | 291 | // SetVersionFlag 292 | func (this *QuicPublicHeader) SetVersionFlag(state bool) { 293 | this.flagVersion = state 294 | } 295 | 296 | // GetVersion 297 | func (this *QuicPublicHeader) GetVersion() QuicVersion { 298 | return this.version 299 | } 300 | 301 | // SetVersion 302 | func (this *QuicPublicHeader) SetVersion(version QuicVersion) { 303 | this.version = version 304 | } 305 | 306 | // GetPublicResetFlag 307 | func (this *QuicPublicHeader) GetPublicResetFlag() bool { 308 | return this.flagPublicReset 309 | } 310 | 311 | // SetPublicResetFlag 312 | func (this *QuicPublicHeader) SetPublicResetFlag(state bool) { 313 | this.flagPublicReset = state 314 | } 315 | 316 | // GetConnectionID 317 | func (this *QuicPublicHeader) GetConnectionID() QuicConnectionID { 318 | return this.connId 319 | } 320 | 321 | // SetConnectionID 322 | func (this *QuicPublicHeader) SetConnectionID(connID QuicConnectionID) { 323 | this.connId = connID 324 | } 325 | 326 | // SetConnectionIdSize 327 | func (this *QuicPublicHeader) SetConnectionIdSize(size int) (err error) { 328 | switch size { 329 | case 0, 1, 4, 8: 330 | this.connIDByteSize = size 331 | return 332 | break 333 | } 334 | return errors.New("QuicPublicHeader.SetConnectionIdSize : invalid size") 335 | } 336 | 337 | // GetSequenceNumber 338 | func (this *QuicPublicHeader) GetSequenceNumber() QuicPacketSequenceNumber { 339 | return this.seqNum 340 | } 341 | 342 | // SetSequenceNumber 343 | func (this *QuicPublicHeader) SetSequenceNumber(seqNum QuicPacketSequenceNumber) { 344 | this.seqNum = seqNum 345 | } 346 | 347 | // SetSequenceNumberSize 348 | func (this *QuicPublicHeader) SetSequenceNumberSize(size int) (err error) { 349 | switch size { 350 | case 1, 2, 4, 6: 351 | this.seqNumByteSize = size 352 | return 353 | break 354 | } 355 | return errors.New("QuicPublicHeader.SetConnectionIdSize : invalid size") 356 | } 357 | -------------------------------------------------------------------------------- /protocol/message.go: -------------------------------------------------------------------------------- 1 | package protocol 2 | 3 | import "encoding/binary" 4 | 5 | // MessageTag is the type definition for message's tag, and tags in tag-value pairs. 6 | type MessageTag uint32 7 | 8 | // MaxNumEntries is the maximum numer of entries supported in a Message. 9 | const MaxMessageTagNumEntries = 128 10 | 11 | // QUIC tag values. 12 | const ( 13 | // Client message tag 14 | TagCHLO = ('C') + ('H' << 8) + ('L' << 16) + ('O' << 24) // Client Hello message tag 15 | 16 | // Server message tag 17 | TagSHLO = ('S') + ('H' << 8) + ('L' << 16) + ('O' << 24) // Server Hello message tag 18 | TagREJ = ('R') + ('E' << 8) + ('J' << 16) + (0 << 24) // Server Rejection message tag 19 | TagSCUP = ('S') + ('C' << 8) + ('U' << 16) + ('P' << 24) // Server Config Update message tag can be used only after handshake complete 20 | 21 | // Public Reset message tag 22 | TagPRST = ('P') + ('R' << 8) + ('S' << 16) + ('T' << 24) // Public Reset message tag 23 | 24 | // Content tags that can be see both on CHLO and REJ 25 | TagVERS = ('V') + ('E' << 8) + ('R' << 16) + ('S' << 24) // Version 26 | TagPAD = ('P') + ('A' << 8) + ('D' << 16) + (0 << 24) // Padding tag 27 | TagSTK = ('S') + ('T' << 8) + ('K' << 16) + (0 << 24) // Source-address Token 28 | 29 | // Client tags in CHLO 30 | TagSNI = ('S') + ('N' << 8) + ('I' << 16) + (0 << 24) // Server Name Indication 31 | TagPDMD = ('P') + ('D' << 8) + ('M' << 16) + ('D' << 24) // Proof demand 32 | TagX509 = ('X') + ('5' << 8) + ('0' << 16) + ('9' << 24) // X.509 certificate, all key types 33 | TagX59R = ('X') + ('5' << 8) + ('9' << 16) + ('R' << 24) // X.509 certificate, RSA keys only 34 | TagCCS = ('C') + ('C' << 8) + ('S' << 16) + (0 << 24) // Common Certificates Sets 35 | TagCCRT = ('C') + ('C' << 8) + ('R' << 16) + ('T' << 24) // Cached Certificates 36 | 37 | // Server tags in REJ 38 | TagSCFG = ('S') + ('C' << 8) + ('F' << 16) + ('G' << 24) // Server Config 39 | TagSNO = ('S') + ('N' << 8) + ('O' << 16) + (0 << 24) // Server Nonce 40 | TagCRT = ('C') + ('R' << 8) + ('T' << 16) + (255 << 24) // Certificate Chain 41 | TagPROF = ('P') + ('R' << 8) + ('O' << 16) + ('F' << 24) // Proof of Authenticity 42 | TagSCID = ('S') + ('C' << 8) + ('I' << 16) + ('D' << 24) // Server Config ID 43 | 44 | TagKEXS = ('K') + ('E' << 8) + ('X' << 16) + ('S' << 24) // Key Exchange algorithms : 45 | TagC255 = ('C') + ('2' << 8) + ('5' << 16) + ('5' << 24) // Curve25519 46 | TagP256 = ('P') + ('2' << 8) + ('5' << 16) + ('6' << 24) // P-256 47 | 48 | TagPUBS = ('P') + ('U' << 8) + ('B' << 16) + ('S' << 24) // List of public values, 24-bit little endian length prefixed, in same order as in KEXS 49 | 50 | TagAEAD = ('A') + ('E' << 8) + ('A' << 16) + ('D' << 24) // Authenticated Encryption algorithms : 51 | TagNULL = ('N') + ('U' << 8) + ('L' << 16) + ('L' << 24) // null algorithm = no encryption with FNV1A-128 12-byte tag 52 | TagAESG = ('A') + ('E' << 8) + ('S' << 16) + ('G' << 24) // AES-GCM with 12-byte tag 53 | TagS20P = ('S') + ('2' << 8) + ('0' << 16) + ('P' << 24) // Salsa20 with Poly1305 54 | 55 | TagORBT = ('O') + ('R' << 8) + ('B' << 16) + ('T' << 24) // Orbit, 8-byte opaque value that identifies strike-register 56 | TagEXPY = ('E') + ('X' << 8) + ('P' << 16) + ('Y' << 24) // Expiry, 64-bit expiry time for server config in UNIX epoch-seconds 57 | TagNONC = ('N') + ('O' << 8) + ('N' << 16) + ('C' << 24) // Client Nonce, 32-bytes consisting of 4 bytes of timestamp(big-endian, UNIX epoch-seconds), 8-bytes of server orbit and 20 bytes of random data 58 | 59 | TagCETV = ('C') + ('E' << 8) + ('T' << 16) + ('V' << 24) // Client Encrypted Tag-Values : 60 | TagCIDK = ('C') + ('I' << 8) + ('D' << 16) + ('K' << 24) // ChannelID key, a pair of 32-bytes big-endian numbers which together specify an (x,y) pair = point on P-256 curve and an ECDSA public key 61 | TagCIDS = ('C') + ('I' << 8) + ('D' << 16) + ('S' << 24) // ChannelID signature, a pair of 32-bytes big-endian numbers which together specify the (r,s) pair of an ECDSA signature of the HKDF input 62 | 63 | TagRREJ = ('R') + ('R' << 8) + ('E' << 16) + ('J' << 24) // Reasons for server sending rejection message tag 64 | TagCADR = ('C') + ('A' << 8) + ('D' << 16) + ('R' << 24) // Client IP address and port 65 | 66 | TagRNON = ('R') + ('N' << 8) + ('O' << 16) + ('N' << 24) // Public reset nonce proof 67 | TagRSEQ = ('R') + ('S' << 8) + ('E' << 16) + ('Q' << 24) // Rejected sequence number 68 | 69 | TagCOPT = ('C') + ('O' << 8) + ('P' << 16) + ('T' << 24) // Connection Options 70 | TagICSL = ('I') + ('C' << 8) + ('S' << 16) + ('L' << 24) // Idle connection state lifetime 71 | TagSCLS = ('S') + ('C' << 8) + ('L' << 16) + ('S' << 24) // Silently close on timeout 72 | TagMSPC = ('M') + ('S' << 8) + ('P' << 16) + ('C' << 24) // Max streams per connection 73 | TagIRTT = ('I') + ('R' << 8) + ('T' << 16) + ('T' << 24) // Estimated initial RTT in us 74 | TagSWND = ('S') + ('W' << 8) + ('N' << 16) + ('D' << 24) // Server’s Initial congestion window 75 | TagSFCW = ('S') + ('F' << 8) + ('C' << 16) + ('W' << 24) // Initial stream flow control receive window 76 | TagCFCW = ('C') + ('F' << 8) + ('C' << 16) + ('W' << 24) // Initial session/connection flow control receive window 77 | 78 | // new Tag = '' + ('' << 8) + ('' << 16) + ('' << 24) // 79 | ) 80 | 81 | // A Message that contains message tag value and associated tag-values pairs. 82 | type Message struct { 83 | msgTag MessageTag 84 | tags []MessageTag 85 | values [][]byte 86 | } 87 | 88 | // NewMessage is a Message factory. 89 | // 90 | // Only TagCHLO, TagREJ, TagSHLO and TagSCUP are valids 'messageTag' values. 91 | // 92 | // 'tags' and 'values' must have the same length, and this length must be less or equal than 'MaxNumEntries' value. 93 | // 94 | // NewMessage returns a nil value in case of invalid inputs. 95 | func NewMessage(messageTag MessageTag) *Message { 96 | switch messageTag { 97 | case TagCHLO, TagREJ, TagSHLO, TagSCUP, TagPRST: 98 | return &Message{ 99 | msgTag: messageTag} 100 | } 101 | return nil 102 | } 103 | 104 | // GetMessageTag return the message tag of the Message. 105 | func (this *Message) GetMessageTag() MessageTag { 106 | return this.msgTag 107 | } 108 | 109 | // IsMessageTag return true if this Message is of the requested tag type, and return false otherwise. 110 | func (this *Message) IsMessageTag(tag MessageTag) bool { 111 | return this.msgTag == tag 112 | } 113 | 114 | // GetNumEntries return the number of entries in the Message. 115 | func (this *Message) GetNumEntries() uint16 { 116 | return uint16(len(this.tags)) 117 | } 118 | 119 | // ContainsTag method return 'true' and the associated tag value []byte if the message contains the requested tag, otherwise returns 'false' and nil. 120 | func (this *Message) ContainsTag(t MessageTag) (bool, []byte) { 121 | for i, v := range this.tags { 122 | if v == t { 123 | return true, this.values[i] 124 | } 125 | } 126 | return false, nil 127 | } 128 | 129 | // UpdateTagValue tries to overwrite the tag value pair in the Message and returns true if tag does already present, and false otherwise. 130 | func (this *Message) UpdateTagValue(tag MessageTag, value []byte) bool { 131 | // Try to overwrite the value if the tag already exist 132 | for i, v := range this.tags { 133 | if v == tag { 134 | this.values[i] = value 135 | return true 136 | } 137 | } 138 | return false 139 | } 140 | 141 | // AddTagValue only adds the tag value pair in the Message if tag does not already exist, and return false without adding it otherwise. 142 | func (this *Message) AddTagValue(tag MessageTag, value []byte) bool { 143 | // Verify that tag is not present. 144 | if res, _ := this.ContainsTag(tag); res { 145 | return false 146 | } 147 | // Append the tag value pair 148 | this.tags = append(this.tags, tag) 149 | this.values = append(this.values, value) 150 | return true 151 | } 152 | 153 | // GetSerializeSize returns the size in byte of the binary version of the Message. 154 | func (this *Message) GetSerializeSize() uint32 { 155 | var l uint32 156 | 157 | for _, v := range this.values { 158 | l += uint32(len(v)) 159 | } 160 | return uint32(len(this.tags))*8 + l + 8 161 | } 162 | 163 | // GetSerialize returns []byte containing the binary serialization of the Message. 164 | func (this *Message) GetSerialize() []byte { 165 | var offset, endoffset uint32 166 | 167 | msg := make([]byte, this.GetSerializeSize()) 168 | // Order the pairs by tag value 169 | if len(this.tags) > 1 { 170 | haveswap := true 171 | for haveswap { 172 | haveswap = false 173 | for i := 0; i < len(this.tags)-1; i++ { 174 | if this.tags[i] > this.tags[i+1] { 175 | // Swap the two 176 | t := this.tags[i] 177 | v := this.values[i] 178 | this.tags[i] = this.tags[i+1] 179 | this.tags[i+1] = t 180 | this.values[i] = this.values[i+1] 181 | this.values[i+1] = v 182 | haveswap = true 183 | } 184 | } 185 | } 186 | } 187 | // Write the message tag 188 | binary.LittleEndian.PutUint32(msg, uint32(this.msgTag)) 189 | offset += 4 190 | // Write the number of tags 191 | binary.LittleEndian.PutUint16(msg[offset:], uint16(len(this.tags))) 192 | offset += 2 193 | // Write the padding 194 | // padding = 0x0000 195 | offset += 2 196 | // Write the tag/offset pairs 197 | for i, t := range this.tags { 198 | // Write the tag 199 | binary.LittleEndian.PutUint32(msg[offset:], uint32(t)) 200 | offset += 4 201 | // Write the value end offset 202 | endoffset += uint32(len(this.values[i])) 203 | binary.LittleEndian.PutUint32(msg[offset:], endoffset) 204 | offset += 4 205 | } 206 | // Write the values pairs 207 | for _, v := range this.values { 208 | copy(msg[offset:], v) 209 | offset += uint32(len(v)) 210 | } 211 | return msg 212 | } 213 | 214 | // IsValid verifies that the message type associated tag-value pairs are valids and returns true in that case, otherwise returns false. 215 | func (this *Message) IsValid() bool { 216 | if len(this.tags) != len(this.values) { 217 | return false 218 | } 219 | if len(this.tags) > MaxMessageTagNumEntries { 220 | return false 221 | } 222 | switch this.msgTag { 223 | case TagCHLO, TagREJ, TagSHLO, TagSCUP: 224 | return true 225 | } 226 | return false 227 | } 228 | 229 | // IsValidCHLO verifies that CHLO associated tag-value pairs are valids and returns true in that case, otherwise returns false. 230 | func (this *Message) IsValidCHLO() bool { 231 | if this.msgTag != TagCHLO { 232 | return false 233 | } 234 | // TODO: add more tests 235 | return true 236 | } 237 | 238 | // IsValidREJ verifies that REJ associated tag-value pairs are valids and returns true in that case, otherwise returns false. 239 | func (this *Message) IsValidREJ() bool { 240 | if this.msgTag != TagREJ { 241 | return false 242 | } 243 | // TODO: add more tests 244 | return true 245 | } 246 | 247 | // IsValidSHLO verifies that SHLO associated tag-value pairs are valids and returns true in that case, otherwise returns false. 248 | func (this *Message) IsValidSHLO() bool { 249 | if this.msgTag != TagSHLO { 250 | return false 251 | } 252 | // TODO: add more tests 253 | return true 254 | } 255 | 256 | // IsValidSCUP verifies that SCUP type associated tag-value pairs are valids and returns true in that case, otherwise returns false. 257 | func (this *Message) IsValidSCUP() bool { 258 | if this.msgTag != TagSCUP { 259 | return false 260 | } 261 | // TODO: add more tests 262 | return true 263 | } 264 | -------------------------------------------------------------------------------- /doc/HKDF.md: -------------------------------------------------------------------------------- 1 | # HMAC-based Key Derivation Function (HKDF) 2 | 3 | Extracts from __RFC5869__: [https://tools.ietf.org/html/rfc5869](https://tools.ietf.org/html/rfc5869) 4 | 5 | ------------------------ 6 | 7 | ## Table of Contents 8 | 9 | * [Introduction](#introduction) 10 | * [HMAC-based Key Derivation Function (HKDF)](#hkdf) 11 | * [Step 1: Extract](#extract) 12 | * [Step 2: Expand](#expand) 13 | * [Notes to HKDF Users](#notes) 14 | * [To Salt or not to Salt](#salt) 15 | * [The ’info’ Input to HKDF](#info) 16 | * [To Skip or not to Skip](#skip) 17 | * [The Role of Independence](#independence) 18 | 19 | ## Introduction 20 | 21 | A key derivation function (KDF) is a basic and essential component of cryptographic systems. Its goal is to take some source of initial 22 | keying material and derive from it one or more cryptographically strong secret keys. This document specifies a simple HMAC-based [HMAC] KDF, named HKDF, which can be used as a building block in various protocols and applications, and is already used in several IETF protocols, including __IKEv2__, __PANA__, and __EAP-AKA__. 23 | 24 | 25 | HKDF follows the "extract-then-expand" paradigm, where the KDF logically consists of two modules: 26 | 27 | 1. The first stage __takes the input keying material and "extracts" from it a fixed-length pseudorandom key K__. 28 | 2. The second stage __"expands" the key K into several additional pseudorandom keys__ (=the output of the KDF). 29 | 30 | In many applications, the input keying material is not necessarily distributed uniformly, and the attacker may have some partial knowledge about it (for example, a Diffie-Hellman value computed by a key exchange protocol) or even partial control of it (as in some entropy-gathering applications). 31 | 32 | * Thus, the goal of the "extract" stage is to "concentrate" the possibly dispersed entropy of the input keying material into a short, but cryptographically strong, pseudorandom key. 33 | * The second stage "expands" the pseudorandom key to the desired length; the number and lengths of the output keys depend on the specific cryptographic algorithms for which the keys are needed. 34 | 35 | In some applications, the input may already be a good pseudorandom key; in these cases, the "extract" stage is not necessary, and the "expand" part can be used alone. 36 | 37 | ## HMAC-based Key Derivation Function (HKDF) 38 | 39 | HMAC-Hash denotes the HMAC function instantiated with hash function ’Hash’. HMAC always has two arguments: 40 | 41 | 1. the first is a key 42 | 2. and the second an input (or message). 43 | 44 | (Note that in the extract step, IKM is used as the HMAC input, not as the HMAC key.) 45 | 46 | * When the message is composed of several elements we use concatenation (denoted __|__ ) in the second argument 47 | * for example: HMAC(K, elem1 | elem2 | elem3) 48 | 49 | ### Step 1: Extract 50 | 51 | ``` 52 | HKDF-Extract(salt, IKM) -> PRK 53 | ``` 54 | 55 | * __Options__ 56 | * Hash a hash function 57 | * HashLen denotes the length of the hash function output in octets 58 | * __Inputs__ 59 | * salt optional salt value (a non-secret random value); if not provided, it is set to a string of HashLen zeros. 60 | * IKM input keying material 61 | * __Output__ 62 | * PRK a pseudorandom key (of HashLen octets) 63 | 64 | The output PRK is calculated as follows: 65 | 66 | ``` 67 | PRK = HMAC-Hash(salt, IKM) 68 | ``` 69 | 70 | ### Step 2: Expand 71 | 72 | ``` 73 | HKDF-Expand(PRK, info, L) -> OKM 74 | ``` 75 | 76 | * __Options__ 77 | * Hash a hash function 78 | * HashLen denotes the length of the hash function output in octets 79 | * __Inputs__ 80 | * PRK a pseudorandom key of at least HashLen octets (usually, the output from the extract step) 81 | * info optional context and application specific information (can be a zero-length string) 82 | * L length of output keying material in octets (<= 255*HashLen) 83 | * __Output__ 84 | * OKM output keying material (of L octets) 85 | 86 | The output OKM is calculated as follows: 87 | 88 | ``` 89 | N = ceil(L/HashLen) 90 | T = T(1) | T(2) | T(3) | ... | T(N) 91 | OKM = first L octets of T 92 | 93 | where: 94 | T(0) = empty string (zero length) 95 | T(1) = HMAC-Hash(PRK, T(0) | info | 0x01) 96 | T(2) = HMAC-Hash(PRK, T(1) | info | 0x02) 97 | T(3) = HMAC-Hash(PRK, T(2) | info | 0x03) 98 | ... 99 | ``` 100 | 101 | (where the constant concatenated to the end of each T(n) is a single octet.) 102 | 103 | ## Notes to HKDF Users 104 | 105 | This section contains a set of guiding principles regarding the use of HKDF. 106 | 107 | A much more extensive account of such principles and design rationale can be found in ["Cryptographic Extraction and Key Derivation: The HKDF Scheme"](http://eprint.iacr.org/2010/264) 108 | 109 | ### To Salt or not to Salt 110 | 111 | * HKDF is defined to operate with and without random salt. 112 | 113 | This is done to accommodate applications where a salt value is not available. We stress, however, that the use of salt adds significantly to the strength of HKDF, ensuring independence between different uses of the hash function, supporting "source-independent" extraction, and strengthening the analytical results that back the HKDF design. 114 | 115 | * Random salt differs fundamentally from the initial keying material in two ways: 116 | 1. it is non-secret 117 | 2. it can be re-used. 118 | 119 | As such, salt values are available to many applications. For example, a pseudorandom number generator (PRNG) that continuously produces outputs by applying HKDF to renewable pools of entropy (e.g., sampled system events) can fix a salt value and use it for multiple applications of HKDF without having to protect the secrecy of the salt. 120 | 121 | In a different application domain, a key agreement protocol deriving cryptographic keys from a Diffie-Hellman exchange can derive a salt value from public nonces exchanged and authenticated between communicating parties as part of the key agreement (this is the approach taken in __IKEv2__). 122 | 123 | * __Ideally, the salt value is a random (or pseudorandom) string of the length HashLen__ 124 | * Yet, even a salt value of less quality (shorter in size or with limited entropy) may still make a significant contribution to the security of the output keying material. 125 | * Designers of applications are therefore encouraged to provide salt values to HKDF if such values can be obtained by the application. 126 | * __Some applications may even have a secret salt value available for use__; in such a case, HKDF provides an even stronger security guarantee. 127 | * An example of such application is __IKEv1__ in its "public-key encryption mode", where the "salt" to the extractor is computed from nonces that are secret; similarly, the pre-shared mode of __IKEv1__ uses a secret salt derived from the pre-shared key. 128 | 129 | ### The ’info’ Input to HKDF 130 | 131 | While the info value is optional in the definition of HKDF, it is often of great importance in applications. Its main objective is to bind the derived key material to application- and context-specific information. 132 | 133 | For example, info may contain a protocol number, algorithm identifiers, user identities, etc. 134 | 135 | * In particular, it may prevent the derivation of the same keying material for different contexts (when the same IKM is used in such different contexts). 136 | * It may also accommodate additional inputs to the key expansion part, if so desired (e.g., an application may want to bind the key material to its length L, thus making L part of the info field). 137 | * There is one technical requirement from info: it should be independent of the input key material value IKM. 138 | 139 | ### To Skip or not to Skip 140 | 141 | In some applications, the input key material IKM may already be present as a cryptographically strong key (for example, the premaster secret in TLS RSA cipher suites would be a pseudorandom string, except for the first two octets). In this case, one can skip the extract part and use IKM directly to key HMAC in the expand step. On the other hand, applications may still use the extract part for the sake of compatibility with the general case. In particular, if IKM is random (or pseudorandom) but longer than an HMAC key, the extract step can serve to output a suitable HMAC key (in the case of HMAC this shortening via the extractor is not strictly necessary since HMAC is defined to work with long keys too). Note, however, that if the IKM is a Diffie-Hellman value, as in the case of TLS with Diffie-Hellman, then the extract part SHOULD NOT be skipped. Doing so would result in using the Diffie-Hellman value g^{xy} itself (which is NOT a uniformly random or pseudorandom string) as the key PRK for HMAC. 142 | 143 | Instead, HKDF should apply the extract step to g^{xy} (preferably with a salt value) and use the resultant PRK as a key to HMAC in the expansion part. 144 | 145 | In the case where the amount of required key bits, L, is no more than HashLen, one could use PRK directly as the OKM. This, however, is NOT RECOMMENDED, especially because it would omit the use of info as part of the derivation process (and adding info as an input to the extract step is not advisable). 146 | 147 | ### The Role of Independence 148 | 149 | The analysis of key derivation functions assumes that the IKM comes from some source modeled as a probability distribution over bit streams of a certain length (e.g., streams produced by an entropy pool, values derived from Diffie-Hellman exponents chosen at random, etc.); each instance of IKM is a sample from that distribution. 150 | 151 | * A major goal of key derivation functions is to ensure that, when applying the KDF to any two values IKM and IKM’ sampled from the (same) source distribution, the resultant keys OKM and OKM’ are essentially independent of each other (in a statistical or computational sense). 152 | * To achieve this goal, it is important that inputs to KDF are selected from appropriate input distributions and also that inputs are chosen independently of each other (technically, it is necessary that each sample will have sufficient entropy, even when conditioned on other inputs to KDF). 153 | * Independence is also an important aspect of the salt value provided to a KDF. 154 | * While there is no need to keep the salt secret, and the same salt value can be used with multiple IKM values, it is assumed that salt values are independent of the IKM. In particular, an application needs to make sure that salt values are not chosen or manipulated by an attacker. 155 | * As an example, consider the case (as in __IKE__) where the salt is derived from nonces supplied by the parties in a key exchange protocol. Before the protocol can use such salt to derive keys, it needs to make sure that these nonces are authenticated as coming from the legitimate parties rather than selected by the attacker (in IKE, for example this authentication is an integral part of the authenticated Diffie-Hellman exchange). 156 | 157 | -------------------------------------------------------------------------------- /doc/CUBIC.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # CUBIC Congestion Control 4 | 5 | Extracts from __draft-rhee-tcpm-cubic-02__: [https://tools.ietf.org/html/draft-rhee-tcpm-cubic-02](https://tools.ietf.org/html/draft-rhee-tcpm-cubic-02) 6 | 7 | --------------------- 8 | 9 | ## Table of Content 10 | 11 | * [Window growth function](#windowgrowth) 12 | * [TCP-friendly region](#tcpfriendly) 13 | * [Concave region](#concaveregion) 14 | * [Convex region](#convexregion) 15 | * [Multiplicative decrease](#multiplicationdecrease) 16 | * [Fast Convergence](#fastconvergence) 17 | * [Discussion](#discussion) 18 | * [Fairness to standard TCP](#fairnesstotcp) 19 | 20 | ## Window growth function 21 | 22 | CUBIC maintains the acknowledgment (ACK) clocking of Standard TCP by increasing congestion window only at the reception of ACK. 23 | The protocol does not make any change to the fast recovery and retransmit of TCP-NewReno [RFC3782] and TCP-SACK [RFC2018]. 24 | During congestion avoidance after fast recovery, CUBIC changes the window update algorithm of Standard TCP. 25 | Suppose that W_max is the window size before the window is reduced in the last fast retransmit and recovery. 26 | 27 | The window growth function of CUBIC uses the following function: 28 | 29 | * _Equation 1:_ 30 | * __W(t) = C*(t-K)^3 + W_max__ 31 | 32 | Where C is a constant fixed to determine the aggressiveness of window growth in high BDP networks, t is the elapsed time from the last window eduction, and K is the time period that the above function takes to increase W to W_max when there is no further loss event and is calculated by using the following equation: 33 | 34 | * _Equation 2:_ 35 | * __K = cubic_root(W_max*beta/C)__ 36 | 37 | where beta is the multiplication decrease factor. We discuss how we set C in the next Section in more details. 38 | 39 | Upon receiving an ACK during congestion avoidance, CUBIC computes the window growth rate during the next RTT period using _Equation 1_. It sets W(t+RTT) as the candidate target value of congestion window. 40 | 41 | Suppose that the current window size is cwnd. Depending on the value of cwnd, CUBIC runs in three different modes. 42 | 43 | 1. __Concave region__ : if cwnd is less than W_max 44 | 2. __TCP friendly region__ : if cwnd is less than the window size that Standard TCP would reach at time t after the last loss event (we describe below how to determine this window size of Standard TCP in term of time t) 45 | 3. __Convex region__ : if cwnd is larger than W_max 46 | 47 | ![CUBIC window growth function](../img/CUBIC.png) 48 | Below, we describe the exact actions taken by CUBIC in each region. 49 | 50 | ### TCP-friendly region 51 | 52 | When receiving an ACK in congestion avoidance, we first check whether the protocol is in the TCP region or not. This is done as follows. We can analyze the window size of Standard TCP in terms of the elapsed time t. Using a simple analysis in [FHP00], we can analyze the average window size of additive increase and multiplicative decrease (AIMD) with an additive factor alpha and a multiplicative factor beta to be the following function: 53 | 54 | * _Equation 3:_ 55 | * __(alpha/2 * (2-beta)/beta * 1/p)^0.5__ 56 | 57 | By the same analysis, the average window size of Standard TCP with alpha=1 and beta=0.5 is (3/2 *1/p) ^ 0.5. 58 | 59 | Thus, for _Equation 3_ to be the same as that of Standard TCP, alpha must be equal to 3*beta/(2-beta). 60 | As Standard TCP increases its window by alpha per RTT, we can get the window size of Standard TCP in terms of the elapsed time t as follows: 61 | 62 | * _Equation 4:_ 63 | * __W_tcp(t) = W_max*(1-beta) + 3 * beta/(2-beta) * t/RTT__ 64 | 65 | 66 | If cwnd is less than W_tcp(t), then the protocol is in the TCP friendly region: 67 | 68 | * In this region __cwnd SHOULD be set to W_tcp(t) at each reception of ACK__. 69 | 70 | ### Concave region 71 | 72 | When receiving an ACK in congestion avoidance, if the protocol is not in the TCP-friendly region and cwnd is less than W_max, then the protocol is in the concave region. 73 | 74 | * In this region, cwnd MUST be incremented by __(W(t+RTT) - cwnd)/cwnd__. 75 | 76 | ### Convex region 77 | 78 | When the window size of CUBIC is larger than W_max, it passes the plateau of the cubic function after which CUBIC follows the convex profile of the cubic function. Since cwnd is larger than the previous saturation point W_max, this indicates that the network conditions might have been perturbed since the last loss event, possibly implying more available bandwidth after some flow departures. Since the Internet is highly asynchronous, some amount of perturbation is always possible without causing a major change in available bandwidth. In this phase, CUBIC is being very careful by very slowly increasing its window size. The convex profile ensures that the window increases very slowly at the beginning and gradually increases its growth rate. We also call this phase as the maximum probing phase since CUBIC is searching for a new W_max. 79 | 80 | * In this region, __cwnd MUST be incremented by (W(t+RTT) - cwnd)/cwnd for each received ACK__. 81 | 82 | ## Multiplicative decrease 83 | 84 | * When a packet loss occurs, CUBIC reduces its window size by a factor of beta 85 | * Parameter __beta SHOULD be set to 0.2__ 86 | 87 | ``` 88 | W_max = cwnd // remember the window size before reduction 89 | cwnd = cwnd * (1-beta) // window reduction 90 | ``` 91 | 92 | A side effect of setting beta to a smaller value than 0.5 is slower convergence. We believe that while a more adaptive setting of beta could result in faster convergence, it will make the analysis of the protocol much harder. This adaptive adjustment of beta is an item for the next version of CUBIC. 93 | 94 | ## Fast convergence 95 | 96 | To improve the convergence speed of CUBIC, we add a heuristic in the protocol. When a new flow joins the network, existing flows in the network need to give up their bandwidth shares to allow the flow some room for growth if the existing flows have been using all the bandwidth of the network. To increase this release of bandwidth by existing flows, the following mechanism called fast convergence SHOULD be implemented. 97 | 98 | With fast convergence, when a loss event occurs, before a window reduction of congestion window, a flow remembers the last value of W_max before it updates W_max for the current loss event. Let us call the last value of W_max to be W_last_max. 99 | 100 | ``` 101 | if (W_max < W_last_max) { // check downward trend, 102 | W_last_max = W_max // remember the last W_max. 103 | W_max = W_max*(2-beta)/2 // further reduce W_max. 104 | } else // check upward trend. 105 | W_last_max = W_max // remember the last W_max. 106 | ``` 107 | 108 | This allows W_max to be slightly less than the original W_max. Since flows spend most of time around their W_max, flows with larger bandwidth shares tend to spend more time around the plateau allowing more time for flows with smaller shares to increase their windows. 109 | 110 | ## Discussion 111 | 112 | With a deterministic loss model where the number of packets between two successive lost events is always 1/p, CUBIC always operates with the concave window profile which greatly simplifies the performance analysis of CUBIC. 113 | 114 | The average window size of CUBIC can be obtained by the following function: 115 | 116 | * _Equation 5:_ 117 | * __(C*(4-beta)/4/beta)^0.25 * (RTT ^ 0.75) / (p ^ 0.75)__ 118 | 119 | With beta set to 0.2, the above formula is reduced to: 120 | 121 | * _Equation 6:_ 122 | * __(C*3.8/0.8)^0.25 * (RTT ^ 0.75) / (p ^ 0.75)__ 123 | 124 | We will determine the value of C in the following subsection using _Equation 6_. 125 | 126 | ### Fairness to standard TCP 127 | 128 | In environments where standard TCP is able to make reasonable use of the available bandwidth, CUBIC does not significantly change this state. 129 | 130 | Standard TCP performs well in the following two types of networks: 131 | 132 | 1. networks with a small bandwidth-delay product (BDP). 133 | 2. networks with a short RTT, but not necessarily a small BDP 134 | 135 | CUBIC is designed to behave very similarly to standard TCP in the above two types of networks. 136 | 137 | The following two tables show the average window size of standard TCP, HSTCP, and CUBIC. 138 | 139 | * The average window size of standard TCP and HSTCP is from [RFC3649]. 140 | * The average window size of CUBIC is calculated by using _Equation 6_ and CUBIC TCP friendly mode for three different values of C. 141 | 142 | _Response function of standard TCP, HSTCP, and CUBIC in networks with __RTT = 100ms__. 143 | The average window size W is in MSS-sized segments._ 144 | 145 | ``` 146 | +----------+-------+--------+-------------+-------------+-----------+ 147 | | Loss | TCP | HSTCP | CUBIC | CUBIC | CUBIC | 148 | | Rate P | | | (C=0.04) | (C=0.4) | (C=4) | 149 | +----------+-------+--------+-------------+-------------+-----------+ 150 | | 10^-2 | 12 | 12 | 12 | 12 | 12 | 151 | | | | | | | | 152 | | 10^-3 | 38 | 38 | 38 | 38 | 66 | 153 | | | | | | | | 154 | | 10^-4 | 120 | 263 | 120 | 209 | 371 | 155 | | | | | | | | 156 | | 10^-5 | 379 | 1795 | 660 | 1174 | 2087 | 157 | | | | | | | | 158 | | 10^-6 | 1200 | 12279 | 3713 | 6602 | 11740 | 159 | | | | | | | | 160 | | 10^-7 | 3795 | 83981 | 20878 | 37126 | 66022 | 161 | | | | | | | | 162 | | 10^-8 | 12000 | 574356 | 117405 | 208780 | 371269 | 163 | +----------+-------+--------+-------------+-------------+-----------+ 164 | ``` 165 | 166 | _Response function of standard TCP, HSTCP, and CUBIC in networks with __RTT = 10ms__. 167 | The average window size W is in MSS-sized segments._ 168 | 169 | ``` 170 | +--------+-----------+-----------+------------+-----------+---------+ 171 | | Loss | Average | Average | CUBIC | CUBIC | CUBIC | 172 | | Rate P | TCP W | HSTCP W | (C=0.04) | (C=0.4) | (C=4) | 173 | +--------+-----------+-----------+------------+-----------+---------+ 174 | | 10^-2 | 12 | 12 | 12 | 12 | 12 | 175 | | | | | | | | 176 | | 10^-3 | 38 | 38 | 38 | 38 | 38 | 177 | | | | | | | | 178 | | 10^-4 | 120 | 263 | 120 | 120 | 120 | 179 | | | | | | | | 180 | | 10^-5 | 379 | 1795 | 379 | 379 | 379 | 181 | | | | | | | | 182 | | 10^-6 | 1200 | 12279 | 1200 | 1200 | 2087 | 183 | | | | | | | | 184 | | 10^-7 | 3795 | 83981 | 3795 | 6603 | 11740 | 185 | | | | | | | | 186 | | 10^-8 | 12000 | 574356 | 20878 | 37126 | 66022 | 187 | +--------+-----------+-----------+------------+-----------+---------+ 188 | ``` 189 | 190 | * Both tables show that CUBIC with any of these three C values is more friendly to TCP than HSTCP, especially in networks with a short RTT where TCP performs reasonably well. For example, in a network with RTT = 10ms and p=10^(-6), TCP has an average window of 1200 packets. 191 | * If the packet size is 1500 bytes, then TCP can achieve an average rate of 1.44 Gbps. In this case, CUBIC with C=0.04 or C=0.4 achieves exactly the same rate as Standard TCP, whereas HSTCP is about ten times more aggressive than Standard TCP. 192 | 193 | We can see that C determines the aggressiveness of CUBIC in competing with other protocols for the bandwidth. 194 | 195 | CUBIC is more friendly to the Standard TCP, if the value of C is lower. However, we do not recommend to set C to a very low value like 0.04, since CUBIC with a low C cannot efficiently use the bandwidth in long RTT and high bandwidth networks. Based on these observations, we find C=0.4 gives a good balance between TCP-friendliness and aggressiveness of window growth. Therefore, __C SHOULD be set to 0.4__. 196 | 197 | With C set to 0.4, _Equation 6_ is reduced to: 198 | 199 | * _Equation 7:_ 200 | * __1.17 * (RTT ^ 0.75) / (p ^ 0.75)__ 201 | 202 | 203 | -------------------------------------------------------------------------------- /crypto/chacha20.go: -------------------------------------------------------------------------------- 1 | package crypto 2 | 3 | import "github.com/romain-jacotin/quic/protocol" 4 | import "errors" 5 | 6 | // ChaCha20 algorithm and test vector from https://tools.ietf.org/html/rfc7539 7 | 8 | type ChaCha20Cipher struct { 9 | grid [16]uint32 10 | buffer [64]byte 11 | } 12 | 13 | // Setup initialize the ChaCha20 grid based on the key, nonce and block counter. 14 | func NewChaCha20Cipher(key, nonce []byte, counter uint32) (*ChaCha20Cipher, error) { 15 | // ChaCha20 uses a 4 x 4 grid of uint32: 16 | // 17 | // +------------+------------+------------+------------+ 18 | // | const 0 | constant 1 | constant 2 | constant 3 | 19 | // | 0x61707865 | 0x3320646e | 0x79622d32 | 0x6b206574 | 20 | // +------------+------------+------------+------------+ 21 | // | key 4 | key 5 | key 6 | key 7 | 22 | // +------------+------------+------------+------------+ 23 | // | key 8 | key 9 | key 10 | key 11 | 24 | // +------------+------------+------------+------------+ 25 | // | block 12 | nonce 13 | nonce 14 | nonce 15 | 26 | // +------------+------------+------------+------------+ 27 | // 28 | // The first four input words are constants: (0x61707865, 0x3320646e, 0x79622d32, 0x6b206574). 29 | // 30 | // Input words 4 through 11 are taken from the 256-bit key by reading the bytes in little-endian order, in 4-byte chunks. 31 | // 32 | // Input words 12 is a block counter. The block counter word is initially zero for 33 | // 34 | // Lastly, words 13, 14 and 15 are taken from an 12-byte nonce, again by reading the bytes in little-endian order, in 4-byte chunks. 35 | 36 | if len(key) < 32 { 37 | return nil, errors.New("ChaCha20.Setup: key must be 32 bytes length") 38 | } 39 | if len(nonce) < 12 { 40 | return nil, errors.New("ChaCha20.Setup: nonce must be 12 bytes length") 41 | } 42 | 43 | cc20 := new(ChaCha20Cipher) 44 | 45 | // constants 46 | cc20.grid[0] = 0x61707865 47 | cc20.grid[1] = 0x3320646e 48 | cc20.grid[2] = 0x79622d32 49 | cc20.grid[3] = 0x6b206574 50 | 51 | // 256 bits key as 8 Little Endian uint32 52 | for j := uint32(0); j < 8; j++ { 53 | cc20.grid[j+4] = 0 54 | for i := uint32(0); i < 4; i++ { 55 | cc20.grid[j+4] += uint32(key[(j<<2)+i]) << (i << 3) 56 | } 57 | } 58 | 59 | // block counter 60 | cc20.grid[12] = counter 61 | 62 | // nonce as 3 consecutives Little Endian uint32 63 | for j := uint32(0); j < 3; j++ { 64 | cc20.grid[j+13] = 0 65 | for i := uint32(0); i < 4; i++ { 66 | cc20.grid[j+13] += uint32(nonce[(j<<2)+i]) << (i << 3) 67 | } 68 | } 69 | return cc20, nil 70 | } 71 | 72 | // SetPacketSequenceNumber initialize the ChaCha20 nonce based on the QUIC packet sequence number and set the block counter to 1. 73 | func (this *ChaCha20Cipher) SetPacketSequenceNumber(sequencenumber protocol.QuicPacketSequenceNumber) { 74 | this.grid[12] = 1 75 | this.grid[14] = uint32(sequencenumber & 0xffffffff) 76 | this.grid[15] = uint32(sequencenumber >> 32) 77 | } 78 | 79 | // Decrypt returns the numbers of decrypted bytes in the plaintext slice of the ciphertext slice and returns an error if the size of plaintext is less than ciphertext length without MAC. 80 | func (this *ChaCha20Cipher) Decrypt(plaintext, ciphertext []byte) (bytescount int, err error) { 81 | l := len(ciphertext) 82 | if len(plaintext) < l { 83 | err = errors.New("ChaCha20Cipher.Decrypt : plaintext must have equal length or more than ciphertext") 84 | return 85 | } 86 | for bytescount = 0; bytescount < l; bytescount++ { 87 | i := bytescount % 64 88 | if i == 0 { 89 | this.GetNextKeystream(&this.buffer) 90 | } 91 | plaintext[bytescount] = ciphertext[bytescount] ^ this.buffer[i] 92 | } 93 | return 94 | } 95 | 96 | // Encrypt returns in the cleartext slice the result of the encrypted plaintext slice. 97 | func (this *ChaCha20Cipher) Encrypt(ciphertext, plaintext []byte) (bytescount int, err error) { 98 | l := len(plaintext) 99 | if len(ciphertext) < l { 100 | err = errors.New("ChaCha20Cipher.Encrypt : ciphertext must have equal length or more than plaintext") 101 | return 102 | } 103 | for bytescount = 0; bytescount < l; bytescount++ { 104 | i := bytescount % 64 105 | if i == 0 { 106 | this.GetNextKeystream(&this.buffer) 107 | } 108 | ciphertext[bytescount] = plaintext[bytescount] ^ this.buffer[i] 109 | } 110 | return 111 | } 112 | 113 | // GetNetxKeystream fills the keystream bytes array corresponding to the current state of ChaCha20 grid and increment the block counter for the next block of keystream. 114 | func (this *ChaCha20Cipher) GetNextKeystream(keystream *[64]byte) { 115 | var x [16]uint32 116 | var a, b, c, d uint32 117 | 118 | // chacha use a 4 x 4 grid of uint32: 119 | // 120 | // +-----+-----+-----+-----+ 121 | // | x0 | x1 | x2 | x3 | 122 | // +-----+-----+-----+-----+ 123 | // | x4 | x5 | x6 | x7 | 124 | // +-----+-----+-----+-----+ 125 | // | x8 | x9 | x10 | x11 | 126 | // +-----+-----+-----+-----+ 127 | // | x12 | x13 | x14 | x15 | 128 | // +-----+-----+-----+-----+ 129 | for i := range x { 130 | x[i] = this.grid[i] 131 | } 132 | 133 | // ChaCha20 consists of 20 rounds, alternating between "column" rounds and "diagonal" rounds. 134 | // Each round applies the "quarterround" function four times, to a different set of words each time. 135 | for i := 0; i < 10; i++ { 136 | 137 | // QUARTER-ROUND on column 1: 138 | // 139 | // +-----+-----+-----+-----+ 140 | // | x0 | | | | 141 | // +-----+-----+-----+-----+ 142 | // | x4 | | | | 143 | // +-----+-----+-----+-----+ 144 | // | x8 | | | | 145 | // +-----+-----+-----+-----+ 146 | // | x12 | | | | 147 | // +-----+-----+-----+-----+ 148 | // 149 | // x[0], x[4], x[8], x[12] = quarterround(x[0], x[4], x[8], x[12]) 150 | a = x[0] 151 | b = x[4] 152 | c = x[8] 153 | d = x[12] 154 | a += b 155 | d ^= a 156 | d = d<<16 | d>>16 // this is a bitwise left rotation 157 | c += d 158 | b ^= c 159 | b = b<<12 | b>>20 // this is a bitwise left rotation 160 | a += b 161 | d ^= a 162 | d = d<<8 | d>>24 // this is a bitwise left rotation 163 | c += d 164 | b ^= c 165 | b = b<<7 | b>>25 // this is a bitwise left rotation 166 | x[0] = a 167 | x[4] = b 168 | x[8] = c 169 | x[12] = d 170 | 171 | // QUARTER-ROUND on column 2: 172 | // 173 | // +-----+-----+-----+-----+ 174 | // | | x1 | | | 175 | // +-----+-----+-----+-----+ 176 | // | | x5 | | | 177 | // +-----+-----+-----+-----+ 178 | // | | x9 | | | 179 | // +-----+-----+-----+-----+ 180 | // | | x13 | | | 181 | // +-----+-----+-----+-----+ 182 | // 183 | // x[1], x[5], x[9], x[13] = quarterround(x[1], x[5], x[9], x[13]) 184 | a = x[1] 185 | b = x[5] 186 | c = x[9] 187 | d = x[13] 188 | a += b 189 | d ^= a 190 | d = d<<16 | d>>16 // this is a bitwise left rotation 191 | c += d 192 | b ^= c 193 | b = b<<12 | b>>20 // this is a bitwise left rotation 194 | a += b 195 | d ^= a 196 | d = d<<8 | d>>24 // this is a bitwise left rotation 197 | c += d 198 | b ^= c 199 | b = b<<7 | b>>25 // this is a bitwise left rotation 200 | x[1] = a 201 | x[5] = b 202 | x[9] = c 203 | x[13] = d 204 | 205 | // QUARTER-ROUND on column 3: 206 | // 207 | // +-----+-----+-----+-----+ 208 | // | | | x2 | | 209 | // +-----+-----+-----+-----+ 210 | // | | | x6 | | 211 | // +-----+-----+-----+-----+ 212 | // | | | x10 | | 213 | // +-----+-----+-----+-----+ 214 | // | | | x14 | | 215 | // +-----+-----+-----+-----+ 216 | // 217 | // x[2], x[6], x[10], x[14] = quarterround(x[2], x[6], x[10], x[14]) 218 | a = x[2] 219 | b = x[6] 220 | c = x[10] 221 | d = x[14] 222 | a += b 223 | d ^= a 224 | d = d<<16 | d>>16 // this is a bitwise left rotation 225 | c += d 226 | b ^= c 227 | b = b<<12 | b>>20 // this is a bitwise left rotation 228 | a += b 229 | d ^= a 230 | d = d<<8 | d>>24 // this is a bitwise left rotation 231 | c += d 232 | b ^= c 233 | b = b<<7 | b>>25 // this is a bitwise left rotation 234 | x[2] = a 235 | x[6] = b 236 | x[10] = c 237 | x[14] = d 238 | 239 | // QUARTER-ROUND on column 4: 240 | // 241 | // +-----+-----+-----+-----+ 242 | // | | | | x3 | 243 | // +-----+-----+-----+-----+ 244 | // | | | | x7 | 245 | // +-----+-----+-----+-----+ 246 | // | | | | x11 | 247 | // +-----+-----+-----+-----+ 248 | // | | | | x15 | 249 | // +-----+-----+-----+-----+ 250 | // 251 | // x[3], x[7], x[11], x[15] = quarterround(x[3], x[7], x[11], x[15]) 252 | a = x[3] 253 | b = x[7] 254 | c = x[11] 255 | d = x[15] 256 | a += b 257 | d ^= a 258 | d = d<<16 | d>>16 // this is a bitwise left rotation 259 | c += d 260 | b ^= c 261 | b = b<<12 | b>>20 // this is a bitwise left rotation 262 | a += b 263 | d ^= a 264 | d = d<<8 | d>>24 // this is a bitwise left rotation 265 | c += d 266 | b ^= c 267 | b = b<<7 | b>>25 // this is a bitwise left rotation 268 | x[3] = a 269 | x[7] = b 270 | x[11] = c 271 | x[15] = d 272 | 273 | // QUARTER-ROUND on diagonal 1: 274 | // 275 | // +-----+-----+-----+-----+ 276 | // | x0 | | | | 277 | // +-----+-----+-----+-----+ 278 | // | | x5 | | | 279 | // +-----+-----+-----+-----+ 280 | // | | | x10 | | 281 | // +-----+-----+-----+-----+ 282 | // | | | | x15 | 283 | // +-----+-----+-----+-----+ 284 | // 285 | // x[0], x[5], x[10], x[15] = quarterround(x[0], x[5], x[10], x[15]) 286 | a = x[0] 287 | b = x[5] 288 | c = x[10] 289 | d = x[15] 290 | a += b 291 | d ^= a 292 | d = d<<16 | d>>16 // this is a bitwise left rotation 293 | c += d 294 | b ^= c 295 | b = b<<12 | b>>20 // this is a bitwise left rotation 296 | a += b 297 | d ^= a 298 | d = d<<8 | d>>24 // this is a bitwise left rotation 299 | c += d 300 | b ^= c 301 | b = b<<7 | b>>25 // this is a bitwise left rotation 302 | x[0] = a 303 | x[5] = b 304 | x[10] = c 305 | x[15] = d 306 | 307 | // QUARTER-ROUND on diagonal 2: 308 | // 309 | // +-----+-----+-----+-----+ 310 | // | | x1 | | | 311 | // +-----+-----+-----+-----+ 312 | // | | | x6 | | 313 | // +-----+-----+-----+-----+ 314 | // | | | | x11 | 315 | // +-----+-----+-----+-----+ 316 | // | x12 | | | | 317 | // +-----+-----+-----+-----+ 318 | // 319 | // x[1], x[6], x[11], x[12] = quarterround(x[1], x[6], x[11], x[12]) 320 | a = x[1] 321 | b = x[6] 322 | c = x[11] 323 | d = x[12] 324 | a += b 325 | d ^= a 326 | d = d<<16 | d>>16 // this is a bitwise left rotation 327 | c += d 328 | b ^= c 329 | b = b<<12 | b>>20 // this is a bitwise left rotation 330 | a += b 331 | d ^= a 332 | d = d<<8 | d>>24 // this is a bitwise left rotation 333 | c += d 334 | b ^= c 335 | b = b<<7 | b>>25 // this is a bitwise left rotation 336 | x[1] = a 337 | x[6] = b 338 | x[11] = c 339 | x[12] = d 340 | 341 | // QUARTER-ROUND on diagonal 3: 342 | // 343 | // +-----+-----+-----+-----+ 344 | // | | | x2 | | 345 | // +-----+-----+-----+-----+ 346 | // | | | | x7 | 347 | // +-----+-----+-----+-----+ 348 | // | x8 | | | | 349 | // +-----+-----+-----+-----+ 350 | // | | x13 | | | 351 | // +-----+-----+-----+-----+ 352 | // 353 | // x[2], x[7], x[8], x[13] = quarterround(x[2], x[7], x[8], x[13]) 354 | a = x[2] 355 | b = x[7] 356 | c = x[8] 357 | d = x[13] 358 | a += b 359 | d ^= a 360 | d = d<<16 | d>>16 // this is a bitwise left rotation 361 | c += d 362 | b ^= c 363 | b = b<<12 | b>>20 // this is a bitwise left rotation 364 | a += b 365 | d ^= a 366 | d = d<<8 | d>>24 // this is a bitwise left rotation 367 | c += d 368 | b ^= c 369 | b = b<<7 | b>>25 // this is a bitwise left rotation 370 | x[2] = a 371 | x[7] = b 372 | x[8] = c 373 | x[13] = d 374 | 375 | // QUARTER-ROUND on diagonal 4: 376 | // 377 | // +-----+-----+-----+-----+ 378 | // | | | | x3 | 379 | // +-----+-----+-----+-----+ 380 | // | x4 | | | | 381 | // +-----+-----+-----+-----+ 382 | // | | x9 | | | 383 | // +-----+-----+-----+-----+ 384 | // | | | x14 | | 385 | // +-----+-----+-----+-----+ 386 | // 387 | // x[3], x[4], x[9], x[14] = quarterround(x[3], x[4], x[9], x[14]) 388 | a = x[3] 389 | b = x[4] 390 | c = x[9] 391 | d = x[14] 392 | a += b 393 | d ^= a 394 | d = d<<16 | d>>16 // this is a bitwise left rotation 395 | c += d 396 | b ^= c 397 | b = b<<12 | b>>20 // this is a bitwise left rotation 398 | a += b 399 | d ^= a 400 | d = d<<8 | d>>24 // this is a bitwise left rotation 401 | c += d 402 | b ^= c 403 | b = b<<7 | b>>25 // this is a bitwise left rotation 404 | x[3] = a 405 | x[4] = b 406 | x[9] = c 407 | x[14] = d 408 | } 409 | 410 | // After 20 rounds of the above processing, the original 16 input words are added to the 16 words to form the 16 output words. 411 | for i := range x { 412 | x[i] += this.grid[i] 413 | } 414 | 415 | // The 64 output bytes are generated from the 16 output words by serialising them in little-endian order and concatenating the results. 416 | for i := 0; i < 64; i += 4 { 417 | j := x[i>>2] 418 | keystream[i] = byte(j) 419 | keystream[i+1] = byte(j >> 8) 420 | keystream[i+2] = byte(j >> 16) 421 | keystream[i+3] = byte(j >> 24) 422 | } 423 | 424 | // Input words 12 is a block counter. 425 | this.grid[12]++ 426 | } 427 | --------------------------------------------------------------------------------