From 73a1a0e509ebfae9f84ffa23429cb260d7e79246 Mon Sep 17 00:00:00 2001 From: Marten Seemann Date: Sun, 26 Aug 2018 17:49:09 +0700 Subject: [PATCH] implement writing of the gQUIC 44 header --- internal/wire/header.go | 83 ++++++++-- internal/wire/header_test.go | 297 ++++++++++++++++++++++++++++------- 2 files changed, 304 insertions(+), 76 deletions(-) diff --git a/internal/wire/header.go b/internal/wire/header.go index 99e5b9309..41a322b81 100644 --- a/internal/wire/header.go +++ b/internal/wire/header.go @@ -45,20 +45,20 @@ type Header struct { var errInvalidPacketNumberLen = errors.New("invalid packet number length") // Write writes the Header. -func (h *Header) Write(b *bytes.Buffer, pers protocol.Perspective, version protocol.VersionNumber) error { - if !version.UsesTLS() { +func (h *Header) Write(b *bytes.Buffer, pers protocol.Perspective, ver protocol.VersionNumber) error { + if !ver.UsesIETFHeaderFormat() { h.IsPublicHeader = true // save that this is a Public Header, so we can log it correctly later - return h.writePublicHeader(b, pers, version) + return h.writePublicHeader(b, pers, ver) } // write an IETF QUIC header if h.IsLongHeader { - return h.writeLongHeader(b) + return h.writeLongHeader(b, ver) } - return h.writeShortHeader(b) + return h.writeShortHeader(b, ver) } // TODO: add support for the key phase -func (h *Header) writeLongHeader(b *bytes.Buffer) error { +func (h *Header) writeLongHeader(b *bytes.Buffer, v protocol.VersionNumber) error { b.WriteByte(byte(0x80 | h.Type)) utils.BigEndian.WriteUint32(b, uint32(h.Version)) connIDLen, err := encodeConnIDLen(h.DestConnectionID, h.SrcConnectionID) @@ -69,7 +69,7 @@ func (h *Header) writeLongHeader(b *bytes.Buffer) error { b.Write(h.DestConnectionID.Bytes()) b.Write(h.SrcConnectionID.Bytes()) - if h.Type == protocol.PacketTypeInitial { + if h.Type == protocol.PacketTypeInitial && v.UsesTokenInHeader() { utils.WriteVarInt(b, uint64(len(h.Token))) b.Write(h.Token) } @@ -89,16 +89,51 @@ func (h *Header) writeLongHeader(b *bytes.Buffer) error { return nil } - utils.WriteVarInt(b, uint64(h.PayloadLen)) - return utils.WriteVarIntPacketNumber(b, h.PacketNumber, h.PacketNumberLen) + if v.UsesLengthInHeader() { + utils.WriteVarInt(b, uint64(h.PayloadLen)) + } + if v.UsesVarintPacketNumbers() { + return utils.WriteVarIntPacketNumber(b, h.PacketNumber, h.PacketNumberLen) + } + utils.BigEndian.WriteUint32(b, uint32(h.PacketNumber)) + if h.Type == protocol.PacketType0RTT && v == protocol.Version44 { + if len(h.DiversificationNonce) != 32 { + return errors.New("invalid diversification nonce length") + } + b.Write(h.DiversificationNonce) + } + return nil } -func (h *Header) writeShortHeader(b *bytes.Buffer) error { +func (h *Header) writeShortHeader(b *bytes.Buffer, v protocol.VersionNumber) error { typeByte := byte(0x30) typeByte |= byte(h.KeyPhase << 6) - b.WriteByte(typeByte) + if !v.UsesVarintPacketNumbers() { + switch h.PacketNumberLen { + case protocol.PacketNumberLen1: + case protocol.PacketNumberLen2: + typeByte |= 0x1 + case protocol.PacketNumberLen4: + typeByte |= 0x2 + default: + return errInvalidPacketNumberLen + } + } + b.WriteByte(typeByte) b.Write(h.DestConnectionID.Bytes()) + + if !v.UsesVarintPacketNumbers() { + switch h.PacketNumberLen { + case protocol.PacketNumberLen1: + b.WriteByte(uint8(h.PacketNumber)) + case protocol.PacketNumberLen2: + utils.BigEndian.WriteUint16(b, uint16(h.PacketNumber)) + case protocol.PacketNumberLen4: + utils.BigEndian.WriteUint32(b, uint32(h.PacketNumber)) + } + return nil + } return utils.WriteVarIntPacketNumber(b, h.PacketNumber, h.PacketNumberLen) } @@ -164,19 +199,25 @@ func (h *Header) writePublicHeader(b *bytes.Buffer, pers protocol.Perspective, _ } // GetLength determines the length of the Header. -func (h *Header) GetLength(version protocol.VersionNumber) (protocol.ByteCount, error) { - if !version.UsesTLS() { +func (h *Header) GetLength(v protocol.VersionNumber) (protocol.ByteCount, error) { + if !v.UsesIETFHeaderFormat() { return h.getPublicHeaderLength() } - return h.getHeaderLength() + return h.getHeaderLength(v) } -func (h *Header) getHeaderLength() (protocol.ByteCount, error) { +func (h *Header) getHeaderLength(v protocol.VersionNumber) (protocol.ByteCount, error) { if h.IsLongHeader { - length := 1 /* type byte */ + 4 /* version */ + 1 /* conn id len byte */ + protocol.ByteCount(h.DestConnectionID.Len()+h.SrcConnectionID.Len()) + utils.VarIntLen(uint64(h.PayloadLen)) + protocol.ByteCount(h.PacketNumberLen) - if h.Type == protocol.PacketTypeInitial { + length := 1 /* type byte */ + 4 /* version */ + 1 /* conn id len byte */ + protocol.ByteCount(h.DestConnectionID.Len()+h.SrcConnectionID.Len()) + protocol.ByteCount(h.PacketNumberLen) + if v.UsesLengthInHeader() { + length += utils.VarIntLen(uint64(h.PayloadLen)) + } + if h.Type == protocol.PacketTypeInitial && v.UsesTokenInHeader() { length += utils.VarIntLen(uint64(len(h.Token))) + protocol.ByteCount(len(h.Token)) } + if h.Type == protocol.PacketType0RTT && v == protocol.Version44 { + length += protocol.ByteCount(len(h.DiversificationNonce)) + } return length, nil } @@ -234,6 +275,14 @@ func (h *Header) logHeader(logger utils.Logger) { logger.Debugf("\tLong Header{Type: %s, DestConnectionID: %s, SrcConnectionID: %s, %sOrigDestConnectionID: %s, Version: %s}", h.Type, h.DestConnectionID, h.SrcConnectionID, token, h.OrigDestConnectionID, h.Version) return } + if h.Version == protocol.Version44 { + var divNonce string + if h.Type == protocol.PacketType0RTT { + divNonce = fmt.Sprintf("Diversification Nonce: %#x, ", h.DiversificationNonce) + } + logger.Debugf("\tLong Header{Type: %s, DestConnectionID: %s, SrcConnectionID: %s, PacketNumber: %#x, PacketNumberLen: %d, %sVersion: %s}", h.Type, h.DestConnectionID, h.SrcConnectionID, h.PacketNumber, h.PacketNumberLen, divNonce, h.Version) + return + } logger.Debugf("\tLong Header{Type: %s, DestConnectionID: %s, SrcConnectionID: %s, %sPacketNumber: %#x, PacketNumberLen: %d, PayloadLen: %d, Version: %s}", h.Type, h.DestConnectionID, h.SrcConnectionID, token, h.PacketNumber, h.PacketNumberLen, h.PayloadLen, h.Version) } } else { diff --git a/internal/wire/header_test.go b/internal/wire/header_test.go index 2f53e189d..9e7964436 100644 --- a/internal/wire/header_test.go +++ b/internal/wire/header_test.go @@ -217,6 +217,114 @@ var _ = Describe("Header", func() { }) }) + Context("gQUIC 44", func() { + Context("Long Header", func() { + It("writes", func() { + err := (&Header{ + IsLongHeader: true, + Type: protocol.PacketTypeInitial, + DestConnectionID: protocol.ConnectionID{1, 2, 3, 4, 5, 6, 7, 8}, + Version: 0xdeadbeef, + PacketNumberLen: protocol.PacketNumberLen4, + PacketNumber: 0xdecafbad, + }).Write(buf, protocol.PerspectiveServer, protocol.Version44) + Expect(err).ToNot(HaveOccurred()) + expected := []byte{ + 0x80 ^ uint8(protocol.PacketTypeInitial), + 0xde, 0xad, 0xbe, 0xef, // version + 0x50, // connection ID lengths + 1, 2, 3, 4, 5, 6, 7, 8, // connection ID + 0xde, 0xca, 0xfb, 0xad, // packet number + } + Expect(buf.Bytes()).To(Equal(expected)) + }) + + It("writes a 0-RTT packet with a Diversification Nonce", func() { + divNonce := bytes.Repeat([]byte{'c'}, 32) + err := (&Header{ + IsLongHeader: true, + Type: protocol.PacketType0RTT, + DestConnectionID: protocol.ConnectionID{1, 2, 3, 4, 5, 6, 7, 8}, + Version: 0xdeadbeef, + PacketNumberLen: protocol.PacketNumberLen4, + PacketNumber: 0xdecafbad, + DiversificationNonce: divNonce, + }).Write(buf, protocol.PerspectiveServer, protocol.Version44) + Expect(err).ToNot(HaveOccurred()) + expected := []byte{ + 0x80 ^ uint8(protocol.PacketType0RTT), + 0xde, 0xad, 0xbe, 0xef, // version + 0x50, // connection ID lengths + 1, 2, 3, 4, 5, 6, 7, 8, // connection ID + 0xde, 0xca, 0xfb, 0xad, // packet number + } + expected = append(expected, divNonce...) + Expect(buf.Bytes()).To(Equal(expected)) + }) + + It("refuses to write a 0-RTT packet with a wrong length Diversification Nonce", func() { + divNonce := bytes.Repeat([]byte{'c'}, 31) + err := (&Header{ + IsLongHeader: true, + Type: protocol.PacketType0RTT, + DestConnectionID: protocol.ConnectionID{1, 2, 3, 4, 5, 6, 7, 8}, + Version: 0xdeadbeef, + PacketNumberLen: protocol.PacketNumberLen4, + PacketNumber: 0xdecafbad, + DiversificationNonce: divNonce, + }).Write(buf, protocol.PerspectiveServer, protocol.Version44) + Expect(err).To(MatchError("invalid diversification nonce length")) + }) + }) + + Context("Short Header", func() { + It("writes a Short Header with a 1 byte packet number", func() { + err := (&Header{ + DestConnectionID: protocol.ConnectionID{1, 2, 3, 4, 5, 6, 7, 8}, + PacketNumberLen: protocol.PacketNumberLen1, + PacketNumber: 0x42, + }).Write(buf, protocol.PerspectiveServer, protocol.Version44) + Expect(err).ToNot(HaveOccurred()) + expected := []byte{ + 0x30, + 1, 2, 3, 4, 5, 6, 7, 8, // connection ID + 0x42, // packet number + } + Expect(buf.Bytes()).To(Equal(expected)) + }) + + It("writes a Short Header with a 2 byte packet number", func() { + err := (&Header{ + DestConnectionID: protocol.ConnectionID{1, 2, 3, 4, 5, 6, 7, 8}, + PacketNumberLen: protocol.PacketNumberLen2, + PacketNumber: 0xcafe, + }).Write(buf, protocol.PerspectiveServer, protocol.Version44) + Expect(err).ToNot(HaveOccurred()) + expected := []byte{ + 0x30 ^ 0x1, + 1, 2, 3, 4, 5, 6, 7, 8, // connection ID + 0xca, 0xfe, // packet number + } + Expect(buf.Bytes()).To(Equal(expected)) + }) + + It("writes a Short Header with a 4 byte packet number", func() { + err := (&Header{ + DestConnectionID: protocol.ConnectionID{1, 2, 3, 4, 5, 6, 7, 8}, + PacketNumberLen: protocol.PacketNumberLen4, + PacketNumber: 0xdeadbeef, + }).Write(buf, protocol.PerspectiveServer, protocol.Version44) + Expect(err).ToNot(HaveOccurred()) + expected := []byte{ + 0x30 ^ 0x2, + 1, 2, 3, 4, 5, 6, 7, 8, // connection ID + 0xde, 0xad, 0xbe, 0xef, // packet number + } + Expect(buf.Bytes()).To(Equal(expected)) + }) + }) + }) + Context("Public Header", func() { connID := protocol.ConnectionID{0x4c, 0xfa, 0x9f, 0x9b, 0x66, 0x86, 0x19, 0xf6} @@ -508,72 +616,116 @@ var _ = Describe("Header", func() { Expect(err).To(MatchError("invalid packet number length: 5")) }) }) - }) - Context("Public Header", func() { - connID := protocol.ConnectionID{1, 2, 3, 4, 5, 6, 7, 8} + Context("gQUIC 44", func() { + It("has the right length for the Long Header", func() { + h := &Header{ + IsLongHeader: true, + Type: protocol.PacketTypeInitial, + PayloadLen: 1, + DestConnectionID: protocol.ConnectionID{1, 2, 3, 4, 5, 6, 7, 8}, + PacketNumberLen: protocol.PacketNumberLen4, + } + expectedLen := 1 /* type byte */ + 4 /* version */ + 1 /* conn ID len */ + 8 /* dest conn id */ + 4 /* packet number */ + Expect(h.GetLength(protocol.Version44)).To(BeEquivalentTo(expectedLen)) + err := h.Write(buf, protocol.PerspectiveClient, protocol.Version44) + Expect(err).ToNot(HaveOccurred()) + Expect(buf.Len()).To(Equal(expectedLen)) + }) - It("errors when PacketNumberLen is not set", func() { - hdr := Header{ - DestConnectionID: connID, - PacketNumber: 0xdecafbad, - } - _, err := hdr.GetLength(versionPublicHeader) - Expect(err).To(MatchError(errPacketNumberLenNotSet)) + It("has the right length for the Long Header containing a Diversification Nonce", func() { + h := &Header{ + IsLongHeader: true, + Type: protocol.PacketType0RTT, + DestConnectionID: protocol.ConnectionID{1, 2, 3, 4, 5, 6, 7, 8}, + PacketNumberLen: protocol.PacketNumberLen4, + DiversificationNonce: bytes.Repeat([]byte{'d'}, 32), + } + expectedLen := 1 /* type byte */ + 4 /* version */ + 1 /* conn ID len */ + 8 /* dest conn id */ + 4 /* packet number */ + 32 /* div nonce */ + Expect(h.GetLength(protocol.Version44)).To(BeEquivalentTo(expectedLen)) + err := h.Write(buf, protocol.PerspectiveServer, protocol.Version44) + Expect(err).ToNot(HaveOccurred()) + Expect(buf.Len()).To(Equal(expectedLen)) + }) + + It("has the right length for a Short Header", func() { + h := &Header{ + DestConnectionID: protocol.ConnectionID{1, 2, 3, 4, 5, 6, 7, 8}, + PacketNumberLen: protocol.PacketNumberLen2, + } + expectedLen := 1 /*type byte*/ + 8 /* conn ID */ + 2 /* packet number */ + Expect(h.GetLength(protocol.Version44)).To(BeEquivalentTo(expectedLen)) + err := h.Write(buf, protocol.PerspectiveServer, protocol.Version44) + Expect(err).ToNot(HaveOccurred()) + Expect(buf.Len()).To(Equal(expectedLen)) + }) }) - It("gets the length of a packet with longest packet number length and connectionID", func() { - hdr := Header{ - DestConnectionID: connID, - PacketNumber: 0xdecafbad, - PacketNumberLen: protocol.PacketNumberLen4, - } - length, err := hdr.GetLength(versionPublicHeader) - Expect(err).ToNot(HaveOccurred()) - Expect(length).To(Equal(protocol.ByteCount(1 + 8 + 4))) // 1 byte public flag, 8 bytes connectionID, and packet number - }) + Context("Public Header", func() { + connID := protocol.ConnectionID{1, 2, 3, 4, 5, 6, 7, 8} - It("gets the lengths of a packet sent by the client with the VersionFlag set", func() { - hdr := Header{ - PacketNumber: 0xdecafbad, - PacketNumberLen: protocol.PacketNumberLen4, - VersionFlag: true, - Version: versionPublicHeader, - } - length, err := hdr.GetLength(versionPublicHeader) - Expect(err).ToNot(HaveOccurred()) - Expect(length).To(Equal(protocol.ByteCount(1 + 4 + 4))) // 1 byte public flag, 4 version number, and packet number - }) + It("errors when PacketNumberLen is not set", func() { + hdr := Header{ + DestConnectionID: connID, + PacketNumber: 0xdecafbad, + } + _, err := hdr.GetLength(versionPublicHeader) + Expect(err).To(MatchError(errPacketNumberLenNotSet)) + }) - It("gets the length of a packet with longest packet number length and omitted connectionID", func() { - hdr := Header{ - PacketNumber: 0xDECAFBAD, - PacketNumberLen: protocol.PacketNumberLen4, - } - length, err := hdr.GetLength(versionPublicHeader) - Expect(err).ToNot(HaveOccurred()) - Expect(length).To(Equal(protocol.ByteCount(1 + 4))) // 1 byte public flag, and packet number - }) + It("gets the length of a packet with longest packet number length and connectionID", func() { + hdr := Header{ + DestConnectionID: connID, + PacketNumber: 0xdecafbad, + PacketNumberLen: protocol.PacketNumberLen4, + } + length, err := hdr.GetLength(versionPublicHeader) + Expect(err).ToNot(HaveOccurred()) + Expect(length).To(Equal(protocol.ByteCount(1 + 8 + 4))) // 1 byte public flag, 8 bytes connectionID, and packet number + }) - It("gets the length of a packet 2 byte packet number length ", func() { - hdr := Header{ - DestConnectionID: connID, - PacketNumber: 0xDECAFBAD, - PacketNumberLen: protocol.PacketNumberLen2, - } - length, err := hdr.GetLength(versionPublicHeader) - Expect(err).ToNot(HaveOccurred()) - Expect(length).To(Equal(protocol.ByteCount(1 + 8 + 2))) // 1 byte public flag, 8 byte connectionID, and packet number - }) + It("gets the lengths of a packet sent by the client with the VersionFlag set", func() { + hdr := Header{ + PacketNumber: 0xdecafbad, + PacketNumberLen: protocol.PacketNumberLen4, + VersionFlag: true, + Version: versionPublicHeader, + } + length, err := hdr.GetLength(versionPublicHeader) + Expect(err).ToNot(HaveOccurred()) + Expect(length).To(Equal(protocol.ByteCount(1 + 4 + 4))) // 1 byte public flag, 4 version number, and packet number + }) - It("works with diversification nonce", func() { - hdr := Header{ - DiversificationNonce: []byte("foo"), - PacketNumberLen: protocol.PacketNumberLen1, - } - length, err := hdr.GetLength(versionPublicHeader) - Expect(err).NotTo(HaveOccurred()) - Expect(length).To(Equal(protocol.ByteCount(1 + 3 + 1))) // 1 byte public flag, 3 byte DiversificationNonce, 1 byte PacketNumber + It("gets the length of a packet with longest packet number length and omitted connectionID", func() { + hdr := Header{ + PacketNumber: 0xDECAFBAD, + PacketNumberLen: protocol.PacketNumberLen4, + } + length, err := hdr.GetLength(versionPublicHeader) + Expect(err).ToNot(HaveOccurred()) + Expect(length).To(Equal(protocol.ByteCount(1 + 4))) // 1 byte public flag, and packet number + }) + + It("gets the length of a packet 2 byte packet number length ", func() { + hdr := Header{ + DestConnectionID: connID, + PacketNumber: 0xDECAFBAD, + PacketNumberLen: protocol.PacketNumberLen2, + } + length, err := hdr.GetLength(versionPublicHeader) + Expect(err).ToNot(HaveOccurred()) + Expect(length).To(Equal(protocol.ByteCount(1 + 8 + 2))) // 1 byte public flag, 8 byte connectionID, and packet number + }) + + It("works with diversification nonce", func() { + hdr := Header{ + DiversificationNonce: []byte("foo"), + PacketNumberLen: protocol.PacketNumberLen1, + } + length, err := hdr.GetLength(versionPublicHeader) + Expect(err).NotTo(HaveOccurred()) + Expect(length).To(Equal(protocol.ByteCount(1 + 3 + 1))) // 1 byte public flag, 3 byte DiversificationNonce, 1 byte PacketNumber + }) }) }) @@ -678,6 +830,34 @@ var _ = Describe("Header", func() { }) }) + Context("gQUIC 44", func() { + It("logs Long Headers", func() { + (&Header{ + IsLongHeader: true, + Type: protocol.PacketTypeHandshake, + PacketNumber: 0x1337, + PacketNumberLen: protocol.PacketNumberLen4, + DestConnectionID: protocol.ConnectionID{0xde, 0xad, 0xbe, 0xef, 0xca, 0xfe, 0x13, 0x37}, + SrcConnectionID: protocol.ConnectionID{0xde, 0xca, 0xfb, 0xad, 0x013, 0x37, 0x13, 0x37}, + Version: protocol.Version44, + }).Log(logger) + Expect(buf.String()).To(ContainSubstring("Long Header{Type: Handshake, DestConnectionID: 0xdeadbeefcafe1337, SrcConnectionID: 0xdecafbad13371337, PacketNumber: 0x1337, PacketNumberLen: 4, Version: gQUIC 44}")) + }) + + It("logs a Long Header with a Diversification Nonce", func() { + (&Header{ + IsLongHeader: true, + Type: protocol.PacketType0RTT, + PacketNumber: 0x1337, + PacketNumberLen: protocol.PacketNumberLen4, + SrcConnectionID: protocol.ConnectionID{0xde, 0xca, 0xfb, 0xad, 0x013, 0x37, 0x13, 0x37}, + DiversificationNonce: []byte{0xde, 0xad, 0xbe, 0xef}, + Version: protocol.Version44, + }).Log(logger) + Expect(buf.String()).To(ContainSubstring("Long Header{Type: 0-RTT Protected, DestConnectionID: (empty), SrcConnectionID: 0xdecafbad13371337, PacketNumber: 0x1337, PacketNumberLen: 4, Diversification Nonce: 0xdeadbeef, Version: gQUIC 44}")) + }) + }) + Context("Public Header", func() { It("logs a Public Header containing a connection ID", func() { (&Header{ @@ -718,6 +898,5 @@ var _ = Describe("Header", func() { Expect(buf.String()).To(ContainSubstring("DiversificationNonce: []byte{0xba, 0xdf, 0x0, 0xd}")) }) }) - }) })