mirror of
https://github.com/refraction-networking/uquic.git
synced 2025-04-04 04:37:36 +03:00
return the encryption level of a packet when decrypting it
This commit is contained in:
parent
811bd20939
commit
a972c7a21e
12 changed files with 85 additions and 47 deletions
|
@ -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{
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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")))
|
||||
})
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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!
|
||||
|
|
|
@ -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")))
|
||||
|
|
|
@ -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)...)
|
||||
|
|
|
@ -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())
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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},
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue