1package quic
2
3import (
4	"bytes"
5	"errors"
6	"time"
7
8	"github.com/lucas-clemente/quic-go/internal/handshake"
9	"github.com/lucas-clemente/quic-go/internal/mocks"
10	"github.com/lucas-clemente/quic-go/internal/protocol"
11	"github.com/lucas-clemente/quic-go/internal/qerr"
12	"github.com/lucas-clemente/quic-go/internal/wire"
13
14	"github.com/golang/mock/gomock"
15
16	. "github.com/onsi/ginkgo"
17	. "github.com/onsi/gomega"
18)
19
20var _ = Describe("Packet Unpacker", func() {
21	const version = protocol.VersionTLS
22
23	var (
24		unpacker *packetUnpacker
25		cs       *mocks.MockCryptoSetup
26		connID   = protocol.ConnectionID{0xde, 0xad, 0xbe, 0xef}
27		payload  = []byte("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.")
28	)
29
30	getHeader := func(extHdr *wire.ExtendedHeader) (*wire.Header, []byte) {
31		buf := &bytes.Buffer{}
32		ExpectWithOffset(1, extHdr.Write(buf, version)).To(Succeed())
33		hdrLen := buf.Len()
34		if extHdr.Length > protocol.ByteCount(extHdr.PacketNumberLen) {
35			buf.Write(make([]byte, int(extHdr.Length)-int(extHdr.PacketNumberLen)))
36		}
37		hdr, _, _, err := wire.ParsePacket(buf.Bytes(), connID.Len())
38		ExpectWithOffset(1, err).ToNot(HaveOccurred())
39		return hdr, buf.Bytes()[:hdrLen]
40	}
41
42	BeforeEach(func() {
43		cs = mocks.NewMockCryptoSetup(mockCtrl)
44		unpacker = newPacketUnpacker(cs, version).(*packetUnpacker)
45	})
46
47	It("errors when the packet is too small to obtain the header decryption sample, for long headers", func() {
48		extHdr := &wire.ExtendedHeader{
49			Header: wire.Header{
50				IsLongHeader:     true,
51				Type:             protocol.PacketTypeHandshake,
52				DestConnectionID: connID,
53				Version:          version,
54			},
55			PacketNumber:    1337,
56			PacketNumberLen: protocol.PacketNumberLen2,
57		}
58		hdr, hdrRaw := getHeader(extHdr)
59		data := append(hdrRaw, make([]byte, 2 /* fill up packet number */ +15 /* need 16 bytes */)...)
60		opener := mocks.NewMockLongHeaderOpener(mockCtrl)
61		cs.EXPECT().GetHandshakeOpener().Return(opener, nil)
62		_, err := unpacker.Unpack(hdr, time.Now(), data)
63		Expect(errors.Is(err, &headerParseError{})).To(BeTrue())
64		var headerErr *headerParseError
65		Expect(errors.As(err, &headerErr)).To(BeTrue())
66		Expect(err).To(MatchError("Packet too small. Expected at least 20 bytes after the header, got 19"))
67	})
68
69	It("errors when the packet is too small to obtain the header decryption sample, for short headers", func() {
70		extHdr := &wire.ExtendedHeader{
71			Header:          wire.Header{DestConnectionID: connID},
72			PacketNumber:    1337,
73			PacketNumberLen: protocol.PacketNumberLen2,
74		}
75		hdr, hdrRaw := getHeader(extHdr)
76		data := append(hdrRaw, make([]byte, 2 /* fill up packet number */ +15 /* need 16 bytes */)...)
77		opener := mocks.NewMockShortHeaderOpener(mockCtrl)
78		cs.EXPECT().Get1RTTOpener().Return(opener, nil)
79		_, err := unpacker.Unpack(hdr, time.Now(), data)
80		Expect(errors.Is(err, &headerParseError{})).To(BeTrue())
81		var headerErr *headerParseError
82		Expect(errors.As(err, &headerErr)).To(BeTrue())
83		Expect(err).To(MatchError("Packet too small. Expected at least 20 bytes after the header, got 19"))
84	})
85
86	It("opens Initial packets", func() {
87		extHdr := &wire.ExtendedHeader{
88			Header: wire.Header{
89				IsLongHeader:     true,
90				Type:             protocol.PacketTypeInitial,
91				Length:           3 + 6, // packet number len + payload
92				DestConnectionID: connID,
93				Version:          version,
94			},
95			PacketNumber:    2,
96			PacketNumberLen: 3,
97		}
98		hdr, hdrRaw := getHeader(extHdr)
99		opener := mocks.NewMockLongHeaderOpener(mockCtrl)
100		gomock.InOrder(
101			cs.EXPECT().GetInitialOpener().Return(opener, nil),
102			opener.EXPECT().DecryptHeader(gomock.Any(), gomock.Any(), gomock.Any()),
103			opener.EXPECT().DecodePacketNumber(protocol.PacketNumber(2), protocol.PacketNumberLen3).Return(protocol.PacketNumber(1234)),
104			opener.EXPECT().Open(gomock.Any(), payload, protocol.PacketNumber(1234), hdrRaw).Return([]byte("decrypted"), nil),
105		)
106		packet, err := unpacker.Unpack(hdr, time.Now(), append(hdrRaw, payload...))
107		Expect(err).ToNot(HaveOccurred())
108		Expect(packet.encryptionLevel).To(Equal(protocol.EncryptionInitial))
109		Expect(packet.data).To(Equal([]byte("decrypted")))
110	})
111
112	It("opens 0-RTT packets", func() {
113		extHdr := &wire.ExtendedHeader{
114			Header: wire.Header{
115				IsLongHeader:     true,
116				Type:             protocol.PacketType0RTT,
117				Length:           3 + 6, // packet number len + payload
118				DestConnectionID: connID,
119				Version:          version,
120			},
121			PacketNumber:    20,
122			PacketNumberLen: 2,
123		}
124		hdr, hdrRaw := getHeader(extHdr)
125		opener := mocks.NewMockLongHeaderOpener(mockCtrl)
126		gomock.InOrder(
127			cs.EXPECT().Get0RTTOpener().Return(opener, nil),
128			opener.EXPECT().DecryptHeader(gomock.Any(), gomock.Any(), gomock.Any()),
129			opener.EXPECT().DecodePacketNumber(protocol.PacketNumber(20), protocol.PacketNumberLen2).Return(protocol.PacketNumber(321)),
130			opener.EXPECT().Open(gomock.Any(), payload, protocol.PacketNumber(321), hdrRaw).Return([]byte("decrypted"), nil),
131		)
132		packet, err := unpacker.Unpack(hdr, time.Now(), append(hdrRaw, payload...))
133		Expect(err).ToNot(HaveOccurred())
134		Expect(packet.encryptionLevel).To(Equal(protocol.Encryption0RTT))
135		Expect(packet.data).To(Equal([]byte("decrypted")))
136	})
137
138	It("opens short header packets", func() {
139		extHdr := &wire.ExtendedHeader{
140			Header:          wire.Header{DestConnectionID: connID},
141			KeyPhase:        protocol.KeyPhaseOne,
142			PacketNumber:    99,
143			PacketNumberLen: protocol.PacketNumberLen4,
144		}
145		hdr, hdrRaw := getHeader(extHdr)
146		opener := mocks.NewMockShortHeaderOpener(mockCtrl)
147		now := time.Now()
148		gomock.InOrder(
149			cs.EXPECT().Get1RTTOpener().Return(opener, nil),
150			opener.EXPECT().DecryptHeader(gomock.Any(), gomock.Any(), gomock.Any()),
151			opener.EXPECT().DecodePacketNumber(protocol.PacketNumber(99), protocol.PacketNumberLen4).Return(protocol.PacketNumber(321)),
152			opener.EXPECT().Open(gomock.Any(), payload, now, protocol.PacketNumber(321), protocol.KeyPhaseOne, hdrRaw).Return([]byte("decrypted"), nil),
153		)
154		packet, err := unpacker.Unpack(hdr, now, append(hdrRaw, payload...))
155		Expect(err).ToNot(HaveOccurred())
156		Expect(packet.encryptionLevel).To(Equal(protocol.Encryption1RTT))
157		Expect(packet.data).To(Equal([]byte("decrypted")))
158	})
159
160	It("returns the error when getting the sealer fails", func() {
161		extHdr := &wire.ExtendedHeader{
162			Header:          wire.Header{DestConnectionID: connID},
163			PacketNumber:    0x1337,
164			PacketNumberLen: 2,
165		}
166		hdr, hdrRaw := getHeader(extHdr)
167		cs.EXPECT().Get1RTTOpener().Return(nil, handshake.ErrKeysNotYetAvailable)
168		_, err := unpacker.Unpack(hdr, time.Now(), append(hdrRaw, payload...))
169		Expect(err).To(MatchError(handshake.ErrKeysNotYetAvailable))
170	})
171
172	It("returns the error when unpacking fails", func() {
173		extHdr := &wire.ExtendedHeader{
174			Header: wire.Header{
175				IsLongHeader:     true,
176				Type:             protocol.PacketTypeHandshake,
177				Length:           3, // packet number len
178				DestConnectionID: connID,
179				Version:          version,
180			},
181			PacketNumber:    2,
182			PacketNumberLen: 3,
183		}
184		hdr, hdrRaw := getHeader(extHdr)
185		opener := mocks.NewMockLongHeaderOpener(mockCtrl)
186		cs.EXPECT().GetHandshakeOpener().Return(opener, nil)
187		opener.EXPECT().DecryptHeader(gomock.Any(), gomock.Any(), gomock.Any())
188		opener.EXPECT().DecodePacketNumber(gomock.Any(), gomock.Any())
189		opener.EXPECT().Open(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, qerr.CryptoBufferExceeded)
190		_, err := unpacker.Unpack(hdr, time.Now(), append(hdrRaw, payload...))
191		Expect(err).To(MatchError(qerr.CryptoBufferExceeded))
192	})
193
194	It("defends against the timing side-channel when the reserved bits are wrong, for long header packets", func() {
195		extHdr := &wire.ExtendedHeader{
196			Header: wire.Header{
197				IsLongHeader:     true,
198				Type:             protocol.PacketTypeHandshake,
199				DestConnectionID: connID,
200				Version:          version,
201			},
202			PacketNumber:    0x1337,
203			PacketNumberLen: 2,
204		}
205		hdr, hdrRaw := getHeader(extHdr)
206		hdrRaw[0] |= 0xc
207		opener := mocks.NewMockLongHeaderOpener(mockCtrl)
208		opener.EXPECT().DecryptHeader(gomock.Any(), gomock.Any(), gomock.Any())
209		cs.EXPECT().GetHandshakeOpener().Return(opener, nil)
210		opener.EXPECT().DecodePacketNumber(gomock.Any(), gomock.Any())
211		opener.EXPECT().Open(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return([]byte("payload"), nil)
212		_, err := unpacker.Unpack(hdr, time.Now(), append(hdrRaw, payload...))
213		Expect(err).To(MatchError(wire.ErrInvalidReservedBits))
214	})
215
216	It("defends against the timing side-channel when the reserved bits are wrong, for short header packets", func() {
217		extHdr := &wire.ExtendedHeader{
218			Header:          wire.Header{DestConnectionID: connID},
219			PacketNumber:    0x1337,
220			PacketNumberLen: 2,
221		}
222		hdr, hdrRaw := getHeader(extHdr)
223		hdrRaw[0] |= 0x18
224		opener := mocks.NewMockShortHeaderOpener(mockCtrl)
225		opener.EXPECT().DecryptHeader(gomock.Any(), gomock.Any(), gomock.Any())
226		cs.EXPECT().Get1RTTOpener().Return(opener, nil)
227		opener.EXPECT().DecodePacketNumber(gomock.Any(), gomock.Any())
228		opener.EXPECT().Open(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return([]byte("payload"), nil)
229		_, err := unpacker.Unpack(hdr, time.Now(), append(hdrRaw, payload...))
230		Expect(err).To(MatchError(wire.ErrInvalidReservedBits))
231	})
232
233	It("returns the decryption error, when unpacking a packet with wrong reserved bits fails", func() {
234		extHdr := &wire.ExtendedHeader{
235			Header:          wire.Header{DestConnectionID: connID},
236			PacketNumber:    0x1337,
237			PacketNumberLen: 2,
238		}
239		hdr, hdrRaw := getHeader(extHdr)
240		hdrRaw[0] |= 0x18
241		opener := mocks.NewMockShortHeaderOpener(mockCtrl)
242		opener.EXPECT().DecryptHeader(gomock.Any(), gomock.Any(), gomock.Any())
243		cs.EXPECT().Get1RTTOpener().Return(opener, nil)
244		opener.EXPECT().DecodePacketNumber(gomock.Any(), gomock.Any())
245		opener.EXPECT().Open(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, handshake.ErrDecryptionFailed)
246		_, err := unpacker.Unpack(hdr, time.Now(), append(hdrRaw, payload...))
247		Expect(err).To(MatchError(handshake.ErrDecryptionFailed))
248	})
249
250	It("decrypts the header", func() {
251		extHdr := &wire.ExtendedHeader{
252			Header: wire.Header{
253				IsLongHeader:     true,
254				Type:             protocol.PacketTypeHandshake,
255				Length:           3, // packet number len
256				DestConnectionID: connID,
257				Version:          version,
258			},
259			PacketNumber:    0x1337,
260			PacketNumberLen: 2,
261		}
262		hdr, hdrRaw := getHeader(extHdr)
263		origHdrRaw := append([]byte{}, hdrRaw...) // save a copy of the header
264		firstHdrByte := hdrRaw[0]
265		hdrRaw[0] ^= 0xff             // invert the first byte
266		hdrRaw[len(hdrRaw)-2] ^= 0xff // invert the packet number
267		hdrRaw[len(hdrRaw)-1] ^= 0xff // invert the packet number
268		Expect(hdrRaw[0]).ToNot(Equal(firstHdrByte))
269		opener := mocks.NewMockLongHeaderOpener(mockCtrl)
270		cs.EXPECT().GetHandshakeOpener().Return(opener, nil)
271		gomock.InOrder(
272			// we're using a 2 byte packet number, so the sample starts at the 3rd payload byte
273			opener.EXPECT().DecryptHeader(
274				[]byte{3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18},
275				&hdrRaw[0],
276				append(hdrRaw[len(hdrRaw)-2:], []byte{1, 2}...)).Do(func(_ []byte, firstByte *byte, pnBytes []byte) {
277				*firstByte ^= 0xff // invert the first byte back
278				for i := range pnBytes {
279					pnBytes[i] ^= 0xff // invert the packet number bytes
280				}
281			}),
282			opener.EXPECT().DecodePacketNumber(protocol.PacketNumber(0x1337), protocol.PacketNumberLen2).Return(protocol.PacketNumber(0x7331)),
283			opener.EXPECT().Open(gomock.Any(), gomock.Any(), protocol.PacketNumber(0x7331), origHdrRaw).Return([]byte{0}, nil),
284		)
285		data := hdrRaw
286		for i := 1; i <= 100; i++ {
287			data = append(data, uint8(i))
288		}
289		packet, err := unpacker.Unpack(hdr, time.Now(), data)
290		Expect(err).ToNot(HaveOccurred())
291		Expect(packet.packetNumber).To(Equal(protocol.PacketNumber(0x7331)))
292	})
293})
294