forked from quic-go/quic-go
add a Transport config option for the key used to encrypt tokens (#4066)
* add a Transport config option for the key used to encrypt tokens * handshake: remove unused error return values
This commit is contained in:
@@ -4,7 +4,6 @@ import (
|
||||
"bytes"
|
||||
"encoding/asn1"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
@@ -45,15 +44,9 @@ type TokenGenerator struct {
|
||||
tokenProtector tokenProtector
|
||||
}
|
||||
|
||||
// NewTokenGenerator initializes a new TookenGenerator
|
||||
func NewTokenGenerator(rand io.Reader) (*TokenGenerator, error) {
|
||||
tokenProtector, err := newTokenProtector(rand)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &TokenGenerator{
|
||||
tokenProtector: tokenProtector,
|
||||
}, nil
|
||||
// NewTokenGenerator initializes a new TokenGenerator
|
||||
func NewTokenGenerator(key TokenProtectorKey) *TokenGenerator {
|
||||
return &TokenGenerator{tokenProtector: newTokenProtector(key)}
|
||||
}
|
||||
|
||||
// NewRetryToken generates a new token for a Retry for a given source address
|
||||
|
||||
@@ -16,9 +16,9 @@ var _ = Describe("Token Generator", func() {
|
||||
var tokenGen *TokenGenerator
|
||||
|
||||
BeforeEach(func() {
|
||||
var err error
|
||||
tokenGen, err = NewTokenGenerator(rand.Reader)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
var key TokenProtectorKey
|
||||
rand.Read(key[:])
|
||||
tokenGen = NewTokenGenerator(key)
|
||||
})
|
||||
|
||||
It("generates a token", func() {
|
||||
|
||||
@@ -3,6 +3,7 @@ package handshake
|
||||
import (
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"crypto/rand"
|
||||
"crypto/sha256"
|
||||
"fmt"
|
||||
"io"
|
||||
@@ -10,6 +11,9 @@ import (
|
||||
"golang.org/x/crypto/hkdf"
|
||||
)
|
||||
|
||||
// TokenProtectorKey is the key used to encrypt both Retry and session resumption tokens.
|
||||
type TokenProtectorKey [32]byte
|
||||
|
||||
// TokenProtector is used to create and verify a token
|
||||
type tokenProtector interface {
|
||||
// NewToken creates a new token
|
||||
@@ -18,40 +22,29 @@ type tokenProtector interface {
|
||||
DecodeToken([]byte) ([]byte, error)
|
||||
}
|
||||
|
||||
const (
|
||||
tokenSecretSize = 32
|
||||
tokenNonceSize = 32
|
||||
)
|
||||
const tokenNonceSize = 32
|
||||
|
||||
// tokenProtector is used to create and verify a token
|
||||
type tokenProtectorImpl struct {
|
||||
rand io.Reader
|
||||
secret []byte
|
||||
key TokenProtectorKey
|
||||
}
|
||||
|
||||
// newTokenProtector creates a source for source address tokens
|
||||
func newTokenProtector(rand io.Reader) (tokenProtector, error) {
|
||||
secret := make([]byte, tokenSecretSize)
|
||||
if _, err := rand.Read(secret); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &tokenProtectorImpl{
|
||||
rand: rand,
|
||||
secret: secret,
|
||||
}, nil
|
||||
func newTokenProtector(key TokenProtectorKey) tokenProtector {
|
||||
return &tokenProtectorImpl{key: key}
|
||||
}
|
||||
|
||||
// NewToken encodes data into a new token.
|
||||
func (s *tokenProtectorImpl) NewToken(data []byte) ([]byte, error) {
|
||||
nonce := make([]byte, tokenNonceSize)
|
||||
if _, err := s.rand.Read(nonce); err != nil {
|
||||
var nonce [tokenNonceSize]byte
|
||||
if _, err := rand.Read(nonce[:]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
aead, aeadNonce, err := s.createAEAD(nonce)
|
||||
aead, aeadNonce, err := s.createAEAD(nonce[:])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return append(nonce, aead.Seal(nil, aeadNonce, data, nil)...), nil
|
||||
return append(nonce[:], aead.Seal(nil, aeadNonce, data, nil)...), nil
|
||||
}
|
||||
|
||||
// DecodeToken decodes a token.
|
||||
@@ -68,7 +61,7 @@ func (s *tokenProtectorImpl) DecodeToken(p []byte) ([]byte, error) {
|
||||
}
|
||||
|
||||
func (s *tokenProtectorImpl) createAEAD(nonce []byte) (cipher.AEAD, []byte, error) {
|
||||
h := hkdf.New(sha256.New, s.secret, nonce, []byte("quic-go token source"))
|
||||
h := hkdf.New(sha256.New, s.key[:], 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
|
||||
|
||||
@@ -7,41 +7,17 @@ import (
|
||||
. "github.com/onsi/gomega"
|
||||
)
|
||||
|
||||
type zeroReader struct{}
|
||||
|
||||
func (r *zeroReader) Read(b []byte) (int, error) {
|
||||
for i := range b {
|
||||
b[i] = 0
|
||||
}
|
||||
return len(b), nil
|
||||
}
|
||||
|
||||
var _ = Describe("Token Protector", func() {
|
||||
var tp tokenProtector
|
||||
|
||||
BeforeEach(func() {
|
||||
var key TokenProtectorKey
|
||||
rand.Read(key[:])
|
||||
var err error
|
||||
tp, err = newTokenProtector(rand.Reader)
|
||||
tp = newTokenProtector(key)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
})
|
||||
|
||||
It("uses the random source", func() {
|
||||
tp1, err := newTokenProtector(&zeroReader{})
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
tp2, err := newTokenProtector(&zeroReader{})
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
t1, err := tp1.NewToken([]byte("foo"))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
t2, err := tp2.NewToken([]byte("foo"))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(t1).To(Equal(t2))
|
||||
tp3, err := newTokenProtector(rand.Reader)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
t3, err := tp3.NewToken([]byte("foo"))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(t3).ToNot(Equal(t1))
|
||||
})
|
||||
|
||||
It("encodes and decodes tokens", func() {
|
||||
token, err := tp.NewToken([]byte("foobar"))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
@@ -51,11 +27,34 @@ var _ = Describe("Token Protector", func() {
|
||||
Expect(decoded).To(Equal([]byte("foobar")))
|
||||
})
|
||||
|
||||
It("fails deconding invalid tokens", func() {
|
||||
It("uses the different keys", func() {
|
||||
var key1, key2 TokenProtectorKey
|
||||
rand.Read(key1[:])
|
||||
rand.Read(key2[:])
|
||||
tp1 := newTokenProtector(key1)
|
||||
tp2 := newTokenProtector(key2)
|
||||
t1, err := tp1.NewToken([]byte("foo"))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
t2, err := tp2.NewToken([]byte("foo"))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
_, err = tp1.DecodeToken(t1)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
_, err = tp1.DecodeToken(t2)
|
||||
Expect(err).To(HaveOccurred())
|
||||
|
||||
// now create another token protector, reusing key1
|
||||
tp3 := newTokenProtector(key1)
|
||||
_, err = tp3.DecodeToken(t1)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
_, err = tp3.DecodeToken(t2)
|
||||
Expect(err).To(HaveOccurred())
|
||||
})
|
||||
|
||||
It("doesn't decode invalid tokens", func() {
|
||||
token, err := tp.NewToken([]byte("foobar"))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
token = token[1:] // remove the first byte
|
||||
_, err = tp.DecodeToken(token)
|
||||
_, err = tp.DecodeToken(token[1:]) // the token is invalid without the first byte
|
||||
Expect(err).To(HaveOccurred())
|
||||
Expect(err.Error()).To(ContainSubstring("message authentication failed"))
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user