move crypto handshake stuff to its own package

This commit is contained in:
Lucas Clemente 2016-04-14 19:50:04 +02:00
parent 04921c29af
commit 0febba87ba
7 changed files with 122 additions and 106 deletions

View file

@ -1,4 +1,4 @@
package quic
package handshake
import "strings"

View file

@ -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)
}

View file

@ -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))
})
})

View file

@ -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")
}

View file

@ -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)
}

View file

@ -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()
}

View file

@ -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{