From 302d2a17156ee3f7c81bb3f92573589315a35f41 Mon Sep 17 00:00:00 2001 From: Marten Seemann Date: Mon, 26 Mar 2018 13:17:39 +0200 Subject: [PATCH] don't use trial decryption for IETF QUIC --- internal/handshake/crypto_setup_tls.go | 26 +++-- internal/handshake/crypto_setup_tls_test.go | 21 +--- internal/handshake/interface.go | 19 +++- mint_utils.go | 17 +++- mint_utils_test.go | 16 ++-- mock_gquic_aead_test.go | 49 ++++++++++ mock_quic_aead_test.go | 61 ++++++++++++ mockgen.go | 2 + packet_packer.go | 10 +- packet_unpacker.go | 100 +++++++++++++++++--- packet_unpacker_test.go | 100 ++++++++++++-------- server_tls.go | 9 +- session.go | 8 +- session_test.go | 17 ---- 14 files changed, 329 insertions(+), 126 deletions(-) create mode 100644 mock_gquic_aead_test.go create mode 100644 mock_quic_aead_test.go diff --git a/internal/handshake/crypto_setup_tls.go b/internal/handshake/crypto_setup_tls.go index b9385ccf29c..43f854068ba 100644 --- a/internal/handshake/crypto_setup_tls.go +++ b/internal/handshake/crypto_setup_tls.go @@ -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 - } - data, err := h.nullAEAD.Open(dst, src, packetNumber, associatedData) - if err != nil { - return nil, protocol.EncryptionUnspecified, err + if h.aead == nil { + return nil, errors.New("no 1-RTT sealer") } - return data, protocol.EncryptionUnencrypted, nil + return h.aead.Open(dst, src, packetNumber, associatedData) } func (h *cryptoSetupTLS) GetSealer() (protocol.EncryptionLevel, Sealer) { diff --git a/internal/handshake/crypto_setup_tls_test.go b/internal/handshake/crypto_setup_tls_test.go index f0293ba6ac3..67bc2fda63c 100644 --- a/internal/handshake/crypto_setup_tls_test.go +++ b/internal/handshake/crypto_setup_tls_test.go @@ -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{}) - 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{}) + _, err := cs.OpenHandshake(nil, []byte("foobar enc"), 10, []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"))) }) }) diff --git a/internal/handshake/interface.go b/internal/handshake/interface.go index caca3ef7c97..8d8fd545538 100644 --- a/internal/handshake/interface.go +++ b/internal/handshake/interface.go @@ -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 { diff --git a/mint_utils.go b/mint_utils.go index b65de21eae0..8b20275a752 100644 --- a/mint_utils.go +++ b/mint_utils.go @@ -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 } } diff --git a/mint_utils_test.go b/mint_utils_test.go index a0905ca9cf0..52b1b37b865 100644 --- a/mint_utils_test.go +++ b/mint_utils_test.go @@ -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()) + decrypted, err := aeadCl.Open(nil, data[len(hdr.Raw):], hdr.PacketNumber, hdr.Raw) Expect(err).ToNot(HaveOccurred()) - Expect(packet.frames).To(Equal([]wire.Frame{f})) + frame, err := wire.ParseNextFrame(bytes.NewReader(decrypted), hdr, versionIETFFrames) + Expect(err).ToNot(HaveOccurred()) + Expect(frame).To(Equal(f)) }) }) }) diff --git a/mock_gquic_aead_test.go b/mock_gquic_aead_test.go new file mode 100644 index 00000000000..c8ac324be5d --- /dev/null +++ b/mock_gquic_aead_test.go @@ -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) +} diff --git a/mock_quic_aead_test.go b/mock_quic_aead_test.go new file mode 100644 index 00000000000..63a2a5a7b46 --- /dev/null +++ b/mock_quic_aead_test.go @@ -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) +} diff --git a/mockgen.go b/mockgen.go index d6886ff1d52..65f385468fd 100644 --- a/mockgen.go +++ b/mockgen.go @@ -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" diff --git a/packet_packer.go b/packet_packer.go index 0b872c01dc1..e4fa653a525 100644 --- a/packet_packer.go +++ b/packet_packer.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, diff --git a/packet_unpacker.go b/packet_unpacker.go index 730592f3dee..1d78b9924e2 100644 --- a/packet_unpacker.go +++ b/packet_unpacker.go @@ -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 { +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) +} + +type packetUnpackerBase struct { version protocol.VersionNumber - aead quicAEAD } -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()) - } +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, diff --git a/packet_unpacker_test.go b/packet_unpacker_test.go index d407811fb90..1ed5b840818 100644 --- a/packet_unpacker_test.go +++ b/packet_unpacker_test.go @@ -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 -} +var _ = Describe("Packet Unpacker (for gQUIC)", func() { + var ( + unpacker *packetUnpackerGQUIC + hdr *wire.Header + aead *MockGQUICAEAD + ) + + BeforeEach(func() { + aead = NewMockGQUICAEAD(mockCtrl) + hdr = &wire.Header{ + PacketNumber: 10, + PacketNumberLen: 1, + Raw: []byte{0x04, 0x4c, 0x01}, + } + unpacker = newPacketUnpackerGQUIC(aead, versionGQUICFrames).(*packetUnpackerGQUIC) + }) + + It("errors if the packet doesn't contain any payload", func() { + 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)) + }) -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 -} + It("saves the encryption level", func() { + 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)) + }) -var _ quicAEAD = &mockAEAD{} + 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", func() { +var _ = Describe("Packet Unpacker (for IETF QUIC)", func() { var ( unpacker *packetUnpacker hdr *wire.Header - hdrBin []byte - data []byte - buf *bytes.Buffer + aead *MockQuicAEAD ) BeforeEach(func() { + aead = NewMockQuicAEAD(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 = newPacketUnpacker(aead, versionIETFFrames).(*packetUnpacker) }) - 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().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("saves the encryption level", func() { - unpacker.version = versionGQUICFrames - f := &wire.ConnectionCloseFrame{ReasonPhrase: "foo"} - err := f.Write(buf, versionGQUICFrames) + 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()) - setData(buf.Bytes()) - unpacker.aead.(*mockAEAD).encLevelOpen = protocol.EncryptionSecure - packet, err := unpacker.Unpack(hdrBin, hdr, data) + 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.encryptionLevel).To(Equal(protocol.EncryptionSecure)) + Expect(packet.frames).To(Equal([]wire.Frame{&wire.PingFrame{}, &wire.BlockedFrame{}})) }) }) diff --git a/server_tls.go b/server_tls.go index 38d7abb156c..ba8b593cf06 100644 --- a/server_tls.go +++ b/server_tls.go @@ -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 { diff --git a/session.go b/session.go index 7befb33bbb1..8f1e56bedd3 100644 --- a/session.go +++ b/session.go @@ -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, diff --git a/session_test.go b/session_test.go index be5c81d00a7..fd8a7e8a25b 100644 --- a/session_test.go +++ b/session_test.go @@ -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)) - }) }) })