return the encryption level of a packet when decrypting it

This commit is contained in:
Marten Seemann 2017-02-24 13:01:17 +07:00
parent 811bd20939
commit a972c7a21e
No known key found for this signature in database
GPG key ID: 3603F40B121FCDEA
12 changed files with 85 additions and 47 deletions

View file

@ -45,9 +45,10 @@ var _ h2quicClient = &Client{}
// NewClient creates a new client
func NewClient(t *QuicRoundTripper, tlsConfig *tls.Config, hostname string) *Client {
c := &Client{
t: t,
hostname: authorityAddr("https", hostname),
responses: make(map[protocol.StreamID]chan *http.Response),
t: t,
hostname: authorityAddr("https", hostname),
responses: make(map[protocol.StreamID]chan *http.Response),
encryptionLevel: protocol.EncryptionUnencrypted,
}
c.cryptoChangedCond = sync.Cond{L: &c.mutex}
c.config = &quic.Config{

View file

@ -90,7 +90,7 @@ var _ = Describe("Client", func() {
})
It("sets the correct crypto level", func() {
Expect(client.encryptionLevel).To(Equal(protocol.Unencrypted))
Expect(client.encryptionLevel).To(Equal(protocol.EncryptionUnencrypted))
client.config.ConnState(session, quic.ConnStateSecure)
Expect(client.encryptionLevel).To(Equal(protocol.EncryptionSecure))
client.config.ConnState(session, quic.ConnStateForwardSecure)

View file

@ -51,7 +51,6 @@ type cryptoSetupClient struct {
connectionParameters ConnectionParametersManager
}
var _ crypto.AEAD = &cryptoSetupClient{}
var _ CryptoSetup = &cryptoSetupClient{}
var (
@ -276,27 +275,31 @@ func (h *cryptoSetupClient) validateVersionList(verTags []byte) bool {
return true
}
func (h *cryptoSetupClient) Open(dst, src []byte, packetNumber protocol.PacketNumber, associatedData []byte) ([]byte, error) {
func (h *cryptoSetupClient) Open(dst, src []byte, packetNumber protocol.PacketNumber, associatedData []byte) ([]byte, protocol.EncryptionLevel, error) {
if h.forwardSecureAEAD != nil {
data, err := h.forwardSecureAEAD.Open(dst, src, packetNumber, associatedData)
if err == nil {
return data, nil
return data, protocol.EncryptionForwardSecure, nil
}
return nil, err
return nil, protocol.EncryptionUnspecified, err
}
if h.secureAEAD != nil {
data, err := h.secureAEAD.Open(dst, src, packetNumber, associatedData)
if err == nil {
h.receivedSecurePacket = true
return data, nil
return data, protocol.EncryptionSecure, nil
}
if h.receivedSecurePacket {
return nil, err
return nil, protocol.EncryptionUnspecified, err
}
}
return (&crypto.NullAEAD{}).Open(dst, src, packetNumber, associatedData)
nullAEAD := &crypto.NullAEAD{}
res, err := nullAEAD.Open(dst, src, packetNumber, associatedData)
if err != nil {
return nil, protocol.EncryptionUnspecified, err
}
return res, protocol.EncryptionUnencrypted, nil
}
func (h *cryptoSetupClient) Seal(dst, src []byte, packetNumber protocol.PacketNumber, associatedData []byte) []byte {

View file

@ -680,25 +680,28 @@ var _ = Describe("Crypto setup", func() {
})
It("is accepted initially", func() {
d, err := cs.Open(nil, foobarFNVSigned, 0, []byte{})
d, enc, err := cs.Open(nil, foobarFNVSigned, 0, []byte{})
Expect(err).ToNot(HaveOccurred())
Expect(d).To(Equal([]byte("foobar")))
Expect(enc).To(Equal(protocol.EncryptionUnencrypted))
})
It("is accepted before the server sent an encrypted packet", func() {
doCompleteREJ()
cs.receivedSecurePacket = false
Expect(cs.secureAEAD).ToNot(BeNil())
d, err := cs.Open(nil, foobarFNVSigned, 0, []byte{})
d, enc, err := cs.Open(nil, foobarFNVSigned, 0, []byte{})
Expect(err).ToNot(HaveOccurred())
Expect(d).To(Equal([]byte("foobar")))
Expect(enc).To(Equal(protocol.EncryptionUnencrypted))
})
It("is not accepted after the server sent an encrypted packet", func() {
doCompleteREJ()
cs.receivedSecurePacket = true
_, err := cs.Open(nil, foobarFNVSigned, 0, []byte{})
_, enc, err := cs.Open(nil, foobarFNVSigned, 0, []byte{})
Expect(err).To(MatchError("authentication failed"))
Expect(enc).To(Equal(protocol.EncryptionUnspecified))
})
})
@ -712,24 +715,27 @@ var _ = Describe("Crypto setup", func() {
It("is accepted", func() {
doCompleteREJ()
d, err := cs.Open(nil, []byte("encrypted"), 0, []byte{})
d, enc, err := cs.Open(nil, []byte("encrypted"), 0, []byte{})
Expect(err).ToNot(HaveOccurred())
Expect(d).To(Equal([]byte("decrypted")))
Expect(enc).To(Equal(protocol.EncryptionSecure))
Expect(cs.receivedSecurePacket).To(BeTrue())
})
It("is not used after receiving the SHLO", func() {
doSHLO()
_, err := cs.Open(nil, []byte("encrypted"), 0, []byte{})
_, enc, err := cs.Open(nil, []byte("encrypted"), 0, []byte{})
Expect(err).To(MatchError("authentication failed"))
Expect(enc).To(Equal(protocol.EncryptionUnspecified))
})
})
Context("forward-secure encryption", func() {
It("is used after receiving the SHLO", func() {
doSHLO()
_, err := cs.Open(nil, []byte("forward secure encrypted"), 0, []byte{})
_, enc, err := cs.Open(nil, []byte("forward secure encrypted"), 0, []byte{})
Expect(err).ToNot(HaveOccurred())
Expect(enc).To(Equal(protocol.EncryptionForwardSecure))
d := cs.Seal(nil, []byte("foobar"), 0, []byte{})
Expect(d).To(Equal([]byte("foobar forward sec")))
})

View file

@ -5,7 +5,7 @@ import "github.com/lucas-clemente/quic-go/protocol"
// CryptoSetup is a crypto setup
type CryptoSetup interface {
HandleCryptoStream() error
Open(dst, src []byte, packetNumber protocol.PacketNumber, associatedData []byte) ([]byte, error)
Open(dst, src []byte, packetNumber protocol.PacketNumber, associatedData []byte) ([]byte, protocol.EncryptionLevel, error)
Seal(dst, src []byte, packetNumber protocol.PacketNumber, associatedData []byte) []byte
LockForSealing()
UnlockForSealing()

View file

@ -43,7 +43,7 @@ type cryptoSetupServer struct {
mutex sync.RWMutex
}
var _ crypto.AEAD = &cryptoSetupServer{}
var _ CryptoSetup = &cryptoSetupServer{}
// NewCryptoSetup creates a new CryptoSetup instance for a server
func NewCryptoSetup(
@ -152,7 +152,7 @@ func (h *cryptoSetupServer) handleMessage(chloData []byte, cryptoData map[Tag][]
}
// Open a message
func (h *cryptoSetupServer) Open(dst, src []byte, packetNumber protocol.PacketNumber, associatedData []byte) ([]byte, error) {
func (h *cryptoSetupServer) Open(dst, src []byte, packetNumber protocol.PacketNumber, associatedData []byte) ([]byte, protocol.EncryptionLevel, error) {
h.mutex.RLock()
defer h.mutex.RUnlock()
@ -160,23 +160,28 @@ func (h *cryptoSetupServer) Open(dst, src []byte, packetNumber protocol.PacketNu
res, err := h.forwardSecureAEAD.Open(dst, src, packetNumber, associatedData)
if err == nil {
h.receivedForwardSecurePacket = true
return res, nil
return res, protocol.EncryptionForwardSecure, nil
}
if h.receivedForwardSecurePacket {
return nil, err
return nil, protocol.EncryptionUnspecified, err
}
}
if h.secureAEAD != nil {
res, err := h.secureAEAD.Open(dst, src, packetNumber, associatedData)
if err == nil {
h.receivedSecurePacket = true
return res, nil
return res, protocol.EncryptionSecure, nil
}
if h.receivedSecurePacket {
return nil, err
return nil, protocol.EncryptionUnspecified, err
}
}
return (&crypto.NullAEAD{}).Open(dst, src, packetNumber, associatedData)
nullAEAD := &crypto.NullAEAD{}
res, err := nullAEAD.Open(dst, src, packetNumber, associatedData)
if err != nil {
return res, protocol.EncryptionUnspecified, err
}
return res, protocol.EncryptionUnencrypted, err
}
// Seal a message, call LockForSealing() before!

View file

@ -577,26 +577,30 @@ var _ = Describe("Crypto setup", func() {
})
It("is accepted initially", func() {
d, err := cs.Open(nil, foobarFNVSigned, 0, []byte{})
d, enc, err := cs.Open(nil, foobarFNVSigned, 0, []byte{})
Expect(err).ToNot(HaveOccurred())
Expect(d).To(Equal([]byte("foobar")))
Expect(enc).To(Equal(protocol.EncryptionUnencrypted))
})
It("is still accepted after CHLO", func() {
doCHLO()
Expect(cs.secureAEAD).ToNot(BeNil())
_, err := cs.Open(nil, foobarFNVSigned, 0, []byte{})
_, enc, err := cs.Open(nil, foobarFNVSigned, 0, []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, err := cs.Open(nil, []byte("encrypted"), 0, []byte{})
d, enc, err := cs.Open(nil, []byte("encrypted"), 0, []byte{})
Expect(enc).To(Equal(protocol.EncryptionSecure))
Expect(err).ToNot(HaveOccurred())
Expect(d).To(Equal([]byte("decrypted")))
_, err = cs.Open(nil, foobarFNVSigned, 0, []byte{})
_, enc, err = cs.Open(nil, foobarFNVSigned, 0, []byte{})
Expect(err).To(MatchError("authentication failed"))
Expect(enc).To(Equal(protocol.EncryptionUnspecified))
})
It("is not used after CHLO", func() {
@ -615,14 +619,15 @@ var _ = Describe("Crypto setup", func() {
It("is accepted after CHLO", func() {
doCHLO()
d, err := cs.Open(nil, []byte("encrypted"), 0, []byte{})
d, enc, err := cs.Open(nil, []byte("encrypted"), 0, []byte{})
Expect(enc).To(Equal(protocol.EncryptionSecure))
Expect(err).ToNot(HaveOccurred())
Expect(d).To(Equal([]byte("decrypted")))
})
It("is not used after receiving forward secure packet", func() {
doCHLO()
_, err := cs.Open(nil, []byte("forward secure encrypted"), 0, []byte{})
_, _, err := cs.Open(nil, []byte("forward secure encrypted"), 0, []byte{})
Expect(err).ToNot(HaveOccurred())
d := cs.Seal(nil, []byte("foobar"), 0, []byte{})
Expect(d).To(Equal([]byte("foobar forward sec")))
@ -630,17 +635,19 @@ var _ = Describe("Crypto setup", func() {
It("is not accepted after receiving forward secure packet", func() {
doCHLO()
_, err := cs.Open(nil, []byte("forward secure encrypted"), 0, []byte{})
_, _, err := cs.Open(nil, []byte("forward secure encrypted"), 0, []byte{})
Expect(err).ToNot(HaveOccurred())
_, err = cs.Open(nil, []byte("encrypted"), 0, []byte{})
_, enc, err := cs.Open(nil, []byte("encrypted"), 0, []byte{})
Expect(err).To(MatchError("authentication failed"))
Expect(enc).To(Equal(protocol.EncryptionUnspecified))
})
})
Context("forward secure encryption", func() {
It("is used after receiving forward secure packet", func() {
doCHLO()
_, err := cs.Open(nil, []byte("forward secure encrypted"), 0, []byte{})
_, enc, err := cs.Open(nil, []byte("forward secure encrypted"), 0, []byte{})
Expect(enc).To(Equal(protocol.EncryptionForwardSecure))
Expect(err).ToNot(HaveOccurred())
d := cs.Seal(nil, []byte("foobar"), 0, []byte{})
Expect(d).To(Equal([]byte("foobar forward sec")))

View file

@ -16,8 +16,8 @@ type mockCryptoSetup struct {
func (m *mockCryptoSetup) HandleCryptoStream() error { return nil }
func (m *mockCryptoSetup) Open(dst, src []byte, packetNumber protocol.PacketNumber, associatedData []byte) ([]byte, error) {
return nil, nil
func (m *mockCryptoSetup) Open(dst, src []byte, packetNumber protocol.PacketNumber, associatedData []byte) ([]byte, protocol.EncryptionLevel, error) {
return nil, protocol.EncryptionUnspecified, nil
}
func (m *mockCryptoSetup) Seal(dst, src []byte, packetNumber protocol.PacketNumber, associatedData []byte) []byte {
return append(src, bytes.Repeat([]byte{0}, 12)...)

View file

@ -5,21 +5,25 @@ import (
"errors"
"fmt"
"github.com/lucas-clemente/quic-go/crypto"
"github.com/lucas-clemente/quic-go/frames"
"github.com/lucas-clemente/quic-go/protocol"
"github.com/lucas-clemente/quic-go/qerr"
)
type quicAEAD interface {
Open(dst, src []byte, packetNumber protocol.PacketNumber, associatedData []byte) ([]byte, protocol.EncryptionLevel, error)
Seal(dst, src []byte, packetNumber protocol.PacketNumber, associatedData []byte) []byte
}
type packetUnpacker struct {
version protocol.VersionNumber
aead crypto.AEAD
aead quicAEAD
}
func (u *packetUnpacker) Unpack(publicHeaderBinary []byte, hdr *PublicHeader, data []byte) (*unpackedPacket, error) {
buf := getPacketBuffer()
defer putPacketBuffer(buf)
decrypted, err := u.aead.Open(buf, data, hdr.PacketNumber, publicHeaderBinary)
decrypted, _, err := u.aead.Open(buf, data, hdr.PacketNumber, publicHeaderBinary)
if err != nil {
// Wrap err in quicError so that public reset is sent by session
return nil, qerr.Error(qerr.DecryptionFailure, err.Error())

View file

@ -12,30 +12,40 @@ import (
. "github.com/onsi/gomega"
)
type mockAEAD struct{}
func (m *mockAEAD) Open(dst, src []byte, packetNumber protocol.PacketNumber, associatedData []byte) ([]byte, protocol.EncryptionLevel, error) {
res, err := (&crypto.NullAEAD{}).Open(dst, src, packetNumber, associatedData)
return res, protocol.EncryptionUnspecified, err
}
func (m *mockAEAD) Seal(dst, src []byte, packetNumber protocol.PacketNumber, associatedData []byte) []byte {
return (&crypto.NullAEAD{}).Seal(dst, src, packetNumber, associatedData)
}
var _ quicAEAD = &mockAEAD{}
var _ = Describe("Packet unpacker", func() {
var (
unpacker *packetUnpacker
hdr *PublicHeader
hdrBin []byte
aead crypto.AEAD
data []byte
buf *bytes.Buffer
)
BeforeEach(func() {
aead = &crypto.NullAEAD{}
hdr = &PublicHeader{
PacketNumber: 10,
PacketNumberLen: 1,
}
hdrBin = []byte{0x04, 0x4c, 0x01}
unpacker = &packetUnpacker{aead: aead}
unpacker = &packetUnpacker{aead: &mockAEAD{}}
data = nil
buf = &bytes.Buffer{}
})
setData := func(p []byte) {
data = aead.Seal(nil, p, 0, hdrBin)
data = unpacker.aead.Seal(nil, p, 0, hdrBin)
}
It("does not read read a private flag for QUIC Version >= 34", func() {

View file

@ -5,8 +5,10 @@ package protocol
type EncryptionLevel int
const (
// Unencrypted is not encrypted
Unencrypted EncryptionLevel = iota
// EncryptionUnspecified is a not specified encryption level
EncryptionUnspecified EncryptionLevel = iota
// EncryptionUnencrypted is not encrypted
EncryptionUnencrypted
// EncryptionSecure is encrypted, but not forward secure
EncryptionSecure
// EncryptionForwardSecure is forward secure

View file

@ -742,7 +742,7 @@ var _ = Describe("Session", func() {
sess.conn.(*mockConnection).remoteAddr = remoteIP
// use the real packetUnpacker here, to make sure this test fails if the error code for failed decryption changes
sess.unpacker = &packetUnpacker{}
sess.unpacker.(*packetUnpacker).aead = &crypto.NullAEAD{}
sess.unpacker.(*packetUnpacker).aead = &mockAEAD{}
p := receivedPacket{
remoteAddr: attackerIP,
publicHeader: &PublicHeader{PacketNumber: 1337},