forked from quic-go/quic-go
implement parsing of sample CHLO crypto stream
This commit is contained in:
89
crypto_stream.go
Normal file
89
crypto_stream.go
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
package quic
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
"errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
// A Tag in the QUIC crypto
|
||||||
|
type Tag uint32
|
||||||
|
|
||||||
|
const (
|
||||||
|
// TagCHLO is a client hello
|
||||||
|
TagCHLO Tag = 'C' + 'H'<<8 + 'L'<<16 + 'O'<<24
|
||||||
|
|
||||||
|
// TagPAD is padding
|
||||||
|
TagPAD Tag = 'P' + 'A'<<8 + 'D'<<16
|
||||||
|
// TagSNI is the server name indication
|
||||||
|
TagSNI Tag = 'S' + 'N'<<8 + 'I'<<16
|
||||||
|
// TagVER is the QUIC version
|
||||||
|
TagVER Tag = 'V' + 'E'<<8 + 'R'<<16
|
||||||
|
// TagCCS is the hash of the common certificate sets
|
||||||
|
TagCCS Tag = 'C' + 'C'<<8 + 'S'<<16
|
||||||
|
// TagMSPC is max streams per connection
|
||||||
|
TagMSPC Tag = 'M' + 'S'<<8 + 'P'<<16 + 'C'<<24
|
||||||
|
// TagUAID is the user agent ID
|
||||||
|
TagUAID Tag = 'U' + 'A'<<8 + 'I'<<16 + 'D'<<24
|
||||||
|
// TagTCID is truncation of the connection ID
|
||||||
|
TagTCID Tag = 'T' + 'C'<<8 + 'I'<<16 + 'D'<<24
|
||||||
|
// TagPDMD is the proof demand
|
||||||
|
TagPDMD Tag = 'P' + 'D'<<8 + 'M'<<16 + 'D'<<24
|
||||||
|
// TagSRBF is the socket receive buffer
|
||||||
|
TagSRBF Tag = 'S' + 'R'<<8 + 'B'<<16 + 'F'<<24
|
||||||
|
// TagICSL is the idle connection state lifetime
|
||||||
|
TagICSL Tag = 'I' + 'C'<<8 + 'S'<<16 + 'L'<<24
|
||||||
|
// TagNONP is the client proof nonce
|
||||||
|
TagNONP Tag = 'N' + 'O'<<8 + 'N'<<16 + 'P'<<24
|
||||||
|
// TagSCLS is the silently close timeout
|
||||||
|
TagSCLS Tag = 'S' + 'C'<<8 + 'L'<<16 + 'S'<<24
|
||||||
|
// TagCSCT is the signed cert timestamp (RFC6962) of leaf cert
|
||||||
|
TagCSCT Tag = 'C' + 'S'<<8 + 'C'<<16 + 'T'<<24
|
||||||
|
// TagCOPT are the connection options
|
||||||
|
TagCOPT Tag = 'C' + 'O'<<8 + 'P'<<16 + 'T'<<24
|
||||||
|
// TagCFCW is the initial session/connection flow control receive window
|
||||||
|
TagCFCW Tag = 'C' + 'F'<<8 + 'C'<<16 + 'W'<<24
|
||||||
|
// TagSFCW is the initial stream flow control receive window.
|
||||||
|
TagSFCW Tag = 'S' + 'F'<<8 + 'C'<<16 + 'W'<<24
|
||||||
|
)
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
36
crypto_stream_test.go
Normal file
36
crypto_stream_test.go
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
package quic
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
. "github.com/onsi/ginkgo"
|
||||||
|
. "github.com/onsi/gomega"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ = Describe("CryptoStream", func() {
|
||||||
|
Context("when parsing", func() {
|
||||||
|
It("parses sample CHLO message", func() {
|
||||||
|
tag, msg, err := ParseCryptoMessage(sampleCHLO)
|
||||||
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
Expect(tag).To(Equal(TagCHLO))
|
||||||
|
Expect(msg).To(Equal(map[Tag][]byte{
|
||||||
|
TagPAD: []byte(strings.Repeat("-", 1016)),
|
||||||
|
TagSNI: []byte("www.example.org"),
|
||||||
|
TagVER: []byte("Q030"),
|
||||||
|
TagCCS: []byte("{&\xe9\xe7\xe4\\q\xff\x01\xe8\x81`\x92\x92\x1a\xe8"),
|
||||||
|
TagMSPC: []byte("d\x00\x00\x00"),
|
||||||
|
TagUAID: []byte("dev Chrome/51.0.2700.0 Intel Mac OS X 10_11_4"),
|
||||||
|
TagTCID: []byte("\x00\x00\x00\x00"),
|
||||||
|
TagSRBF: []byte("\x00\x00\x10\x00"),
|
||||||
|
TagICSL: []byte("\x1e\x00\x00\x00"),
|
||||||
|
TagNONP: []byte("\xe1\x84T\x1b\xe3\xd6|\x1fi\xb2N\x9eF\xf4Fݫ\xe5\xdef\x94\xf6\xb2\xee\x01ĥw\xfe\xc9\v\xa3"),
|
||||||
|
TagSCLS: []byte("\x01\x00\x00\x00"),
|
||||||
|
TagCSCT: []byte{},
|
||||||
|
TagCOPT: []byte("FIXD"),
|
||||||
|
TagSFCW: []byte("\x00\x00`\x00"),
|
||||||
|
TagCFCW: []byte("\x00\x00\xf0\x00"),
|
||||||
|
TagPDMD: []byte("X509"),
|
||||||
|
}))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
3
data_test.go
Normal file
3
data_test.go
Normal file
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user