Commit 83906404 authored by ginuerzh's avatar ginuerzh

update vendor for QUIC (#322)

parent ed8e276c
...@@ -33,7 +33,7 @@ func (session *quicSession) GetConn() (*quicConn, error) { ...@@ -33,7 +33,7 @@ func (session *quicSession) GetConn() (*quicConn, error) {
} }
func (session *quicSession) Close() error { func (session *quicSession) Close() error {
return session.session.Close(nil) return session.session.Close()
} }
type quicTransporter struct { type quicTransporter struct {
...@@ -226,7 +226,7 @@ func (l *quicListener) sessionLoop(session quic.Session) { ...@@ -226,7 +226,7 @@ func (l *quicListener) sessionLoop(session quic.Session) {
stream, err := session.AcceptStream() stream, err := session.AcceptStream()
if err != nil { if err != nil {
log.Log("[quic] accept stream:", err) log.Log("[quic] accept stream:", err)
session.Close(err) session.Close()
return return
} }
......
The MIT License (MIT) The MIT License (MIT)
Copyright (c) 2016 Lucas Clemente Copyright (c) 2014 cheekybits
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal
...@@ -19,3 +19,4 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ...@@ -19,3 +19,4 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE. SOFTWARE.
// Package generic contains the generic marker types.
package generic
package generic
// Type is the placeholder type that indicates a generic value.
// When genny is executed, variables of this type will be replaced with
// references to the specific types.
// var GenericType generic.Type
type Type interface{}
// Number is the placehoder type that indiccates a generic numerical value.
// When genny is executed, variables of this type will be replaced with
// references to the specific types.
// var GenericType generic.Number
type Number float64
MIT License
Copyright (c) 2016 Lucas Clemente
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
# aes12
This package modifies the AES-GCM implementation from Go's standard library to use 12 byte tag sizes. It is not intended for a general audience, and used in [quic-go](https://github.com/lucas-clemente/quic-go).
To make use of the in-place encryption / decryption feature, the `dst` parameter to `Seal` and `Open` should be 16 bytes longer than plaintext, not 12.
Command for testing:
```
go test . --bench=. && GOARCH=386 go test . --bench=.
```
The output (on my machine):
```
BenchmarkAESGCMSeal1K-8 3000000 467 ns/op 2192.37 MB/s
BenchmarkAESGCMOpen1K-8 3000000 416 ns/op 2456.72 MB/s
BenchmarkAESGCMSeal8K-8 500000 2742 ns/op 2986.53 MB/s
BenchmarkAESGCMOpen8K-8 500000 2791 ns/op 2934.65 MB/s
PASS
ok github.com/lucas-clemente/aes12 6.383s
BenchmarkAESGCMSeal1K-8 50000 35233 ns/op 29.06 MB/s
BenchmarkAESGCMOpen1K-8 50000 34529 ns/op 29.66 MB/s
BenchmarkAESGCMSeal8K-8 5000 262678 ns/op 31.19 MB/s
BenchmarkAESGCMOpen8K-8 5000 267296 ns/op 30.65 MB/s
PASS
ok github.com/lucas-clemente/aes12 6.972s
```
// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build amd64
package aes12
import "crypto/subtle"
// The following functions are defined in gcm_amd64.s.
func hasGCMAsm() bool
//go:noescape
func aesEncBlock(dst, src *[16]byte, ks []uint32)
//go:noescape
func gcmAesInit(productTable *[256]byte, ks []uint32)
//go:noescape
func gcmAesData(productTable *[256]byte, data []byte, T *[16]byte)
//go:noescape
func gcmAesEnc(productTable *[256]byte, dst, src []byte, ctr, T *[16]byte, ks []uint32)
//go:noescape
func gcmAesDec(productTable *[256]byte, dst, src []byte, ctr, T *[16]byte, ks []uint32)
//go:noescape
func gcmAesFinish(productTable *[256]byte, tagMask, T *[16]byte, pLen, dLen uint64)
// aesCipherGCM implements crypto/cipher.gcmAble so that crypto/cipher.NewGCM
// will use the optimised implementation in this file when possible. Instances
// of this type only exist when hasGCMAsm returns true.
type aesCipherGCM struct {
aesCipherAsm
}
// Assert that aesCipherGCM implements the gcmAble interface.
var _ gcmAble = (*aesCipherGCM)(nil)
// NewGCM returns the AES cipher wrapped in Galois Counter Mode. This is only
// called by crypto/cipher.NewGCM via the gcmAble interface.
func (c *aesCipherGCM) NewGCM(nonceSize int) (AEAD, error) {
g := &gcmAsm{ks: c.enc, nonceSize: nonceSize}
gcmAesInit(&g.productTable, g.ks)
return g, nil
}
type gcmAsm struct {
// ks is the key schedule, the length of which depends on the size of
// the AES key.
ks []uint32
// productTable contains pre-computed multiples of the binary-field
// element used in GHASH.
productTable [256]byte
// nonceSize contains the expected size of the nonce, in bytes.
nonceSize int
}
func (g *gcmAsm) NonceSize() int {
return g.nonceSize
}
func (*gcmAsm) Overhead() int {
return gcmTagSize
}
// Seal encrypts and authenticates plaintext. See the AEAD interface for
// details.
func (g *gcmAsm) Seal(dst, nonce, plaintext, data []byte) []byte {
if len(nonce) != g.nonceSize {
panic("cipher: incorrect nonce length given to GCM")
}
var counter, tagMask [gcmBlockSize]byte
if len(nonce) == gcmStandardNonceSize {
// Init counter to nonce||1
copy(counter[:], nonce)
counter[gcmBlockSize-1] = 1
} else {
// Otherwise counter = GHASH(nonce)
gcmAesData(&g.productTable, nonce, &counter)
gcmAesFinish(&g.productTable, &tagMask, &counter, uint64(len(nonce)), uint64(0))
}
aesEncBlock(&tagMask, &counter, g.ks)
var tagOut [16]byte
gcmAesData(&g.productTable, data, &tagOut)
ret, out := sliceForAppend(dst, len(plaintext)+gcmTagSize)
if len(plaintext) > 0 {
gcmAesEnc(&g.productTable, out, plaintext, &counter, &tagOut, g.ks)
}
gcmAesFinish(&g.productTable, &tagMask, &tagOut, uint64(len(plaintext)), uint64(len(data)))
copy(out[len(plaintext):], tagOut[:gcmTagSize])
return ret
}
// Open authenticates and decrypts ciphertext. See the AEAD interface
// for details.
func (g *gcmAsm) Open(dst, nonce, ciphertext, data []byte) ([]byte, error) {
if len(nonce) != g.nonceSize {
panic("cipher: incorrect nonce length given to GCM")
}
if len(ciphertext) < gcmTagSize {
return nil, errOpen
}
tag := ciphertext[len(ciphertext)-gcmTagSize:]
ciphertext = ciphertext[:len(ciphertext)-gcmTagSize]
// See GCM spec, section 7.1.
var counter, tagMask [gcmBlockSize]byte
if len(nonce) == gcmStandardNonceSize {
// Init counter to nonce||1
copy(counter[:], nonce)
counter[gcmBlockSize-1] = 1
} else {
// Otherwise counter = GHASH(nonce)
gcmAesData(&g.productTable, nonce, &counter)
gcmAesFinish(&g.productTable, &tagMask, &counter, uint64(len(nonce)), uint64(0))
}
aesEncBlock(&tagMask, &counter, g.ks)
var expectedTag [16]byte
gcmAesData(&g.productTable, data, &expectedTag)
ret, out := sliceForAppend(dst, len(ciphertext))
if len(ciphertext) > 0 {
gcmAesDec(&g.productTable, out, ciphertext, &counter, &expectedTag, g.ks)
}
gcmAesFinish(&g.productTable, &tagMask, &expectedTag, uint64(len(ciphertext)), uint64(len(data)))
if subtle.ConstantTimeCompare(expectedTag[:12], tag) != 1 {
for i := range out {
out[i] = 0
}
return nil, errOpen
}
return ret, nil
}
// Copyright 2012 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
#include "textflag.h"
// func hasAsm() bool
// returns whether AES-NI is supported
TEXT ·hasAsm(SB),NOSPLIT,$0
XORQ AX, AX
INCL AX
CPUID
SHRQ $25, CX
ANDQ $1, CX
MOVB CX, ret+0(FP)
RET
// func encryptBlockAsm(nr int, xk *uint32, dst, src *byte)
TEXT ·encryptBlockAsm(SB),NOSPLIT,$0
MOVQ nr+0(FP), CX
MOVQ xk+8(FP), AX
MOVQ dst+16(FP), DX
MOVQ src+24(FP), BX
MOVUPS 0(AX), X1
MOVUPS 0(BX), X0
ADDQ $16, AX
PXOR X1, X0
SUBQ $12, CX
JE Lenc196
JB Lenc128
Lenc256:
MOVUPS 0(AX), X1
AESENC X1, X0
MOVUPS 16(AX), X1
AESENC X1, X0
ADDQ $32, AX
Lenc196:
MOVUPS 0(AX), X1
AESENC X1, X0
MOVUPS 16(AX), X1
AESENC X1, X0
ADDQ $32, AX
Lenc128:
MOVUPS 0(AX), X1
AESENC X1, X0
MOVUPS 16(AX), X1
AESENC X1, X0
MOVUPS 32(AX), X1
AESENC X1, X0
MOVUPS 48(AX), X1
AESENC X1, X0
MOVUPS 64(AX), X1
AESENC X1, X0
MOVUPS 80(AX), X1
AESENC X1, X0
MOVUPS 96(AX), X1
AESENC X1, X0
MOVUPS 112(AX), X1
AESENC X1, X0
MOVUPS 128(AX), X1
AESENC X1, X0
MOVUPS 144(AX), X1
AESENCLAST X1, X0
MOVUPS X0, 0(DX)
RET
// func decryptBlockAsm(nr int, xk *uint32, dst, src *byte)
TEXT ·decryptBlockAsm(SB),NOSPLIT,$0
MOVQ nr+0(FP), CX
MOVQ xk+8(FP), AX
MOVQ dst+16(FP), DX
MOVQ src+24(FP), BX
MOVUPS 0(AX), X1
MOVUPS 0(BX), X0
ADDQ $16, AX
PXOR X1, X0
SUBQ $12, CX
JE Ldec196
JB Ldec128
Ldec256:
MOVUPS 0(AX), X1
AESDEC X1, X0
MOVUPS 16(AX), X1
AESDEC X1, X0
ADDQ $32, AX
Ldec196:
MOVUPS 0(AX), X1
AESDEC X1, X0
MOVUPS 16(AX), X1
AESDEC X1, X0
ADDQ $32, AX
Ldec128:
MOVUPS 0(AX), X1
AESDEC X1, X0
MOVUPS 16(AX), X1
AESDEC X1, X0
MOVUPS 32(AX), X1
AESDEC X1, X0
MOVUPS 48(AX), X1
AESDEC X1, X0
MOVUPS 64(AX), X1
AESDEC X1, X0
MOVUPS 80(AX), X1
AESDEC X1, X0
MOVUPS 96(AX), X1
AESDEC X1, X0
MOVUPS 112(AX), X1
AESDEC X1, X0
MOVUPS 128(AX), X1
AESDEC X1, X0
MOVUPS 144(AX), X1
AESDECLAST X1, X0
MOVUPS X0, 0(DX)
RET
// func expandKeyAsm(nr int, key *byte, enc, dec *uint32) {
// Note that round keys are stored in uint128 format, not uint32
TEXT ·expandKeyAsm(SB),NOSPLIT,$0
MOVQ nr+0(FP), CX
MOVQ key+8(FP), AX
MOVQ enc+16(FP), BX
MOVQ dec+24(FP), DX
MOVUPS (AX), X0
// enc
MOVUPS X0, (BX)
ADDQ $16, BX
PXOR X4, X4 // _expand_key_* expect X4 to be zero
CMPL CX, $12
JE Lexp_enc196
JB Lexp_enc128
Lexp_enc256:
MOVUPS 16(AX), X2
MOVUPS X2, (BX)
ADDQ $16, BX
AESKEYGENASSIST $0x01, X2, X1
CALL _expand_key_256a<>(SB)
AESKEYGENASSIST $0x01, X0, X1
CALL _expand_key_256b<>(SB)
AESKEYGENASSIST $0x02, X2, X1
CALL _expand_key_256a<>(SB)
AESKEYGENASSIST $0x02, X0, X1
CALL _expand_key_256b<>(SB)
AESKEYGENASSIST $0x04, X2, X1
CALL _expand_key_256a<>(SB)
AESKEYGENASSIST $0x04, X0, X1
CALL _expand_key_256b<>(SB)
AESKEYGENASSIST $0x08, X2, X1
CALL _expand_key_256a<>(SB)
AESKEYGENASSIST $0x08, X0, X1
CALL _expand_key_256b<>(SB)
AESKEYGENASSIST $0x10, X2, X1
CALL _expand_key_256a<>(SB)
AESKEYGENASSIST $0x10, X0, X1
CALL _expand_key_256b<>(SB)
AESKEYGENASSIST $0x20, X2, X1
CALL _expand_key_256a<>(SB)
AESKEYGENASSIST $0x20, X0, X1
CALL _expand_key_256b<>(SB)
AESKEYGENASSIST $0x40, X2, X1
CALL _expand_key_256a<>(SB)
JMP Lexp_dec
Lexp_enc196:
MOVQ 16(AX), X2
AESKEYGENASSIST $0x01, X2, X1
CALL _expand_key_192a<>(SB)
AESKEYGENASSIST $0x02, X2, X1
CALL _expand_key_192b<>(SB)
AESKEYGENASSIST $0x04, X2, X1
CALL _expand_key_192a<>(SB)
AESKEYGENASSIST $0x08, X2, X1
CALL _expand_key_192b<>(SB)
AESKEYGENASSIST $0x10, X2, X1
CALL _expand_key_192a<>(SB)
AESKEYGENASSIST $0x20, X2, X1
CALL _expand_key_192b<>(SB)
AESKEYGENASSIST $0x40, X2, X1
CALL _expand_key_192a<>(SB)
AESKEYGENASSIST $0x80, X2, X1
CALL _expand_key_192b<>(SB)
JMP Lexp_dec
Lexp_enc128:
AESKEYGENASSIST $0x01, X0, X1
CALL _expand_key_128<>(SB)
AESKEYGENASSIST $0x02, X0, X1
CALL _expand_key_128<>(SB)
AESKEYGENASSIST $0x04, X0, X1
CALL _expand_key_128<>(SB)
AESKEYGENASSIST $0x08, X0, X1
CALL _expand_key_128<>(SB)
AESKEYGENASSIST $0x10, X0, X1
CALL _expand_key_128<>(SB)
AESKEYGENASSIST $0x20, X0, X1
CALL _expand_key_128<>(SB)
AESKEYGENASSIST $0x40, X0, X1
CALL _expand_key_128<>(SB)
AESKEYGENASSIST $0x80, X0, X1
CALL _expand_key_128<>(SB)
AESKEYGENASSIST $0x1b, X0, X1
CALL _expand_key_128<>(SB)
AESKEYGENASSIST $0x36, X0, X1
CALL _expand_key_128<>(SB)
Lexp_dec:
// dec
SUBQ $16, BX
MOVUPS (BX), X1
MOVUPS X1, (DX)
DECQ CX
Lexp_dec_loop:
MOVUPS -16(BX), X1
AESIMC X1, X0
MOVUPS X0, 16(DX)
SUBQ $16, BX
ADDQ $16, DX
DECQ CX
JNZ Lexp_dec_loop
MOVUPS -16(BX), X0
MOVUPS X0, 16(DX)
RET
TEXT _expand_key_128<>(SB),NOSPLIT,$0
PSHUFD $0xff, X1, X1
SHUFPS $0x10, X0, X4
PXOR X4, X0
SHUFPS $0x8c, X0, X4
PXOR X4, X0
PXOR X1, X0
MOVUPS X0, (BX)
ADDQ $16, BX
RET
TEXT _expand_key_192a<>(SB),NOSPLIT,$0
PSHUFD $0x55, X1, X1
SHUFPS $0x10, X0, X4
PXOR X4, X0
SHUFPS $0x8c, X0, X4
PXOR X4, X0
PXOR X1, X0
MOVAPS X2, X5
MOVAPS X2, X6
PSLLDQ $0x4, X5
PSHUFD $0xff, X0, X3
PXOR X3, X2
PXOR X5, X2
MOVAPS X0, X1
SHUFPS $0x44, X0, X6
MOVUPS X6, (BX)
SHUFPS $0x4e, X2, X1
MOVUPS X1, 16(BX)
ADDQ $32, BX
RET
TEXT _expand_key_192b<>(SB),NOSPLIT,$0
PSHUFD $0x55, X1, X1
SHUFPS $0x10, X0, X4
PXOR X4, X0
SHUFPS $0x8c, X0, X4
PXOR X4, X0
PXOR X1, X0
MOVAPS X2, X5
PSLLDQ $0x4, X5
PSHUFD $0xff, X0, X3
PXOR X3, X2
PXOR X5, X2
MOVUPS X0, (BX)
ADDQ $16, BX
RET
TEXT _expand_key_256a<>(SB),NOSPLIT,$0
JMP _expand_key_128<>(SB)
TEXT _expand_key_256b<>(SB),NOSPLIT,$0
PSHUFD $0xaa, X1, X1
SHUFPS $0x10, X2, X4
PXOR X4, X2
SHUFPS $0x8c, X2, X4
PXOR X4, X2
PXOR X1, X2
MOVUPS X2, (BX)
ADDQ $16, BX
RET
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// This Go implementation is derived in part from the reference
// ANSI C implementation, which carries the following notice:
//
// rijndael-alg-fst.c
//
// @version 3.0 (December 2000)
//
// Optimised ANSI C code for the Rijndael cipher (now AES)
//
// @author Vincent Rijmen <vincent.rijmen@esat.kuleuven.ac.be>
// @author Antoon Bosselaers <antoon.bosselaers@esat.kuleuven.ac.be>
// @author Paulo Barreto <paulo.barreto@terra.com.br>
//
// This code is hereby placed in the public domain.
//
// THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS
// OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
// BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
// EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// See FIPS 197 for specification, and see Daemen and Rijmen's Rijndael submission
// for implementation details.
// http://www.csrc.nist.gov/publications/fips/fips197/fips-197.pdf
// http://csrc.nist.gov/archive/aes/rijndael/Rijndael-ammended.pdf
package aes12
// Encrypt one block from src into dst, using the expanded key xk.
func encryptBlockGo(xk []uint32, dst, src []byte) {
var s0, s1, s2, s3, t0, t1, t2, t3 uint32
s0 = uint32(src[0])<<24 | uint32(src[1])<<16 | uint32(src[2])<<8 | uint32(src[3])
s1 = uint32(src[4])<<24 | uint32(src[5])<<16 | uint32(src[6])<<8 | uint32(src[7])
s2 = uint32(src[8])<<24 | uint32(src[9])<<16 | uint32(src[10])<<8 | uint32(src[11])
s3 = uint32(src[12])<<24 | uint32(src[13])<<16 | uint32(src[14])<<8 | uint32(src[15])
// First round just XORs input with key.
s0 ^= xk[0]
s1 ^= xk[1]
s2 ^= xk[2]
s3 ^= xk[3]
// Middle rounds shuffle using tables.
// Number of rounds is set by length of expanded key.
nr := len(xk)/4 - 2 // - 2: one above, one more below
k := 4
for r := 0; r < nr; r++ {
t0 = xk[k+0] ^ te0[uint8(s0>>24)] ^ te1[uint8(s1>>16)] ^ te2[uint8(s2>>8)] ^ te3[uint8(s3)]
t1 = xk[k+1] ^ te0[uint8(s1>>24)] ^ te1[uint8(s2>>16)] ^ te2[uint8(s3>>8)] ^ te3[uint8(s0)]
t2 = xk[k+2] ^ te0[uint8(s2>>24)] ^ te1[uint8(s3>>16)] ^ te2[uint8(s0>>8)] ^ te3[uint8(s1)]
t3 = xk[k+3] ^ te0[uint8(s3>>24)] ^ te1[uint8(s0>>16)] ^ te2[uint8(s1>>8)] ^ te3[uint8(s2)]
k += 4
s0, s1, s2, s3 = t0, t1, t2, t3
}
// Last round uses s-box directly and XORs to produce output.
s0 = uint32(sbox0[t0>>24])<<24 | uint32(sbox0[t1>>16&0xff])<<16 | uint32(sbox0[t2>>8&0xff])<<8 | uint32(sbox0[t3&0xff])
s1 = uint32(sbox0[t1>>24])<<24 | uint32(sbox0[t2>>16&0xff])<<16 | uint32(sbox0[t3>>8&0xff])<<8 | uint32(sbox0[t0&0xff])
s2 = uint32(sbox0[t2>>24])<<24 | uint32(sbox0[t3>>16&0xff])<<16 | uint32(sbox0[t0>>8&0xff])<<8 | uint32(sbox0[t1&0xff])
s3 = uint32(sbox0[t3>>24])<<24 | uint32(sbox0[t0>>16&0xff])<<16 | uint32(sbox0[t1>>8&0xff])<<8 | uint32(sbox0[t2&0xff])
s0 ^= xk[k+0]
s1 ^= xk[k+1]
s2 ^= xk[k+2]
s3 ^= xk[k+3]
dst[0], dst[1], dst[2], dst[3] = byte(s0>>24), byte(s0>>16), byte(s0>>8), byte(s0)
dst[4], dst[5], dst[6], dst[7] = byte(s1>>24), byte(s1>>16), byte(s1>>8), byte(s1)
dst[8], dst[9], dst[10], dst[11] = byte(s2>>24), byte(s2>>16), byte(s2>>8), byte(s2)
dst[12], dst[13], dst[14], dst[15] = byte(s3>>24), byte(s3>>16), byte(s3>>8), byte(s3)
}
// Decrypt one block from src into dst, using the expanded key xk.
func decryptBlockGo(xk []uint32, dst, src []byte) {
var s0, s1, s2, s3, t0, t1, t2, t3 uint32
s0 = uint32(src[0])<<24 | uint32(src[1])<<16 | uint32(src[2])<<8 | uint32(src[3])
s1 = uint32(src[4])<<24 | uint32(src[5])<<16 | uint32(src[6])<<8 | uint32(src[7])
s2 = uint32(src[8])<<24 | uint32(src[9])<<16 | uint32(src[10])<<8 | uint32(src[11])
s3 = uint32(src[12])<<24 | uint32(src[13])<<16 | uint32(src[14])<<8 | uint32(src[15])
// First round just XORs input with key.
s0 ^= xk[0]
s1 ^= xk[1]
s2 ^= xk[2]
s3 ^= xk[3]
// Middle rounds shuffle using tables.
// Number of rounds is set by length of expanded key.
nr := len(xk)/4 - 2 // - 2: one above, one more below
k := 4
for r := 0; r < nr; r++ {
t0 = xk[k+0] ^ td0[uint8(s0>>24)] ^ td1[uint8(s3>>16)] ^ td2[uint8(s2>>8)] ^ td3[uint8(s1)]
t1 = xk[k+1] ^ td0[uint8(s1>>24)] ^ td1[uint8(s0>>16)] ^ td2[uint8(s3>>8)] ^ td3[uint8(s2)]
t2 = xk[k+2] ^ td0[uint8(s2>>24)] ^ td1[uint8(s1>>16)] ^ td2[uint8(s0>>8)] ^ td3[uint8(s3)]
t3 = xk[k+3] ^ td0[uint8(s3>>24)] ^ td1[uint8(s2>>16)] ^ td2[uint8(s1>>8)] ^ td3[uint8(s0)]
k += 4
s0, s1, s2, s3 = t0, t1, t2, t3
}
// Last round uses s-box directly and XORs to produce output.
s0 = uint32(sbox1[t0>>24])<<24 | uint32(sbox1[t3>>16&0xff])<<16 | uint32(sbox1[t2>>8&0xff])<<8 | uint32(sbox1[t1&0xff])
s1 = uint32(sbox1[t1>>24])<<24 | uint32(sbox1[t0>>16&0xff])<<16 | uint32(sbox1[t3>>8&0xff])<<8 | uint32(sbox1[t2&0xff])
s2 = uint32(sbox1[t2>>24])<<24 | uint32(sbox1[t1>>16&0xff])<<16 | uint32(sbox1[t0>>8&0xff])<<8 | uint32(sbox1[t3&0xff])
s3 = uint32(sbox1[t3>>24])<<24 | uint32(sbox1[t2>>16&0xff])<<16 | uint32(sbox1[t1>>8&0xff])<<8 | uint32(sbox1[t0&0xff])
s0 ^= xk[k+0]
s1 ^= xk[k+1]
s2 ^= xk[k+2]
s3 ^= xk[k+3]
dst[0], dst[1], dst[2], dst[3] = byte(s0>>24), byte(s0>>16), byte(s0>>8), byte(s0)
dst[4], dst[5], dst[6], dst[7] = byte(s1>>24), byte(s1>>16), byte(s1>>8), byte(s1)
dst[8], dst[9], dst[10], dst[11] = byte(s2>>24), byte(s2>>16), byte(s2>>8), byte(s2)
dst[12], dst[13], dst[14], dst[15] = byte(s3>>24), byte(s3>>16), byte(s3>>8), byte(s3)
}
// Apply sbox0 to each byte in w.
func subw(w uint32) uint32 {
return uint32(sbox0[w>>24])<<24 |
uint32(sbox0[w>>16&0xff])<<16 |
uint32(sbox0[w>>8&0xff])<<8 |
uint32(sbox0[w&0xff])
}
// Rotate
func rotw(w uint32) uint32 { return w<<8 | w>>24 }
// Key expansion algorithm. See FIPS-197, Figure 11.
// Their rcon[i] is our powx[i-1] << 24.
func expandKeyGo(key []byte, enc, dec []uint32) {
// Encryption key setup.
var i int
nk := len(key) / 4
for i = 0; i < nk; i++ {
enc[i] = uint32(key[4*i])<<24 | uint32(key[4*i+1])<<16 | uint32(key[4*i+2])<<8 | uint32(key[4*i+3])
}
for ; i < len(enc); i++ {
t := enc[i-1]
if i%nk == 0 {
t = subw(rotw(t)) ^ (uint32(powx[i/nk-1]) << 24)
} else if nk > 6 && i%nk == 4 {
t = subw(t)
}
enc[i] = enc[i-nk] ^ t
}
// Derive decryption key from encryption key.
// Reverse the 4-word round key sets from enc to produce dec.
// All sets but the first and last get the MixColumn transform applied.
if dec == nil {
return
}
n := len(enc)
for i := 0; i < n; i += 4 {
ei := n - i - 4
for j := 0; j < 4; j++ {
x := enc[ei+j]
if i > 0 && i+4 < n {
x = td0[sbox0[x>>24]] ^ td1[sbox0[x>>16&0xff]] ^ td2[sbox0[x>>8&0xff]] ^ td3[sbox0[x&0xff]]
}
dec[i+j] = x
}
}
}
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package aes12
import "strconv"
// The AES block size in bytes.
const BlockSize = 16
// A cipher is an instance of AES encryption using a particular key.
type aesCipher struct {
enc []uint32
dec []uint32
}
type KeySizeError int
func (k KeySizeError) Error() string {
return "crypto/aes: invalid key size " + strconv.Itoa(int(k))
}
// NewCipher creates and returns a new Block.
// The key argument should be the AES key,
// either 16, 24, or 32 bytes to select
// AES-128, AES-192, or AES-256.
func NewCipher(key []byte) (Block, error) {
k := len(key)
switch k {
default:
return nil, KeySizeError(k)
case 16, 24, 32:
break
}
return newCipher(key)
}
// newCipherGeneric creates and returns a new Block
// implemented in pure Go.
func newCipherGeneric(key []byte) (Block, error) {
n := len(key) + 28
c := aesCipher{make([]uint32, n), make([]uint32, n)}
expandKeyGo(key, c.enc, c.dec)
return &c, nil
}
func (c *aesCipher) BlockSize() int { return BlockSize }
func (c *aesCipher) Encrypt(dst, src []byte) {
if len(src) < BlockSize {
panic("crypto/aes: input not full block")
}
if len(dst) < BlockSize {
panic("crypto/aes: output not full block")
}
encryptBlockGo(c.enc, dst, src)
}
func (c *aesCipher) Decrypt(dst, src []byte) {
if len(src) < BlockSize {
panic("crypto/aes: input not full block")
}
if len(dst) < BlockSize {
panic("crypto/aes: output not full block")
}
decryptBlockGo(c.dec, dst, src)
}
// Copyright 2010 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// package aes12 implements standard block cipher modes that can be wrapped
// around low-level block cipher implementations.
// See http://csrc.nist.gov/groups/ST/toolkit/BCM/current_modes.html
// and NIST Special Publication 800-38A.
package aes12
// A Block represents an implementation of block cipher
// using a given key. It provides the capability to encrypt
// or decrypt individual blocks. The mode implementations
// extend that capability to streams of blocks.
type Block interface {
// BlockSize returns the cipher's block size.
BlockSize() int
// Encrypt encrypts the first block in src into dst.
// Dst and src may point at the same memory.
Encrypt(dst, src []byte)
// Decrypt decrypts the first block in src into dst.
// Dst and src may point at the same memory.
Decrypt(dst, src []byte)
}
// A Stream represents a stream cipher.
type Stream interface {
// XORKeyStream XORs each byte in the given slice with a byte from the
// cipher's key stream. Dst and src may point to the same memory.
// If len(dst) < len(src), XORKeyStream should panic. It is acceptable
// to pass a dst bigger than src, and in that case, XORKeyStream will
// only update dst[:len(src)] and will not touch the rest of dst.
XORKeyStream(dst, src []byte)
}
// A BlockMode represents a block cipher running in a block-based mode (CBC,
// ECB etc).
type BlockMode interface {
// BlockSize returns the mode's block size.
BlockSize() int
// CryptBlocks encrypts or decrypts a number of blocks. The length of
// src must be a multiple of the block size. Dst and src may point to
// the same memory.
CryptBlocks(dst, src []byte)
}
// Utility routines
func dup(p []byte) []byte {
q := make([]byte, len(p))
copy(q, p)
return q
}
// Copyright 2012 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package aes12
// defined in asm_amd64.s
func hasAsm() bool
func encryptBlockAsm(nr int, xk *uint32, dst, src *byte)
func decryptBlockAsm(nr int, xk *uint32, dst, src *byte)
func expandKeyAsm(nr int, key *byte, enc *uint32, dec *uint32)
type aesCipherAsm struct {
aesCipher
}
var useAsm = hasAsm()
func newCipher(key []byte) (Block, error) {
if !useAsm {
return newCipherGeneric(key)
}
n := len(key) + 28
c := aesCipherAsm{aesCipher{make([]uint32, n), make([]uint32, n)}}
rounds := 10
switch len(key) {
case 128 / 8:
rounds = 10
case 192 / 8:
rounds = 12
case 256 / 8:
rounds = 14
}
expandKeyAsm(rounds, &key[0], &c.enc[0], &c.dec[0])
if hasGCMAsm() {
return &aesCipherGCM{c}, nil
}
return &c, nil
}
func (c *aesCipherAsm) BlockSize() int { return BlockSize }
func (c *aesCipherAsm) Encrypt(dst, src []byte) {
if len(src) < BlockSize {
panic("crypto/aes: input not full block")
}
if len(dst) < BlockSize {
panic("crypto/aes: output not full block")
}
encryptBlockAsm(len(c.enc)/4-1, &c.enc[0], &dst[0], &src[0])
}
func (c *aesCipherAsm) Decrypt(dst, src []byte) {
if len(src) < BlockSize {
panic("crypto/aes: input not full block")
}
if len(dst) < BlockSize {
panic("crypto/aes: output not full block")
}
decryptBlockAsm(len(c.dec)/4-1, &c.dec[0], &dst[0], &src[0])
}
// expandKey is used by BenchmarkExpand to ensure that the asm implementation
// of key expansion is used for the benchmark when it is available.
func expandKey(key []byte, enc, dec []uint32) {
if useAsm {
rounds := 10 // rounds needed for AES128
switch len(key) {
case 192 / 8:
rounds = 12
case 256 / 8:
rounds = 14
}
expandKeyAsm(rounds, &key[0], &enc[0], &dec[0])
} else {
expandKeyGo(key, enc, dec)
}
}
// Copyright 2012 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build !amd64
package aes12
// newCipher calls the newCipherGeneric function
// directly. Platforms with hardware accelerated
// implementations of AES should implement their
// own version of newCipher (which may then call
// newCipherGeneric if needed).
func newCipher(key []byte) (Block, error) {
return newCipherGeneric(key)
}
// expandKey is used by BenchmarkExpand and should
// call an assembly implementation if one is available.
func expandKey(key []byte, enc, dec []uint32) {
expandKeyGo(key, enc, dec)
}
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
// Copyright 2013 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package aes12
import (
"runtime"
"unsafe"
)
const wordSize = int(unsafe.Sizeof(uintptr(0)))
const supportsUnaligned = runtime.GOARCH == "386" || runtime.GOARCH == "amd64" || runtime.GOARCH == "ppc64" || runtime.GOARCH == "ppc64le" || runtime.GOARCH == "s390x"
// fastXORBytes xors in bulk. It only works on architectures that
// support unaligned read/writes.
func fastXORBytes(dst, a, b []byte) int {
n := len(a)
if len(b) < n {
n = len(b)
}
w := n / wordSize
if w > 0 {
dw := *(*[]uintptr)(unsafe.Pointer(&dst))
aw := *(*[]uintptr)(unsafe.Pointer(&a))
bw := *(*[]uintptr)(unsafe.Pointer(&b))
for i := 0; i < w; i++ {
dw[i] = aw[i] ^ bw[i]
}
}
for i := (n - n%wordSize); i < n; i++ {
dst[i] = a[i] ^ b[i]
}
return n
}
func safeXORBytes(dst, a, b []byte) int {
n := len(a)
if len(b) < n {
n = len(b)
}
for i := 0; i < n; i++ {
dst[i] = a[i] ^ b[i]
}
return n
}
// xorBytes xors the bytes in a and b. The destination is assumed to have enough
// space. Returns the number of bytes xor'd.
func xorBytes(dst, a, b []byte) int {
if supportsUnaligned {
return fastXORBytes(dst, a, b)
} else {
// TODO(hanwen): if (dst, a, b) have common alignment
// we could still try fastXORBytes. It is not clear
// how often this happens, and it's only worth it if
// the block encryption itself is hardware
// accelerated.
return safeXORBytes(dst, a, b)
}
}
// fastXORWords XORs multiples of 4 or 8 bytes (depending on architecture.)
// The arguments are assumed to be of equal length.
func fastXORWords(dst, a, b []byte) {
dw := *(*[]uintptr)(unsafe.Pointer(&dst))
aw := *(*[]uintptr)(unsafe.Pointer(&a))
bw := *(*[]uintptr)(unsafe.Pointer(&b))
n := len(b) / wordSize
for i := 0; i < n; i++ {
dw[i] = aw[i] ^ bw[i]
}
}
func xorWords(dst, a, b []byte) {
if supportsUnaligned {
fastXORWords(dst, a, b)
} else {
safeXORBytes(dst, a, b)
}
}
# fnv128a
Implementation of the FNV-1a 128bit hash in go
// Package fnv128a implements FNV-1 and FNV-1a, non-cryptographic hash functions
// created by Glenn Fowler, Landon Curt Noll, and Phong Vo.
// See https://en.wikipedia.org/wiki/Fowler-Noll-Vo_hash_function.
//
// Write() algorithm taken and modified from github.com/romain-jacotin/quic
package fnv128a
import "hash"
// Hash128 is the common interface implemented by all 128-bit hash functions.
type Hash128 interface {
hash.Hash
Sum128() (uint64, uint64)
}
type sum128a struct {
v0, v1, v2, v3 uint64
}
var _ Hash128 = &sum128a{}
// New1 returns a new 128-bit FNV-1a hash.Hash.
func New() Hash128 {
s := &sum128a{}
s.Reset()
return s
}
func (s *sum128a) Reset() {
s.v0 = 0x6295C58D
s.v1 = 0x62B82175
s.v2 = 0x07BB0142
s.v3 = 0x6C62272E
}
func (s *sum128a) Sum128() (uint64, uint64) {
return s.v3<<32 | s.v2, s.v1<<32 | s.v0
}
func (s *sum128a) Write(data []byte) (int, error) {
var t0, t1, t2, t3 uint64
const fnv128PrimeLow = 0x0000013B
const fnv128PrimeShift = 24
for _, v := range data {
// xor the bottom with the current octet
s.v0 ^= uint64(v)
// multiply by the 128 bit FNV magic prime mod 2^128
// fnv_prime = 309485009821345068724781371 (decimal)
// = 0x0000000001000000000000000000013B (hexadecimal)
// = 0x00000000 0x01000000 0x00000000 0x0000013B (in 4*32 words)
// = 0x0 1<<fnv128PrimeShift 0x0 fnv128PrimeLow
//
// fnv128PrimeLow = 0x0000013B
// fnv128PrimeShift = 24
// multiply by the lowest order digit base 2^32 and by the other non-zero digit
t0 = s.v0 * fnv128PrimeLow
t1 = s.v1 * fnv128PrimeLow
t2 = s.v2*fnv128PrimeLow + s.v0<<fnv128PrimeShift
t3 = s.v3*fnv128PrimeLow + s.v1<<fnv128PrimeShift
// propagate carries
t1 += (t0 >> 32)
t2 += (t1 >> 32)
t3 += (t2 >> 32)
s.v0 = t0 & 0xffffffff
s.v1 = t1 & 0xffffffff
s.v2 = t2 & 0xffffffff
s.v3 = t3 // & 0xffffffff
// Doing a s.v3 &= 0xffffffff is not really needed since it simply
// removes multiples of 2^128. We can discard these excess bits
// outside of the loop when writing the hash in Little Endian.
}
return len(data), nil
}
func (s *sum128a) Size() int { return 16 }
func (s *sum128a) BlockSize() int { return 1 }
func (s *sum128a) Sum(in []byte) []byte {
panic("FNV: not supported")
}
MIT License
Copyright (c) 2016 Lucas Clemente
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
#!/usr/bin/env ruby
#
# Extract the common certificate sets from the chromium source to go
#
# Usage:
# createCertSets.rb 1 ~/src/chromium/src/net/quic/crypto/common_cert_set_1*
# createCertSets.rb 2 ~/src/chromium/src/net/quic/crypto/common_cert_set_2*
n = ARGV.shift
mainFile = ARGV.shift
dataFiles = ARGV
data = "package certsets\n"
data += File.read(mainFile)
data += (dataFiles.map{|p| File.read(p)}).join
# Good enough
data.gsub!(/\/\*(.*?)\*\//m, '')
data.gsub!(/^#include.+/, '')
data.gsub!(/^#if 0(.*?)\n#endif/m, '')
data.gsub!(/^static const size_t kNumCerts.+/, '')
data.gsub!(/static const size_t kLens[^}]+};/m, '')
data.gsub!('static const unsigned char* const kCerts[] = {', "var CertSet#{n} = [][]byte{")
data.gsub!('static const uint64_t kHash = UINT64_C', "const CertSet#{n}Hash uint64 = ")
data.gsub!(/static const unsigned char kDERCert(\d+)\[\] = /, "var kDERCert\\1 = []byte")
data.gsub!(/kDERCert(\d+)/, "certSet#{n}Cert\\1")
File.write("cert_set_#{n}.go", data)
system("gofmt -w -s cert_set_#{n}.go")
# Changelog # Changelog
## v0.6.0 (unreleased) ## v0.10.0 (2018-08-28)
- Add support for QUIC 44, drop support for QUIC 42.
## v0.9.0 (2018-08-15)
- Add a `quic.Config` option for the length of the connection ID (for IETF QUIC).
- Split Session.Close into one method for regular closing and one for closing with an error.
## v0.8.0 (2018-06-26)
- Add support for unidirectional streams (for IETF QUIC).
- Add a `quic.Config` option for the maximum number of incoming streams.
- Add support for QUIC 42 and 43.
- Add dial functions that use a context.
- Multiplex clients on a net.PacketConn, when using Dial(conn).
## v0.7.0 (2018-02-03)
- The lower boundary for packets included in ACKs is now derived, and the value sent in STOP_WAITING frames is ignored.
- Remove `DialNonFWSecure` and `DialAddrNonFWSecure`.
- Expose the `ConnectionState` in the `Session` (experimental API).
- Implement packet pacing.
## v0.6.0 (2017-12-12)
- Add support for QUIC 39, drop support for QUIC 35 - 37 - Add support for QUIC 39, drop support for QUIC 35 - 37
- Added `quic.Config` options for maximal flow control windows - Added `quic.Config` options for maximal flow control windows
......
...@@ -3,16 +3,24 @@ ...@@ -3,16 +3,24 @@
<img src="docs/quic.png" width=303 height=124> <img src="docs/quic.png" width=303 height=124>
[![Godoc Reference](https://img.shields.io/badge/godoc-reference-blue.svg?style=flat-square)](https://godoc.org/github.com/lucas-clemente/quic-go) [![Godoc Reference](https://img.shields.io/badge/godoc-reference-blue.svg?style=flat-square)](https://godoc.org/github.com/lucas-clemente/quic-go)
[![Linux Build Status](https://img.shields.io/travis/lucas-clemente/quic-go/master.svg?style=flat-square&label=linux+build)](https://travis-ci.org/lucas-clemente/quic-go) [![Travis Build Status](https://img.shields.io/travis/lucas-clemente/quic-go/master.svg?style=flat-square&label=Travis+build)](https://travis-ci.org/lucas-clemente/quic-go)
[![CircleCI Build Status](https://img.shields.io/circleci/project/github/lucas-clemente/quic-go.svg?style=flat-square&label=CircleCI+build)](https://circleci.com/gh/lucas-clemente/quic-go)
[![Windows Build Status](https://img.shields.io/appveyor/ci/lucas-clemente/quic-go/master.svg?style=flat-square&label=windows+build)](https://ci.appveyor.com/project/lucas-clemente/quic-go/branch/master) [![Windows Build Status](https://img.shields.io/appveyor/ci/lucas-clemente/quic-go/master.svg?style=flat-square&label=windows+build)](https://ci.appveyor.com/project/lucas-clemente/quic-go/branch/master)
[![Code Coverage](https://img.shields.io/codecov/c/github/lucas-clemente/quic-go/master.svg?style=flat-square)](https://codecov.io/gh/lucas-clemente/quic-go/) [![Code Coverage](https://img.shields.io/codecov/c/github/lucas-clemente/quic-go/master.svg?style=flat-square)](https://codecov.io/gh/lucas-clemente/quic-go/)
quic-go is an implementation of the [QUIC](https://en.wikipedia.org/wiki/QUIC) protocol in Go. quic-go is an implementation of the [QUIC](https://en.wikipedia.org/wiki/QUIC) protocol in Go. It roughly implements the [IETF QUIC draft](https://github.com/quicwg/base-drafts), although we don't fully support any of the draft versions at the moment.
## Roadmap ## Version compatibility
quic-go is compatible with the current version(s) of Google Chrome and QUIC as deployed on Google's servers. We're actively tracking the development of the Chrome code to ensure compatibility as the protocol evolves. In that process, we're dropping support for old QUIC versions. Since quic-go is under active development, there's no guarantee that two builds of different commits are interoperable. The QUIC version used in the *master* branch is just a placeholder, and should not be considered stable.
As Google's QUIC versions are expected to converge towards the [IETF QUIC draft](https://github.com/quicwg/base-drafts), quic-go will eventually implement that draft.
If you want to use quic-go as a library in other projects, please consider using a [tagged release](https://github.com/lucas-clemente/quic-go/releases). These releases expose [experimental QUIC versions](https://github.com/quicwg/base-drafts/wiki/QUIC-Versions), which are guaranteed to be stable.
## Google QUIC
quic-go used to support both the QUIC versions supported by Google Chrome and QUIC as deployed on Google's servers, as well as IETF QUIC. Due to the divergence of the two protocols, we decided to not support both versions any more.
The *master* branch **only** supports IETF QUIC. For Google QUIC support, please refer to the [gquic branch](https://github.com/lucas-clemente/quic-go/tree/gquic).
## Guides ## Guides
...@@ -26,31 +34,19 @@ Running tests: ...@@ -26,31 +34,19 @@ Running tests:
go test ./... go test ./...
### Running the example server ### HTTP mapping
go run example/main.go -www /var/www/
Using the `quic_client` from chromium: We're currently not implementing the HTTP mapping as described in the [QUIC over HTTP draft](https://quicwg.org/base-drafts/draft-ietf-quic-http.html). The HTTP mapping here is a leftover from Google QUIC.
quic_client --host=127.0.0.1 --port=6121 --v=1 https://quic.clemente.io
Using Chrome:
/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome --user-data-dir=/tmp/chrome --no-proxy-server --enable-quic --origin-to-force-quic-on=quic.clemente.io:443 --host-resolver-rules='MAP quic.clemente.io:443 127.0.0.1:6121' https://quic.clemente.io
### QUIC without HTTP/2 ### QUIC without HTTP/2
Take a look at [this echo example](example/echo/echo.go). Take a look at [this echo example](example/echo/echo.go).
### Using the example client
go run example/client/main.go https://clemente.io
## Usage ## Usage
### As a server ### As a server
See the [example server](example/main.go) or try out [Caddy](https://github.com/mholt/caddy) (from version 0.9, [instructions here](https://github.com/mholt/caddy/wiki/QUIC)). Starting a QUIC server is very similar to the standard lib http in go: See the [example server](example/main.go). Starting a QUIC server is very similar to the standard lib http in go:
```go ```go
http.Handle("/", http.FileServer(http.Dir(wwwDir))) http.Handle("/", http.FileServer(http.Dir(wwwDir)))
......
package main
import (
_ "github.com/clipperhouse/linkedlist"
_ "github.com/clipperhouse/slice"
_ "github.com/clipperhouse/stringer"
)
package ackhandler
import (
"errors"
"time"
"github.com/lucas-clemente/quic-go/internal/protocol"
"github.com/lucas-clemente/quic-go/internal/wire"
)
var errInvalidPacketNumber = errors.New("ReceivedPacketHandler: Invalid packet number")
type receivedPacketHandler struct {
largestObserved protocol.PacketNumber
lowerLimit protocol.PacketNumber
largestObservedReceivedTime time.Time
packetHistory *receivedPacketHistory
ackSendDelay time.Duration
packetsReceivedSinceLastAck int
retransmittablePacketsReceivedSinceLastAck int
ackQueued bool
ackAlarm time.Time
lastAck *wire.AckFrame
version protocol.VersionNumber
}
// NewReceivedPacketHandler creates a new receivedPacketHandler
func NewReceivedPacketHandler(version protocol.VersionNumber) ReceivedPacketHandler {
return &receivedPacketHandler{
packetHistory: newReceivedPacketHistory(),
ackSendDelay: protocol.AckSendDelay,
version: version,
}
}
func (h *receivedPacketHandler) ReceivedPacket(packetNumber protocol.PacketNumber, shouldInstigateAck bool) error {
if packetNumber == 0 {
return errInvalidPacketNumber
}
if packetNumber > h.largestObserved {
h.largestObserved = packetNumber
h.largestObservedReceivedTime = time.Now()
}
if packetNumber <= h.lowerLimit {
return nil
}
if err := h.packetHistory.ReceivedPacket(packetNumber); err != nil {
return err
}
h.maybeQueueAck(packetNumber, shouldInstigateAck)
return nil
}
// SetLowerLimit sets a lower limit for acking packets.
// Packets with packet numbers smaller or equal than p will not be acked.
func (h *receivedPacketHandler) SetLowerLimit(p protocol.PacketNumber) {
h.lowerLimit = p
h.packetHistory.DeleteUpTo(p)
}
func (h *receivedPacketHandler) maybeQueueAck(packetNumber protocol.PacketNumber, shouldInstigateAck bool) {
h.packetsReceivedSinceLastAck++
if shouldInstigateAck {
h.retransmittablePacketsReceivedSinceLastAck++
}
// always ack the first packet
if h.lastAck == nil {
h.ackQueued = true
}
if h.version < protocol.Version39 {
// Always send an ack every 20 packets in order to allow the peer to discard
// information from the SentPacketManager and provide an RTT measurement.
// From QUIC 39, this is not needed anymore, since the peer will regularly send a retransmittable packet.
if h.packetsReceivedSinceLastAck >= protocol.MaxPacketsReceivedBeforeAckSend {
h.ackQueued = true
}
}
// if the packet number is smaller than the largest acked packet, it must have been reported missing with the last ACK
// note that it cannot be a duplicate because they're already filtered out by ReceivedPacket()
if h.lastAck != nil && packetNumber < h.lastAck.LargestAcked {
h.ackQueued = true
}
// check if a new missing range above the previously was created
if h.lastAck != nil && h.packetHistory.GetHighestAckRange().First > h.lastAck.LargestAcked {
h.ackQueued = true
}
if !h.ackQueued && shouldInstigateAck {
if h.retransmittablePacketsReceivedSinceLastAck >= protocol.RetransmittablePacketsBeforeAck {
h.ackQueued = true
} else {
if h.ackAlarm.IsZero() {
h.ackAlarm = time.Now().Add(h.ackSendDelay)
}
}
}
if h.ackQueued {
// cancel the ack alarm
h.ackAlarm = time.Time{}
}
}
func (h *receivedPacketHandler) GetAckFrame() *wire.AckFrame {
if !h.ackQueued && (h.ackAlarm.IsZero() || h.ackAlarm.After(time.Now())) {
return nil
}
ackRanges := h.packetHistory.GetAckRanges()
ack := &wire.AckFrame{
LargestAcked: h.largestObserved,
LowestAcked: ackRanges[len(ackRanges)-1].First,
PacketReceivedTime: h.largestObservedReceivedTime,
}
if len(ackRanges) > 1 {
ack.AckRanges = ackRanges
}
h.lastAck = ack
h.ackAlarm = time.Time{}
h.ackQueued = false
h.packetsReceivedSinceLastAck = 0
h.retransmittablePacketsReceivedSinceLastAck = 0
return ack
}
func (h *receivedPacketHandler) GetAlarmTimeout() time.Time { return h.ackAlarm }
package ackhandler
import (
"github.com/lucas-clemente/quic-go/internal/protocol"
"github.com/lucas-clemente/quic-go/internal/wire"
)
// This stopWaitingManager is not supposed to satisfy the StopWaitingManager interface, which is a remnant of the legacy AckHandler, and should be remove once we drop support for QUIC 33
type stopWaitingManager struct {
largestLeastUnackedSent protocol.PacketNumber
nextLeastUnacked protocol.PacketNumber
lastStopWaitingFrame *wire.StopWaitingFrame
}
func (s *stopWaitingManager) GetStopWaitingFrame(force bool) *wire.StopWaitingFrame {
if s.nextLeastUnacked <= s.largestLeastUnackedSent {
if force {
return s.lastStopWaitingFrame
}
return nil
}
s.largestLeastUnackedSent = s.nextLeastUnacked
swf := &wire.StopWaitingFrame{
LeastUnacked: s.nextLeastUnacked,
}
s.lastStopWaitingFrame = swf
return swf
}
func (s *stopWaitingManager) ReceivedAck(ack *wire.AckFrame) {
if ack.LargestAcked >= s.nextLeastUnacked {
s.nextLeastUnacked = ack.LargestAcked + 1
}
}
func (s *stopWaitingManager) QueuedRetransmissionForPacketNumber(p protocol.PacketNumber) {
if p >= s.nextLeastUnacked {
s.nextLeastUnacked = p + 1
}
}
...@@ -10,16 +10,18 @@ environment: ...@@ -10,16 +10,18 @@ environment:
- GOARCH: 386 - GOARCH: 386
- GOARCH: amd64 - GOARCH: amd64
hosts:
quic.clemente.io: 127.0.0.1
clone_folder: c:\gopath\src\github.com\lucas-clemente\quic-go clone_folder: c:\gopath\src\github.com\lucas-clemente\quic-go
install: install:
- rmdir c:\go /s /q - rmdir c:\go /s /q
- appveyor DownloadFile https://storage.googleapis.com/golang/go1.9.2.windows-amd64.zip - appveyor DownloadFile https://storage.googleapis.com/golang/go1.11.windows-amd64.zip
- 7z x go1.9.2.windows-amd64.zip -y -oC:\ > NUL - 7z x go1.11.windows-amd64.zip -y -oC:\ > NUL
- set PATH=%PATH%;%GOPATH%\bin\windows_%GOARCH%;%GOPATH%\bin - set PATH=%PATH%;%GOPATH%\bin\windows_%GOARCH%;%GOPATH%\bin
- echo %PATH% - echo %PATH%
- echo %GOPATH% - echo %GOPATH%
- git submodule update --init --recursive
- go get github.com/onsi/ginkgo/ginkgo - go get github.com/onsi/ginkgo/ginkgo
- go get github.com/onsi/gomega - go get github.com/onsi/gomega
- go version - go version
......
...@@ -8,19 +8,20 @@ import ( ...@@ -8,19 +8,20 @@ import (
var bufferPool sync.Pool var bufferPool sync.Pool
func getPacketBuffer() []byte { func getPacketBuffer() *[]byte {
return bufferPool.Get().([]byte) return bufferPool.Get().(*[]byte)
} }
func putPacketBuffer(buf []byte) { func putPacketBuffer(buf *[]byte) {
if cap(buf) != int(protocol.MaxReceivePacketSize) { if cap(*buf) != int(protocol.MaxReceivePacketSize) {
panic("putPacketBuffer called with packet of wrong size!") panic("putPacketBuffer called with packet of wrong size!")
} }
bufferPool.Put(buf[:0]) bufferPool.Put(buf)
} }
func init() { func init() {
bufferPool.New = func() interface{} { bufferPool.New = func() interface{} {
return make([]byte, 0, protocol.MaxReceivePacketSize) b := make([]byte, 0, protocol.MaxReceivePacketSize)
return &b
} }
} }
coverage: coverage:
round: nearest round: nearest
ignore: ignore:
- ackhandler/packet_linkedlist.go - streams_map_incoming_bidi.go
- streams_map_incoming_uni.go
- streams_map_outgoing_bidi.go
- streams_map_outgoing_uni.go
- h2quic/gzipreader.go - h2quic/gzipreader.go
- h2quic/response.go - h2quic/response.go
- internal/ackhandler/packet_linkedlist.go
- internal/utils/byteinterval_linkedlist.go - internal/utils/byteinterval_linkedlist.go
- internal/utils/packetinterval_linkedlist.go - internal/utils/packetinterval_linkedlist.go
- internal/utils/linkedlist/linkedlist.go
status: status:
project: project:
default: default:
......
package quic
import (
"errors"
"fmt"
"io"
"github.com/lucas-clemente/quic-go/internal/protocol"
"github.com/lucas-clemente/quic-go/internal/utils"
"github.com/lucas-clemente/quic-go/internal/wire"
)
type cryptoStream interface {
// for receiving data
HandleCryptoFrame(*wire.CryptoFrame) error
GetCryptoData() []byte
Finish() error
// for sending data
io.Writer
HasData() bool
PopCryptoFrame(protocol.ByteCount) *wire.CryptoFrame
}
type cryptoStreamImpl struct {
queue *frameSorter
msgBuf []byte
highestOffset protocol.ByteCount
finished bool
writeOffset protocol.ByteCount
writeBuf []byte
}
func newCryptoStream() cryptoStream {
return &cryptoStreamImpl{
queue: newFrameSorter(),
}
}
func (s *cryptoStreamImpl) HandleCryptoFrame(f *wire.CryptoFrame) error {
highestOffset := f.Offset + protocol.ByteCount(len(f.Data))
if maxOffset := highestOffset; maxOffset > protocol.MaxCryptoStreamOffset {
return fmt.Errorf("received invalid offset %d on crypto stream, maximum allowed %d", maxOffset, protocol.MaxCryptoStreamOffset)
}
if s.finished {
if highestOffset > s.highestOffset {
// reject crypto data received after this stream was already finished
return errors.New("received crypto data after change of encryption level")
}
// ignore data with a smaller offset than the highest received
// could e.g. be a retransmission
return nil
}
s.highestOffset = utils.MaxByteCount(s.highestOffset, highestOffset)
if err := s.queue.Push(f.Data, f.Offset, false); err != nil {
return err
}
for {
data, _ := s.queue.Pop()
if data == nil {
return nil
}
s.msgBuf = append(s.msgBuf, data...)
}
}
// GetCryptoData retrieves data that was received in CRYPTO frames
func (s *cryptoStreamImpl) GetCryptoData() []byte {
if len(s.msgBuf) < 4 {
return nil
}
msgLen := 4 + int(s.msgBuf[1])<<16 + int(s.msgBuf[2])<<8 + int(s.msgBuf[3])
if len(s.msgBuf) < msgLen {
return nil
}
msg := make([]byte, msgLen)
copy(msg, s.msgBuf[:msgLen])
s.msgBuf = s.msgBuf[msgLen:]
return msg
}
func (s *cryptoStreamImpl) Finish() error {
if s.queue.HasMoreData() {
return errors.New("encryption level changed, but crypto stream has more data to read")
}
s.finished = true
return nil
}
// Writes writes data that should be sent out in CRYPTO frames
func (s *cryptoStreamImpl) Write(p []byte) (int, error) {
s.writeBuf = append(s.writeBuf, p...)
return len(p), nil
}
func (s *cryptoStreamImpl) HasData() bool {
return len(s.writeBuf) > 0
}
func (s *cryptoStreamImpl) PopCryptoFrame(maxLen protocol.ByteCount) *wire.CryptoFrame {
f := &wire.CryptoFrame{Offset: s.writeOffset}
n := utils.MinByteCount(f.MaxDataLen(maxLen), protocol.ByteCount(len(s.writeBuf)))
f.Data = s.writeBuf[:n]
s.writeBuf = s.writeBuf[n:]
s.writeOffset += n
return f
}
package quic
import (
"fmt"
"github.com/lucas-clemente/quic-go/internal/protocol"
"github.com/lucas-clemente/quic-go/internal/wire"
)
type cryptoDataHandler interface {
HandleMessage([]byte, protocol.EncryptionLevel) bool
}
type cryptoStreamManager struct {
cryptoHandler cryptoDataHandler
initialStream cryptoStream
handshakeStream cryptoStream
}
func newCryptoStreamManager(
cryptoHandler cryptoDataHandler,
initialStream cryptoStream,
handshakeStream cryptoStream,
) *cryptoStreamManager {
return &cryptoStreamManager{
cryptoHandler: cryptoHandler,
initialStream: initialStream,
handshakeStream: handshakeStream,
}
}
func (m *cryptoStreamManager) HandleCryptoFrame(frame *wire.CryptoFrame, encLevel protocol.EncryptionLevel) (bool /* encryption level changed */, error) {
var str cryptoStream
switch encLevel {
case protocol.EncryptionInitial:
str = m.initialStream
case protocol.EncryptionHandshake:
str = m.handshakeStream
default:
return false, fmt.Errorf("received CRYPTO frame with unexpected encryption level: %s", encLevel)
}
if err := str.HandleCryptoFrame(frame); err != nil {
return false, err
}
for {
data := str.GetCryptoData()
if data == nil {
return false, nil
}
if encLevelFinished := m.cryptoHandler.HandleMessage(data, encLevel); encLevelFinished {
return true, str.Finish()
}
}
}
...@@ -5,51 +5,55 @@ import ( ...@@ -5,51 +5,55 @@ import (
"github.com/lucas-clemente/quic-go/internal/protocol" "github.com/lucas-clemente/quic-go/internal/protocol"
"github.com/lucas-clemente/quic-go/internal/utils" "github.com/lucas-clemente/quic-go/internal/utils"
"github.com/lucas-clemente/quic-go/internal/wire"
) )
type streamFrameSorter struct { type frameSorter struct {
queuedFrames map[protocol.ByteCount]*wire.StreamFrame queue map[protocol.ByteCount][]byte
readPosition protocol.ByteCount readPos protocol.ByteCount
gaps *utils.ByteIntervalList finalOffset protocol.ByteCount
gaps *utils.ByteIntervalList
} }
var ( var errDuplicateStreamData = errors.New("Duplicate Stream Data")
errTooManyGapsInReceivedStreamData = errors.New("Too many gaps in received StreamFrame data")
errDuplicateStreamData = errors.New("Duplicate Stream Data")
errEmptyStreamData = errors.New("Stream Data empty")
)
func newStreamFrameSorter() *streamFrameSorter { func newFrameSorter() *frameSorter {
s := streamFrameSorter{ s := frameSorter{
gaps: utils.NewByteIntervalList(), gaps: utils.NewByteIntervalList(),
queuedFrames: make(map[protocol.ByteCount]*wire.StreamFrame), queue: make(map[protocol.ByteCount][]byte),
finalOffset: protocol.MaxByteCount,
} }
s.gaps.PushFront(utils.ByteInterval{Start: 0, End: protocol.MaxByteCount}) s.gaps.PushFront(utils.ByteInterval{Start: 0, End: protocol.MaxByteCount})
return &s return &s
} }
func (s *streamFrameSorter) Push(frame *wire.StreamFrame) error { func (s *frameSorter) Push(data []byte, offset protocol.ByteCount, fin bool) error {
if frame.DataLen() == 0 { err := s.push(data, offset, fin)
if frame.FinBit { if err == errDuplicateStreamData {
s.queuedFrames[frame.Offset] = frame return nil
return nil }
} return err
return errEmptyStreamData }
func (s *frameSorter) push(data []byte, offset protocol.ByteCount, fin bool) error {
if fin {
s.finalOffset = offset + protocol.ByteCount(len(data))
}
if len(data) == 0 {
return nil
} }
var wasCut bool var wasCut bool
if oldFrame, ok := s.queuedFrames[frame.Offset]; ok { if oldData, ok := s.queue[offset]; ok {
if frame.DataLen() <= oldFrame.DataLen() { if len(data) <= len(oldData) {
return errDuplicateStreamData return errDuplicateStreamData
} }
frame.Data = frame.Data[oldFrame.DataLen():] data = data[len(oldData):]
frame.Offset += oldFrame.DataLen() offset += protocol.ByteCount(len(oldData))
wasCut = true wasCut = true
} }
start := frame.Offset start := offset
end := frame.Offset + frame.DataLen() end := offset + protocol.ByteCount(len(data))
// skip all gaps that are before this stream frame // skip all gaps that are before this stream frame
var gap *utils.ByteIntervalElement var gap *utils.ByteIntervalElement
...@@ -69,9 +73,9 @@ func (s *streamFrameSorter) Push(frame *wire.StreamFrame) error { ...@@ -69,9 +73,9 @@ func (s *streamFrameSorter) Push(frame *wire.StreamFrame) error {
if start < gap.Value.Start { if start < gap.Value.Start {
add := gap.Value.Start - start add := gap.Value.Start - start
frame.Offset += add offset += add
start += add start += add
frame.Data = frame.Data[add:] data = data[add:]
wasCut = true wasCut = true
} }
...@@ -89,15 +93,15 @@ func (s *streamFrameSorter) Push(frame *wire.StreamFrame) error { ...@@ -89,15 +93,15 @@ func (s *streamFrameSorter) Push(frame *wire.StreamFrame) error {
break break
} }
// delete queued frames completely covered by the current frame // delete queued frames completely covered by the current frame
delete(s.queuedFrames, endGap.Value.End) delete(s.queue, endGap.Value.End)
endGap = nextEndGap endGap = nextEndGap
} }
if end > endGap.Value.End { if end > endGap.Value.End {
cutLen := end - endGap.Value.End cutLen := end - endGap.Value.End
len := frame.DataLen() - cutLen len := protocol.ByteCount(len(data)) - cutLen
end -= cutLen end -= cutLen
frame.Data = frame.Data[:len] data = data[:len]
wasCut = true wasCut = true
} }
...@@ -130,32 +134,30 @@ func (s *streamFrameSorter) Push(frame *wire.StreamFrame) error { ...@@ -130,32 +134,30 @@ func (s *streamFrameSorter) Push(frame *wire.StreamFrame) error {
} }
if s.gaps.Len() > protocol.MaxStreamFrameSorterGaps { if s.gaps.Len() > protocol.MaxStreamFrameSorterGaps {
return errTooManyGapsInReceivedStreamData return errors.New("Too many gaps in received data")
} }
if wasCut { if wasCut {
data := make([]byte, frame.DataLen()) newData := make([]byte, len(data))
copy(data, frame.Data) copy(newData, data)
frame.Data = data data = newData
} }
s.queuedFrames[frame.Offset] = frame s.queue[offset] = data
return nil return nil
} }
func (s *streamFrameSorter) Pop() *wire.StreamFrame { func (s *frameSorter) Pop() ([]byte /* data */, bool /* fin */) {
frame := s.Head() data, ok := s.queue[s.readPos]
if frame != nil { if !ok {
s.readPosition += frame.DataLen() return nil, s.readPos >= s.finalOffset
delete(s.queuedFrames, frame.Offset)
} }
return frame delete(s.queue, s.readPos)
s.readPos += protocol.ByteCount(len(data))
return data, s.readPos >= s.finalOffset
} }
func (s *streamFrameSorter) Head() *wire.StreamFrame { // HasMoreData says if there is any more data queued at *any* offset.
frame, ok := s.queuedFrames[s.readPosition] func (s *frameSorter) HasMoreData() bool {
if ok { return len(s.queue) > 0
return frame
}
return nil
} }
This diff is collapsed.
package ackhandler
//go:generate genny -pkg ackhandler -in ../utils/linkedlist/linkedlist.go -out packet_linkedlist.go gen Item=Packet
...@@ -16,8 +16,6 @@ func stripNonRetransmittableFrames(fs []wire.Frame) []wire.Frame { ...@@ -16,8 +16,6 @@ func stripNonRetransmittableFrames(fs []wire.Frame) []wire.Frame {
// IsFrameRetransmittable returns true if the frame should be retransmitted. // IsFrameRetransmittable returns true if the frame should be retransmitted.
func IsFrameRetransmittable(f wire.Frame) bool { func IsFrameRetransmittable(f wire.Frame) bool {
switch f.(type) { switch f.(type) {
case *wire.StopWaitingFrame:
return false
case *wire.AckFrame: case *wire.AckFrame:
return false return false
default: default:
......
package crypto
import (
"bytes"
"github.com/lucas-clemente/quic-go-certificates"
)
type certSet [][]byte
var certSets = map[uint64]certSet{
certsets.CertSet2Hash: certsets.CertSet2,
certsets.CertSet3Hash: certsets.CertSet3,
}
// findCertInSet searches for the cert in the set. Negative return value means not found.
func (s *certSet) findCertInSet(cert []byte) int {
for i, c := range *s {
if bytes.Equal(c, cert) {
return i
}
}
return -1
}
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment