mirror of
https://github.com/refraction-networking/uquic.git
synced 2025-04-03 20:27:35 +03:00
name the quic.Cookie to quic.Token
This commit is contained in:
parent
06da72ae4e
commit
1d6707325f
12 changed files with 234 additions and 232 deletions
|
@ -3,6 +3,7 @@
|
|||
## v0.12.0 (unreleased)
|
||||
|
||||
- Implement HTTP/3.
|
||||
- Rename `quic.Cookie` to `quic.Token` and `quic.Config.AcceptCookie` to `quic.Config.AcceptToken`.
|
||||
|
||||
## v0.11.0 (2019-04-05)
|
||||
|
||||
|
|
|
@ -113,8 +113,8 @@ var _ = Describe("Handshake RTT tests", func() {
|
|||
expectDurationInRTTs(2)
|
||||
})
|
||||
|
||||
It("establishes a connection in 1 RTT when the server doesn't require a Cookie", func() {
|
||||
serverConfig.AcceptCookie = func(_ net.Addr, _ *quic.Cookie) bool {
|
||||
It("establishes a connection in 1 RTT when the server doesn't require a token", func() {
|
||||
serverConfig.AcceptToken = func(_ net.Addr, _ *quic.Token) bool {
|
||||
return true
|
||||
}
|
||||
runServerAndProxy()
|
||||
|
@ -128,7 +128,7 @@ var _ = Describe("Handshake RTT tests", func() {
|
|||
})
|
||||
|
||||
It("establishes a connection in 2 RTTs if a HelloRetryRequest is performed", func() {
|
||||
serverConfig.AcceptCookie = func(_ net.Addr, _ *quic.Cookie) bool {
|
||||
serverConfig.AcceptToken = func(_ net.Addr, _ *quic.Token) bool {
|
||||
return true
|
||||
}
|
||||
serverTLSConfig.CurvePreferences = []tls.CurveID{tls.CurveP384}
|
||||
|
@ -142,8 +142,8 @@ var _ = Describe("Handshake RTT tests", func() {
|
|||
expectDurationInRTTs(2)
|
||||
})
|
||||
|
||||
It("doesn't complete the handshake when the server never accepts the Cookie", func() {
|
||||
serverConfig.AcceptCookie = func(_ net.Addr, _ *quic.Cookie) bool {
|
||||
It("doesn't complete the handshake when the server never accepts the token", func() {
|
||||
serverConfig.AcceptToken = func(_ net.Addr, _ *quic.Token) bool {
|
||||
return false
|
||||
}
|
||||
clientConfig.HandshakeTimeout = 500 * time.Millisecond
|
||||
|
|
|
@ -196,7 +196,7 @@ var _ = Describe("Handshake tests", func() {
|
|||
}
|
||||
|
||||
BeforeEach(func() {
|
||||
serverConfig.AcceptCookie = func(net.Addr, *quic.Cookie) bool { return true }
|
||||
serverConfig.AcceptToken = func(net.Addr, *quic.Token) bool { return true }
|
||||
var err error
|
||||
// start the server, but don't call Accept
|
||||
server, err = quic.ListenAddr("localhost:0", testdata.GetTLSConfig(), serverConfig)
|
||||
|
|
12
interface.go
12
interface.go
|
@ -16,8 +16,8 @@ type StreamID = protocol.StreamID
|
|||
// A VersionNumber is a QUIC version number.
|
||||
type VersionNumber = protocol.VersionNumber
|
||||
|
||||
// A Cookie can be used to verify the ownership of the client address.
|
||||
type Cookie struct {
|
||||
// A Token can be used to verify the ownership of the client address.
|
||||
type Token struct {
|
||||
RemoteAddr string
|
||||
SentTime time.Time
|
||||
}
|
||||
|
@ -187,11 +187,11 @@ type Config struct {
|
|||
// If the timeout is exceeded, the connection is closed.
|
||||
// If this value is zero, the timeout is set to 30 seconds.
|
||||
IdleTimeout time.Duration
|
||||
// AcceptCookie determines if a Cookie is accepted.
|
||||
// It is called with cookie = nil if the client didn't send an Cookie.
|
||||
// If not set, it verifies that the address matches, and that the Cookie was issued within the last 24 hours.
|
||||
// AcceptToken determines if a Token is accepted.
|
||||
// It is called with token = nil if the client didn't send a token.
|
||||
// If not set, it verifies that the address matches, and that the token was issued within the last 24 hours.
|
||||
// This option is only valid for the server.
|
||||
AcceptCookie func(clientAddr net.Addr, cookie *Cookie) bool
|
||||
AcceptToken func(clientAddr net.Addr, token *Token) bool
|
||||
// MaxReceiveStreamFlowControlWindow is the maximum stream-level flow control window for receiving data.
|
||||
// If this value is zero, it will default to 1 MB for the server and 6 MB for the client.
|
||||
MaxReceiveStreamFlowControlWindow uint64
|
||||
|
|
|
@ -1,109 +0,0 @@
|
|||
package handshake
|
||||
|
||||
import (
|
||||
"encoding/asn1"
|
||||
"fmt"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/lucas-clemente/quic-go/internal/protocol"
|
||||
)
|
||||
|
||||
const (
|
||||
cookiePrefixIP byte = iota
|
||||
cookiePrefixString
|
||||
)
|
||||
|
||||
// A Cookie is derived from the client address and can be used to verify the ownership of this address.
|
||||
type Cookie struct {
|
||||
RemoteAddr string
|
||||
OriginalDestConnectionID protocol.ConnectionID
|
||||
// The time that the Cookie was issued (resolution 1 second)
|
||||
SentTime time.Time
|
||||
}
|
||||
|
||||
// token is the struct that is used for ASN1 serialization and deserialization
|
||||
type token struct {
|
||||
RemoteAddr []byte
|
||||
OriginalDestConnectionID []byte
|
||||
|
||||
Timestamp int64
|
||||
}
|
||||
|
||||
// A CookieGenerator generates Cookies
|
||||
type CookieGenerator struct {
|
||||
cookieProtector cookieProtector
|
||||
}
|
||||
|
||||
// NewCookieGenerator initializes a new CookieGenerator
|
||||
func NewCookieGenerator() (*CookieGenerator, error) {
|
||||
cookieProtector, err := newCookieProtector()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &CookieGenerator{
|
||||
cookieProtector: cookieProtector,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// NewToken generates a new Cookie for a given source address
|
||||
func (g *CookieGenerator) NewToken(raddr net.Addr, origConnID protocol.ConnectionID) ([]byte, error) {
|
||||
data, err := asn1.Marshal(token{
|
||||
RemoteAddr: encodeRemoteAddr(raddr),
|
||||
OriginalDestConnectionID: origConnID,
|
||||
Timestamp: time.Now().Unix(),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return g.cookieProtector.NewToken(data)
|
||||
}
|
||||
|
||||
// DecodeToken decodes a Cookie
|
||||
func (g *CookieGenerator) DecodeToken(encrypted []byte) (*Cookie, error) {
|
||||
// if the client didn't send any Cookie, DecodeToken will be called with a nil-slice
|
||||
if len(encrypted) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
data, err := g.cookieProtector.DecodeToken(encrypted)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
t := &token{}
|
||||
rest, err := asn1.Unmarshal(data, t)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(rest) != 0 {
|
||||
return nil, fmt.Errorf("rest when unpacking token: %d", len(rest))
|
||||
}
|
||||
cookie := &Cookie{
|
||||
RemoteAddr: decodeRemoteAddr(t.RemoteAddr),
|
||||
SentTime: time.Unix(t.Timestamp, 0),
|
||||
}
|
||||
if len(t.OriginalDestConnectionID) > 0 {
|
||||
cookie.OriginalDestConnectionID = protocol.ConnectionID(t.OriginalDestConnectionID)
|
||||
}
|
||||
return cookie, nil
|
||||
}
|
||||
|
||||
// encodeRemoteAddr encodes a remote address such that it can be saved in the Cookie
|
||||
func encodeRemoteAddr(remoteAddr net.Addr) []byte {
|
||||
if udpAddr, ok := remoteAddr.(*net.UDPAddr); ok {
|
||||
return append([]byte{cookiePrefixIP}, udpAddr.IP...)
|
||||
}
|
||||
return append([]byte{cookiePrefixString}, []byte(remoteAddr.String())...)
|
||||
}
|
||||
|
||||
// decodeRemoteAddr decodes the remote address saved in the Cookie
|
||||
func decodeRemoteAddr(data []byte) string {
|
||||
// data will never be empty for a Cookie that we generated. Check it to be on the safe side
|
||||
if len(data) == 0 {
|
||||
return ""
|
||||
}
|
||||
if data[0] == cookiePrefixIP {
|
||||
return net.IP(data[1:]).String()
|
||||
}
|
||||
return string(data[1:])
|
||||
}
|
110
internal/handshake/token_generator.go
Normal file
110
internal/handshake/token_generator.go
Normal file
|
@ -0,0 +1,110 @@
|
|||
package handshake
|
||||
|
||||
import (
|
||||
"encoding/asn1"
|
||||
"fmt"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/lucas-clemente/quic-go/internal/protocol"
|
||||
)
|
||||
|
||||
const (
|
||||
tokenPrefixIP byte = iota
|
||||
tokenPrefixString
|
||||
)
|
||||
|
||||
// A Token is derived from the client address and can be used to verify the ownership of this address.
|
||||
type Token struct {
|
||||
RemoteAddr string
|
||||
OriginalDestConnectionID protocol.ConnectionID
|
||||
// The time that the Token was issued (resolution 1 second)
|
||||
SentTime time.Time
|
||||
}
|
||||
|
||||
// token is the struct that is used for ASN1 serialization and deserialization
|
||||
type token struct {
|
||||
RemoteAddr []byte
|
||||
OriginalDestConnectionID []byte
|
||||
|
||||
Timestamp int64
|
||||
}
|
||||
|
||||
// A TokenGenerator generates tokens
|
||||
type TokenGenerator struct {
|
||||
tokenProtector tokenProtector
|
||||
}
|
||||
|
||||
// NewTokenGenerator initializes a new TookenGenerator
|
||||
func NewTokenGenerator() (*TokenGenerator, error) {
|
||||
tokenProtector, err := newTokenProtector()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &TokenGenerator{
|
||||
tokenProtector: tokenProtector,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// NewToken generates a new token for a given source address
|
||||
func (g *TokenGenerator) NewToken(raddr net.Addr, origConnID protocol.ConnectionID) ([]byte, error) {
|
||||
data, err := asn1.Marshal(token{
|
||||
RemoteAddr: encodeRemoteAddr(raddr),
|
||||
OriginalDestConnectionID: origConnID,
|
||||
Timestamp: time.Now().Unix(),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return g.tokenProtector.NewToken(data)
|
||||
}
|
||||
|
||||
// DecodeToken decodes a token
|
||||
func (g *TokenGenerator) DecodeToken(encrypted []byte) (*Token, error) {
|
||||
// if the client didn't send any token, DecodeToken will be called with a nil-slice
|
||||
if len(encrypted) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
data, err := g.tokenProtector.DecodeToken(encrypted)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
t := &token{}
|
||||
rest, err := asn1.Unmarshal(data, t)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(rest) != 0 {
|
||||
return nil, fmt.Errorf("rest when unpacking token: %d", len(rest))
|
||||
}
|
||||
token := &Token{
|
||||
RemoteAddr: decodeRemoteAddr(t.RemoteAddr),
|
||||
SentTime: time.Unix(t.Timestamp, 0),
|
||||
}
|
||||
if len(t.OriginalDestConnectionID) > 0 {
|
||||
token.OriginalDestConnectionID = protocol.ConnectionID(t.OriginalDestConnectionID)
|
||||
}
|
||||
return token, nil
|
||||
}
|
||||
|
||||
// encodeRemoteAddr encodes a remote address such that it can be saved in the token
|
||||
func encodeRemoteAddr(remoteAddr net.Addr) []byte {
|
||||
if udpAddr, ok := remoteAddr.(*net.UDPAddr); ok {
|
||||
return append([]byte{tokenPrefixIP}, udpAddr.IP...)
|
||||
}
|
||||
return append([]byte{tokenPrefixString}, []byte(remoteAddr.String())...)
|
||||
}
|
||||
|
||||
// decodeRemoteAddr decodes the remote address saved in the token
|
||||
func decodeRemoteAddr(data []byte) string {
|
||||
// data will never be empty for a token that we generated.
|
||||
// Check it to be on the safe side
|
||||
if len(data) == 0 {
|
||||
return ""
|
||||
}
|
||||
if data[0] == tokenPrefixIP {
|
||||
return net.IP(data[1:]).String()
|
||||
}
|
||||
return string(data[1:])
|
||||
}
|
|
@ -11,64 +11,64 @@ import (
|
|||
. "github.com/onsi/gomega"
|
||||
)
|
||||
|
||||
var _ = Describe("Cookie Generator", func() {
|
||||
var cookieGen *CookieGenerator
|
||||
var _ = Describe("Token Generator", func() {
|
||||
var tokenGen *TokenGenerator
|
||||
|
||||
BeforeEach(func() {
|
||||
var err error
|
||||
cookieGen, err = NewCookieGenerator()
|
||||
tokenGen, err = NewTokenGenerator()
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
})
|
||||
|
||||
It("generates a Cookie", func() {
|
||||
It("generates a token", func() {
|
||||
ip := net.IPv4(127, 0, 0, 1)
|
||||
token, err := cookieGen.NewToken(&net.UDPAddr{IP: ip, Port: 1337}, nil)
|
||||
token, err := tokenGen.NewToken(&net.UDPAddr{IP: ip, Port: 1337}, nil)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(token).ToNot(BeEmpty())
|
||||
})
|
||||
|
||||
It("works with nil tokens", func() {
|
||||
cookie, err := cookieGen.DecodeToken(nil)
|
||||
token, err := tokenGen.DecodeToken(nil)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(cookie).To(BeNil())
|
||||
Expect(token).To(BeNil())
|
||||
})
|
||||
|
||||
It("accepts a valid cookie", func() {
|
||||
It("accepts a valid token", func() {
|
||||
ip := net.IPv4(192, 168, 0, 1)
|
||||
token, err := cookieGen.NewToken(
|
||||
tokenEnc, err := tokenGen.NewToken(
|
||||
&net.UDPAddr{IP: ip, Port: 1337},
|
||||
nil,
|
||||
)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
cookie, err := cookieGen.DecodeToken(token)
|
||||
token, err := tokenGen.DecodeToken(tokenEnc)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(cookie.RemoteAddr).To(Equal("192.168.0.1"))
|
||||
// the time resolution of the Cookie is just 1 second
|
||||
// if Cookie generation and this check happen in "different seconds", the difference will be between 1 and 2 seconds
|
||||
Expect(cookie.SentTime).To(BeTemporally("~", time.Now(), 2*time.Second))
|
||||
Expect(cookie.OriginalDestConnectionID).To(BeNil())
|
||||
Expect(token.RemoteAddr).To(Equal("192.168.0.1"))
|
||||
// the time resolution of the token is just 1 second
|
||||
// if token generation and this check happen in "different seconds", the difference will be between 1 and 2 seconds
|
||||
Expect(token.SentTime).To(BeTemporally("~", time.Now(), 2*time.Second))
|
||||
Expect(token.OriginalDestConnectionID).To(BeNil())
|
||||
})
|
||||
|
||||
It("saves the connection ID", func() {
|
||||
token, err := cookieGen.NewToken(
|
||||
tokenEnc, err := tokenGen.NewToken(
|
||||
&net.UDPAddr{},
|
||||
protocol.ConnectionID{0xde, 0xad, 0xbe, 0xef},
|
||||
)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
cookie, err := cookieGen.DecodeToken(token)
|
||||
token, err := tokenGen.DecodeToken(tokenEnc)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(cookie.OriginalDestConnectionID).To(Equal(protocol.ConnectionID{0xde, 0xad, 0xbe, 0xef}))
|
||||
Expect(token.OriginalDestConnectionID).To(Equal(protocol.ConnectionID{0xde, 0xad, 0xbe, 0xef}))
|
||||
})
|
||||
|
||||
It("rejects invalid tokens", func() {
|
||||
_, err := cookieGen.DecodeToken([]byte("invalid token"))
|
||||
_, err := tokenGen.DecodeToken([]byte("invalid token"))
|
||||
Expect(err).To(HaveOccurred())
|
||||
})
|
||||
|
||||
It("rejects tokens that cannot be decoded", func() {
|
||||
token, err := cookieGen.cookieProtector.NewToken([]byte("foobar"))
|
||||
token, err := tokenGen.tokenProtector.NewToken([]byte("foobar"))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
_, err = cookieGen.DecodeToken(token)
|
||||
_, err = tokenGen.DecodeToken(token)
|
||||
Expect(err).To(HaveOccurred())
|
||||
})
|
||||
|
||||
|
@ -76,9 +76,9 @@ var _ = Describe("Cookie Generator", func() {
|
|||
t, err := asn1.Marshal(token{RemoteAddr: []byte("foobar")})
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
t = append(t, []byte("rest")...)
|
||||
enc, err := cookieGen.cookieProtector.NewToken(t)
|
||||
enc, err := tokenGen.tokenProtector.NewToken(t)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
_, err = cookieGen.DecodeToken(enc)
|
||||
_, err = tokenGen.DecodeToken(enc)
|
||||
Expect(err).To(MatchError("rest when unpacking token: 4"))
|
||||
})
|
||||
|
||||
|
@ -86,9 +86,9 @@ var _ = Describe("Cookie Generator", func() {
|
|||
It("doesn't panic if a tokens has no data", func() {
|
||||
t, err := asn1.Marshal(token{RemoteAddr: []byte("")})
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
enc, err := cookieGen.cookieProtector.NewToken(t)
|
||||
enc, err := tokenGen.tokenProtector.NewToken(t)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
_, err = cookieGen.DecodeToken(enc)
|
||||
_, err = tokenGen.DecodeToken(enc)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
})
|
||||
|
||||
|
@ -103,26 +103,26 @@ var _ = Describe("Cookie Generator", func() {
|
|||
ip := net.ParseIP(addr)
|
||||
Expect(ip).ToNot(BeNil())
|
||||
raddr := &net.UDPAddr{IP: ip, Port: 1337}
|
||||
token, err := cookieGen.NewToken(raddr, nil)
|
||||
tokenEnc, err := tokenGen.NewToken(raddr, nil)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
cookie, err := cookieGen.DecodeToken(token)
|
||||
token, err := tokenGen.DecodeToken(tokenEnc)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(cookie.RemoteAddr).To(Equal(ip.String()))
|
||||
// the time resolution of the Cookie is just 1 second
|
||||
// if Cookie generation and this check happen in "different seconds", the difference will be between 1 and 2 seconds
|
||||
Expect(cookie.SentTime).To(BeTemporally("~", time.Now(), 2*time.Second))
|
||||
Expect(token.RemoteAddr).To(Equal(ip.String()))
|
||||
// the time resolution of the token is just 1 second
|
||||
// if token generation and this check happen in "different seconds", the difference will be between 1 and 2 seconds
|
||||
Expect(token.SentTime).To(BeTemporally("~", time.Now(), 2*time.Second))
|
||||
}
|
||||
})
|
||||
|
||||
It("uses the string representation an address that is not a UDP address", func() {
|
||||
raddr := &net.TCPAddr{IP: net.IPv4(192, 168, 13, 37), Port: 1337}
|
||||
token, err := cookieGen.NewToken(raddr, nil)
|
||||
tokenEnc, err := tokenGen.NewToken(raddr, nil)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
cookie, err := cookieGen.DecodeToken(token)
|
||||
token, err := tokenGen.DecodeToken(tokenEnc)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(cookie.RemoteAddr).To(Equal("192.168.13.37:1337"))
|
||||
// the time resolution of the Cookie is just 1 second
|
||||
// if Cookie generation and this check happen in "different seconds", the difference will be between 1 and 2 seconds
|
||||
Expect(cookie.SentTime).To(BeTemporally("~", time.Now(), 2*time.Second))
|
||||
Expect(token.RemoteAddr).To(Equal("192.168.13.37:1337"))
|
||||
// the time resolution of the token is just 1 second
|
||||
// if token generation and this check happen in "different seconds", the difference will be between 1 and 2 seconds
|
||||
Expect(token.SentTime).To(BeTemporally("~", time.Now(), 2*time.Second))
|
||||
})
|
||||
})
|
|
@ -11,8 +11,8 @@ import (
|
|||
"golang.org/x/crypto/hkdf"
|
||||
)
|
||||
|
||||
// CookieProtector is used to create and verify a cookie
|
||||
type cookieProtector interface {
|
||||
// TokenProtector is used to create and verify a token
|
||||
type tokenProtector interface {
|
||||
// NewToken creates a new token
|
||||
NewToken([]byte) ([]byte, error)
|
||||
// DecodeToken decodes a token
|
||||
|
@ -20,27 +20,27 @@ type cookieProtector interface {
|
|||
}
|
||||
|
||||
const (
|
||||
cookieSecretSize = 32
|
||||
cookieNonceSize = 32
|
||||
tokenSecretSize = 32
|
||||
tokenNonceSize = 32
|
||||
)
|
||||
|
||||
// cookieProtector is used to create and verify a cookie
|
||||
type cookieProtectorImpl struct {
|
||||
// tokenProtector is used to create and verify a token
|
||||
type tokenProtectorImpl struct {
|
||||
secret []byte
|
||||
}
|
||||
|
||||
// newCookieProtector creates a source for source address tokens
|
||||
func newCookieProtector() (cookieProtector, error) {
|
||||
secret := make([]byte, cookieSecretSize)
|
||||
// newTokenProtector creates a source for source address tokens
|
||||
func newTokenProtector() (tokenProtector, error) {
|
||||
secret := make([]byte, tokenSecretSize)
|
||||
if _, err := rand.Read(secret); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &cookieProtectorImpl{secret: secret}, nil
|
||||
return &tokenProtectorImpl{secret: secret}, nil
|
||||
}
|
||||
|
||||
// NewToken encodes data into a new token.
|
||||
func (s *cookieProtectorImpl) NewToken(data []byte) ([]byte, error) {
|
||||
nonce := make([]byte, cookieNonceSize)
|
||||
func (s *tokenProtectorImpl) NewToken(data []byte) ([]byte, error) {
|
||||
nonce := make([]byte, tokenNonceSize)
|
||||
if _, err := rand.Read(nonce); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -52,20 +52,20 @@ func (s *cookieProtectorImpl) NewToken(data []byte) ([]byte, error) {
|
|||
}
|
||||
|
||||
// DecodeToken decodes a token.
|
||||
func (s *cookieProtectorImpl) DecodeToken(p []byte) ([]byte, error) {
|
||||
if len(p) < cookieNonceSize {
|
||||
func (s *tokenProtectorImpl) DecodeToken(p []byte) ([]byte, error) {
|
||||
if len(p) < tokenNonceSize {
|
||||
return nil, fmt.Errorf("Token too short: %d", len(p))
|
||||
}
|
||||
nonce := p[:cookieNonceSize]
|
||||
nonce := p[:tokenNonceSize]
|
||||
aead, aeadNonce, err := s.createAEAD(nonce)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return aead.Open(nil, aeadNonce, p[cookieNonceSize:], nil)
|
||||
return aead.Open(nil, aeadNonce, p[tokenNonceSize:], nil)
|
||||
}
|
||||
|
||||
func (s *cookieProtectorImpl) createAEAD(nonce []byte) (cipher.AEAD, []byte, error) {
|
||||
h := hkdf.New(sha256.New, s.secret, nonce, []byte("quic-go cookie source"))
|
||||
func (s *tokenProtectorImpl) createAEAD(nonce []byte) (cipher.AEAD, []byte, error) {
|
||||
h := hkdf.New(sha256.New, s.secret, nonce, []byte("quic-go token source"))
|
||||
key := make([]byte, 32) // use a 32 byte key, in order to select AES-256
|
||||
if _, err := io.ReadFull(h, key); err != nil {
|
||||
return nil, nil, err
|
|
@ -5,35 +5,35 @@ import (
|
|||
. "github.com/onsi/gomega"
|
||||
)
|
||||
|
||||
var _ = Describe("Cookie Protector", func() {
|
||||
var cp cookieProtector
|
||||
var _ = Describe("Token Protector", func() {
|
||||
var tp tokenProtector
|
||||
|
||||
BeforeEach(func() {
|
||||
var err error
|
||||
cp, err = newCookieProtector()
|
||||
tp, err = newTokenProtector()
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
})
|
||||
|
||||
It("encodes and decodes tokens", func() {
|
||||
token, err := cp.NewToken([]byte("foobar"))
|
||||
token, err := tp.NewToken([]byte("foobar"))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(token).ToNot(ContainSubstring("foobar"))
|
||||
decoded, err := cp.DecodeToken(token)
|
||||
decoded, err := tp.DecodeToken(token)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(decoded).To(Equal([]byte("foobar")))
|
||||
})
|
||||
|
||||
It("fails deconding invalid tokens", func() {
|
||||
token, err := cp.NewToken([]byte("foobar"))
|
||||
token, err := tp.NewToken([]byte("foobar"))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
token = token[1:] // remove the first byte
|
||||
_, err = cp.DecodeToken(token)
|
||||
_, err = tp.DecodeToken(token)
|
||||
Expect(err).To(HaveOccurred())
|
||||
Expect(err.Error()).To(ContainSubstring("message authentication failed"))
|
||||
})
|
||||
|
||||
It("errors when decoding too short tokens", func() {
|
||||
_, err := cp.DecodeToken([]byte("foobar"))
|
||||
_, err := tp.DecodeToken([]byte("foobar"))
|
||||
Expect(err).To(MatchError("Token too short: 6"))
|
||||
})
|
||||
})
|
|
@ -57,8 +57,8 @@ const MaxTrackedSkippedPackets = 10
|
|||
// If the queue is full, new connection attempts will be rejected.
|
||||
const MaxAcceptQueueSize = 32
|
||||
|
||||
// CookieExpiryTime is the valid time of a cookie
|
||||
const CookieExpiryTime = 24 * time.Hour
|
||||
// TokenExpiryTime is the valid time of a token
|
||||
const TokenExpiryTime = 24 * time.Hour
|
||||
|
||||
// MaxOutstandingSentPackets is maximum number of packets saved for retransmission.
|
||||
// When reached, it imposes a soft limit on sending new packets:
|
||||
|
|
32
server.go
32
server.go
|
@ -84,7 +84,7 @@ type server struct {
|
|||
// If it is started with Listen, we take a packet conn as a parameter.
|
||||
createdPacketConn bool
|
||||
|
||||
cookieGenerator *handshake.CookieGenerator
|
||||
tokenGenerator *handshake.TokenGenerator
|
||||
|
||||
sessionHandler packetHandlerManager
|
||||
|
||||
|
@ -186,19 +186,19 @@ func (s *server) setup() error {
|
|||
}()
|
||||
},
|
||||
}
|
||||
cookieGenerator, err := handshake.NewCookieGenerator()
|
||||
tokenGenerator, err := handshake.NewTokenGenerator()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
s.cookieGenerator = cookieGenerator
|
||||
s.tokenGenerator = tokenGenerator
|
||||
return nil
|
||||
}
|
||||
|
||||
var defaultAcceptCookie = func(clientAddr net.Addr, cookie *Cookie) bool {
|
||||
if cookie == nil {
|
||||
var defaultAcceptToken = func(clientAddr net.Addr, token *Token) bool {
|
||||
if token == nil {
|
||||
return false
|
||||
}
|
||||
if time.Now().After(cookie.SentTime.Add(protocol.CookieExpiryTime)) {
|
||||
if time.Now().After(token.SentTime.Add(protocol.TokenExpiryTime)) {
|
||||
return false
|
||||
}
|
||||
var sourceAddr string
|
||||
|
@ -207,7 +207,7 @@ var defaultAcceptCookie = func(clientAddr net.Addr, cookie *Cookie) bool {
|
|||
} else {
|
||||
sourceAddr = clientAddr.String()
|
||||
}
|
||||
return sourceAddr == cookie.RemoteAddr
|
||||
return sourceAddr == token.RemoteAddr
|
||||
}
|
||||
|
||||
// populateServerConfig populates fields in the quic.Config with their default values, if none are set
|
||||
|
@ -221,9 +221,9 @@ func populateServerConfig(config *Config) *Config {
|
|||
versions = protocol.SupportedVersions
|
||||
}
|
||||
|
||||
vsa := defaultAcceptCookie
|
||||
if config.AcceptCookie != nil {
|
||||
vsa = config.AcceptCookie
|
||||
verifyToken := defaultAcceptToken
|
||||
if config.AcceptToken != nil {
|
||||
verifyToken = config.AcceptToken
|
||||
}
|
||||
|
||||
handshakeTimeout := protocol.DefaultHandshakeTimeout
|
||||
|
@ -264,7 +264,7 @@ func populateServerConfig(config *Config) *Config {
|
|||
Versions: versions,
|
||||
HandshakeTimeout: handshakeTimeout,
|
||||
IdleTimeout: idleTimeout,
|
||||
AcceptCookie: vsa,
|
||||
AcceptToken: verifyToken,
|
||||
KeepAlive: config.KeepAlive,
|
||||
MaxReceiveStreamFlowControlWindow: maxReceiveStreamFlowControlWindow,
|
||||
MaxReceiveConnectionFlowControlWindow: maxReceiveConnectionFlowControlWindow,
|
||||
|
@ -381,19 +381,19 @@ func (s *server) handleInitialImpl(p *receivedPacket, hdr *wire.Header) (quicSes
|
|||
return nil, nil, errors.New("too short connection ID")
|
||||
}
|
||||
|
||||
var cookie *Cookie
|
||||
var token *Token
|
||||
var origDestConnectionID protocol.ConnectionID
|
||||
if len(hdr.Token) > 0 {
|
||||
c, err := s.cookieGenerator.DecodeToken(hdr.Token)
|
||||
c, err := s.tokenGenerator.DecodeToken(hdr.Token)
|
||||
if err == nil {
|
||||
cookie = &Cookie{
|
||||
token = &Token{
|
||||
RemoteAddr: c.RemoteAddr,
|
||||
SentTime: c.SentTime,
|
||||
}
|
||||
origDestConnectionID = c.OriginalDestConnectionID
|
||||
}
|
||||
}
|
||||
if !s.config.AcceptCookie(p.remoteAddr, cookie) {
|
||||
if !s.config.AcceptToken(p.remoteAddr, token) {
|
||||
// Log the Initial packet now.
|
||||
// If no Retry is sent, the packet will be logged by the session.
|
||||
(&wire.ExtendedHeader{Header: *hdr}).Log(s.logger)
|
||||
|
@ -468,7 +468,7 @@ func (s *server) createNewSession(
|
|||
}
|
||||
|
||||
func (s *server) sendRetry(remoteAddr net.Addr, hdr *wire.Header) error {
|
||||
token, err := s.cookieGenerator.NewToken(remoteAddr, hdr.DestConnectionID)
|
||||
token, err := s.tokenGenerator.NewToken(remoteAddr, hdr.DestConnectionID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -69,7 +69,7 @@ var _ = Describe("Server", func() {
|
|||
Expect(server.config.Versions).To(Equal(protocol.SupportedVersions))
|
||||
Expect(server.config.HandshakeTimeout).To(Equal(protocol.DefaultHandshakeTimeout))
|
||||
Expect(server.config.IdleTimeout).To(Equal(protocol.DefaultIdleTimeout))
|
||||
Expect(reflect.ValueOf(server.config.AcceptCookie)).To(Equal(reflect.ValueOf(defaultAcceptCookie)))
|
||||
Expect(reflect.ValueOf(server.config.AcceptToken)).To(Equal(reflect.ValueOf(defaultAcceptToken)))
|
||||
Expect(server.config.KeepAlive).To(BeFalse())
|
||||
// stop the listener
|
||||
Expect(ln.Close()).To(Succeed())
|
||||
|
@ -77,10 +77,10 @@ var _ = Describe("Server", func() {
|
|||
|
||||
It("setups with the right values", func() {
|
||||
supportedVersions := []protocol.VersionNumber{protocol.VersionTLS}
|
||||
acceptCookie := func(_ net.Addr, _ *Cookie) bool { return true }
|
||||
acceptToken := func(_ net.Addr, _ *Token) bool { return true }
|
||||
config := Config{
|
||||
Versions: supportedVersions,
|
||||
AcceptCookie: acceptCookie,
|
||||
AcceptToken: acceptToken,
|
||||
HandshakeTimeout: 1337 * time.Hour,
|
||||
IdleTimeout: 42 * time.Minute,
|
||||
KeepAlive: true,
|
||||
|
@ -93,7 +93,7 @@ var _ = Describe("Server", func() {
|
|||
Expect(server.config.Versions).To(Equal(supportedVersions))
|
||||
Expect(server.config.HandshakeTimeout).To(Equal(1337 * time.Hour))
|
||||
Expect(server.config.IdleTimeout).To(Equal(42 * time.Minute))
|
||||
Expect(reflect.ValueOf(server.config.AcceptCookie)).To(Equal(reflect.ValueOf(acceptCookie)))
|
||||
Expect(reflect.ValueOf(server.config.AcceptToken)).To(Equal(reflect.ValueOf(acceptToken)))
|
||||
Expect(server.config.KeepAlive).To(BeTrue())
|
||||
Expect(server.config.StatelessResetKey).To(Equal([]byte("foobar")))
|
||||
// stop the listener
|
||||
|
@ -179,19 +179,19 @@ var _ = Describe("Server", func() {
|
|||
))
|
||||
})
|
||||
|
||||
It("decodes the cookie from the Token field", func() {
|
||||
It("decodes the token from the Token field", func() {
|
||||
raddr := &net.UDPAddr{
|
||||
IP: net.IPv4(192, 168, 13, 37),
|
||||
Port: 1337,
|
||||
}
|
||||
done := make(chan struct{})
|
||||
serv.config.AcceptCookie = func(addr net.Addr, cookie *Cookie) bool {
|
||||
serv.config.AcceptToken = func(addr net.Addr, token *Token) bool {
|
||||
Expect(addr).To(Equal(raddr))
|
||||
Expect(cookie).ToNot(BeNil())
|
||||
Expect(token).ToNot(BeNil())
|
||||
close(done)
|
||||
return false
|
||||
}
|
||||
token, err := serv.cookieGenerator.NewToken(raddr, nil)
|
||||
token, err := serv.tokenGenerator.NewToken(raddr, nil)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
packet := getPacket(&wire.Header{
|
||||
IsLongHeader: true,
|
||||
|
@ -204,15 +204,15 @@ var _ = Describe("Server", func() {
|
|||
Eventually(done).Should(BeClosed())
|
||||
})
|
||||
|
||||
It("passes an empty cookie to the callback, if decoding fails", func() {
|
||||
It("passes an empty token to the callback, if decoding fails", func() {
|
||||
raddr := &net.UDPAddr{
|
||||
IP: net.IPv4(192, 168, 13, 37),
|
||||
Port: 1337,
|
||||
}
|
||||
done := make(chan struct{})
|
||||
serv.config.AcceptCookie = func(addr net.Addr, cookie *Cookie) bool {
|
||||
serv.config.AcceptToken = func(addr net.Addr, token *Token) bool {
|
||||
Expect(addr).To(Equal(raddr))
|
||||
Expect(cookie).To(BeNil())
|
||||
Expect(token).To(BeNil())
|
||||
close(done)
|
||||
return false
|
||||
}
|
||||
|
@ -249,8 +249,8 @@ var _ = Describe("Server", func() {
|
|||
Expect(hdr.SupportedVersions).ToNot(ContainElement(protocol.VersionNumber(0x42)))
|
||||
})
|
||||
|
||||
It("replies with a Retry packet, if a Cookie is required", func() {
|
||||
serv.config.AcceptCookie = func(_ net.Addr, _ *Cookie) bool { return false }
|
||||
It("replies with a Retry packet, if a Token is required", func() {
|
||||
serv.config.AcceptToken = func(_ net.Addr, _ *Token) bool { return false }
|
||||
hdr := &wire.Header{
|
||||
IsLongHeader: true,
|
||||
Type: protocol.PacketTypeInitial,
|
||||
|
@ -272,8 +272,8 @@ var _ = Describe("Server", func() {
|
|||
Expect(replyHdr.Token).ToNot(BeEmpty())
|
||||
})
|
||||
|
||||
It("creates a session, if no Cookie is required", func() {
|
||||
serv.config.AcceptCookie = func(_ net.Addr, _ *Cookie) bool { return true }
|
||||
It("creates a session, if no Token is required", func() {
|
||||
serv.config.AcceptToken = func(_ net.Addr, _ *Token) bool { return true }
|
||||
hdr := &wire.Header{
|
||||
IsLongHeader: true,
|
||||
Type: protocol.PacketTypeInitial,
|
||||
|
@ -320,7 +320,7 @@ var _ = Describe("Server", func() {
|
|||
})
|
||||
|
||||
It("rejects new connection attempts if the accept queue is full", func() {
|
||||
serv.config.AcceptCookie = func(_ net.Addr, _ *Cookie) bool { return true }
|
||||
serv.config.AcceptToken = func(_ net.Addr, _ *Token) bool { return true }
|
||||
senderAddr := &net.UDPAddr{IP: net.IPv4(1, 2, 3, 4), Port: 42}
|
||||
|
||||
hdr := &wire.Header{
|
||||
|
@ -375,7 +375,7 @@ var _ = Describe("Server", func() {
|
|||
})
|
||||
|
||||
It("doesn't accept new sessions if they were closed in the mean time", func() {
|
||||
serv.config.AcceptCookie = func(_ net.Addr, _ *Cookie) bool { return true }
|
||||
serv.config.AcceptToken = func(_ net.Addr, _ *Token) bool { return true }
|
||||
senderAddr := &net.UDPAddr{IP: net.IPv4(1, 2, 3, 4), Port: 42}
|
||||
|
||||
hdr := &wire.Header{
|
||||
|
@ -543,51 +543,51 @@ var _ = Describe("Server", func() {
|
|||
var _ = Describe("default source address verification", func() {
|
||||
It("accepts a token", func() {
|
||||
remoteAddr := &net.UDPAddr{IP: net.IPv4(192, 168, 0, 1)}
|
||||
cookie := &Cookie{
|
||||
token := &Token{
|
||||
RemoteAddr: "192.168.0.1",
|
||||
SentTime: time.Now().Add(-protocol.CookieExpiryTime).Add(time.Second), // will expire in 1 second
|
||||
SentTime: time.Now().Add(-protocol.TokenExpiryTime).Add(time.Second), // will expire in 1 second
|
||||
}
|
||||
Expect(defaultAcceptCookie(remoteAddr, cookie)).To(BeTrue())
|
||||
Expect(defaultAcceptToken(remoteAddr, token)).To(BeTrue())
|
||||
})
|
||||
|
||||
It("requests verification if no token is provided", func() {
|
||||
remoteAddr := &net.UDPAddr{IP: net.IPv4(192, 168, 0, 1)}
|
||||
Expect(defaultAcceptCookie(remoteAddr, nil)).To(BeFalse())
|
||||
Expect(defaultAcceptToken(remoteAddr, nil)).To(BeFalse())
|
||||
})
|
||||
|
||||
It("rejects a token if the address doesn't match", func() {
|
||||
remoteAddr := &net.UDPAddr{IP: net.IPv4(192, 168, 0, 1)}
|
||||
cookie := &Cookie{
|
||||
token := &Token{
|
||||
RemoteAddr: "127.0.0.1",
|
||||
SentTime: time.Now(),
|
||||
}
|
||||
Expect(defaultAcceptCookie(remoteAddr, cookie)).To(BeFalse())
|
||||
Expect(defaultAcceptToken(remoteAddr, token)).To(BeFalse())
|
||||
})
|
||||
|
||||
It("accepts a token for a remote address is not a UDP address", func() {
|
||||
remoteAddr := &net.TCPAddr{IP: net.IPv4(192, 168, 0, 1), Port: 1337}
|
||||
cookie := &Cookie{
|
||||
token := &Token{
|
||||
RemoteAddr: "192.168.0.1:1337",
|
||||
SentTime: time.Now(),
|
||||
}
|
||||
Expect(defaultAcceptCookie(remoteAddr, cookie)).To(BeTrue())
|
||||
Expect(defaultAcceptToken(remoteAddr, token)).To(BeTrue())
|
||||
})
|
||||
|
||||
It("rejects an invalid token for a remote address is not a UDP address", func() {
|
||||
remoteAddr := &net.TCPAddr{IP: net.IPv4(192, 168, 0, 1), Port: 1337}
|
||||
cookie := &Cookie{
|
||||
token := &Token{
|
||||
RemoteAddr: "192.168.0.1:7331", // mismatching port
|
||||
SentTime: time.Now(),
|
||||
}
|
||||
Expect(defaultAcceptCookie(remoteAddr, cookie)).To(BeFalse())
|
||||
Expect(defaultAcceptToken(remoteAddr, token)).To(BeFalse())
|
||||
})
|
||||
|
||||
It("rejects an expired token", func() {
|
||||
remoteAddr := &net.UDPAddr{IP: net.IPv4(192, 168, 0, 1)}
|
||||
cookie := &Cookie{
|
||||
token := &Token{
|
||||
RemoteAddr: "192.168.0.1",
|
||||
SentTime: time.Now().Add(-protocol.CookieExpiryTime).Add(-time.Second), // expired 1 second ago
|
||||
SentTime: time.Now().Add(-protocol.TokenExpiryTime).Add(-time.Second), // expired 1 second ago
|
||||
}
|
||||
Expect(defaultAcceptCookie(remoteAddr, cookie)).To(BeFalse())
|
||||
Expect(defaultAcceptToken(remoteAddr, token)).To(BeFalse())
|
||||
})
|
||||
})
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue