diff --git a/internal/protocol/protocol.go b/internal/protocol/protocol.go index 9aa0890b3..ab57153a4 100644 --- a/internal/protocol/protocol.go +++ b/internal/protocol/protocol.go @@ -77,6 +77,9 @@ const DefaultTCPMSS ByteCount = 1460 // ClientHelloMinimumSize is the minimum size the server expects an inchoate CHLO to have. const ClientHelloMinimumSize = 1024 +// MinInitialPacketSize is the minimum size an Initial packet (in IETF QUIC) is requried to have. +const MinInitialPacketSize = 1200 + // MaxClientHellos is the maximum number of times we'll send a client hello // The value 3 accounts for: // * one failure due to an incorrect or missing source-address token diff --git a/packet_packer.go b/packet_packer.go index ed75db2fa..aabddee1a 100644 --- a/packet_packer.go +++ b/packet_packer.go @@ -290,7 +290,6 @@ func (p *packetPacker) getHeader(encLevel protocol.EncryptionLevel) *wire.Header header.IsLongHeader = true if !p.hasSentPacket && p.perspective == protocol.PerspectiveClient { header.Type = protocol.PacketTypeInitial - // TODO(#886): add padding } else { header.Type = protocol.PacketTypeHandshake } @@ -327,12 +326,27 @@ func (p *packetPacker) writeAndSealPacket( return nil, err } payloadStartIndex := buffer.Len() + + // the Initial packet needs to be padded, so the last STREAM frame must have the data length present + if header.Type == protocol.PacketTypeInitial { + lastFrame := payloadFrames[len(payloadFrames)-1] + if sf, ok := lastFrame.(*wire.StreamFrame); ok { + sf.DataLenPresent = true + } + } for _, frame := range payloadFrames { - err := frame.Write(buffer, p.version) - if err != nil { + if err := frame.Write(buffer, p.version); err != nil { return nil, err } } + // if this is an IETF QUIC Initial packet, we need to pad it to fulfill the minimum size requirement + // in gQUIC, padding is handled in the CHLO + if header.Type == protocol.PacketTypeInitial { + paddingLen := protocol.MinInitialPacketSize - sealer.Overhead() - buffer.Len() + if paddingLen > 0 { + buffer.Write(bytes.Repeat([]byte{0}, paddingLen)) + } + } if protocol.ByteCount(buffer.Len()+sealer.Overhead()) > protocol.MaxPacketSize { return nil, errors.New("PacketPacker BUG: packet too large") } diff --git a/packet_packer_test.go b/packet_packer_test.go index eb9e97867..50cedd90e 100644 --- a/packet_packer_test.go +++ b/packet_packer_test.go @@ -767,6 +767,21 @@ var _ = Describe("Packet packer", func() { Expect(err).To(MatchError("PacketPacker BUG: packet too large")) }) + It("pads Initial packets to the required minimum packet size", func() { + packer.version = protocol.VersionTLS + packer.hasSentPacket = false + packer.perspective = protocol.PerspectiveClient + packer.cryptoSetup.(*mockCryptoSetup).encLevelSealCrypto = protocol.EncryptionUnencrypted + cryptoStream.dataForWriting = []byte("foobar") + packet, err := packer.PackPacket() + Expect(err).ToNot(HaveOccurred()) + Expect(packet.raw).To(HaveLen(protocol.MinInitialPacketSize)) + Expect(packet.frames).To(HaveLen(1)) + sf := packet.frames[0].(*wire.StreamFrame) + Expect(sf.Data).To(Equal([]byte("foobar"))) + Expect(sf.DataLenPresent).To(BeTrue()) + }) + It("refuses to retransmit packets that were sent with forward-secure encryption", func() { p := &ackhandler.Packet{ EncryptionLevel: protocol.EncryptionForwardSecure,