diff --git a/data_test.go b/handshake/data_test.go similarity index 99% rename from data_test.go rename to handshake/data_test.go index fb2c23e4..5e94ef86 100644 --- a/data_test.go +++ b/handshake/data_test.go @@ -1,4 +1,4 @@ -package quic +package handshake import "strings" diff --git a/handshake/handshake_message.go b/handshake/handshake_message.go new file mode 100644 index 00000000..34a43de0 --- /dev/null +++ b/handshake/handshake_message.go @@ -0,0 +1,86 @@ +package handshake + +import ( + "bytes" + "encoding/binary" + "errors" + "sort" + + "github.com/lucas-clemente/quic-go/utils" +) + +var ( + errHandshakeMessageEOF = errors.New("ParseHandshakeMessage: Unexpected EOF") +) + +// ParseHandshakeMessage reads a crypto message +func ParseHandshakeMessage(data []byte) (Tag, map[Tag][]byte, error) { + if len(data) < 8 { + return 0, nil, errHandshakeMessageEOF + } + + messageTag := Tag(binary.LittleEndian.Uint32(data[0:4])) + nPairs := int(binary.LittleEndian.Uint16(data[4:6])) + + data = data[8:] + + // We need space for at least nPairs * 8 bytes + if len(data) < int(nPairs)*8 { + return 0, nil, errHandshakeMessageEOF + } + + resultMap := map[Tag][]byte{} + + dataStart := 0 + for indexPos := 0; indexPos < nPairs*8; indexPos += 8 { + // We know from the check above that data is long enough for the index + tag := Tag(binary.LittleEndian.Uint32(data[indexPos : indexPos+4])) + dataEnd := int(binary.LittleEndian.Uint32(data[indexPos+4 : indexPos+8])) + + if dataEnd > len(data) { + return 0, nil, errHandshakeMessageEOF + } + if dataEnd < dataStart { + return 0, nil, errors.New("invalid end offset in crypto message") + } + + resultMap[tag] = data[nPairs*8+dataStart : nPairs*8+dataEnd] + dataStart = dataEnd + } + + return messageTag, resultMap, nil +} + +// WriteHandshakeMessage writes a crypto message +func WriteHandshakeMessage(b *bytes.Buffer, messageTag Tag, data map[Tag][]byte) { + utils.WriteUint32(b, uint32(messageTag)) + utils.WriteUint16(b, uint16(len(data))) + utils.WriteUint16(b, 0) + + // Save current position in the buffer, so that we can update the index in-place later + indexStart := b.Len() + + indexData := make([]byte, 8*len(data)) + b.Write(indexData) // Will be updated later + + // Sort the tags + tags := make([]uint32, len(data)) + i := 0 + for t := range data { + tags[i] = uint32(t) + i++ + } + sort.Sort(utils.Uint32Slice(tags)) + + offset := uint32(0) + for i, t := range tags { + v := data[Tag(t)] + b.Write(v) + offset += uint32(len(v)) + binary.LittleEndian.PutUint32(indexData[i*8:], t) + binary.LittleEndian.PutUint32(indexData[i*8+4:], offset) + } + + // Now we write the index data for real + copy(b.Bytes()[indexStart:], indexData) +} diff --git a/crypto_stream_test.go b/handshake/handshake_message_test.go similarity index 71% rename from crypto_stream_test.go rename to handshake/handshake_message_test.go index 15be5833..7c771bfe 100644 --- a/crypto_stream_test.go +++ b/handshake/handshake_message_test.go @@ -1,4 +1,4 @@ -package quic +package handshake import ( "bytes" @@ -7,10 +7,10 @@ import ( . "github.com/onsi/gomega" ) -var _ = Describe("CryptoStream", func() { +var _ = Describe("Handshake Message", func() { Context("when parsing", func() { It("parses sample CHLO message", func() { - tag, msg, err := ParseCryptoMessage(sampleCHLO) + tag, msg, err := ParseHandshakeMessage(sampleCHLO) Expect(err).ToNot(HaveOccurred()) Expect(tag).To(Equal(TagCHLO)) Expect(msg).To(Equal(sampleCHLOMap)) @@ -20,7 +20,7 @@ var _ = Describe("CryptoStream", func() { Context("when writing", func() { It("writes sample message", func() { b := &bytes.Buffer{} - WriteCryptoMessage(b, TagCHLO, sampleCHLOMap) + WriteHandshakeMessage(b, TagCHLO, sampleCHLOMap) Expect(b.Bytes()).To(Equal(sampleCHLO)) }) }) diff --git a/handshake/handshake_suite_test.go b/handshake/handshake_suite_test.go new file mode 100644 index 00000000..74d9e7bb --- /dev/null +++ b/handshake/handshake_suite_test.go @@ -0,0 +1,13 @@ +package handshake + +import ( + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + + "testing" +) + +func TestQuicGo(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "Handshake Suite") +} diff --git a/crypto_stream.go b/handshake/tags.go similarity index 54% rename from crypto_stream.go rename to handshake/tags.go index 1d7f2677..1dbc821f 100644 --- a/crypto_stream.go +++ b/handshake/tags.go @@ -1,13 +1,4 @@ -package quic - -import ( - "bytes" - "encoding/binary" - "errors" - "sort" - - "github.com/lucas-clemente/quic-go/utils" -) +package handshake // A Tag in the QUIC crypto type Tag uint32 @@ -75,79 +66,3 @@ const ( // TagCERT is the CERT data TagCERT Tag = 0xff545243 ) - -var ( - errCryptoMessageEOF = errors.New("ParseCryptoMessage: Unexpected EOF") -) - -// ParseCryptoMessage reads a crypto message -func ParseCryptoMessage(data []byte) (Tag, map[Tag][]byte, error) { - if len(data) < 8 { - return 0, nil, errCryptoMessageEOF - } - - messageTag := Tag(binary.LittleEndian.Uint32(data[0:4])) - nPairs := int(binary.LittleEndian.Uint16(data[4:6])) - - data = data[8:] - - // We need space for at least nPairs * 8 bytes - if len(data) < int(nPairs)*8 { - return 0, nil, errCryptoMessageEOF - } - - resultMap := map[Tag][]byte{} - - dataStart := 0 - for indexPos := 0; indexPos < nPairs*8; indexPos += 8 { - // We know from the check above that data is long enough for the index - tag := Tag(binary.LittleEndian.Uint32(data[indexPos : indexPos+4])) - dataEnd := int(binary.LittleEndian.Uint32(data[indexPos+4 : indexPos+8])) - - if dataEnd > len(data) { - return 0, nil, errCryptoMessageEOF - } - if dataEnd < dataStart { - return 0, nil, errors.New("invalid end offset in crypto message") - } - - resultMap[tag] = data[nPairs*8+dataStart : nPairs*8+dataEnd] - dataStart = dataEnd - } - - return messageTag, resultMap, nil -} - -// WriteCryptoMessage writes a crypto message -func WriteCryptoMessage(b *bytes.Buffer, messageTag Tag, data map[Tag][]byte) { - utils.WriteUint32(b, uint32(messageTag)) - utils.WriteUint16(b, uint16(len(data))) - utils.WriteUint16(b, 0) - - // Save current position in the buffer, so that we can update the index in-place later - indexStart := b.Len() - - indexData := make([]byte, 8*len(data)) - b.Write(indexData) // Will be updated later - - // Sort the tags - tags := make([]uint32, len(data)) - i := 0 - for t := range data { - tags[i] = uint32(t) - i++ - } - sort.Sort(utils.Uint32Slice(tags)) - - offset := uint32(0) - for i, t := range tags { - v := data[Tag(t)] - b.Write(v) - offset += uint32(len(v)) - binary.LittleEndian.PutUint32(indexData[i*8:], t) - binary.LittleEndian.PutUint32(indexData[i*8+4:], offset) - } - - // Now we write the index data for real - copy(b.Bytes()[indexStart:], indexData) -} diff --git a/server_config.go b/server_config.go index f788fb3b..0d223c59 100644 --- a/server_config.go +++ b/server_config.go @@ -4,6 +4,7 @@ import ( "bytes" "github.com/lucas-clemente/quic-go/crypto" + "github.com/lucas-clemente/quic-go/handshake" ) // ServerConfig is a server config @@ -23,14 +24,14 @@ func NewServerConfig(kex crypto.KeyExchange, kd *crypto.KeyData) *ServerConfig { // Get the server config binary representation func (s *ServerConfig) Get() []byte { var serverConfig bytes.Buffer - WriteCryptoMessage(&serverConfig, TagSCFG, map[Tag][]byte{ - TagSCID: []byte{0xC5, 0x1C, 0x73, 0x6B, 0x8F, 0x48, 0x49, 0xAE, 0xB3, 0x00, 0xA2, 0xD4, 0x4B, 0xA0, 0xCF, 0xDF}, - TagKEXS: []byte("C255"), - TagAEAD: []byte("AESG"), - TagPUBS: append([]byte{0x20, 0x00, 0x00}, s.kex.PublicKey()...), - TagOBIT: []byte{0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7}, - TagEXPY: []byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, - TagVER: []byte("Q032"), + handshake.WriteHandshakeMessage(&serverConfig, handshake.TagSCFG, map[handshake.Tag][]byte{ + handshake.TagSCID: []byte{0xC5, 0x1C, 0x73, 0x6B, 0x8F, 0x48, 0x49, 0xAE, 0xB3, 0x00, 0xA2, 0xD4, 0x4B, 0xA0, 0xCF, 0xDF}, + handshake.TagKEXS: []byte("C255"), + handshake.TagAEAD: []byte("AESG"), + handshake.TagPUBS: append([]byte{0x20, 0x00, 0x00}, s.kex.PublicKey()...), + handshake.TagOBIT: []byte{0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7}, + handshake.TagEXPY: []byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, + handshake.TagVER: []byte("Q032"), }) return serverConfig.Bytes() } diff --git a/session.go b/session.go index 02e8eb50..64ea97d1 100644 --- a/session.go +++ b/session.go @@ -6,6 +6,7 @@ import ( "net" "github.com/lucas-clemente/quic-go/crypto" + "github.com/lucas-clemente/quic-go/handshake" "github.com/lucas-clemente/quic-go/protocol" ) @@ -60,17 +61,17 @@ func (s *Session) HandlePacket(addr *net.UDPAddr, publicHeaderBinary []byte, pub panic("streamid not 1") } - messageTag, cryptoData, err := ParseCryptoMessage(frame.Data) + messageTag, cryptoData, err := handshake.ParseHandshakeMessage(frame.Data) if err != nil { panic(err) } // TODO: Switch client messages here - if messageTag != TagCHLO { + if messageTag != handshake.TagCHLO { panic("expected CHLO") } - if _, ok := cryptoData[TagPUBS]; ok { + if _, ok := cryptoData[handshake.TagPUBS]; ok { panic("received CHLO with PUBS") } @@ -79,10 +80,10 @@ func (s *Session) HandlePacket(addr *net.UDPAddr, publicHeaderBinary []byte, pub return err } var serverReply bytes.Buffer - WriteCryptoMessage(&serverReply, TagREJ, map[Tag][]byte{ - TagSCFG: s.ServerConfig.Get(), - TagCERT: s.ServerConfig.GetCertData(), - TagPROF: proof, + handshake.WriteHandshakeMessage(&serverReply, handshake.TagREJ, map[handshake.Tag][]byte{ + handshake.TagSCFG: s.ServerConfig.Get(), + handshake.TagCERT: s.ServerConfig.GetCertData(), + handshake.TagPROF: proof, }) s.SendFrames([]Frame{