Merge pull request #899 from lucas-clemente/fix-898

encrypt unprotected packets with a connection-dependent key
This commit is contained in:
Marten Seemann
2017-10-27 22:52:04 +07:00
committed by GitHub
19 changed files with 396 additions and 266 deletions

View File

@@ -3,9 +3,9 @@ package crypto
import "github.com/lucas-clemente/quic-go/internal/protocol"
// NewNullAEAD creates a NullAEAD
func NewNullAEAD(p protocol.Perspective, v protocol.VersionNumber) AEAD {
func NewNullAEAD(p protocol.Perspective, connID protocol.ConnectionID, v protocol.VersionNumber) (AEAD, error) {
if v.UsesTLS() {
return &nullAEADFNV64a{}
return newNullAEADAESGCM(connID, p)
}
return &nullAEADFNV128a{perspective: p}
return &nullAEADFNV128a{perspective: p}, nil
}

View File

@@ -0,0 +1,44 @@
package crypto
import (
"crypto"
"encoding/binary"
"github.com/bifurcation/mint"
"github.com/lucas-clemente/quic-go/internal/protocol"
)
var quicVersion1Salt = []byte{0xaf, 0xc8, 0x24, 0xec, 0x5f, 0xc7, 0x7e, 0xca, 0x1e, 0x9d, 0x36, 0xf3, 0x7f, 0xb2, 0xd4, 0x65, 0x18, 0xc3, 0x66, 0x39}
func newNullAEADAESGCM(connectionID protocol.ConnectionID, pers protocol.Perspective) (AEAD, error) {
clientSecret, serverSecret := computeSecrets(connectionID)
var mySecret, otherSecret []byte
if pers == protocol.PerspectiveClient {
mySecret = clientSecret
otherSecret = serverSecret
} else {
mySecret = serverSecret
otherSecret = clientSecret
}
myKey, myIV := computeNullAEADKeyAndIV(mySecret)
otherKey, otherIV := computeNullAEADKeyAndIV(otherSecret)
return NewAEADAESGCM(otherKey, myKey, otherIV, myIV)
}
func computeSecrets(connectionID protocol.ConnectionID) (clientSecret, serverSecret []byte) {
connID := make([]byte, 8)
binary.BigEndian.PutUint64(connID, uint64(connectionID))
cleartextSecret := mint.HkdfExtract(crypto.SHA256, []byte(quicVersion1Salt), connID)
clientSecret = mint.HkdfExpandLabel(crypto.SHA256, cleartextSecret, "QUIC client cleartext Secret", []byte{}, crypto.SHA256.Size())
serverSecret = mint.HkdfExpandLabel(crypto.SHA256, cleartextSecret, "QUIC server cleartext Secret", []byte{}, crypto.SHA256.Size())
return
}
func computeNullAEADKeyAndIV(secret []byte) (key, iv []byte) {
key = mint.HkdfExpandLabel(crypto.SHA256, secret, "key", nil, 16)
iv = mint.HkdfExpandLabel(crypto.SHA256, secret, "iv", nil, 12)
return
}

View File

@@ -0,0 +1,84 @@
package crypto
import (
"github.com/lucas-clemente/quic-go/internal/protocol"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)
var _ = Describe("NullAEAD using AES-GCM", func() {
// values taken from https://github.com/quicwg/base-drafts/wiki/Test-Vector-for-the-Clear-Text-AEAD-key-derivation
Context("using the test vector from the QUIC WG Wiki", func() {
connID := protocol.ConnectionID(0x8394c8f03e515708)
It("computes the secrets", func() {
clientSecret, serverSecret := computeSecrets(connID)
Expect(clientSecret).To(Equal([]byte{
0x31, 0xba, 0x96, 0x68, 0x73, 0xf7, 0xf4, 0x53,
0xe6, 0xc8, 0xa1, 0xbf, 0x78, 0xed, 0x70, 0x13,
0xfa, 0xd8, 0x3f, 0xfc, 0xee, 0xfc, 0x95, 0x68,
0x81, 0xcd, 0x24, 0x1c, 0x0a, 0xe3, 0xa7, 0xa6,
}))
Expect(serverSecret).To(Equal([]byte{
0x91, 0xa9, 0xe4, 0x22, 0x2c, 0xcb, 0xb9, 0xa9,
0x8f, 0x14, 0xc8, 0xe1, 0xbe, 0xfd, 0x6a, 0x79,
0xf0, 0x4e, 0x42, 0xa2, 0x4f, 0xbe, 0xb4, 0x83,
0x1f, 0x50, 0x26, 0x80, 0x7a, 0xe8, 0x4c, 0xc3,
}))
})
It("computes the client key and IV", func() {
clientSecret, _ := computeSecrets(connID)
key, iv := computeNullAEADKeyAndIV(clientSecret)
Expect(key).To(Equal([]byte{
0x2e, 0xbd, 0x78, 0x00, 0xdb, 0xed, 0x20, 0x10,
0xe5, 0xa2, 0x1c, 0x4a, 0xd2, 0x4b, 0x4e, 0xc3,
}))
Expect(iv).To(Equal([]byte{
0x55, 0x44, 0x0d, 0x5f, 0xf7, 0x50, 0x3d, 0xe4,
0x99, 0x7b, 0xfd, 0x6b,
}))
})
It("computes the server key and IV", func() {
_, serverSecret := computeSecrets(connID)
key, iv := computeNullAEADKeyAndIV(serverSecret)
Expect(key).To(Equal([]byte{
0xc8, 0xea, 0x1b, 0xc1, 0x71, 0xe5, 0x2b, 0xae,
0x71, 0xfb, 0x78, 0x39, 0x52, 0xc7, 0xb8, 0xfc,
}))
Expect(iv).To(Equal([]byte{
0x57, 0x82, 0x3b, 0x85, 0x2c, 0x7e, 0xf9, 0xe3,
0x80, 0x2b, 0x69, 0x0b,
}))
})
})
It("seals and opens", func() {
connectionID := protocol.ConnectionID(0x1234567890)
clientAEAD, err := newNullAEADAESGCM(connectionID, protocol.PerspectiveClient)
Expect(err).ToNot(HaveOccurred())
serverAEAD, err := newNullAEADAESGCM(connectionID, protocol.PerspectiveServer)
Expect(err).ToNot(HaveOccurred())
clientMessage := clientAEAD.Seal(nil, []byte("foobar"), 42, []byte("aad"))
m, err := serverAEAD.Open(nil, clientMessage, 42, []byte("aad"))
Expect(err).ToNot(HaveOccurred())
Expect(m).To(Equal([]byte("foobar")))
serverMessage := serverAEAD.Seal(nil, []byte("raboof"), 99, []byte("daa"))
m, err = clientAEAD.Open(nil, serverMessage, 99, []byte("daa"))
Expect(err).ToNot(HaveOccurred())
Expect(m).To(Equal([]byte("raboof")))
})
It("doesn't work if initialized with different connection IDs", func() {
clientAEAD, err := newNullAEADAESGCM(1, protocol.PerspectiveClient)
Expect(err).ToNot(HaveOccurred())
serverAEAD, err := newNullAEADAESGCM(2, protocol.PerspectiveServer)
Expect(err).ToNot(HaveOccurred())
clientMessage := clientAEAD.Seal(nil, []byte("foobar"), 42, []byte("aad"))
_, err = serverAEAD.Open(nil, clientMessage, 42, []byte("aad"))
Expect(err).To(MatchError("cipher: message authentication failed"))
})
})

View File

@@ -15,8 +15,8 @@ var _ = Describe("NullAEAD using FNV128a", func() {
var aeadClient AEAD
BeforeEach(func() {
aeadServer = NewNullAEAD(protocol.PerspectiveServer, protocol.Version37)
aeadClient = NewNullAEAD(protocol.PerspectiveClient, protocol.Version37)
aeadServer = &nullAEADFNV128a{protocol.PerspectiveServer}
aeadClient = &nullAEADFNV128a{protocol.PerspectiveClient}
})
It("seals and opens, client => server", func() {

View File

@@ -1,50 +0,0 @@
package crypto
import (
"encoding/binary"
"errors"
"hash/fnv"
"github.com/lucas-clemente/quic-go/internal/protocol"
)
type nullAEADFNV64a struct{}
var _ AEAD = &nullAEADFNV64a{}
// Open and verify the ciphertext
func (n *nullAEADFNV64a) Open(dst, src []byte, packetNumber protocol.PacketNumber, associatedData []byte) ([]byte, error) {
if len(src) < 8 {
return nil, errors.New("NullAEAD: ciphertext cannot be less than 8 bytes long")
}
data := src[:len(src)-8]
hash := fnv.New64a()
hash.Write(associatedData)
hash.Write(data)
if hash.Sum64() != binary.BigEndian.Uint64(src[len(src)-8:]) {
return nil, errors.New("NullAEAD: failed to authenticate received data")
}
return data, nil
}
// Seal writes hash and ciphertext to the buffer
func (n *nullAEADFNV64a) Seal(dst, src []byte, packetNumber protocol.PacketNumber, associatedData []byte) []byte {
if cap(dst) < 8+len(src) {
dst = make([]byte, 8+len(src))
} else {
dst = dst[:8+len(src)]
}
hash := fnv.New64a()
hash.Write(associatedData)
hash.Write(src)
copy(dst, src)
binary.BigEndian.PutUint64(dst[len(src):], hash.Sum64())
return dst
}
func (n *nullAEADFNV64a) Overhead() int {
return 8
}

View File

@@ -1,66 +0,0 @@
package crypto
import (
"encoding/binary"
"hash/fnv"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)
var _ = Describe("NullAEAD using FNV128a", func() {
var hash64 []byte
var aead AEAD
aad := []byte("All human beings are born free and equal in dignity and rights.")
plainText := []byte("They are endowed with reason and conscience and should act towards one another in a spirit of brotherhood.")
BeforeEach(func() {
aead = &nullAEADFNV64a{}
hash := fnv.New64a()
hash.Write(aad)
hash.Write(plainText)
hash64 = make([]byte, 8)
binary.BigEndian.PutUint64(hash64, hash.Sum64())
})
It("opens", func() {
data, err := aead.Open(nil, append(plainText, hash64...), 0, aad)
Expect(err).ToNot(HaveOccurred())
Expect(data).To(Equal(plainText))
})
It("fails", func() {
_, err := aead.Open(nil, append(plainText, hash64...), 0, append(aad, []byte{0x42}...))
Expect(err).To(MatchError("NullAEAD: failed to authenticate received data"))
})
It("rejects short ciphertexts", func() {
_, err := aead.Open(nil, []byte{1, 2, 3, 4, 5, 6, 7}, 0, []byte{})
Expect(err).To(MatchError("NullAEAD: ciphertext cannot be less than 8 bytes long"))
})
It("opens empty messages", func() {
hash := fnv.New64a()
h := make([]byte, 8)
binary.BigEndian.PutUint64(h, hash.Sum64())
data, err := aead.Open(nil, h, 0, []byte{})
Expect(err).ToNot(HaveOccurred())
Expect(data).To(BeEmpty())
})
It("seals", func() {
sealed := aead.Seal(nil, plainText, 0, aad)
Expect(sealed).To(Equal(append(plainText, hash64...)))
Expect(sealed).To(HaveLen(len(plainText) + aead.Overhead()))
})
It("seals in-place", func() {
buf := make([]byte, 6, 6+8)
copy(buf, []byte("foobar"))
res := aead.Seal(buf[0:0], buf, 0, nil)
// buf = buf[:8+6]
Expect(buf[:6]).To(Equal([]byte("foobar")))
// Expect(res[:6]).To(Equal([]byte("foobar")))
Expect(buf[0 : 6+8]).To(Equal(res))
})
})

View File

@@ -8,9 +8,10 @@ import (
var _ = Describe("NullAEAD", func() {
It("selects the right FVN variant", func() {
Expect(NewNullAEAD(protocol.PerspectiveClient, protocol.Version39)).To(Equal(&nullAEADFNV128a{
connID := protocol.ConnectionID(0x42)
Expect(NewNullAEAD(protocol.PerspectiveClient, connID, protocol.Version39)).To(Equal(&nullAEADFNV128a{
perspective: protocol.PerspectiveClient,
}))
Expect(NewNullAEAD(protocol.PerspectiveClient, protocol.VersionTLS)).To(Equal(&nullAEADFNV64a{}))
Expect(NewNullAEAD(protocol.PerspectiveClient, connID, protocol.VersionTLS)).To(BeAssignableToTypeOf(&aeadAESGCM{}))
})
})

View File

@@ -76,6 +76,10 @@ func NewCryptoSetupClient(
aeadChanged chan<- protocol.EncryptionLevel,
negotiatedVersions []protocol.VersionNumber,
) (CryptoSetup, error) {
nullAEAD, err := crypto.NewNullAEAD(protocol.PerspectiveClient, connID, version)
if err != nil {
return nil, err
}
return &cryptoSetupClient{
cryptoStream: cryptoStream,
hostname: hostname,
@@ -85,7 +89,7 @@ func NewCryptoSetupClient(
params: params,
keyDerivation: crypto.DeriveQuicCryptoAESKeys,
keyExchange: getEphermalKEX,
nullAEAD: crypto.NewNullAEAD(protocol.PerspectiveClient, version),
nullAEAD: nullAEAD,
paramsChan: paramsChan,
aeadChanged: aeadChanged,
negotiatedVersions: negotiatedVersions,

View File

@@ -8,6 +8,7 @@ import (
"time"
"github.com/lucas-clemente/quic-go/internal/crypto"
"github.com/lucas-clemente/quic-go/internal/mocks/crypto"
"github.com/lucas-clemente/quic-go/internal/protocol"
"github.com/lucas-clemente/quic-go/internal/utils"
"github.com/lucas-clemente/quic-go/qerr"
@@ -99,11 +100,7 @@ var _ = Describe("Client Crypto Setup", func() {
divNonce: divNonce,
pers: pers,
}
encLevel := protocol.EncryptionSecure
if forwardSecure {
encLevel = protocol.EncryptionForwardSecure
}
return &mockAEAD{encLevel: encLevel, sharedSecret: sharedSecret}, nil
return mockcrypto.NewMockAEAD(mockCtrl), nil
}
stream = newMockStream()
@@ -128,7 +125,7 @@ var _ = Describe("Client Crypto Setup", func() {
cs.certManager = certManager
cs.keyDerivation = keyDerivation
cs.keyExchange = func() crypto.KeyExchange { return &mockKEX{ephermal: true} }
cs.nullAEAD = &mockAEAD{encLevel: protocol.EncryptionUnencrypted}
cs.nullAEAD = mockcrypto.NewMockAEAD(mockCtrl)
cs.cryptoStream = stream
})
@@ -709,21 +706,24 @@ var _ = Describe("Client Crypto Setup", func() {
Context("null encryption", func() {
It("is used initially", func() {
cs.nullAEAD.(*mockcrypto.MockAEAD).EXPECT().Seal(nil, []byte("foobar"), protocol.PacketNumber(10), []byte{}).Return([]byte("foobar unencrypted"))
enc, sealer := cs.GetSealer()
Expect(enc).To(Equal(protocol.EncryptionUnencrypted))
d := sealer.Seal(nil, []byte("foobar"), 0, []byte{})
d := sealer.Seal(nil, []byte("foobar"), 10, []byte{})
Expect(d).To(Equal([]byte("foobar unencrypted")))
})
It("is used for the crypto stream", func() {
cs.nullAEAD.(*mockcrypto.MockAEAD).EXPECT().Seal(nil, []byte("foobar"), protocol.PacketNumber(1), []byte{}).Return([]byte("foobar unencrypted"))
enc, sealer := cs.GetSealerForCryptoStream()
Expect(enc).To(Equal(protocol.EncryptionUnencrypted))
d := sealer.Seal(nil, []byte("foobar"), 0, []byte{})
d := sealer.Seal(nil, []byte("foobar"), 1, []byte{})
Expect(d).To(Equal([]byte("foobar unencrypted")))
})
It("is accepted initially", func() {
d, enc, err := cs.Open(nil, []byte("unencrypted"), 0, []byte{})
cs.nullAEAD.(*mockcrypto.MockAEAD).EXPECT().Open(nil, []byte("unencrypted"), protocol.PacketNumber(1), []byte{}).Return([]byte("decrypted"), nil)
d, enc, err := cs.Open(nil, []byte("unencrypted"), 1, []byte{})
Expect(err).ToNot(HaveOccurred())
Expect(d).To(Equal([]byte("decrypted")))
Expect(enc).To(Equal(protocol.EncryptionUnencrypted))
@@ -731,9 +731,11 @@ var _ = Describe("Client Crypto Setup", func() {
It("is accepted before the server sent an encrypted packet", func() {
doCompleteREJ()
cs.secureAEAD.(*mockcrypto.MockAEAD).EXPECT().Open(nil, []byte("unencrypted"), protocol.PacketNumber(1), []byte{}).Return(nil, errors.New("authentication failed"))
cs.nullAEAD.(*mockcrypto.MockAEAD).EXPECT().Open(nil, []byte("unencrypted"), protocol.PacketNumber(1), []byte{}).Return([]byte("decrypted"), nil)
cs.receivedSecurePacket = false
Expect(cs.secureAEAD).ToNot(BeNil())
d, enc, err := cs.Open(nil, []byte("unencrypted"), 0, []byte{})
d, enc, err := cs.Open(nil, []byte("unencrypted"), 1, []byte{})
Expect(err).ToNot(HaveOccurred())
Expect(d).To(Equal([]byte("decrypted")))
Expect(enc).To(Equal(protocol.EncryptionUnencrypted))
@@ -741,14 +743,16 @@ var _ = Describe("Client Crypto Setup", func() {
It("is not accepted after the server sent an encrypted packet", func() {
doCompleteREJ()
cs.secureAEAD.(*mockcrypto.MockAEAD).EXPECT().Open(nil, []byte("unencrypted"), protocol.PacketNumber(3), []byte{}).Return(nil, errors.New("authentication failed"))
cs.receivedSecurePacket = true
_, enc, err := cs.Open(nil, []byte("unecnrypted"), 0, []byte{})
_, enc, err := cs.Open(nil, []byte("unencrypted"), 3, []byte{})
Expect(err).To(MatchError("authentication failed"))
Expect(enc).To(Equal(protocol.EncryptionUnspecified))
})
It("errors if the has the wrong hash", func() {
_, enc, err := cs.Open(nil, []byte("not unecnrypted"), 0, []byte{})
cs.nullAEAD.(*mockcrypto.MockAEAD).EXPECT().Open(nil, []byte("not unencrypted"), protocol.PacketNumber(3), []byte{}).Return(nil, errors.New("authentication failed"))
_, enc, err := cs.Open(nil, []byte("not unencrypted"), 3, []byte{})
Expect(err).To(MatchError("authentication failed"))
Expect(enc).To(Equal(protocol.EncryptionUnspecified))
})
@@ -757,16 +761,18 @@ var _ = Describe("Client Crypto Setup", func() {
Context("initial encryption", func() {
It("is used immediately when available", func() {
doCompleteREJ()
cs.secureAEAD.(*mockcrypto.MockAEAD).EXPECT().Seal(nil, []byte("foobar"), protocol.PacketNumber(1), []byte{}).Return([]byte("foobar secure"))
cs.receivedSecurePacket = false
enc, sealer := cs.GetSealer()
Expect(enc).To(Equal(protocol.EncryptionSecure))
d := sealer.Seal(nil, []byte("foobar"), 0, []byte{})
Expect(d).To(Equal([]byte("foobar normal sec")))
d := sealer.Seal(nil, []byte("foobar"), 1, []byte{})
Expect(d).To(Equal([]byte("foobar secure")))
})
It("is accepted", func() {
doCompleteREJ()
d, enc, err := cs.Open(nil, []byte("encrypted"), 0, []byte{})
cs.secureAEAD.(*mockcrypto.MockAEAD).EXPECT().Open(nil, []byte("encrypted"), protocol.PacketNumber(3), []byte{}).Return([]byte("decrypted"), nil)
d, enc, err := cs.Open(nil, []byte("encrypted"), 3, []byte{})
Expect(err).ToNot(HaveOccurred())
Expect(d).To(Equal([]byte("decrypted")))
Expect(enc).To(Equal(protocol.EncryptionSecure))
@@ -775,16 +781,18 @@ var _ = Describe("Client Crypto Setup", func() {
It("is not used after receiving the SHLO", func() {
doSHLO()
_, enc, err := cs.Open(nil, []byte("encrypted"), 0, []byte{})
cs.forwardSecureAEAD.(*mockcrypto.MockAEAD).EXPECT().Open(nil, []byte("encrypted"), protocol.PacketNumber(30), []byte{}).Return(nil, errors.New("authentication failed"))
_, enc, err := cs.Open(nil, []byte("encrypted"), 30, []byte{})
Expect(err).To(MatchError("authentication failed"))
Expect(enc).To(Equal(protocol.EncryptionUnspecified))
})
It("is not used for the crypto stream", func() {
doCompleteREJ()
cs.nullAEAD.(*mockcrypto.MockAEAD).EXPECT().Seal(nil, []byte("foobar"), protocol.PacketNumber(3), []byte{}).Return([]byte("foobar unencrypted"))
enc, sealer := cs.GetSealerForCryptoStream()
Expect(enc).To(Equal(protocol.EncryptionUnencrypted))
d := sealer.Seal(nil, []byte("foobar"), 0, []byte{})
d := sealer.Seal(nil, []byte("foobar"), 3, []byte{})
Expect(d).To(Equal([]byte("foobar unencrypted")))
})
})
@@ -792,38 +800,43 @@ var _ = Describe("Client Crypto Setup", func() {
Context("forward-secure encryption", func() {
It("is used after receiving the SHLO", func() {
doSHLO()
_, enc, err := cs.Open(nil, []byte("forward secure encrypted"), 0, []byte{})
cs.forwardSecureAEAD.(*mockcrypto.MockAEAD).EXPECT().Open(nil, []byte("shlo"), protocol.PacketNumber(4), []byte{})
cs.forwardSecureAEAD.(*mockcrypto.MockAEAD).EXPECT().Seal(nil, []byte("foobar"), protocol.PacketNumber(10), []byte{}).Return([]byte("foobar forward sec"))
_, enc, err := cs.Open(nil, []byte("shlo"), 4, []byte{})
Expect(err).ToNot(HaveOccurred())
Expect(enc).To(Equal(protocol.EncryptionForwardSecure))
enc, sealer := cs.GetSealer()
Expect(enc).To(Equal(protocol.EncryptionForwardSecure))
d := sealer.Seal(nil, []byte("foobar"), 0, []byte{})
d := sealer.Seal(nil, []byte("foobar"), 10, []byte{})
Expect(d).To(Equal([]byte("foobar forward sec")))
})
It("is not used for the crypto stream", func() {
doSHLO()
cs.nullAEAD.(*mockcrypto.MockAEAD).EXPECT().Seal(nil, []byte("foobar"), protocol.PacketNumber(3), []byte{}).Return([]byte("foobar unencrypted"))
enc, sealer := cs.GetSealerForCryptoStream()
Expect(enc).To(Equal(protocol.EncryptionUnencrypted))
d := sealer.Seal(nil, []byte("foobar"), 0, []byte{})
d := sealer.Seal(nil, []byte("foobar"), 3, []byte{})
Expect(d).To(Equal([]byte("foobar unencrypted")))
})
})
Context("forcing encryption levels", func() {
It("forces null encryption", func() {
cs.nullAEAD.(*mockcrypto.MockAEAD).EXPECT().Seal(nil, []byte("foobar"), protocol.PacketNumber(4), []byte{}).Return([]byte("foobar unencrypted"))
sealer, err := cs.GetSealerWithEncryptionLevel(protocol.EncryptionUnencrypted)
Expect(err).ToNot(HaveOccurred())
d := sealer.Seal(nil, []byte("foobar"), 0, []byte{})
d := sealer.Seal(nil, []byte("foobar"), 4, []byte{})
Expect(d).To(Equal([]byte("foobar unencrypted")))
})
It("forces initial encryption", func() {
doCompleteREJ()
cs.secureAEAD.(*mockcrypto.MockAEAD).EXPECT().Seal(nil, []byte("foobar"), protocol.PacketNumber(3), []byte{}).Return([]byte("foobar secure"))
sealer, err := cs.GetSealerWithEncryptionLevel(protocol.EncryptionSecure)
Expect(err).ToNot(HaveOccurred())
d := sealer.Seal(nil, []byte("foobar"), 0, []byte{})
Expect(d).To(Equal([]byte("foobar normal sec")))
d := sealer.Seal(nil, []byte("foobar"), 3, []byte{})
Expect(d).To(Equal([]byte("foobar secure")))
})
It("errors of no AEAD for initial encryption is available", func() {
@@ -834,9 +847,10 @@ var _ = Describe("Client Crypto Setup", func() {
It("forces forward-secure encryption", func() {
doSHLO()
cs.forwardSecureAEAD.(*mockcrypto.MockAEAD).EXPECT().Seal(nil, []byte("foobar"), protocol.PacketNumber(4), []byte{}).Return([]byte("foobar forward sec"))
sealer, err := cs.GetSealerWithEncryptionLevel(protocol.EncryptionForwardSecure)
Expect(err).ToNot(HaveOccurred())
d := sealer.Seal(nil, []byte("foobar"), 0, []byte{})
d := sealer.Seal(nil, []byte("foobar"), 4, []byte{})
Expect(d).To(Equal([]byte("foobar forward sec")))
})

View File

@@ -78,6 +78,10 @@ func NewCryptoSetup(
paramsChan chan<- TransportParameters,
aeadChanged chan<- protocol.EncryptionLevel,
) (CryptoSetup, error) {
nullAEAD, err := crypto.NewNullAEAD(protocol.PerspectiveServer, connID, version)
if err != nil {
return nil, err
}
return &cryptoSetupServer{
cryptoStream: cryptoStream,
connID: connID,
@@ -87,7 +91,7 @@ func NewCryptoSetup(
scfg: scfg,
keyDerivation: crypto.DeriveQuicCryptoAESKeys,
keyExchange: getEphermalKEX,
nullAEAD: crypto.NewNullAEAD(protocol.PerspectiveServer, version),
nullAEAD: nullAEAD,
params: params,
acceptSTKCallback: acceptSTK,
sentSHLO: make(chan struct{}),

View File

@@ -8,6 +8,7 @@ import (
"time"
"github.com/lucas-clemente/quic-go/internal/crypto"
"github.com/lucas-clemente/quic-go/internal/mocks/crypto"
"github.com/lucas-clemente/quic-go/internal/protocol"
"github.com/lucas-clemente/quic-go/internal/utils"
"github.com/lucas-clemente/quic-go/qerr"
@@ -55,58 +56,8 @@ func (*mockSigner) GetLeafCert(sni string) ([]byte, error) {
return []byte("certuncompressed"), nil
}
type mockAEAD struct {
encLevel protocol.EncryptionLevel
sharedSecret []byte
}
var _ crypto.AEAD = &mockAEAD{}
func (m *mockAEAD) Seal(dst, src []byte, packetNumber protocol.PacketNumber, associatedData []byte) []byte {
if cap(dst) < len(src)+12 {
dst = make([]byte, len(src)+12)
}
dst = dst[:len(src)+12]
copy(dst, src)
switch m.encLevel {
case protocol.EncryptionUnencrypted:
copy(dst[len(src):], []byte(" unencrypted"))
case protocol.EncryptionSecure:
copy(dst[len(src):], []byte(" normal sec"))
case protocol.EncryptionForwardSecure:
copy(dst[len(src):], []byte(" forward sec"))
default:
Fail("invalid encryption level")
}
return dst
}
func (m *mockAEAD) Open(dst, src []byte, packetNumber protocol.PacketNumber, associatedData []byte) ([]byte, error) {
if m.encLevel == protocol.EncryptionUnencrypted && string(src) == "unencrypted" ||
m.encLevel == protocol.EncryptionForwardSecure && string(src) == "forward secure encrypted" ||
m.encLevel == protocol.EncryptionSecure && string(src) == "encrypted" {
return []byte("decrypted"), nil
}
return nil, errors.New("authentication failed")
}
func (m *mockAEAD) Overhead() int {
return 12
}
var expectedInitialNonceLen int
var expectedFSNonceLen int
func mockQuicCryptoKeyDerivation(forwardSecure bool, sharedSecret, nonces []byte, connID protocol.ConnectionID, chlo []byte, scfg []byte, cert []byte, divNonce []byte, pers protocol.Perspective) (crypto.AEAD, error) {
var encLevel protocol.EncryptionLevel
if forwardSecure {
encLevel = protocol.EncryptionForwardSecure
Expect(nonces).To(HaveLen(expectedFSNonceLen))
} else {
encLevel = protocol.EncryptionSecure
Expect(nonces).To(HaveLen(expectedInitialNonceLen))
}
return &mockAEAD{encLevel: encLevel, sharedSecret: sharedSecret}, nil
return mockcrypto.NewMockAEAD(mockCtrl), nil
}
type mockStream struct {
@@ -180,11 +131,15 @@ var _ = Describe("Server Crypto Setup", func() {
sourceAddrValid bool
)
const (
expectedInitialNonceLen = 32
expectedFSNonceLen = 64
)
BeforeEach(func() {
var err error
remoteAddr := &net.UDPAddr{IP: net.IPv4(1, 2, 3, 4), Port: 1234}
expectedInitialNonceLen = 32
expectedFSNonceLen = 64
// use a buffered channel here, so that we can parse a CHLO without having to receive the TransportParameters to avoid blocking
paramsChan = make(chan TransportParameters, 1)
aeadChanged = make(chan protocol.EncryptionLevel, 2)
@@ -222,7 +177,7 @@ var _ = Describe("Server Crypto Setup", func() {
cs.acceptSTKCallback = func(_ net.Addr, _ *Cookie) bool { return sourceAddrValid }
cs.keyDerivation = mockQuicCryptoKeyDerivation
cs.keyExchange = func() crypto.KeyExchange { return &mockKEX{ephermal: true} }
cs.nullAEAD = &mockAEAD{encLevel: protocol.EncryptionUnencrypted}
cs.nullAEAD = mockcrypto.NewMockAEAD(mockCtrl)
cs.cryptoStream = stream
})
@@ -232,7 +187,7 @@ var _ = Describe("Server Crypto Setup", func() {
Context("diversification nonce", func() {
BeforeEach(func() {
cs.secureAEAD = &mockAEAD{}
cs.secureAEAD = mockcrypto.NewMockAEAD(mockCtrl)
cs.receivedForwardSecurePacket = false
Expect(cs.DiversificationNonce()).To(BeEmpty())
@@ -336,6 +291,20 @@ var _ = Describe("Server Crypto Setup", func() {
})
It("generates SHLO messages", func() {
var checkedSecure, checkedForwardSecure bool
cs.keyDerivation = func(forwardSecure bool, sharedSecret, nonces []byte, connID protocol.ConnectionID, chlo []byte, scfg []byte, cert []byte, divNonce []byte, pers protocol.Perspective) (crypto.AEAD, error) {
if forwardSecure {
Expect(nonces).To(HaveLen(expectedFSNonceLen))
checkedForwardSecure = true
Expect(sharedSecret).To(Equal([]byte("shared ephermal")))
} else {
Expect(nonces).To(HaveLen(expectedInitialNonceLen))
Expect(sharedSecret).To(Equal([]byte("shared key")))
checkedSecure = true
}
return mockcrypto.NewMockAEAD(mockCtrl), nil
}
response, err := cs.handleCHLO("", []byte("chlo-data"), map[Tag][]byte{
TagPUBS: []byte("pubs-c"),
TagNONC: nonce32,
@@ -351,12 +320,8 @@ var _ = Describe("Server Crypto Setup", func() {
utils.BigEndian.WriteUint32(b, uint32(v))
Expect(response).To(ContainSubstring(string(b.Bytes())))
}
Expect(cs.secureAEAD).ToNot(BeNil())
Expect(cs.secureAEAD.(*mockAEAD).encLevel).To(Equal(protocol.EncryptionSecure))
Expect(cs.secureAEAD.(*mockAEAD).sharedSecret).To(Equal([]byte("shared key")))
Expect(cs.forwardSecureAEAD).ToNot(BeNil())
Expect(cs.forwardSecureAEAD.(*mockAEAD).sharedSecret).To(Equal([]byte("shared ephermal")))
Expect(cs.forwardSecureAEAD.(*mockAEAD).encLevel).To(Equal(protocol.EncryptionForwardSecure))
Expect(checkedSecure).To(BeTrue())
Expect(checkedForwardSecure).To(BeTrue())
})
It("handles long handshake", func() {
@@ -576,65 +541,79 @@ var _ = Describe("Server Crypto Setup", func() {
Context("null encryption", func() {
It("is used initially", func() {
cs.nullAEAD.(*mockcrypto.MockAEAD).EXPECT().Seal(nil, []byte("foobar"), protocol.PacketNumber(10), []byte{}).Return([]byte("foobar signed"))
enc, sealer := cs.GetSealer()
Expect(enc).To(Equal(protocol.EncryptionUnencrypted))
d := sealer.Seal(nil, []byte("foobar"), 0, []byte{})
Expect(d).To(Equal([]byte("foobar unencrypted")))
d := sealer.Seal(nil, []byte("foobar"), 10, []byte{})
Expect(d).To(Equal([]byte("foobar signed")))
})
It("is used for crypto stream", func() {
It("is used for the crypto stream", func() {
cs.nullAEAD.(*mockcrypto.MockAEAD).EXPECT().Seal(nil, []byte("foobar"), protocol.PacketNumber(0), []byte{})
enc, sealer := cs.GetSealerForCryptoStream()
Expect(enc).To(Equal(protocol.EncryptionUnencrypted))
d := sealer.Seal(nil, []byte("foobar"), 0, []byte{})
Expect(d).To(Equal([]byte("foobar unencrypted")))
sealer.Seal(nil, []byte("foobar"), 0, []byte{})
})
It("is accepted initially", func() {
d, enc, err := cs.Open(nil, []byte("unencrypted"), 0, []byte{})
cs.nullAEAD.(*mockcrypto.MockAEAD).EXPECT().Open(nil, []byte("unencrypted"), protocol.PacketNumber(5), []byte{}).Return([]byte("decrypted"), nil)
d, enc, err := cs.Open(nil, []byte("unencrypted"), 5, []byte{})
Expect(err).ToNot(HaveOccurred())
Expect(d).To(Equal([]byte("decrypted")))
Expect(enc).To(Equal(protocol.EncryptionUnencrypted))
})
It("errors if the has the wrong hash", func() {
_, enc, err := cs.Open(nil, []byte("not unencrypted"), 0, []byte{})
cs.nullAEAD.(*mockcrypto.MockAEAD).EXPECT().Open(nil, []byte("not unencrypted"), protocol.PacketNumber(5), []byte{}).Return(nil, errors.New("authentication failed"))
_, enc, err := cs.Open(nil, []byte("not unencrypted"), 5, []byte{})
Expect(err).To(MatchError("authentication failed"))
Expect(enc).To(Equal(protocol.EncryptionUnspecified))
})
It("is still accepted after CHLO", func() {
doCHLO()
// it tries forward secure and secure decryption first
cs.forwardSecureAEAD.(*mockcrypto.MockAEAD).EXPECT().Open(nil, []byte("unencrypted"), protocol.PacketNumber(99), []byte{}).Return(nil, errors.New("authentication failed"))
cs.secureAEAD.(*mockcrypto.MockAEAD).EXPECT().Open(nil, []byte("unencrypted"), protocol.PacketNumber(99), []byte{}).Return(nil, errors.New("authentication failed"))
cs.nullAEAD.(*mockcrypto.MockAEAD).EXPECT().Open(nil, []byte("unencrypted"), protocol.PacketNumber(99), []byte{})
Expect(cs.secureAEAD).ToNot(BeNil())
_, enc, err := cs.Open(nil, []byte("unencrypted"), 0, []byte{})
_, enc, err := cs.Open(nil, []byte("unencrypted"), 99, []byte{})
Expect(err).ToNot(HaveOccurred())
Expect(enc).To(Equal(protocol.EncryptionUnencrypted))
})
It("is not accepted after receiving secure packet", func() {
doCHLO()
Expect(cs.secureAEAD).ToNot(BeNil())
d, enc, err := cs.Open(nil, []byte("encrypted"), 0, []byte{})
// first receive a secure packet
cs.forwardSecureAEAD.(*mockcrypto.MockAEAD).EXPECT().Open(nil, []byte("encrypted"), protocol.PacketNumber(98), []byte{}).Return(nil, errors.New("authentication failed"))
cs.secureAEAD.(*mockcrypto.MockAEAD).EXPECT().Open(nil, []byte("encrypted"), protocol.PacketNumber(98), []byte{}).Return([]byte("decrypted"), nil)
d, enc, err := cs.Open(nil, []byte("encrypted"), 98, []byte{})
Expect(enc).To(Equal(protocol.EncryptionSecure))
Expect(err).ToNot(HaveOccurred())
Expect(d).To(Equal([]byte("decrypted")))
_, enc, err = cs.Open(nil, []byte("foobar unencrypted"), 0, []byte{})
// now receive an unencrypted packet
cs.forwardSecureAEAD.(*mockcrypto.MockAEAD).EXPECT().Open(nil, []byte("unencrypted"), protocol.PacketNumber(99), []byte{}).Return(nil, errors.New("authentication failed"))
cs.secureAEAD.(*mockcrypto.MockAEAD).EXPECT().Open(nil, []byte("unencrypted"), protocol.PacketNumber(99), []byte{}).Return(nil, errors.New("authentication failed"))
_, enc, err = cs.Open(nil, []byte("unencrypted"), 99, []byte{})
Expect(err).To(MatchError("authentication failed"))
Expect(enc).To(Equal(protocol.EncryptionUnspecified))
})
It("is not used after CHLO", func() {
doCHLO()
cs.forwardSecureAEAD.(*mockcrypto.MockAEAD).EXPECT().Seal(nil, []byte("foobar"), protocol.PacketNumber(0), []byte{})
enc, sealer := cs.GetSealer()
Expect(enc).ToNot(Equal(protocol.EncryptionUnencrypted))
d := sealer.Seal(nil, []byte("foobar"), 0, []byte{})
Expect(d).ToNot(Equal([]byte("foobar unencrypted")))
sealer.Seal(nil, []byte("foobar"), 0, []byte{})
})
})
Context("initial encryption", func() {
It("is accepted after CHLO", func() {
doCHLO()
d, enc, err := cs.Open(nil, []byte("encrypted"), 0, []byte{})
cs.forwardSecureAEAD.(*mockcrypto.MockAEAD).EXPECT().Open(nil, []byte("encrypted"), protocol.PacketNumber(98), []byte{}).Return(nil, errors.New("authentication failed"))
cs.secureAEAD.(*mockcrypto.MockAEAD).EXPECT().Open(nil, []byte("encrypted"), protocol.PacketNumber(98), []byte{}).Return([]byte("decrypted"), nil)
d, enc, err := cs.Open(nil, []byte("encrypted"), 98, []byte{})
Expect(enc).To(Equal(protocol.EncryptionSecure))
Expect(err).ToNot(HaveOccurred())
Expect(d).To(Equal([]byte("decrypted")))
@@ -642,34 +621,41 @@ var _ = Describe("Server Crypto Setup", func() {
It("is not accepted after receiving forward secure packet", func() {
doCHLO()
_, _, err := cs.Open(nil, []byte("forward secure encrypted"), 0, []byte{})
// receive a forward secure packet
cs.forwardSecureAEAD.(*mockcrypto.MockAEAD).EXPECT().Open(nil, []byte("forward secure encrypted"), protocol.PacketNumber(11), []byte{})
_, _, err := cs.Open(nil, []byte("forward secure encrypted"), 11, []byte{})
Expect(err).ToNot(HaveOccurred())
_, enc, err := cs.Open(nil, []byte("encrypted"), 0, []byte{})
// receive a secure packet
cs.forwardSecureAEAD.(*mockcrypto.MockAEAD).EXPECT().Open(nil, []byte("encrypted"), protocol.PacketNumber(12), []byte{}).Return(nil, errors.New("authentication failed"))
_, enc, err := cs.Open(nil, []byte("encrypted"), 12, []byte{})
Expect(err).To(MatchError("authentication failed"))
Expect(enc).To(Equal(protocol.EncryptionUnspecified))
})
It("is used for crypto stream", func() {
It("is used for the crypto stream", func() {
doCHLO()
cs.secureAEAD.(*mockcrypto.MockAEAD).EXPECT().Seal(nil, []byte("foobar"), protocol.PacketNumber(1), []byte{}).Return([]byte("foobar crypto stream"))
enc, sealer := cs.GetSealerForCryptoStream()
Expect(enc).To(Equal(protocol.EncryptionSecure))
d := sealer.Seal(nil, []byte("foobar"), 0, []byte{})
Expect(d).To(Equal([]byte("foobar normal sec")))
d := sealer.Seal(nil, []byte("foobar"), 1, []byte{})
Expect(d).To(Equal([]byte("foobar crypto stream")))
})
})
Context("forward secure encryption", func() {
It("is used after the CHLO", func() {
doCHLO()
cs.forwardSecureAEAD.(*mockcrypto.MockAEAD).EXPECT().Seal(nil, []byte("foobar"), protocol.PacketNumber(20), []byte{}).Return([]byte("foobar forward sec"))
enc, sealer := cs.GetSealer()
Expect(enc).To(Equal(protocol.EncryptionForwardSecure))
d := sealer.Seal(nil, []byte("foobar"), 0, []byte{})
d := sealer.Seal(nil, []byte("foobar"), 20, []byte{})
Expect(d).To(Equal([]byte("foobar forward sec")))
})
It("regards the handshake as complete once it receives a forward encrypted packet", func() {
doCHLO()
_, _, err := cs.Open(nil, []byte("forward secure encrypted"), 0, []byte{})
cs.forwardSecureAEAD.(*mockcrypto.MockAEAD).EXPECT().Open(nil, []byte("forward secure encrypted"), protocol.PacketNumber(200), []byte{})
_, _, err := cs.Open(nil, []byte("forward secure encrypted"), 200, []byte{})
Expect(err).ToNot(HaveOccurred())
Expect(aeadChanged).To(BeClosed())
})
@@ -677,18 +663,20 @@ var _ = Describe("Server Crypto Setup", func() {
Context("forcing encryption levels", func() {
It("forces null encryption", func() {
cs.nullAEAD.(*mockcrypto.MockAEAD).EXPECT().Seal(nil, []byte("foobar"), protocol.PacketNumber(11), []byte{}).Return([]byte("foobar unencrypted"))
sealer, err := cs.GetSealerWithEncryptionLevel(protocol.EncryptionUnencrypted)
Expect(err).ToNot(HaveOccurred())
d := sealer.Seal(nil, []byte("foobar"), 0, []byte{})
d := sealer.Seal(nil, []byte("foobar"), 11, []byte{})
Expect(d).To(Equal([]byte("foobar unencrypted")))
})
It("forces initial encryption", func() {
doCHLO()
cs.secureAEAD.(*mockcrypto.MockAEAD).EXPECT().Seal(nil, []byte("foobar"), protocol.PacketNumber(12), []byte{}).Return([]byte("foobar secure"))
sealer, err := cs.GetSealerWithEncryptionLevel(protocol.EncryptionSecure)
Expect(err).ToNot(HaveOccurred())
d := sealer.Seal(nil, []byte("foobar"), 0, []byte{})
Expect(d).To(Equal([]byte("foobar normal sec")))
d := sealer.Seal(nil, []byte("foobar"), 12, []byte{})
Expect(d).To(Equal([]byte("foobar secure")))
})
It("errors if no AEAD for initial encryption is available", func() {
@@ -699,9 +687,10 @@ var _ = Describe("Server Crypto Setup", func() {
It("forces forward-secure encryption", func() {
doCHLO()
cs.forwardSecureAEAD.(*mockcrypto.MockAEAD).EXPECT().Seal(nil, []byte("foobar"), protocol.PacketNumber(13), []byte{}).Return([]byte("foobar forward sec"))
sealer, err := cs.GetSealerWithEncryptionLevel(protocol.EncryptionForwardSecure)
Expect(err).ToNot(HaveOccurred())
d := sealer.Seal(nil, []byte("foobar"), 0, []byte{})
d := sealer.Seal(nil, []byte("foobar"), 13, []byte{})
Expect(d).To(Equal([]byte("foobar forward sec")))
})

View File

@@ -32,6 +32,7 @@ type cryptoSetupTLS struct {
// NewCryptoSetupTLSServer creates a new TLS CryptoSetup instance for a server
func NewCryptoSetupTLSServer(
cryptoStream io.ReadWriter,
connID protocol.ConnectionID,
tlsConfig *tls.Config,
params *TransportParameters,
paramsChan chan<- TransportParameters,
@@ -49,10 +50,15 @@ func NewCryptoSetupTLSServer(
return nil, err
}
nullAEAD, err := crypto.NewNullAEAD(protocol.PerspectiveServer, connID, version)
if err != nil {
return nil, err
}
return &cryptoSetupTLS{
perspective: protocol.PerspectiveServer,
tls: &mintController{mintConn},
nullAEAD: crypto.NewNullAEAD(protocol.PerspectiveServer, version),
nullAEAD: nullAEAD,
keyDerivation: crypto.DeriveAESKeys,
aeadChanged: aeadChanged,
}, nil
@@ -61,7 +67,8 @@ func NewCryptoSetupTLSServer(
// NewCryptoSetupTLSClient creates a new TLS CryptoSetup instance for a client
func NewCryptoSetupTLSClient(
cryptoStream io.ReadWriter,
hostname string, // only needed for the client
connID protocol.ConnectionID,
hostname string,
tlsConfig *tls.Config,
params *TransportParameters,
paramsChan chan<- TransportParameters,
@@ -81,10 +88,15 @@ func NewCryptoSetupTLSClient(
return nil, err
}
nullAEAD, err := crypto.NewNullAEAD(protocol.PerspectiveClient, connID, version)
if err != nil {
return nil, err
}
return &cryptoSetupTLS{
perspective: protocol.PerspectiveClient,
tls: &mintController{mintConn},
nullAEAD: crypto.NewNullAEAD(protocol.PerspectiveClient, version),
nullAEAD: nullAEAD,
keyDerivation: crypto.DeriveAESKeys,
aeadChanged: aeadChanged,
}, nil

View File

@@ -1,10 +1,12 @@
package handshake
import (
"errors"
"fmt"
"github.com/bifurcation/mint"
"github.com/lucas-clemente/quic-go/internal/crypto"
"github.com/lucas-clemente/quic-go/internal/mocks/crypto"
"github.com/lucas-clemente/quic-go/internal/protocol"
"github.com/lucas-clemente/quic-go/internal/testdata"
@@ -30,7 +32,7 @@ func (h *fakeMintTLS) SetExtensionHandler(mint.AppExtensionHandler) error {
}
func mockKeyDerivation(crypto.TLSExporter, protocol.Perspective) (crypto.AEAD, error) {
return &mockAEAD{encLevel: protocol.EncryptionForwardSecure}, nil
return mockcrypto.NewMockAEAD(mockCtrl), nil
}
var _ = Describe("TLS Crypto Setup", func() {
@@ -45,6 +47,7 @@ var _ = Describe("TLS Crypto Setup", func() {
aeadChanged = make(chan protocol.EncryptionLevel, 2)
csInt, err := NewCryptoSetupTLSServer(
nil,
1,
testdata.GetTLSConfig(),
&TransportParameters{},
paramsChan,
@@ -54,6 +57,7 @@ var _ = Describe("TLS Crypto Setup", func() {
)
Expect(err).ToNot(HaveOccurred())
cs = csInt.(*cryptoSetupTLS)
cs.nullAEAD = mockcrypto.NewMockAEAD(mockCtrl)
})
It("errors when the handshake fails", func() {
@@ -73,8 +77,6 @@ var _ = Describe("TLS Crypto Setup", func() {
})
Context("escalating crypto", func() {
var foobarFNVSigned []byte // a "foobar", FNV signed
doHandshake := func() {
cs.tls = &fakeMintTLS{result: mint.AlertNoAlert}
cs.keyDerivation = mockKeyDerivation
@@ -82,43 +84,42 @@ var _ = Describe("TLS Crypto Setup", func() {
Expect(err).ToNot(HaveOccurred())
}
BeforeEach(func() {
nullAEAD := crypto.NewNullAEAD(protocol.PerspectiveServer, protocol.VersionTLS)
foobarFNVSigned = nullAEAD.Seal(nil, []byte("foobar"), 0, nil)
})
Context("null encryption", func() {
It("is used initially", func() {
cs.nullAEAD.(*mockcrypto.MockAEAD).EXPECT().Seal(nil, []byte("foobar"), protocol.PacketNumber(5), []byte{}).Return([]byte("foobar signed"))
enc, sealer := cs.GetSealer()
Expect(enc).To(Equal(protocol.EncryptionUnencrypted))
d := sealer.Seal(nil, []byte("foobar"), 0, []byte{})
Expect(d).To(Equal(foobarFNVSigned))
d := sealer.Seal(nil, []byte("foobar"), 5, []byte{})
Expect(d).To(Equal([]byte("foobar signed")))
})
It("is accepted initially", func() {
d, enc, err := cs.Open(nil, foobarFNVSigned, 0, []byte{})
cs.nullAEAD.(*mockcrypto.MockAEAD).EXPECT().Open(nil, []byte("foobar enc"), protocol.PacketNumber(10), []byte{}).Return([]byte("foobar"), nil)
d, enc, err := cs.Open(nil, []byte("foobar enc"), 10, []byte{})
Expect(err).ToNot(HaveOccurred())
Expect(d).To(Equal([]byte("foobar")))
Expect(enc).To(Equal(protocol.EncryptionUnencrypted))
})
It("is used for crypto stream", func() {
cs.nullAEAD.(*mockcrypto.MockAEAD).EXPECT().Seal(nil, []byte("foobar"), protocol.PacketNumber(20), []byte{}).Return([]byte("foobar signed"))
enc, sealer := cs.GetSealerForCryptoStream()
Expect(enc).To(Equal(protocol.EncryptionUnencrypted))
d := sealer.Seal(nil, []byte("foobar"), 0, []byte{})
Expect(d).To(Equal(foobarFNVSigned))
d := sealer.Seal(nil, []byte("foobar"), 20, []byte{})
Expect(d).To(Equal([]byte("foobar signed")))
})
It("errors if the has the wrong hash", func() {
foobarFNVSigned[0]++
_, enc, err := cs.Open(nil, foobarFNVSigned, 0, []byte{})
Expect(err).To(MatchError("NullAEAD: failed to authenticate received data"))
cs.nullAEAD.(*mockcrypto.MockAEAD).EXPECT().Open(nil, []byte("foobar enc"), protocol.PacketNumber(10), []byte{}).Return(nil, errors.New("authentication failed"))
_, enc, err := cs.Open(nil, []byte("foobar enc"), 10, []byte{})
Expect(err).To(MatchError("authentication failed"))
Expect(enc).To(Equal(protocol.EncryptionUnspecified))
})
It("is not accepted after the handshake completes", func() {
doHandshake()
_, enc, err := cs.Open(nil, foobarFNVSigned, 0, []byte{})
cs.aead.(*mockcrypto.MockAEAD).EXPECT().Open(nil, []byte("foobar encrypted"), protocol.PacketNumber(1), []byte{}).Return(nil, errors.New("authentication failed"))
_, enc, err := cs.Open(nil, []byte("foobar encrypted"), 1, []byte{})
Expect(err).To(MatchError("authentication failed"))
Expect(enc).To(Equal(protocol.EncryptionUnspecified))
})
@@ -127,15 +128,17 @@ var _ = Describe("TLS Crypto Setup", func() {
Context("forward-secure encryption", func() {
It("is used for sealing after the handshake completes", func() {
doHandshake()
cs.aead.(*mockcrypto.MockAEAD).EXPECT().Seal(nil, []byte("foobar"), protocol.PacketNumber(5), []byte{}).Return([]byte("foobar forward sec"))
enc, sealer := cs.GetSealer()
Expect(enc).To(Equal(protocol.EncryptionForwardSecure))
d := sealer.Seal(nil, []byte("foobar"), 0, nil)
d := sealer.Seal(nil, []byte("foobar"), 5, []byte{})
Expect(d).To(Equal([]byte("foobar forward sec")))
})
It("is used for opening after the handshake completes", func() {
doHandshake()
d, enc, err := cs.Open(nil, []byte("forward secure encrypted"), 0, nil)
cs.aead.(*mockcrypto.MockAEAD).EXPECT().Open(nil, []byte("encrypted"), protocol.PacketNumber(6), []byte{}).Return([]byte("decrypted"), nil)
d, enc, err := cs.Open(nil, []byte("encrypted"), 6, []byte{})
Expect(err).ToNot(HaveOccurred())
Expect(enc).To(Equal(protocol.EncryptionForwardSecure))
Expect(d).To(Equal([]byte("decrypted")))
@@ -145,17 +148,19 @@ var _ = Describe("TLS Crypto Setup", func() {
Context("forcing encryption levels", func() {
It("forces null encryption", func() {
doHandshake()
cs.nullAEAD.(*mockcrypto.MockAEAD).EXPECT().Seal(nil, []byte("foobar"), protocol.PacketNumber(5), []byte{}).Return([]byte("foobar signed"))
sealer, err := cs.GetSealerWithEncryptionLevel(protocol.EncryptionUnencrypted)
Expect(err).ToNot(HaveOccurred())
d := sealer.Seal(nil, []byte("foobar"), 0, []byte{})
Expect(d).To(Equal(foobarFNVSigned))
d := sealer.Seal(nil, []byte("foobar"), 5, []byte{})
Expect(d).To(Equal([]byte("foobar signed")))
})
It("forces forward-secure encryption", func() {
doHandshake()
cs.aead.(*mockcrypto.MockAEAD).EXPECT().Seal(nil, []byte("foobar"), protocol.PacketNumber(5), []byte{}).Return([]byte("foobar forward sec"))
sealer, err := cs.GetSealerWithEncryptionLevel(protocol.EncryptionForwardSecure)
Expect(err).ToNot(HaveOccurred())
d := sealer.Seal(nil, []byte("foobar"), 0, []byte{})
d := sealer.Seal(nil, []byte("foobar"), 5, []byte{})
Expect(d).To(Equal([]byte("foobar forward sec")))
})

View File

@@ -1,6 +1,7 @@
package handshake
import (
"github.com/golang/mock/gomock"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
@@ -11,3 +12,13 @@ func TestQuicGo(t *testing.T) {
RegisterFailHandler(Fail)
RunSpecs(t, "Handshake Suite")
}
var mockCtrl *gomock.Controller
var _ = BeforeEach(func() {
mockCtrl = gomock.NewController(GinkgoT())
})
var _ = AfterEach(func() {
mockCtrl.Finish()
})

View File

@@ -0,0 +1,71 @@
// Code generated by MockGen. DO NOT EDIT.
// Source: github.com/lucas-clemente/quic-go/internal/crypto (interfaces: AEAD)
package mockcrypto
import (
reflect "reflect"
gomock "github.com/golang/mock/gomock"
protocol "github.com/lucas-clemente/quic-go/internal/protocol"
)
// MockAEAD is a mock of AEAD interface
type MockAEAD struct {
ctrl *gomock.Controller
recorder *MockAEADMockRecorder
}
// MockAEADMockRecorder is the mock recorder for MockAEAD
type MockAEADMockRecorder struct {
mock *MockAEAD
}
// NewMockAEAD creates a new mock instance
func NewMockAEAD(ctrl *gomock.Controller) *MockAEAD {
mock := &MockAEAD{ctrl: ctrl}
mock.recorder = &MockAEADMockRecorder{mock}
return mock
}
// EXPECT returns an object that allows the caller to indicate expected use
func (_m *MockAEAD) EXPECT() *MockAEADMockRecorder {
return _m.recorder
}
// Open mocks base method
func (_m *MockAEAD) Open(_param0 []byte, _param1 []byte, _param2 protocol.PacketNumber, _param3 []byte) ([]byte, error) {
ret := _m.ctrl.Call(_m, "Open", _param0, _param1, _param2, _param3)
ret0, _ := ret[0].([]byte)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// Open indicates an expected call of Open
func (_mr *MockAEADMockRecorder) Open(arg0, arg1, arg2, arg3 interface{}) *gomock.Call {
return _mr.mock.ctrl.RecordCallWithMethodType(_mr.mock, "Open", reflect.TypeOf((*MockAEAD)(nil).Open), arg0, arg1, arg2, arg3)
}
// Overhead mocks base method
func (_m *MockAEAD) Overhead() int {
ret := _m.ctrl.Call(_m, "Overhead")
ret0, _ := ret[0].(int)
return ret0
}
// Overhead indicates an expected call of Overhead
func (_mr *MockAEADMockRecorder) Overhead() *gomock.Call {
return _mr.mock.ctrl.RecordCallWithMethodType(_mr.mock, "Overhead", reflect.TypeOf((*MockAEAD)(nil).Overhead))
}
// Seal mocks base method
func (_m *MockAEAD) Seal(_param0 []byte, _param1 []byte, _param2 protocol.PacketNumber, _param3 []byte) []byte {
ret := _m.ctrl.Call(_m, "Seal", _param0, _param1, _param2, _param3)
ret0, _ := ret[0].([]byte)
return ret0
}
// Seal indicates an expected call of Seal
func (_mr *MockAEADMockRecorder) Seal(arg0, arg1, arg2, arg3 interface{}) *gomock.Call {
return _mr.mock.ctrl.RecordCallWithMethodType(_mr.mock, "Seal", reflect.TypeOf((*MockAEAD)(nil).Seal), arg0, arg1, arg2, arg3)
}

View File

@@ -2,5 +2,6 @@ package mocks
//go:generate sh -c "./mockgen_internal.sh mocks stream_flow_controller.go github.com/lucas-clemente/quic-go/internal/flowcontrol StreamFlowController"
//go:generate sh -c "./mockgen_internal.sh mocks connection_flow_controller.go github.com/lucas-clemente/quic-go/internal/flowcontrol ConnectionFlowController"
//go:generate sh -c "./mockgen_internal.sh mockcrypto crypto/aead.go github.com/lucas-clemente/quic-go/internal/crypto AEAD"
//go:generate sh -c "./mockgen_stream.sh mocks stream.go github.com/lucas-clemente/quic-go StreamI"
//go:generate sh -c "goimports -w ."

View File

@@ -17,12 +17,14 @@ type mockAEAD struct {
}
func (m *mockAEAD) Open(dst, src []byte, packetNumber protocol.PacketNumber, associatedData []byte) ([]byte, protocol.EncryptionLevel, error) {
nullAEAD := crypto.NewNullAEAD(protocol.PerspectiveClient, protocol.VersionWhatever)
nullAEAD, err := crypto.NewNullAEAD(protocol.PerspectiveClient, 0x1337, protocol.VersionWhatever)
Expect(err).ToNot(HaveOccurred())
res, err := nullAEAD.Open(dst, src, packetNumber, associatedData)
return res, m.encLevelOpen, err
}
func (m *mockAEAD) Seal(dst, src []byte, packetNumber protocol.PacketNumber, associatedData []byte) ([]byte, protocol.EncryptionLevel) {
nullAEAD := crypto.NewNullAEAD(protocol.PerspectiveServer, protocol.VersionWhatever)
nullAEAD, err := crypto.NewNullAEAD(protocol.PerspectiveServer, 0x1337, protocol.VersionWhatever)
Expect(err).ToNot(HaveOccurred())
return nullAEAD.Seal(dst, src, packetNumber, associatedData), protocol.EncryptionUnspecified
}

View File

@@ -185,8 +185,9 @@ var _ = Describe("Server", func() {
It("closes and deletes sessions", func() {
serv.deleteClosedSessionsAfter = time.Second // make sure that the nil value for the closed session doesn't get deleted in this test
nullAEAD := crypto.NewNullAEAD(protocol.PerspectiveServer, protocol.VersionWhatever)
err := serv.handlePacket(nil, nil, append(firstPacket, nullAEAD.Seal(nil, nil, 0, firstPacket)...))
nullAEAD, err := crypto.NewNullAEAD(protocol.PerspectiveServer, connID, protocol.VersionWhatever)
Expect(err).ToNot(HaveOccurred())
err = serv.handlePacket(nil, nil, append(firstPacket, nullAEAD.Seal(nil, nil, 0, firstPacket)...))
Expect(err).ToNot(HaveOccurred())
Expect(serv.sessions).To(HaveLen(1))
Expect(serv.sessions[connID]).ToNot(BeNil())
@@ -199,8 +200,9 @@ var _ = Describe("Server", func() {
It("deletes nil session entries after a wait time", func() {
serv.deleteClosedSessionsAfter = 25 * time.Millisecond
nullAEAD := crypto.NewNullAEAD(protocol.PerspectiveServer, protocol.VersionWhatever)
err := serv.handlePacket(nil, nil, append(firstPacket, nullAEAD.Seal(nil, nil, 0, firstPacket)...))
nullAEAD, err := crypto.NewNullAEAD(protocol.PerspectiveServer, connID, protocol.VersionWhatever)
Expect(err).ToNot(HaveOccurred())
err = serv.handlePacket(nil, nil, append(firstPacket, nullAEAD.Seal(nil, nil, 0, firstPacket)...))
Expect(err).ToNot(HaveOccurred())
Expect(serv.sessions).To(HaveLen(1))
Expect(serv.sessions).To(HaveKey(connID))

View File

@@ -212,6 +212,7 @@ func (s *session) setup(
if s.version.UsesTLS() {
s.cryptoSetup, err = handshake.NewCryptoSetupTLSServer(
s.cryptoStream,
s.connectionID,
tlsConf,
transportParams,
paramsChan,
@@ -238,6 +239,7 @@ func (s *session) setup(
if s.version.UsesTLS() {
s.cryptoSetup, err = handshake.NewCryptoSetupTLSClient(
s.cryptoStream,
s.connectionID,
hostname,
tlsConf,
transportParams,