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(err).To(BeAssignableToTypeOf(&headerParseError{})) 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(err).To(BeAssignableToTypeOf(&headerParseError{})) 81 Expect(err).To(MatchError("Packet too small. Expected at least 20 bytes after the header, got 19")) 82 }) 83 84 It("opens Initial packets", func() { 85 extHdr := &wire.ExtendedHeader{ 86 Header: wire.Header{ 87 IsLongHeader: true, 88 Type: protocol.PacketTypeInitial, 89 Length: 3 + 6, // packet number len + payload 90 DestConnectionID: connID, 91 Version: version, 92 }, 93 PacketNumber: 2, 94 PacketNumberLen: 3, 95 } 96 hdr, hdrRaw := getHeader(extHdr) 97 opener := mocks.NewMockLongHeaderOpener(mockCtrl) 98 gomock.InOrder( 99 cs.EXPECT().GetInitialOpener().Return(opener, nil), 100 opener.EXPECT().DecryptHeader(gomock.Any(), gomock.Any(), gomock.Any()), 101 opener.EXPECT().DecodePacketNumber(protocol.PacketNumber(2), protocol.PacketNumberLen3).Return(protocol.PacketNumber(1234)), 102 opener.EXPECT().Open(gomock.Any(), payload, protocol.PacketNumber(1234), hdrRaw).Return([]byte("decrypted"), nil), 103 ) 104 packet, err := unpacker.Unpack(hdr, time.Now(), append(hdrRaw, payload...)) 105 Expect(err).ToNot(HaveOccurred()) 106 Expect(packet.encryptionLevel).To(Equal(protocol.EncryptionInitial)) 107 Expect(packet.data).To(Equal([]byte("decrypted"))) 108 }) 109 110 It("opens 0-RTT packets", func() { 111 extHdr := &wire.ExtendedHeader{ 112 Header: wire.Header{ 113 IsLongHeader: true, 114 Type: protocol.PacketType0RTT, 115 Length: 3 + 6, // packet number len + payload 116 DestConnectionID: connID, 117 Version: version, 118 }, 119 PacketNumber: 20, 120 PacketNumberLen: 2, 121 } 122 hdr, hdrRaw := getHeader(extHdr) 123 opener := mocks.NewMockLongHeaderOpener(mockCtrl) 124 gomock.InOrder( 125 cs.EXPECT().Get0RTTOpener().Return(opener, nil), 126 opener.EXPECT().DecryptHeader(gomock.Any(), gomock.Any(), gomock.Any()), 127 opener.EXPECT().DecodePacketNumber(protocol.PacketNumber(20), protocol.PacketNumberLen2).Return(protocol.PacketNumber(321)), 128 opener.EXPECT().Open(gomock.Any(), payload, protocol.PacketNumber(321), hdrRaw).Return([]byte("decrypted"), nil), 129 ) 130 packet, err := unpacker.Unpack(hdr, time.Now(), append(hdrRaw, payload...)) 131 Expect(err).ToNot(HaveOccurred()) 132 Expect(packet.encryptionLevel).To(Equal(protocol.Encryption0RTT)) 133 Expect(packet.data).To(Equal([]byte("decrypted"))) 134 }) 135 136 It("opens short header packets", func() { 137 extHdr := &wire.ExtendedHeader{ 138 Header: wire.Header{DestConnectionID: connID}, 139 KeyPhase: protocol.KeyPhaseOne, 140 PacketNumber: 99, 141 PacketNumberLen: protocol.PacketNumberLen4, 142 } 143 hdr, hdrRaw := getHeader(extHdr) 144 opener := mocks.NewMockShortHeaderOpener(mockCtrl) 145 now := time.Now() 146 gomock.InOrder( 147 cs.EXPECT().Get1RTTOpener().Return(opener, nil), 148 opener.EXPECT().DecryptHeader(gomock.Any(), gomock.Any(), gomock.Any()), 149 opener.EXPECT().DecodePacketNumber(protocol.PacketNumber(99), protocol.PacketNumberLen4).Return(protocol.PacketNumber(321)), 150 opener.EXPECT().Open(gomock.Any(), payload, now, protocol.PacketNumber(321), protocol.KeyPhaseOne, hdrRaw).Return([]byte("decrypted"), nil), 151 ) 152 packet, err := unpacker.Unpack(hdr, now, append(hdrRaw, payload...)) 153 Expect(err).ToNot(HaveOccurred()) 154 Expect(packet.encryptionLevel).To(Equal(protocol.Encryption1RTT)) 155 Expect(packet.data).To(Equal([]byte("decrypted"))) 156 }) 157 158 It("returns the error when getting the sealer fails", func() { 159 extHdr := &wire.ExtendedHeader{ 160 Header: wire.Header{DestConnectionID: connID}, 161 PacketNumber: 0x1337, 162 PacketNumberLen: 2, 163 } 164 hdr, hdrRaw := getHeader(extHdr) 165 cs.EXPECT().Get1RTTOpener().Return(nil, handshake.ErrKeysNotYetAvailable) 166 _, err := unpacker.Unpack(hdr, time.Now(), append(hdrRaw, payload...)) 167 Expect(err).To(MatchError(handshake.ErrKeysNotYetAvailable)) 168 }) 169 170 It("returns the error when unpacking fails", func() { 171 extHdr := &wire.ExtendedHeader{ 172 Header: wire.Header{ 173 IsLongHeader: true, 174 Type: protocol.PacketTypeHandshake, 175 Length: 3, // packet number len 176 DestConnectionID: connID, 177 Version: version, 178 }, 179 PacketNumber: 2, 180 PacketNumberLen: 3, 181 } 182 hdr, hdrRaw := getHeader(extHdr) 183 opener := mocks.NewMockLongHeaderOpener(mockCtrl) 184 cs.EXPECT().GetHandshakeOpener().Return(opener, nil) 185 opener.EXPECT().DecryptHeader(gomock.Any(), gomock.Any(), gomock.Any()) 186 opener.EXPECT().DecodePacketNumber(gomock.Any(), gomock.Any()) 187 unpackErr := &qerr.TransportError{ErrorCode: qerr.CryptoBufferExceeded} 188 opener.EXPECT().Open(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, unpackErr) 189 _, err := unpacker.Unpack(hdr, time.Now(), append(hdrRaw, payload...)) 190 Expect(err).To(MatchError(unpackErr)) 191 }) 192 193 It("defends against the timing side-channel when the reserved bits are wrong, for long header packets", func() { 194 extHdr := &wire.ExtendedHeader{ 195 Header: wire.Header{ 196 IsLongHeader: true, 197 Type: protocol.PacketTypeHandshake, 198 DestConnectionID: connID, 199 Version: version, 200 }, 201 PacketNumber: 0x1337, 202 PacketNumberLen: 2, 203 } 204 hdr, hdrRaw := getHeader(extHdr) 205 hdrRaw[0] |= 0xc 206 opener := mocks.NewMockLongHeaderOpener(mockCtrl) 207 opener.EXPECT().DecryptHeader(gomock.Any(), gomock.Any(), gomock.Any()) 208 cs.EXPECT().GetHandshakeOpener().Return(opener, nil) 209 opener.EXPECT().DecodePacketNumber(gomock.Any(), gomock.Any()) 210 opener.EXPECT().Open(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return([]byte("payload"), nil) 211 _, err := unpacker.Unpack(hdr, time.Now(), append(hdrRaw, payload...)) 212 Expect(err).To(MatchError(wire.ErrInvalidReservedBits)) 213 }) 214 215 It("defends against the timing side-channel when the reserved bits are wrong, for short header packets", func() { 216 extHdr := &wire.ExtendedHeader{ 217 Header: wire.Header{DestConnectionID: connID}, 218 PacketNumber: 0x1337, 219 PacketNumberLen: 2, 220 } 221 hdr, hdrRaw := getHeader(extHdr) 222 hdrRaw[0] |= 0x18 223 opener := mocks.NewMockShortHeaderOpener(mockCtrl) 224 opener.EXPECT().DecryptHeader(gomock.Any(), gomock.Any(), gomock.Any()) 225 cs.EXPECT().Get1RTTOpener().Return(opener, nil) 226 opener.EXPECT().DecodePacketNumber(gomock.Any(), gomock.Any()) 227 opener.EXPECT().Open(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return([]byte("payload"), nil) 228 _, err := unpacker.Unpack(hdr, time.Now(), append(hdrRaw, payload...)) 229 Expect(err).To(MatchError(wire.ErrInvalidReservedBits)) 230 }) 231 232 It("returns the decryption error, when unpacking a packet with wrong reserved bits fails", func() { 233 extHdr := &wire.ExtendedHeader{ 234 Header: wire.Header{DestConnectionID: connID}, 235 PacketNumber: 0x1337, 236 PacketNumberLen: 2, 237 } 238 hdr, hdrRaw := getHeader(extHdr) 239 hdrRaw[0] |= 0x18 240 opener := mocks.NewMockShortHeaderOpener(mockCtrl) 241 opener.EXPECT().DecryptHeader(gomock.Any(), gomock.Any(), gomock.Any()) 242 cs.EXPECT().Get1RTTOpener().Return(opener, nil) 243 opener.EXPECT().DecodePacketNumber(gomock.Any(), gomock.Any()) 244 opener.EXPECT().Open(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, handshake.ErrDecryptionFailed) 245 _, err := unpacker.Unpack(hdr, time.Now(), append(hdrRaw, payload...)) 246 Expect(err).To(MatchError(handshake.ErrDecryptionFailed)) 247 }) 248 249 It("decrypts the header", func() { 250 extHdr := &wire.ExtendedHeader{ 251 Header: wire.Header{ 252 IsLongHeader: true, 253 Type: protocol.PacketTypeHandshake, 254 Length: 3, // packet number len 255 DestConnectionID: connID, 256 Version: version, 257 }, 258 PacketNumber: 0x1337, 259 PacketNumberLen: 2, 260 } 261 hdr, hdrRaw := getHeader(extHdr) 262 origHdrRaw := append([]byte{}, hdrRaw...) // save a copy of the header 263 firstHdrByte := hdrRaw[0] 264 hdrRaw[0] ^= 0xff // invert the first byte 265 hdrRaw[len(hdrRaw)-2] ^= 0xff // invert the packet number 266 hdrRaw[len(hdrRaw)-1] ^= 0xff // invert the packet number 267 Expect(hdrRaw[0]).ToNot(Equal(firstHdrByte)) 268 opener := mocks.NewMockLongHeaderOpener(mockCtrl) 269 cs.EXPECT().GetHandshakeOpener().Return(opener, nil) 270 gomock.InOrder( 271 // we're using a 2 byte packet number, so the sample starts at the 3rd payload byte 272 opener.EXPECT().DecryptHeader( 273 []byte{3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18}, 274 &hdrRaw[0], 275 append(hdrRaw[len(hdrRaw)-2:], []byte{1, 2}...)).Do(func(_ []byte, firstByte *byte, pnBytes []byte) { 276 *firstByte ^= 0xff // invert the first byte back 277 for i := range pnBytes { 278 pnBytes[i] ^= 0xff // invert the packet number bytes 279 } 280 }), 281 opener.EXPECT().DecodePacketNumber(protocol.PacketNumber(0x1337), protocol.PacketNumberLen2).Return(protocol.PacketNumber(0x7331)), 282 opener.EXPECT().Open(gomock.Any(), gomock.Any(), protocol.PacketNumber(0x7331), origHdrRaw).Return([]byte{0}, nil), 283 ) 284 data := hdrRaw 285 for i := 1; i <= 100; i++ { 286 data = append(data, uint8(i)) 287 } 288 packet, err := unpacker.Unpack(hdr, time.Now(), data) 289 Expect(err).ToNot(HaveOccurred()) 290 Expect(packet.packetNumber).To(Equal(protocol.PacketNumber(0x7331))) 291 }) 292}) 293