├── 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 | [](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 | [](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 | [](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 | 
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 |
--------------------------------------------------------------------------------