mirror of
https://github.com/refraction-networking/uquic.git
synced 2025-04-03 20:27:35 +03:00
don't use trial decryption for IETF QUIC
This commit is contained in:
parent
a298bd01c9
commit
302d2a1715
14 changed files with 333 additions and 130 deletions
|
@ -31,6 +31,8 @@ type cryptoSetupTLS struct {
|
|||
handshakeEvent chan<- struct{}
|
||||
}
|
||||
|
||||
var _ CryptoSetupTLS = &cryptoSetupTLS{}
|
||||
|
||||
// NewCryptoSetupTLSServer creates a new TLS CryptoSetup instance for a server
|
||||
func NewCryptoSetupTLSServer(
|
||||
tls MintTLS,
|
||||
|
@ -38,7 +40,7 @@ func NewCryptoSetupTLSServer(
|
|||
nullAEAD crypto.AEAD,
|
||||
handshakeEvent chan<- struct{},
|
||||
version protocol.VersionNumber,
|
||||
) CryptoSetup {
|
||||
) CryptoSetupTLS {
|
||||
return &cryptoSetupTLS{
|
||||
tls: tls,
|
||||
cryptoStream: cryptoStream,
|
||||
|
@ -57,7 +59,7 @@ func NewCryptoSetupTLSClient(
|
|||
handshakeEvent chan<- struct{},
|
||||
tls MintTLS,
|
||||
version protocol.VersionNumber,
|
||||
) (CryptoSetup, error) {
|
||||
) (CryptoSetupTLS, error) {
|
||||
nullAEAD, err := crypto.NewNullAEAD(protocol.PerspectiveClient, connID, version)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -107,22 +109,18 @@ handshakeLoop:
|
|||
return nil
|
||||
}
|
||||
|
||||
func (h *cryptoSetupTLS) Open(dst, src []byte, packetNumber protocol.PacketNumber, associatedData []byte) ([]byte, protocol.EncryptionLevel, error) {
|
||||
func (h *cryptoSetupTLS) OpenHandshake(dst, src []byte, packetNumber protocol.PacketNumber, associatedData []byte) ([]byte, error) {
|
||||
return h.nullAEAD.Open(dst, src, packetNumber, associatedData)
|
||||
}
|
||||
|
||||
func (h *cryptoSetupTLS) Open1RTT(dst, src []byte, packetNumber protocol.PacketNumber, associatedData []byte) ([]byte, error) {
|
||||
h.mutex.RLock()
|
||||
defer h.mutex.RUnlock()
|
||||
|
||||
if h.aead != nil {
|
||||
data, err := h.aead.Open(dst, src, packetNumber, associatedData)
|
||||
if err != nil {
|
||||
return nil, protocol.EncryptionUnspecified, err
|
||||
}
|
||||
return data, protocol.EncryptionForwardSecure, nil
|
||||
if h.aead == nil {
|
||||
return nil, errors.New("no 1-RTT sealer")
|
||||
}
|
||||
data, err := h.nullAEAD.Open(dst, src, packetNumber, associatedData)
|
||||
if err != nil {
|
||||
return nil, protocol.EncryptionUnspecified, err
|
||||
}
|
||||
return data, protocol.EncryptionUnencrypted, nil
|
||||
return h.aead.Open(dst, src, packetNumber, associatedData)
|
||||
}
|
||||
|
||||
func (h *cryptoSetupTLS) GetSealer() (protocol.EncryptionLevel, Sealer) {
|
||||
|
|
|
@ -108,12 +108,11 @@ var _ = Describe("TLS Crypto Setup", func() {
|
|||
Expect(d).To(Equal([]byte("foobar signed")))
|
||||
})
|
||||
|
||||
It("is accepted initially", func() {
|
||||
It("is used for opening", func() {
|
||||
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{})
|
||||
d, err := cs.OpenHandshake(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() {
|
||||
|
@ -126,17 +125,8 @@ var _ = Describe("TLS Crypto Setup", func() {
|
|||
|
||||
It("errors if the has the wrong hash", func() {
|
||||
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{})
|
||||
_, err := cs.OpenHandshake(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()
|
||||
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))
|
||||
})
|
||||
})
|
||||
|
||||
|
@ -150,12 +140,11 @@ var _ = Describe("TLS Crypto Setup", func() {
|
|||
Expect(d).To(Equal([]byte("foobar forward sec")))
|
||||
})
|
||||
|
||||
It("is used for opening after the handshake completes", func() {
|
||||
It("is used for opening", func() {
|
||||
doHandshake()
|
||||
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{})
|
||||
d, err := cs.Open1RTT(nil, []byte("encrypted"), 6, []byte{})
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(enc).To(Equal(protocol.EncryptionForwardSecure))
|
||||
Expect(d).To(Equal([]byte("decrypted")))
|
||||
})
|
||||
})
|
||||
|
|
|
@ -35,9 +35,7 @@ type MintTLS interface {
|
|||
SetCryptoStream(io.ReadWriter)
|
||||
}
|
||||
|
||||
// CryptoSetup is a crypto setup
|
||||
type CryptoSetup interface {
|
||||
Open(dst, src []byte, packetNumber protocol.PacketNumber, associatedData []byte) ([]byte, protocol.EncryptionLevel, error)
|
||||
type baseCryptoSetup interface {
|
||||
HandleCryptoStream() error
|
||||
ConnectionState() ConnectionState
|
||||
|
||||
|
@ -46,6 +44,21 @@ type CryptoSetup interface {
|
|||
GetSealerForCryptoStream() (protocol.EncryptionLevel, Sealer)
|
||||
}
|
||||
|
||||
// CryptoSetup is the crypto setup used by gQUIC
|
||||
type CryptoSetup interface {
|
||||
baseCryptoSetup
|
||||
|
||||
Open(dst, src []byte, packetNumber protocol.PacketNumber, associatedData []byte) ([]byte, protocol.EncryptionLevel, error)
|
||||
}
|
||||
|
||||
// CryptoSetupTLS is the crypto setup used by IETF QUIC
|
||||
type CryptoSetupTLS interface {
|
||||
baseCryptoSetup
|
||||
|
||||
OpenHandshake(dst, src []byte, packetNumber protocol.PacketNumber, associatedData []byte) ([]byte, error)
|
||||
Open1RTT(dst, src []byte, packetNumber protocol.PacketNumber, associatedData []byte) ([]byte, error)
|
||||
}
|
||||
|
||||
// ConnectionState records basic details about the QUIC connection.
|
||||
// Warning: This API should not be considered stable and might change soon.
|
||||
type ConnectionState struct {
|
||||
|
|
|
@ -108,16 +108,23 @@ func tlsToMintConfig(tlsConf *tls.Config, pers protocol.Perspective) (*mint.Conf
|
|||
// unpackInitialOrRetryPacket unpacks packets Initial and Retry packets
|
||||
// These packets must contain a STREAM_FRAME for the crypto stream, starting at offset 0.
|
||||
func unpackInitialPacket(aead crypto.AEAD, hdr *wire.Header, data []byte, version protocol.VersionNumber) (*wire.StreamFrame, error) {
|
||||
unpacker := &packetUnpacker{aead: &nullAEAD{aead}, version: version}
|
||||
packet, err := unpacker.Unpack(hdr.Raw, hdr, data)
|
||||
buf := *getPacketBuffer()
|
||||
buf = buf[:0]
|
||||
defer putPacketBuffer(&buf)
|
||||
|
||||
decrypted, err := aead.Open(buf, data, hdr.PacketNumber, hdr.Raw)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var frame *wire.StreamFrame
|
||||
for _, f := range packet.frames {
|
||||
r := bytes.NewReader(decrypted)
|
||||
for {
|
||||
f, err := wire.ParseNextFrame(r, hdr, version)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var ok bool
|
||||
frame, ok = f.(*wire.StreamFrame)
|
||||
if ok {
|
||||
if frame, ok = f.(*wire.StreamFrame); ok || frame == nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
|
|
@ -141,14 +141,6 @@ var _ = Describe("Packing and unpacking Initial packets", func() {
|
|||
})
|
||||
|
||||
Context("packing", func() {
|
||||
var unpacker *packetUnpacker
|
||||
|
||||
BeforeEach(func() {
|
||||
aeadCl, err := crypto.NewNullAEAD(protocol.PerspectiveClient, connID, ver)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
unpacker = &packetUnpacker{aead: &nullAEAD{aeadCl}, version: ver}
|
||||
})
|
||||
|
||||
It("packs a packet", func() {
|
||||
f := &wire.StreamFrame{
|
||||
Data: []byte("foobar"),
|
||||
|
@ -156,9 +148,13 @@ var _ = Describe("Packing and unpacking Initial packets", func() {
|
|||
}
|
||||
data, err := packUnencryptedPacket(aead, hdr, f, protocol.PerspectiveServer)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
packet, err := unpacker.Unpack(hdr.Raw, hdr, data[len(hdr.Raw):])
|
||||
aeadCl, err := crypto.NewNullAEAD(protocol.PerspectiveClient, connID, ver)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(packet.frames).To(Equal([]wire.Frame{f}))
|
||||
decrypted, err := aeadCl.Open(nil, data[len(hdr.Raw):], hdr.PacketNumber, hdr.Raw)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
frame, err := wire.ParseNextFrame(bytes.NewReader(decrypted), hdr, versionIETFFrames)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(frame).To(Equal(f))
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
49
mock_gquic_aead_test.go
Normal file
49
mock_gquic_aead_test.go
Normal file
|
@ -0,0 +1,49 @@
|
|||
// Code generated by MockGen. DO NOT EDIT.
|
||||
// Source: github.com/lucas-clemente/quic-go (interfaces: GQUICAEAD)
|
||||
|
||||
// Package quic is a generated GoMock package.
|
||||
package quic
|
||||
|
||||
import (
|
||||
reflect "reflect"
|
||||
|
||||
gomock "github.com/golang/mock/gomock"
|
||||
protocol "github.com/lucas-clemente/quic-go/internal/protocol"
|
||||
)
|
||||
|
||||
// MockGQUICAEAD is a mock of GQUICAEAD interface
|
||||
type MockGQUICAEAD struct {
|
||||
ctrl *gomock.Controller
|
||||
recorder *MockGQUICAEADMockRecorder
|
||||
}
|
||||
|
||||
// MockGQUICAEADMockRecorder is the mock recorder for MockGQUICAEAD
|
||||
type MockGQUICAEADMockRecorder struct {
|
||||
mock *MockGQUICAEAD
|
||||
}
|
||||
|
||||
// NewMockGQUICAEAD creates a new mock instance
|
||||
func NewMockGQUICAEAD(ctrl *gomock.Controller) *MockGQUICAEAD {
|
||||
mock := &MockGQUICAEAD{ctrl: ctrl}
|
||||
mock.recorder = &MockGQUICAEADMockRecorder{mock}
|
||||
return mock
|
||||
}
|
||||
|
||||
// EXPECT returns an object that allows the caller to indicate expected use
|
||||
func (m *MockGQUICAEAD) EXPECT() *MockGQUICAEADMockRecorder {
|
||||
return m.recorder
|
||||
}
|
||||
|
||||
// Open mocks base method
|
||||
func (m *MockGQUICAEAD) Open(arg0, arg1 []byte, arg2 protocol.PacketNumber, arg3 []byte) ([]byte, protocol.EncryptionLevel, error) {
|
||||
ret := m.ctrl.Call(m, "Open", arg0, arg1, arg2, arg3)
|
||||
ret0, _ := ret[0].([]byte)
|
||||
ret1, _ := ret[1].(protocol.EncryptionLevel)
|
||||
ret2, _ := ret[2].(error)
|
||||
return ret0, ret1, ret2
|
||||
}
|
||||
|
||||
// Open indicates an expected call of Open
|
||||
func (mr *MockGQUICAEADMockRecorder) Open(arg0, arg1, arg2, arg3 interface{}) *gomock.Call {
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Open", reflect.TypeOf((*MockGQUICAEAD)(nil).Open), arg0, arg1, arg2, arg3)
|
||||
}
|
61
mock_quic_aead_test.go
Normal file
61
mock_quic_aead_test.go
Normal file
|
@ -0,0 +1,61 @@
|
|||
// Code generated by MockGen. DO NOT EDIT.
|
||||
// Source: github.com/lucas-clemente/quic-go (interfaces: QuicAEAD)
|
||||
|
||||
// Package quic is a generated GoMock package.
|
||||
package quic
|
||||
|
||||
import (
|
||||
reflect "reflect"
|
||||
|
||||
gomock "github.com/golang/mock/gomock"
|
||||
protocol "github.com/lucas-clemente/quic-go/internal/protocol"
|
||||
)
|
||||
|
||||
// MockQuicAEAD is a mock of QuicAEAD interface
|
||||
type MockQuicAEAD struct {
|
||||
ctrl *gomock.Controller
|
||||
recorder *MockQuicAEADMockRecorder
|
||||
}
|
||||
|
||||
// MockQuicAEADMockRecorder is the mock recorder for MockQuicAEAD
|
||||
type MockQuicAEADMockRecorder struct {
|
||||
mock *MockQuicAEAD
|
||||
}
|
||||
|
||||
// NewMockQuicAEAD creates a new mock instance
|
||||
func NewMockQuicAEAD(ctrl *gomock.Controller) *MockQuicAEAD {
|
||||
mock := &MockQuicAEAD{ctrl: ctrl}
|
||||
mock.recorder = &MockQuicAEADMockRecorder{mock}
|
||||
return mock
|
||||
}
|
||||
|
||||
// EXPECT returns an object that allows the caller to indicate expected use
|
||||
func (m *MockQuicAEAD) EXPECT() *MockQuicAEADMockRecorder {
|
||||
return m.recorder
|
||||
}
|
||||
|
||||
// Open1RTT mocks base method
|
||||
func (m *MockQuicAEAD) Open1RTT(arg0, arg1 []byte, arg2 protocol.PacketNumber, arg3 []byte) ([]byte, error) {
|
||||
ret := m.ctrl.Call(m, "Open1RTT", arg0, arg1, arg2, arg3)
|
||||
ret0, _ := ret[0].([]byte)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// Open1RTT indicates an expected call of Open1RTT
|
||||
func (mr *MockQuicAEADMockRecorder) Open1RTT(arg0, arg1, arg2, arg3 interface{}) *gomock.Call {
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Open1RTT", reflect.TypeOf((*MockQuicAEAD)(nil).Open1RTT), arg0, arg1, arg2, arg3)
|
||||
}
|
||||
|
||||
// OpenHandshake mocks base method
|
||||
func (m *MockQuicAEAD) OpenHandshake(arg0, arg1 []byte, arg2 protocol.PacketNumber, arg3 []byte) ([]byte, error) {
|
||||
ret := m.ctrl.Call(m, "OpenHandshake", arg0, arg1, arg2, arg3)
|
||||
ret0, _ := ret[0].([]byte)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// OpenHandshake indicates an expected call of OpenHandshake
|
||||
func (mr *MockQuicAEADMockRecorder) OpenHandshake(arg0, arg1, arg2, arg3 interface{}) *gomock.Call {
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "OpenHandshake", reflect.TypeOf((*MockQuicAEAD)(nil).OpenHandshake), arg0, arg1, arg2, arg3)
|
||||
}
|
|
@ -11,4 +11,6 @@ package quic
|
|||
//go:generate sh -c "sed -i '' 's/quic_go.//g' mock_stream_getter_test.go mock_stream_manager_test.go"
|
||||
//go:generate sh -c "./mockgen_private.sh quic mock_unpacker_test.go github.com/lucas-clemente/quic-go unpacker Unpacker"
|
||||
//go:generate sh -c "sed -i '' 's/quic_go.//g' mock_unpacker_test.go mock_unpacker_test.go"
|
||||
//go:generate sh -c "./mockgen_private.sh quic mock_quic_aead_test.go github.com/lucas-clemente/quic-go quicAEAD QuicAEAD"
|
||||
//go:generate sh -c "./mockgen_private.sh quic mock_gquic_aead_test.go github.com/lucas-clemente/quic-go gQUICAEAD GQUICAEAD"
|
||||
//go:generate sh -c "goimports -w mock*_test.go"
|
||||
|
|
|
@ -33,6 +33,12 @@ func (p *packedPacket) ToAckHandlerPacket() *ackhandler.Packet {
|
|||
}
|
||||
}
|
||||
|
||||
type sealingManager interface {
|
||||
GetSealer() (protocol.EncryptionLevel, handshake.Sealer)
|
||||
GetSealerForCryptoStream() (protocol.EncryptionLevel, handshake.Sealer)
|
||||
GetSealerWithEncryptionLevel(protocol.EncryptionLevel) (handshake.Sealer, error)
|
||||
}
|
||||
|
||||
type streamFrameSource interface {
|
||||
HasCryptoStreamData() bool
|
||||
PopCryptoStreamFrame(protocol.ByteCount) *wire.StreamFrame
|
||||
|
@ -43,8 +49,8 @@ type packetPacker struct {
|
|||
connectionID protocol.ConnectionID
|
||||
perspective protocol.Perspective
|
||||
version protocol.VersionNumber
|
||||
cryptoSetup handshake.CryptoSetup
|
||||
divNonce []byte
|
||||
cryptoSetup sealingManager
|
||||
|
||||
packetNumberGenerator *packetNumberGenerator
|
||||
getPacketNumberLen func(protocol.PacketNumber) protocol.PacketNumberLen
|
||||
|
@ -66,7 +72,7 @@ func newPacketPacker(connectionID protocol.ConnectionID,
|
|||
getPacketNumberLen func(protocol.PacketNumber) protocol.PacketNumberLen,
|
||||
remoteAddr net.Addr, // only used for determining the max packet size
|
||||
divNonce []byte,
|
||||
cryptoSetup handshake.CryptoSetup,
|
||||
cryptoSetup sealingManager,
|
||||
streamFramer streamFrameSource,
|
||||
perspective protocol.Perspective,
|
||||
version protocol.VersionNumber,
|
||||
|
|
|
@ -13,32 +13,26 @@ type unpackedPacket struct {
|
|||
frames []wire.Frame
|
||||
}
|
||||
|
||||
type quicAEAD interface {
|
||||
type gQUICAEAD interface {
|
||||
Open(dst, src []byte, packetNumber protocol.PacketNumber, associatedData []byte) ([]byte, protocol.EncryptionLevel, error)
|
||||
}
|
||||
|
||||
type packetUnpacker struct {
|
||||
version protocol.VersionNumber
|
||||
aead quicAEAD
|
||||
type quicAEAD interface {
|
||||
OpenHandshake(dst, src []byte, packetNumber protocol.PacketNumber, associatedData []byte) ([]byte, error)
|
||||
Open1RTT(dst, src []byte, packetNumber protocol.PacketNumber, associatedData []byte) ([]byte, error)
|
||||
}
|
||||
|
||||
func (u *packetUnpacker) Unpack(headerBinary []byte, hdr *wire.Header, data []byte) (*unpackedPacket, error) {
|
||||
buf := *getPacketBuffer()
|
||||
buf = buf[:0]
|
||||
defer putPacketBuffer(&buf)
|
||||
decrypted, encryptionLevel, err := u.aead.Open(buf, data, hdr.PacketNumber, headerBinary)
|
||||
if err != nil {
|
||||
// Wrap err in quicError so that public reset is sent by session
|
||||
return nil, qerr.Error(qerr.DecryptionFailure, err.Error())
|
||||
}
|
||||
r := bytes.NewReader(decrypted)
|
||||
type packetUnpackerBase struct {
|
||||
version protocol.VersionNumber
|
||||
}
|
||||
|
||||
func (u *packetUnpackerBase) parseFrames(decrypted []byte, hdr *wire.Header) ([]wire.Frame, error) {
|
||||
r := bytes.NewReader(decrypted)
|
||||
if r.Len() == 0 {
|
||||
return nil, qerr.MissingPayload
|
||||
}
|
||||
|
||||
fs := make([]wire.Frame, 0, 2)
|
||||
|
||||
// Read all frames in the packet
|
||||
for {
|
||||
frame, err := wire.ParseNextFrame(r, hdr, u.version)
|
||||
|
@ -50,6 +44,84 @@ func (u *packetUnpacker) Unpack(headerBinary []byte, hdr *wire.Header, data []by
|
|||
}
|
||||
fs = append(fs, frame)
|
||||
}
|
||||
return fs, nil
|
||||
}
|
||||
|
||||
// The packetUnpackerGQUIC unpacks gQUIC packets.
|
||||
type packetUnpackerGQUIC struct {
|
||||
packetUnpackerBase
|
||||
aead gQUICAEAD
|
||||
}
|
||||
|
||||
var _ unpacker = &packetUnpackerGQUIC{}
|
||||
|
||||
func newPacketUnpackerGQUIC(aead gQUICAEAD, version protocol.VersionNumber) unpacker {
|
||||
return &packetUnpackerGQUIC{
|
||||
packetUnpackerBase: packetUnpackerBase{version: version},
|
||||
aead: aead,
|
||||
}
|
||||
}
|
||||
|
||||
func (u *packetUnpackerGQUIC) Unpack(headerBinary []byte, hdr *wire.Header, data []byte) (*unpackedPacket, error) {
|
||||
buf := *getPacketBuffer()
|
||||
buf = buf[:0]
|
||||
defer putPacketBuffer(&buf)
|
||||
decrypted, encryptionLevel, err := u.aead.Open(buf, data, hdr.PacketNumber, headerBinary)
|
||||
if err != nil {
|
||||
// Wrap err in quicError so that public reset is sent by session
|
||||
return nil, qerr.Error(qerr.DecryptionFailure, err.Error())
|
||||
}
|
||||
|
||||
fs, err := u.parseFrames(decrypted, hdr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &unpackedPacket{
|
||||
encryptionLevel: encryptionLevel,
|
||||
frames: fs,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// The packetUnpacker unpacks IETF QUIC packets.
|
||||
type packetUnpacker struct {
|
||||
packetUnpackerBase
|
||||
aead quicAEAD
|
||||
}
|
||||
|
||||
var _ unpacker = &packetUnpacker{}
|
||||
|
||||
func newPacketUnpacker(aead quicAEAD, version protocol.VersionNumber) unpacker {
|
||||
return &packetUnpacker{
|
||||
packetUnpackerBase: packetUnpackerBase{version: version},
|
||||
aead: aead,
|
||||
}
|
||||
}
|
||||
|
||||
func (u *packetUnpacker) Unpack(headerBinary []byte, hdr *wire.Header, data []byte) (*unpackedPacket, error) {
|
||||
buf := *getPacketBuffer()
|
||||
buf = buf[:0]
|
||||
defer putPacketBuffer(&buf)
|
||||
|
||||
var decrypted []byte
|
||||
var encryptionLevel protocol.EncryptionLevel
|
||||
var err error
|
||||
if hdr.IsLongHeader {
|
||||
decrypted, err = u.aead.OpenHandshake(buf, data, hdr.PacketNumber, headerBinary)
|
||||
encryptionLevel = protocol.EncryptionUnencrypted
|
||||
} else {
|
||||
decrypted, err = u.aead.Open1RTT(buf, data, hdr.PacketNumber, headerBinary)
|
||||
encryptionLevel = protocol.EncryptionForwardSecure
|
||||
}
|
||||
if err != nil {
|
||||
// Wrap err in quicError so that public reset is sent by session
|
||||
return nil, qerr.Error(qerr.DecryptionFailure, err.Error())
|
||||
}
|
||||
|
||||
fs, err := u.parseFrames(decrypted, hdr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &unpackedPacket{
|
||||
encryptionLevel: encryptionLevel,
|
||||
|
|
|
@ -3,7 +3,7 @@ package quic
|
|||
import (
|
||||
"bytes"
|
||||
|
||||
"github.com/lucas-clemente/quic-go/internal/crypto"
|
||||
"github.com/golang/mock/gomock"
|
||||
"github.com/lucas-clemente/quic-go/internal/protocol"
|
||||
"github.com/lucas-clemente/quic-go/internal/wire"
|
||||
"github.com/lucas-clemente/quic-go/qerr"
|
||||
|
@ -12,63 +12,87 @@ import (
|
|||
. "github.com/onsi/gomega"
|
||||
)
|
||||
|
||||
type mockAEAD struct {
|
||||
encLevelOpen protocol.EncryptionLevel
|
||||
}
|
||||
|
||||
func (m *mockAEAD) Open(dst, src []byte, packetNumber protocol.PacketNumber, associatedData []byte) ([]byte, protocol.EncryptionLevel, error) {
|
||||
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, err := crypto.NewNullAEAD(protocol.PerspectiveServer, 0x1337, protocol.VersionWhatever)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
return nullAEAD.Seal(dst, src, packetNumber, associatedData), protocol.EncryptionUnspecified
|
||||
}
|
||||
|
||||
var _ quicAEAD = &mockAEAD{}
|
||||
|
||||
var _ = Describe("Packet unpacker", func() {
|
||||
var _ = Describe("Packet Unpacker (for gQUIC)", func() {
|
||||
var (
|
||||
unpacker *packetUnpacker
|
||||
unpacker *packetUnpackerGQUIC
|
||||
hdr *wire.Header
|
||||
hdrBin []byte
|
||||
data []byte
|
||||
buf *bytes.Buffer
|
||||
aead *MockGQUICAEAD
|
||||
)
|
||||
|
||||
BeforeEach(func() {
|
||||
aead = NewMockGQUICAEAD(mockCtrl)
|
||||
hdr = &wire.Header{
|
||||
PacketNumber: 10,
|
||||
PacketNumberLen: 1,
|
||||
Raw: []byte{0x04, 0x4c, 0x01},
|
||||
}
|
||||
hdrBin = []byte{0x04, 0x4c, 0x01}
|
||||
unpacker = &packetUnpacker{aead: &mockAEAD{}}
|
||||
data = nil
|
||||
buf = &bytes.Buffer{}
|
||||
unpacker = newPacketUnpackerGQUIC(aead, versionGQUICFrames).(*packetUnpackerGQUIC)
|
||||
})
|
||||
|
||||
setData := func(p []byte) {
|
||||
data, _ = unpacker.aead.(*mockAEAD).Seal(nil, p, 0, hdrBin)
|
||||
}
|
||||
|
||||
It("errors if the packet doesn't contain any payload", func() {
|
||||
setData(nil)
|
||||
_, err := unpacker.Unpack(hdrBin, hdr, data)
|
||||
data := []byte("foobar")
|
||||
aead.EXPECT().Open(gomock.Any(), []byte("foobar"), hdr.PacketNumber, hdr.Raw).Return([]byte{}, protocol.EncryptionForwardSecure, nil)
|
||||
_, err := unpacker.Unpack(hdr.Raw, hdr, data)
|
||||
Expect(err).To(MatchError(qerr.MissingPayload))
|
||||
})
|
||||
|
||||
It("saves the encryption level", func() {
|
||||
unpacker.version = versionGQUICFrames
|
||||
f := &wire.ConnectionCloseFrame{ReasonPhrase: "foo"}
|
||||
err := f.Write(buf, versionGQUICFrames)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
setData(buf.Bytes())
|
||||
unpacker.aead.(*mockAEAD).encLevelOpen = protocol.EncryptionSecure
|
||||
packet, err := unpacker.Unpack(hdrBin, hdr, data)
|
||||
aead.EXPECT().Open(gomock.Any(), gomock.Any(), hdr.PacketNumber, hdr.Raw).Return([]byte{0}, protocol.EncryptionSecure, nil)
|
||||
packet, err := unpacker.Unpack(hdr.Raw, hdr, nil)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(packet.encryptionLevel).To(Equal(protocol.EncryptionSecure))
|
||||
})
|
||||
|
||||
It("unpacks the frames", func() {
|
||||
buf := &bytes.Buffer{}
|
||||
(&wire.PingFrame{}).Write(buf, versionGQUICFrames)
|
||||
(&wire.BlockedFrame{}).Write(buf, versionGQUICFrames)
|
||||
aead.EXPECT().Open(gomock.Any(), gomock.Any(), hdr.PacketNumber, hdr.Raw).Return(buf.Bytes(), protocol.EncryptionForwardSecure, nil)
|
||||
packet, err := unpacker.Unpack(hdr.Raw, hdr, nil)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(packet.frames).To(Equal([]wire.Frame{&wire.PingFrame{}, &wire.BlockedFrame{}}))
|
||||
})
|
||||
})
|
||||
|
||||
var _ = Describe("Packet Unpacker (for IETF QUIC)", func() {
|
||||
var (
|
||||
unpacker *packetUnpacker
|
||||
hdr *wire.Header
|
||||
aead *MockQuicAEAD
|
||||
)
|
||||
|
||||
BeforeEach(func() {
|
||||
aead = NewMockQuicAEAD(mockCtrl)
|
||||
hdr = &wire.Header{
|
||||
PacketNumber: 10,
|
||||
PacketNumberLen: 1,
|
||||
Raw: []byte{0x04, 0x4c, 0x01},
|
||||
}
|
||||
unpacker = newPacketUnpacker(aead, versionIETFFrames).(*packetUnpacker)
|
||||
})
|
||||
|
||||
It("errors if the packet doesn't contain any payload", func() {
|
||||
data := []byte("foobar")
|
||||
aead.EXPECT().Open1RTT(gomock.Any(), []byte("foobar"), hdr.PacketNumber, hdr.Raw).Return([]byte{}, nil)
|
||||
_, err := unpacker.Unpack(hdr.Raw, hdr, data)
|
||||
Expect(err).To(MatchError(qerr.MissingPayload))
|
||||
})
|
||||
|
||||
It("opens handshake packets", func() {
|
||||
hdr.IsLongHeader = true
|
||||
aead.EXPECT().OpenHandshake(gomock.Any(), gomock.Any(), hdr.PacketNumber, hdr.Raw).Return([]byte{0}, nil)
|
||||
packet, err := unpacker.Unpack(hdr.Raw, hdr, nil)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(packet.encryptionLevel).To(Equal(protocol.EncryptionUnencrypted))
|
||||
})
|
||||
|
||||
It("unpacks the frames", func() {
|
||||
buf := &bytes.Buffer{}
|
||||
(&wire.PingFrame{}).Write(buf, versionIETFFrames)
|
||||
(&wire.BlockedFrame{}).Write(buf, versionIETFFrames)
|
||||
aead.EXPECT().Open1RTT(gomock.Any(), gomock.Any(), hdr.PacketNumber, hdr.Raw).Return(buf.Bytes(), nil)
|
||||
packet, err := unpacker.Unpack(hdr.Raw, hdr, nil)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(packet.frames).To(Equal([]wire.Frame{&wire.PingFrame{}, &wire.BlockedFrame{}}))
|
||||
})
|
||||
})
|
||||
|
|
|
@ -21,9 +21,12 @@ type nullAEAD struct {
|
|||
|
||||
var _ quicAEAD = &nullAEAD{}
|
||||
|
||||
func (n *nullAEAD) Open(dst, src []byte, packetNumber protocol.PacketNumber, associatedData []byte) ([]byte, protocol.EncryptionLevel, error) {
|
||||
data, err := n.aead.Open(dst, src, packetNumber, associatedData)
|
||||
return data, protocol.EncryptionUnencrypted, err
|
||||
func (n *nullAEAD) OpenHandshake(dst, src []byte, packetNumber protocol.PacketNumber, associatedData []byte) ([]byte, error) {
|
||||
return n.aead.Open(dst, src, packetNumber, associatedData)
|
||||
}
|
||||
|
||||
func (n *nullAEAD) Open1RTT(dst, src []byte, packetNumber protocol.PacketNumber, associatedData []byte) ([]byte, error) {
|
||||
return nil, errors.New("no 1-RTT keys")
|
||||
}
|
||||
|
||||
type tlsSession struct {
|
||||
|
|
|
@ -189,7 +189,7 @@ func newSession(
|
|||
return nil, err
|
||||
}
|
||||
s.cryptoStreamHandler = cs
|
||||
s.unpacker = &packetUnpacker{aead: cs, version: s.version}
|
||||
s.unpacker = newPacketUnpackerGQUIC(cs, s.version)
|
||||
s.streamsMap = newStreamsMapLegacy(s.newStream, s.config.MaxIncomingStreams, s.perspective)
|
||||
s.streamFramer = newStreamFramer(s.cryptoStream, s.streamsMap, s.version)
|
||||
s.packer = newPacketPacker(s.connectionID,
|
||||
|
@ -252,7 +252,7 @@ var newClientSession = func(
|
|||
}
|
||||
s.cryptoStreamHandler = cs
|
||||
s.divNonceChan = divNonceChan
|
||||
s.unpacker = &packetUnpacker{aead: cs, version: s.version}
|
||||
s.unpacker = newPacketUnpackerGQUIC(cs, s.version)
|
||||
s.streamsMap = newStreamsMapLegacy(s.newStream, s.config.MaxIncomingStreams, s.perspective)
|
||||
s.streamFramer = newStreamFramer(s.cryptoStream, s.streamsMap, s.version)
|
||||
s.packer = newPacketPacker(s.connectionID,
|
||||
|
@ -314,7 +314,7 @@ func newTLSServerSession(
|
|||
}
|
||||
s.peerParams = peerParams
|
||||
s.processTransportParameters(peerParams)
|
||||
s.unpacker = &packetUnpacker{aead: cs, version: s.version}
|
||||
s.unpacker = newPacketUnpacker(cs, s.version)
|
||||
return s, nil
|
||||
}
|
||||
|
||||
|
@ -353,7 +353,7 @@ var newTLSClientSession = func(
|
|||
return nil, err
|
||||
}
|
||||
s.cryptoStreamHandler = cs
|
||||
s.unpacker = &packetUnpacker{aead: cs, version: s.version}
|
||||
s.unpacker = newPacketUnpacker(cs, s.version)
|
||||
s.streamsMap = newStreamsMap(s, s.newFlowController, s.config.MaxIncomingStreams, s.config.MaxIncomingUniStreams, s.perspective, s.version)
|
||||
s.streamFramer = newStreamFramer(s.cryptoStream, s.streamsMap, s.version)
|
||||
s.packer = newPacketPacker(s.connectionID,
|
||||
|
|
|
@ -653,23 +653,6 @@ var _ = Describe("Session", func() {
|
|||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(sess.conn.(*mockConnection).remoteAddr).To(Equal(origAddr))
|
||||
})
|
||||
|
||||
It("doesn't change the remote address if authenticating the packet fails", func() {
|
||||
remoteIP := &net.IPAddr{IP: net.IPv4(192, 168, 0, 100)}
|
||||
attackerIP := &net.IPAddr{IP: net.IPv4(192, 168, 0, 102)}
|
||||
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 = &mockAEAD{}
|
||||
p := receivedPacket{
|
||||
remoteAddr: attackerIP,
|
||||
header: &wire.Header{PacketNumber: 1337},
|
||||
}
|
||||
err := sess.handlePacketImpl(&p)
|
||||
quicErr := err.(*qerr.QuicError)
|
||||
Expect(quicErr.ErrorCode).To(Equal(qerr.DecryptionFailure))
|
||||
Expect(sess.conn.(*mockConnection).remoteAddr).To(Equal(remoteIP))
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue