diff --git a/dtls.go b/dtls.go index aa914e3..c9190cd 100644 --- a/dtls.go +++ b/dtls.go @@ -2,7 +2,7 @@ package mint import ( "fmt" - "github.com/bifurcation/mint/syntax" + "github.com/cisco/go-tls-syntax" "time" ) diff --git a/extensions.go b/extensions.go index 07cb16c..325dde0 100644 --- a/extensions.go +++ b/extensions.go @@ -3,7 +3,7 @@ package mint import ( "bytes" "fmt" - "github.com/bifurcation/mint/syntax" + "github.com/cisco/go-tls-syntax" ) type ExtensionBody interface { diff --git a/handshake-messages.go b/handshake-messages.go index e8e3aef..ef5550d 100644 --- a/handshake-messages.go +++ b/handshake-messages.go @@ -7,7 +7,7 @@ import ( "encoding/binary" "fmt" - "github.com/bifurcation/mint/syntax" + "github.com/cisco/go-tls-syntax" ) type HandshakeMessageBody interface { diff --git a/server-state-machine.go b/server-state-machine.go index e392a5d..57527e4 100644 --- a/server-state-machine.go +++ b/server-state-machine.go @@ -7,7 +7,7 @@ import ( "hash" "reflect" - "github.com/bifurcation/mint/syntax" + "github.com/cisco/go-tls-syntax" ) // Server State Machine diff --git a/syntax/README.md b/syntax/README.md deleted file mode 100644 index 537b9b4..0000000 --- a/syntax/README.md +++ /dev/null @@ -1,84 +0,0 @@ -TLS Syntax -========== - -TLS defines [its own syntax](https://tlswg.github.io/tls13-spec/#rfc.section.3) -for describing structures used in that protocol. To facilitate experimentation -with TLS in Go, this module maps that syntax to the Go structure syntax, taking -advantage of Go's type annotations to encode non-type information carried in the -TLS presentation format. - -For example, in the TLS specification, a ClientHello message has the following -structure: - -~~~~~ -uint16 ProtocolVersion; -opaque Random[32]; -uint8 CipherSuite[2]; -enum { ... (65535)} ExtensionType; - -struct { - ExtensionType extension_type; - opaque extension_data<0..2^16-1>; -} Extension; - -struct { - ProtocolVersion legacy_version = 0x0303; /* TLS v1.2 */ - Random random; - opaque legacy_session_id<0..32>; - CipherSuite cipher_suites<2..2^16-2>; - opaque legacy_compression_methods<1..2^8-1>; - Extension extensions<0..2^16-1>; -} ClientHello; -~~~~~ - -This maps to the following Go type definitions: - -~~~~~ -type protocolVersion uint16 -type random [32]byte -type cipherSuite uint16 // or [2]byte -type extensionType uint16 - -type extension struct { - ExtensionType extensionType - ExtensionData []byte `tls:"head=2"` -} - -type clientHello struct { - LegacyVersion protocolVersion - Random random - LegacySessionID []byte `tls:"head=1,max=32"` - CipherSuites []cipherSuite `tls:"head=2,min=2"` - LegacyCompressionMethods []byte `tls:"head=1,min=1"` - Extensions []extension `tls:"head=2"` -} -~~~~~ - -Then you can just declare, marshal, and unmarshal structs just like you would -with, say JSON. - -The available annotations right now are all related to vectors: - -* `head`: The number of bytes of length to use as a "header" -* `min`: The minimum length of the vector, in bytes -* `max`: The maximum length of the vector, in bytes - -## Not supported - -* The `select()` syntax for creating alternate version of the same struct (see, - e.g., the KeyShare extension) - -* The backreference syntax for array lengths or select parameters, as in `opaque - fragment[TLSPlaintext.length]`. Note, however, that in cases where the length - immediately preceds the array, these can be reframed as vectors with - appropriate sizes. - - -QUIC Extensions Syntax -====================== -syntax also supports some minor extensions to allow implementing QUIC. - -* The `varint` annotation describes a QUIC-style varint -* `head=none` means no header, i.e., the bytes are encoded directly on the wire. - On reading, the decoder will consume all available data. -* `head=varint` means to encode the header as a varint diff --git a/syntax/decode.go b/syntax/decode.go deleted file mode 100644 index 7cedc3c..0000000 --- a/syntax/decode.go +++ /dev/null @@ -1,437 +0,0 @@ -package syntax - -import ( - "bytes" - "fmt" - "reflect" - "runtime" -) - -func Unmarshal(data []byte, v interface{}) (int, error) { - // Check for well-formedness. - // Avoids filling out half a data structure - // before discovering a JSON syntax error. - d := decodeState{} - d.Write(data) - return d.unmarshal(v) -} - -// Unmarshaler is the interface implemented by types that can -// unmarshal a TLS description of themselves. Note that unlike the -// JSON unmarshaler interface, it is not known a priori how much of -// the input data will be consumed. So the Unmarshaler must state -// how much of the input data it consumed. -type Unmarshaler interface { - UnmarshalTLS([]byte) (int, error) -} - -type decodeState struct { - bytes.Buffer -} - -func (d *decodeState) unmarshal(v interface{}) (read int, err error) { - defer func() { - if r := recover(); r != nil { - if _, ok := r.(runtime.Error); ok { - panic(r) - } - if s, ok := r.(string); ok { - panic(s) - } - err = r.(error) - } - }() - - rv := reflect.ValueOf(v) - if rv.Kind() != reflect.Ptr || rv.IsNil() { - return 0, fmt.Errorf("Invalid unmarshal target (non-pointer or nil)") - } - - read = d.value(rv) - return read, nil -} - -func (e *decodeState) value(v reflect.Value) int { - return valueDecoder(v)(e, v, fieldOptions{}) -} - -type decoderFunc func(e *decodeState, v reflect.Value, opts fieldOptions) int - -func valueDecoder(v reflect.Value) decoderFunc { - return typeDecoder(v.Type().Elem()) -} - -func typeDecoder(t reflect.Type) decoderFunc { - // Note: Omits the caching / wait-group things that encoding/json uses - return newTypeDecoder(t) -} - -var ( - unmarshalerType = reflect.TypeOf(new(Unmarshaler)).Elem() -) - -func newTypeDecoder(t reflect.Type) decoderFunc { - var dec decoderFunc - if t.Kind() != reflect.Ptr && reflect.PtrTo(t).Implements(unmarshalerType) { - dec = unmarshalerDecoder - } else { - switch t.Kind() { - case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: - dec = uintDecoder - case reflect.Array: - dec = newArrayDecoder(t) - case reflect.Slice: - dec = newSliceDecoder(t) - case reflect.Map: - dec = newMapDecoder(t) - case reflect.Struct: - dec = newStructDecoder(t) - case reflect.Ptr: - dec = newPointerDecoder(t) - default: - panic(fmt.Errorf("Unsupported type (%s)", t)) - } - } - - if reflect.PtrTo(t).Implements(validatorType) { - dec = newValidatorDecoder(dec) - } - - return dec -} - -///// Specific decoders below - -func omitDecoder(d *decodeState, v reflect.Value, opts fieldOptions) int { - return 0 -} - -////////// - -func unmarshalerDecoder(d *decodeState, v reflect.Value, opts fieldOptions) int { - um, ok := v.Interface().(Unmarshaler) - if !ok { - panic(fmt.Errorf("Non-Unmarshaler passed to unmarshalerEncoder")) - } - - read, err := um.UnmarshalTLS(d.Bytes()) - if err != nil { - panic(err) - } - - if read > d.Len() { - panic(fmt.Errorf("Invalid return value from UnmarshalTLS")) - } - - d.Next(read) - return read -} - -////////// - -func newValidatorDecoder(raw decoderFunc) decoderFunc { - return func(d *decodeState, v reflect.Value, opts fieldOptions) int { - read := raw(d, v, opts) - - val, ok := v.Interface().(Validator) - if !ok { - panic(fmt.Errorf("Non-Validator passed to validatorDecoder")) - } - - if err := val.ValidForTLS(); err != nil { - panic(fmt.Errorf("Decoded invalid TLS value: %v", err)) - } - - return read - } -} - -////////// - -func uintDecoder(d *decodeState, v reflect.Value, opts fieldOptions) int { - if opts.varint { - return varintDecoder(d, v, opts) - } - - uintLen := int(v.Elem().Type().Size()) - buf := d.Next(uintLen) - if len(buf) != uintLen { - panic(fmt.Errorf("Insufficient data to read uint")) - } - - return setUintFromBuffer(v, buf) -} - -func varintDecoder(d *decodeState, v reflect.Value, opts fieldOptions) int { - l, val := readVarint(d) - - uintLen := int(v.Elem().Type().Size()) - if uintLen < l { - panic(fmt.Errorf("Uint too small to fit varint: %d < %d", uintLen, l)) - } - - v.Elem().SetUint(val) - - return l -} - -func readVarint(d *decodeState) (int, uint64) { - // Read the first octet and decide the size of the presented varint - first := d.Next(1) - if len(first) != 1 { - panic(fmt.Errorf("Insufficient data to read varint length")) - } - - twoBits := uint(first[0] >> 6) - varintLen := 1 << twoBits - - rest := d.Next(varintLen - 1) - if len(rest) != varintLen-1 { - panic(fmt.Errorf("Insufficient data to read varint")) - } - - buf := append(first, rest...) - buf[0] &= 0x3f - - return len(buf), decodeUintFromBuffer(buf) -} - -func decodeUintFromBuffer(buf []byte) uint64 { - val := uint64(0) - for _, b := range buf { - val = (val << 8) + uint64(b) - } - - return val -} - -func setUintFromBuffer(v reflect.Value, buf []byte) int { - v.Elem().SetUint(decodeUintFromBuffer(buf)) - return len(buf) -} - -////////// - -type arrayDecoder struct { - elemDec decoderFunc -} - -func (ad *arrayDecoder) decode(d *decodeState, v reflect.Value, opts fieldOptions) int { - n := v.Elem().Type().Len() - read := 0 - for i := 0; i < n; i += 1 { - read += ad.elemDec(d, v.Elem().Index(i).Addr(), opts) - } - return read -} - -func newArrayDecoder(t reflect.Type) decoderFunc { - dec := &arrayDecoder{typeDecoder(t.Elem())} - return dec.decode -} - -////////// - -func decodeLength(d *decodeState, opts fieldOptions) (int, int) { - read := 0 - length := 0 - switch { - case opts.omitHeader: - read = 0 - length = d.Len() - - case opts.varintHeader: - var length64 uint64 - read, length64 = readVarint(d) - length = int(length64) - - case opts.headerSize > 0: - lengthBytes := d.Next(int(opts.headerSize)) - if len(lengthBytes) != int(opts.headerSize) { - panic(fmt.Errorf("Not enough data to read header")) - } - read = len(lengthBytes) - length = int(decodeUintFromBuffer(lengthBytes)) - - default: - panic(fmt.Errorf("Cannot decode a slice without a header length")) - } - - // Check that the length is OK - if opts.maxSize > 0 && length > opts.maxSize { - panic(fmt.Errorf("Length of vector exceeds declared max")) - } - if length < opts.minSize { - panic(fmt.Errorf("Length of vector below declared min")) - } - - return read, length -} - -////////// - -type sliceDecoder struct { - elementType reflect.Type - elementDec decoderFunc -} - -func (sd *sliceDecoder) decode(d *decodeState, v reflect.Value, opts fieldOptions) int { - // Determine the length of the vector - read, length := decodeLength(d, opts) - - // Decode elements - elemData := d.Next(length) - if len(elemData) != length { - panic(fmt.Errorf("Not enough data to read elements")) - } - - elemBuf := &decodeState{} - elemBuf.Write(elemData) - elems := []reflect.Value{} - for elemBuf.Len() > 0 { - elem := reflect.New(sd.elementType) - read += sd.elementDec(elemBuf, elem, opts) - elems = append(elems, elem) - } - - v.Elem().Set(reflect.MakeSlice(v.Elem().Type(), len(elems), len(elems))) - for i := 0; i < len(elems); i += 1 { - v.Elem().Index(i).Set(elems[i].Elem()) - } - return read -} - -func newSliceDecoder(t reflect.Type) decoderFunc { - dec := &sliceDecoder{ - elementType: t.Elem(), - elementDec: typeDecoder(t.Elem()), - } - return dec.decode -} - -////////// - -type mapDecoder struct { - keyType reflect.Type - valType reflect.Type - keyDec decoderFunc - valDec decoderFunc -} - -func (md mapDecoder) decode(d *decodeState, v reflect.Value, opts fieldOptions) int { - // Determine the length of the data - read, length := decodeLength(d, opts) - - // Decode key/value pairs - elemData := d.Next(length) - if len(elemData) != length { - panic(fmt.Errorf("Not enough data to read elements")) - } - - mapType := reflect.MapOf(md.keyType, md.valType) - v.Elem().Set(reflect.MakeMap(mapType)) - - nullOpts := fieldOptions{} - elemBuf := &decodeState{} - elemBuf.Write(elemData) - for elemBuf.Len() > 0 { - key := reflect.New(md.keyType) - read += md.keyDec(elemBuf, key, nullOpts) - - val := reflect.New(md.valType) - read += md.valDec(elemBuf, val, nullOpts) - - v.Elem().SetMapIndex(key.Elem(), val.Elem()) - } - - return read -} - -func newMapDecoder(t reflect.Type) decoderFunc { - md := mapDecoder{ - keyType: t.Key(), - valType: t.Elem(), - keyDec: typeDecoder(t.Key()), - valDec: typeDecoder(t.Elem()), - } - - return md.decode -} - -////////// - -type structDecoder struct { - fieldOpts []fieldOptions - fieldDecs []decoderFunc -} - -func (sd *structDecoder) decode(d *decodeState, v reflect.Value, opts fieldOptions) int { - read := 0 - for i := range sd.fieldDecs { - read += sd.fieldDecs[i](d, v.Elem().Field(i).Addr(), sd.fieldOpts[i]) - } - return read -} - -func newStructDecoder(t reflect.Type) decoderFunc { - n := t.NumField() - sd := structDecoder{ - fieldOpts: make([]fieldOptions, n), - fieldDecs: make([]decoderFunc, n), - } - - for i := 0; i < n; i += 1 { - f := t.Field(i) - - tag := f.Tag.Get("tls") - opts := parseTag(tag) - - if !opts.ValidForType(f.Type) { - panic(fmt.Errorf("Tags invalid for field type")) - } - - sd.fieldOpts[i] = opts - if sd.fieldOpts[i].omit { - sd.fieldDecs[i] = omitDecoder - } else { - sd.fieldDecs[i] = typeDecoder(f.Type) - } - } - - return sd.decode -} - -////////// - -type pointerDecoder struct { - base decoderFunc -} - -func (pd *pointerDecoder) decode(d *decodeState, v reflect.Value, opts fieldOptions) int { - readBase := 0 - if opts.optional { - readBase = 1 - flag := d.Next(1) - switch flag[0] { - case optionalFlagAbsent: - indir := v.Elem() - indir.Set(reflect.Zero(indir.Type())) - return 1 - - case optionalFlagPresent: - // No action; continue as normal - - default: - panic(fmt.Errorf("Invalid flag byte for optional: [%x]", flag)) - } - } - - v.Elem().Set(reflect.New(v.Elem().Type().Elem())) - return readBase + pd.base(d, v.Elem(), opts) -} - -func newPointerDecoder(t reflect.Type) decoderFunc { - baseDecoder := typeDecoder(t.Elem()) - pd := pointerDecoder{base: baseDecoder} - return pd.decode -} diff --git a/syntax/decode_test.go b/syntax/decode_test.go deleted file mode 100644 index 86170c7..0000000 --- a/syntax/decode_test.go +++ /dev/null @@ -1,152 +0,0 @@ -package syntax - -import ( - "reflect" - "testing" -) - -func TestDecodeUnsupported(t *testing.T) { - dummyBuffer := []byte(nil) - - var yi int - read, err := Unmarshal(dummyBuffer, yi) - if err == nil || read != 0 { - t.Fatalf("Agreed to unmarshal to a non-pointer") - } - - read, err = Unmarshal(dummyBuffer, nil) - if err == nil || read != 0 { - t.Fatalf("Agreed to unmarshal to a nil pointer") - } -} - -func TestDecodeErrors(t *testing.T) { - vector0x20 := append([]byte{0x20}, buffer(0x20)...) - errorCases := map[string]struct { - template interface{} - encoding []byte - }{ - "unsupported": { - template: float64(0), - encoding: buffer(0), - }, - - "uint-too-small": { - template: uint32(0), - encoding: unhex("7fff"), - }, - - "varint-too-big": { - template: struct { - V uint8 `tls:"varint"` - }{}, - encoding: unhex("7fff"), - }, - - // Slice errors - "no-head": { - template: struct{ V []byte }{}, - encoding: buffer(0), - }, - - "overflow": { - template: struct { - V []byte `tls:"head=1,max=31"` - }{}, - encoding: vector0x20, - }, - - "overflow-no-head": { - template: struct { - V []byte `tls:"head=none,max=31"` - }{}, - encoding: buffer(32), - }, - - "underflow": { - template: struct { - V []byte `tls:"head=1,min=33"` - }{}, - encoding: vector0x20, - }, - - "underflow-no-head": { - template: struct { - V []byte `tls:"head=none,min=33"` - }{}, - encoding: buffer(32), - }, - - "too-short-for-head": { - template: struct { - V []byte `tls:"head=3"` - }{}, - encoding: vector0x20[:2], - }, - - "too-short-for-value": { - template: struct { - V []byte `tls:"head=3"` - }{}, - encoding: vector0x20, - }, - - "too-short-for-varint-head-length": { - template: struct { - V []byte `tls:"head=varint"` - }{}, - encoding: vector0x20[:0], - }, - - "too-short-for-varint-head": { - template: struct { - V []byte `tls:"head=varint"` - }{}, - encoding: unhex("40"), - }, - - "invalid-head-tag": { - template: struct { - V int `tls:"head=2"` - }{V: 0}, - encoding: unhex(""), - }, - - "invalid-varint-tag": { - template: struct { - V struct{} `tls:"varint"` - }{V: struct{}{}}, - encoding: unhex(""), - }, - - "invalid-optional-tag": { - template: struct { - V int `tls:"optional"` - }{V: 0}, - encoding: unhex(""), - }, - - // Optional errors - "invalid-optional-flag": { - template: struct { - V *uint8 `tls:"optional"` - }{}, - encoding: unhex("0203"), - }, - - // Validator errors - "invalid-validator": { - template: CrypticString(""), - encoding: unhex("056069677b6e"), - }, - } - - for label, testCase := range errorCases { - decodedPointer := reflect.New(reflect.TypeOf(testCase.template)) - read, err := Unmarshal(testCase.encoding, decodedPointer.Interface()) - t.Logf("[%s] -> [%v]", label, err) - if err == nil || read > 0 { - t.Fatalf("Incorrectly allowed unmarshal [%s]: %v", label, err) - } - } -} diff --git a/syntax/encode.go b/syntax/encode.go deleted file mode 100644 index 55a0899..0000000 --- a/syntax/encode.go +++ /dev/null @@ -1,402 +0,0 @@ -package syntax - -import ( - "bytes" - "fmt" - "reflect" - "runtime" - "sort" -) - -func Marshal(v interface{}) ([]byte, error) { - e := &encodeState{} - err := e.marshal(v, fieldOptions{}) - if err != nil { - return nil, err - } - return e.Bytes(), nil -} - -// Marshaler is the interface implemented by types that -// have a defined TLS encoding. -type Marshaler interface { - MarshalTLS() ([]byte, error) -} - -type encodeState struct { - bytes.Buffer -} - -func (e *encodeState) marshal(v interface{}, opts fieldOptions) (err error) { - defer func() { - if r := recover(); r != nil { - if _, ok := r.(runtime.Error); ok { - panic(r) - } - if s, ok := r.(string); ok { - panic(s) - } - err = r.(error) - } - }() - e.reflectValue(reflect.ValueOf(v), opts) - return nil -} - -func (e *encodeState) reflectValue(v reflect.Value, opts fieldOptions) { - valueEncoder(v)(e, v, opts) -} - -type encoderFunc func(e *encodeState, v reflect.Value, opts fieldOptions) - -func valueEncoder(v reflect.Value) encoderFunc { - if !v.IsValid() { - panic(fmt.Errorf("Cannot encode an invalid value")) - } - return typeEncoder(v.Type()) -} - -func typeEncoder(t reflect.Type) encoderFunc { - // Note: Omits the caching / wait-group things that encoding/json uses - return newTypeEncoder(t) -} - -var ( - marshalerType = reflect.TypeOf(new(Marshaler)).Elem() -) - -func newTypeEncoder(t reflect.Type) encoderFunc { - var enc encoderFunc - if t.Implements(marshalerType) { - enc = marshalerEncoder - } else { - switch t.Kind() { - case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: - enc = uintEncoder - case reflect.Array: - enc = newArrayEncoder(t) - case reflect.Slice: - enc = newSliceEncoder(t) - case reflect.Struct: - enc = newStructEncoder(t) - case reflect.Map: - enc = newMapEncoder(t) - case reflect.Ptr: - enc = newPointerEncoder(t) - default: - panic(fmt.Errorf("Unsupported type (%s)", t)) - } - } - - if t.Implements(validatorType) { - enc = newValidatorEncoder(enc) - } - - return enc -} - -///// Specific encoders below - -func omitEncoder(e *encodeState, v reflect.Value, opts fieldOptions) { - // This space intentionally left blank -} - -////////// - -func marshalerEncoder(e *encodeState, v reflect.Value, opts fieldOptions) { - if v.Kind() == reflect.Ptr && v.IsNil() && !opts.optional { - panic(fmt.Errorf("Cannot encode nil pointer")) - } - - if v.Kind() == reflect.Ptr && opts.optional { - if v.IsNil() { - writeUint(e, uint64(optionalFlagAbsent), 1) - return - } - - writeUint(e, uint64(optionalFlagPresent), 1) - } - - m, ok := v.Interface().(Marshaler) - if !ok { - panic(fmt.Errorf("Non-Marshaler passed to marshalerEncoder")) - } - - b, err := m.MarshalTLS() - if err == nil { - _, err = e.Write(b) - } - - if err != nil { - panic(err) - } -} - -////////// - -func newValidatorEncoder(raw encoderFunc) encoderFunc { - return func(e *encodeState, v reflect.Value, opts fieldOptions) { - if v.Kind() == reflect.Ptr && v.IsNil() { - // Cannot validate nil values; just pass through to encoder - raw(e, v, opts) - return - } - - val, ok := v.Interface().(Validator) - if !ok { - panic(fmt.Errorf("Non-Validator passed to validatorEncoder")) - } - - if err := val.ValidForTLS(); err != nil { - panic(fmt.Errorf("Invalid TLS value: %v", err)) - } - - raw(e, v, opts) - } -} - -////////// - -func uintEncoder(e *encodeState, v reflect.Value, opts fieldOptions) { - if opts.varint { - varintEncoder(e, v, opts) - return - } - - writeUint(e, v.Uint(), int(v.Type().Size())) -} - -func varintEncoder(e *encodeState, v reflect.Value, opts fieldOptions) { - writeVarint(e, v.Uint()) -} - -func writeVarint(e *encodeState, u uint64) { - if (u >> 62) > 0 { - panic(fmt.Errorf("uint value is too big for varint")) - } - - var varintLen int - for _, len := range []uint{1, 2, 4, 8} { - if u < (uint64(1) << (8*len - 2)) { - varintLen = int(len) - break - } - } - - twoBits := map[int]uint64{1: 0x00, 2: 0x01, 4: 0x02, 8: 0x03}[varintLen] - shift := uint(8*varintLen - 2) - writeUint(e, u|(twoBits<> uint(8*(len-i-1))) - } - e.Write(data) -} - -////////// - -type arrayEncoder struct { - elemEnc encoderFunc -} - -func (ae *arrayEncoder) encode(e *encodeState, v reflect.Value, opts fieldOptions) { - n := v.Len() - for i := 0; i < n; i += 1 { - ae.elemEnc(e, v.Index(i), opts) - } -} - -func newArrayEncoder(t reflect.Type) encoderFunc { - enc := &arrayEncoder{typeEncoder(t.Elem())} - return enc.encode -} - -////////// - -func encodeLength(e *encodeState, n int, opts fieldOptions) { - if opts.maxSize > 0 && n > opts.maxSize { - panic(fmt.Errorf("Encoded length more than max [%d > %d]", n, opts.maxSize)) - } - if n < opts.minSize { - panic(fmt.Errorf("Encoded length less than min [%d < %d]", n, opts.minSize)) - } - - switch { - case opts.omitHeader: - // None. - - case opts.varintHeader: - writeVarint(e, uint64(n)) - - case opts.headerSize > 0: - if n>>uint(8*opts.headerSize) > 0 { - panic(fmt.Errorf("Encoded length too long for header length [%d, %d]", n, opts.headerSize)) - } - - writeUint(e, uint64(n), int(opts.headerSize)) - - default: - panic(fmt.Errorf("Cannot encode a slice without a header length")) - } -} - -////////// - -type sliceEncoder struct { - ae *arrayEncoder -} - -func (se *sliceEncoder) encode(e *encodeState, v reflect.Value, opts fieldOptions) { - arrayState := &encodeState{} - se.ae.encode(arrayState, v, opts) - - encodeLength(e, arrayState.Len(), opts) - e.Write(arrayState.Bytes()) -} - -func newSliceEncoder(t reflect.Type) encoderFunc { - enc := &sliceEncoder{&arrayEncoder{typeEncoder(t.Elem())}} - return enc.encode -} - -////////// - -type structEncoder struct { - fieldOpts []fieldOptions - fieldEncs []encoderFunc -} - -func (se *structEncoder) encode(e *encodeState, v reflect.Value, opts fieldOptions) { - for i := range se.fieldEncs { - se.fieldEncs[i](e, v.Field(i), se.fieldOpts[i]) - } -} - -func newStructEncoder(t reflect.Type) encoderFunc { - n := t.NumField() - se := structEncoder{ - fieldOpts: make([]fieldOptions, n), - fieldEncs: make([]encoderFunc, n), - } - - for i := 0; i < n; i += 1 { - f := t.Field(i) - tag := f.Tag.Get("tls") - opts := parseTag(tag) - - if !opts.ValidForType(f.Type) { - panic(fmt.Errorf("Tags invalid for field type")) - } - - se.fieldOpts[i] = opts - if opts.omit { - se.fieldEncs[i] = omitEncoder - } else { - se.fieldEncs[i] = typeEncoder(f.Type) - } - } - - return se.encode -} - -////////// - -type mapEncoder struct { - keyEnc encoderFunc - valEnc encoderFunc -} - -type encMap struct { - keyEncs [][]byte - valEncs [][]byte -} - -func (em encMap) Len() int { return len(em.keyEncs) } - -func (em *encMap) Swap(i, j int) { - em.keyEncs[i], em.keyEncs[j] = em.keyEncs[j], em.keyEncs[i] - em.valEncs[i], em.valEncs[j] = em.valEncs[j], em.valEncs[i] -} - -func (em encMap) Less(i, j int) bool { - return bytes.Compare(em.keyEncs[i], em.keyEncs[j]) < 0 -} - -func (em encMap) Size() int { - size := 0 - for i := range em.keyEncs { - size += len(em.keyEncs[i]) + len(em.valEncs[i]) - } - return size -} - -func (em encMap) Encode(e *encodeState) { - for i := range em.keyEncs { - e.Write(em.keyEncs[i]) - e.Write(em.valEncs[i]) - } -} - -func (me *mapEncoder) encode(e *encodeState, v reflect.Value, opts fieldOptions) { - enc := &encMap{ - keyEncs: make([][]byte, v.Len()), - valEncs: make([][]byte, v.Len()), - } - nullOpts := fieldOptions{} - it := v.MapRange() - for i := 0; i < enc.Len() && it.Next(); i++ { - keyState := &encodeState{} - me.keyEnc(keyState, it.Key(), nullOpts) - enc.keyEncs[i] = keyState.Bytes() - - valState := &encodeState{} - me.valEnc(valState, it.Value(), nullOpts) - enc.valEncs[i] = valState.Bytes() - } - - sort.Sort(enc) - - encodeLength(e, enc.Size(), opts) - enc.Encode(e) -} - -func newMapEncoder(t reflect.Type) encoderFunc { - me := mapEncoder{ - keyEnc: typeEncoder(t.Key()), - valEnc: typeEncoder(t.Elem()), - } - - return me.encode -} - -////////// - -type pointerEncoder struct { - base encoderFunc -} - -func (pe pointerEncoder) encode(e *encodeState, v reflect.Value, opts fieldOptions) { - if v.IsNil() && !opts.optional { - panic(fmt.Errorf("Cannot encode nil pointer")) - } - - if opts.optional { - if v.IsNil() { - writeUint(e, uint64(optionalFlagAbsent), 1) - return - } - - writeUint(e, uint64(optionalFlagPresent), 1) - } - - pe.base(e, v.Elem(), opts) -} - -func newPointerEncoder(t reflect.Type) encoderFunc { - baseEncoder := typeEncoder(t.Elem()) - pe := pointerEncoder{base: baseEncoder} - return pe.encode -} diff --git a/syntax/encode_test.go b/syntax/encode_test.go deleted file mode 100644 index ffed7d6..0000000 --- a/syntax/encode_test.go +++ /dev/null @@ -1,56 +0,0 @@ -package syntax - -import ( - "strings" - "testing" -) - -func TestEncodeErrors(t *testing.T) { - errorCases := map[string]interface{}{ - "unsupported": float64(0), - - "varint-too-big": struct { - V uint64 `tls:"varint"` - }{V: uint64(1) << 63}, - - "no-head": struct { - V []byte - }{V: buffer(0x20)}, - - "head-too-short": struct { - V []byte `tls:"head=1"` - }{V: buffer(0x100)}, - - "overflow": struct { - V []byte `tls:"head=1,max=31"` - }{V: buffer(0x20)}, - - "underflow": struct { - V []byte `tls:"head=1,min=33"` - }{V: buffer(0x20)}, - - "nil": struct{ V *uint8 }{V: nil}, - - "invalid-head-tag": struct { - V int `tls:"head=2"` - }{V: 0}, - - "invalid-varint-tag": struct { - V struct{} `tls:"varint"` - }{V: struct{}{}}, - - "invalid-optional-tag": struct { - V int `tls:"optional"` - }{V: 0}, - - "invalid-validator": CrypticString(strings.Repeat("A", 257)), - } - - for label, badValue := range errorCases { - encoded, err := Marshal(badValue) - t.Logf("[%s] -> [%v]", label, err) - if err == nil { - t.Fatalf("Incorrectly allowed marshal [%s]: [%x]", label, encoded) - } - } -} diff --git a/syntax/success_test.go b/syntax/success_test.go deleted file mode 100644 index 43e528e..0000000 --- a/syntax/success_test.go +++ /dev/null @@ -1,294 +0,0 @@ -package syntax - -import ( - "bytes" - "encoding/hex" - "fmt" - "reflect" - "strings" - "testing" -) - -func unhex(encoded string) []byte { - decoded, err := hex.DecodeString(encoded) - if err != nil { - panic(err) - } - return decoded -} - -func buffer(size int) []byte { - return bytes.Repeat([]byte{0xA0}, size) -} - -func hexBuffer(size int) string { - return strings.Repeat("A0", size) -} - -type CrypticString string - -// A CrypticString marshalls as one length octet followed by the -// UTF-8 bytes of the string, XOR'ed with an increasing sequence -// starting with the length plus one (L+1, L+2, ...). -func (cs CrypticString) MarshalTLS() ([]byte, error) { - l := byte(len(cs)) - b := []byte(cs) - for i := range b { - b[i] ^= l + byte(i) + 1 - } - return append([]byte{l}, b...), nil -} - -func (cs *CrypticString) UnmarshalTLS(data []byte) (int, error) { - if len(data) == 0 { - return 0, fmt.Errorf("Length of CrypticString must be at least 1") - } - - l := data[0] - if len(data) < int(l)+1 { - return 0, fmt.Errorf("TLS data not long enough for CrypticString") - } - - b := data[1 : l+1] - for i := range b { - b[i] ^= l + byte(i) + 1 - } - - *cs = CrypticString(string(b)) - - return int(l + 1), nil -} - -func (cs CrypticString) ValidForTLS() error { - if len(cs) > 256 { - return fmt.Errorf("CrypticString length to large: %d", len(cs)) - } - - if string(cs) == "fnord" { - return fmt.Errorf("Forbidden value") - } - - return nil -} - -func TestSuccessCases(t *testing.T) { - dummyUint16 := uint16(0xFFFF) - crypticHello := CrypticString("hello") - testCases := map[string]struct { - value interface{} - encoding []byte - }{ - // Uints - "uint8": { - value: uint8(0xA0), - encoding: unhex("A0"), - }, - "uint16": { - value: uint16(0xB0A0), - encoding: unhex("B0A0"), - }, - "uint32": { - value: uint32(0xD0C0B0A0), - encoding: unhex("D0C0B0A0"), - }, - "uint64": { - value: uint64(0xD0C0B0A090807060), - encoding: unhex("D0C0B0A090807060"), - }, - - // Varints - "varint8": { - value: struct { - V uint8 `tls:"varint"` - }{V: 0x3F}, - encoding: unhex("3F"), - }, - "varint16": { - value: struct { - V uint16 `tls:"varint"` - }{V: 0x3FFF}, - encoding: unhex("7FFF"), - }, - "varint32": { - value: struct { - V uint32 `tls:"varint"` - }{V: 0x3FFFFFFF}, - encoding: unhex("BFFFFFFF"), - }, - "varint64": { - value: struct { - V uint64 `tls:"varint"` - }{V: 0x3FFFFFFFFFFFFFFF}, - encoding: unhex("FFFFFFFFFFFFFFFF"), - }, - - // Arrays - "array": { - value: [5]uint16{0x0102, 0x0304, 0x0506, 0x0708, 0x090a}, - encoding: unhex("0102030405060708090a"), - }, - - // Slices - "slice-0x20": { - value: struct { - V []byte `tls:"head=1"` - }{ - V: buffer(0x20), - }, - encoding: unhex("20" + hexBuffer(0x20)), - }, - "slice-0x200": { - value: struct { - V []byte `tls:"head=2"` - }{ - V: buffer(0x200), - }, - encoding: unhex("0200" + hexBuffer(0x200)), - }, - "slice-0x20000": { - value: struct { - V []byte `tls:"head=3"` - }{ - V: buffer(0x20000), - }, - encoding: unhex("020000" + hexBuffer(0x20000)), - }, - "slice-none": { - value: struct { - V []byte `tls:"head=none"` - }{ - V: buffer(0x3FFF), - }, - encoding: unhex(hexBuffer(0x3FFF)), - }, - "slice-varint": { - value: struct { - V []byte `tls:"head=varint"` - }{ - V: buffer(0x3FFF), - }, - encoding: unhex("7FFF" + hexBuffer(0x3FFF)), - }, - - // Maps - "map": { - value: struct { - V map[uint16]uint8 `tls:"head=1"` - }{ - V: map[uint16]uint8{2: 1, 1: 2}, - }, - encoding: unhex("06000102000201"), - }, - - // Struct - "struct": { - value: struct { - A uint16 - B []uint8 `tls:"head=2"` - C [4]uint32 - }{ - A: 0xB0A0, - B: []uint8{0xA0, 0xA1, 0xA2, 0xA3, 0xA4}, - C: [4]uint32{0x10111213, 0x20212223, 0x30313233, 0x40414243}, - }, - encoding: unhex("B0A0" + "0005A0A1A2A3A4" + "10111213202122233031323340414243"), - }, - "struct-omit": { - value: struct { - A uint16 - B []uint8 `tls:"omit"` - C [4]uint32 - }{ - A: 0xB0A0, - B: nil, - C: [4]uint32{0x10111213, 0x20212223, 0x30313233, 0x40414243}, - }, - encoding: unhex("B0A0" + "10111213202122233031323340414243"), - }, - "struct-pointer": { - value: struct{ V *uint16 }{V: &dummyUint16}, - encoding: unhex("FFFF"), - }, - - // Optional - "optional-absent": { - value: struct { - A *uint16 `tls:"optional"` - }{ - A: nil, - }, - encoding: unhex("00"), - }, - "optional-present": { - value: struct { - A *uint16 `tls:"optional"` - }{ - A: &dummyUint16, - }, - encoding: unhex("01FFFF"), - }, - "optional-marshaler-absent": { - value: struct { - A *CrypticString `tls:"optional"` - }{ - A: nil, - }, - encoding: unhex("00"), - }, - "optional-marshaler-present": { - value: struct { - A *CrypticString `tls:"optional"` - }{ - A: &crypticHello, - }, - encoding: unhex("01056e62646565"), - }, - - // Marshaler - "marshaler": { - value: crypticHello, - encoding: unhex("056e62646565"), - }, - "struct-marshaler": { - value: struct { - A CrypticString - B uint16 - C CrypticString - }{ - A: CrypticString("hello"), - B: 0xB0A0, - C: CrypticString("... world!"), - }, - encoding: unhex("056e62646565" + "B0A0" + "0a2522232e787f637e7735"), - }, - } - for label, testCase := range testCases { - // Test that encode succeeds - encoding, err := Marshal(testCase.value) - if err != nil { - t.Fatalf("Encode error [%s]: %v", label, err) - } - - if !bytes.Equal(encoding, testCase.encoding) { - t.Fatalf("Invalid encoding [%s]: %x != %x", label, encoding, testCase.encoding) - } - - // Test that decode succeeds - decodedPointer := reflect.New(reflect.TypeOf(testCase.value)) - read, err := Unmarshal(testCase.encoding, decodedPointer.Interface()) - if err != nil { - t.Fatalf("Decode error [%s]: %v", label, err) - } - - if read != len(testCase.encoding) { - t.Fatalf("Decode failed to consume buffer [%s]: %v != %v", label, read, len(testCase.encoding)) - } - - decodedValue := decodedPointer.Elem().Interface() - if !reflect.DeepEqual(decodedValue, testCase.value) { - t.Fatalf("Invalid decoded value [%s]: %v != %v", label, decodedValue, testCase.value) - } - - t.Logf("PASS [%s]", label) - } -} diff --git a/syntax/tags.go b/syntax/tags.go deleted file mode 100644 index 7734eea..0000000 --- a/syntax/tags.go +++ /dev/null @@ -1,173 +0,0 @@ -package syntax - -import ( - "fmt" - "reflect" - "strconv" - "strings" -) - -// Allow types to mark themselves as valid for TLS to marshal/unmarshal -type Validator interface { - ValidForTLS() error -} - -var ( - validatorType = reflect.TypeOf(new(Validator)).Elem() -) - -// `tls:"head=2,min=2,max=255,varint"` - -type fieldOptions struct { - omitHeader bool // whether to omit the slice header - varintHeader bool // whether to encode the header length as a varint - headerSize int // length of length in bytes - minSize int // minimum vector size in bytes - maxSize int // maximum vector size in bytes - - varint bool // whether to encode as a varint - optional bool // whether to encode pointer as optional - omit bool // whether to skip a field -} - -func mutuallyExclusive(vals []bool) bool { - set := 0 - for _, val := range vals { - if val { - set += 1 - } - } - return set <= 1 -} - -func (opts fieldOptions) Consistent() bool { - // No more than one of the header options must be set - headerPaths := []bool{opts.omitHeader, opts.varintHeader, opts.headerSize > 1} - if !mutuallyExclusive(headerPaths) { - return false - } - - // Max must be greater than min - if opts.maxSize > 0 && opts.minSize > opts.maxSize { - return false - } - - // varint and optional are mutually exclusive with each other, and with the slice options - headerOpts := (opts.omitHeader || opts.varintHeader || opts.headerSize > 1 || opts.maxSize > 0 || opts.minSize > 0) - encodePaths := []bool{headerOpts, opts.varint, opts.optional} - if !mutuallyExclusive(encodePaths) { - return false - } - - // Omit is mutually exclusive with everything else - otherThanOmit := (headerOpts || opts.varint || opts.optional) - if !mutuallyExclusive([]bool{opts.omit, otherThanOmit}) { - return false - } - - return true -} - -func (opts fieldOptions) ValidForType(t reflect.Type) bool { - headerType := t.Kind() == reflect.Slice || t.Kind() == reflect.Map - headerTags := opts.omitHeader || opts.varintHeader || (opts.headerSize != 0) || - (opts.minSize != 0) || (opts.maxSize != 0) - if headerTags && !headerType { - return false - } - - uintRequired := opts.varint - if uintRequired { - switch t.Kind() { - case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: - default: - return false - } - } - - ptrRequired := opts.optional - if ptrRequired && t.Kind() != reflect.Ptr { - return false - } - - return true -} - -var ( - varintOption = "varint" - optionalOption = "optional" - omitOption = "omit" - - headOptionNone = "none" - headOptionVarint = "varint" - headValueNoHead = uint(255) - headValueVarint = uint(254) - - optionalFlagAbsent uint8 = 0 - optionalFlagPresent uint8 = 1 -) - -func atoi(a string) int { - i, err := strconv.Atoi(a) - if err != nil { - panic(fmt.Errorf("Invalid header size: %v", err)) - } - return i -} - -// parseTag parses a struct field's "tls" tag as a comma-separated list of -// name=value pairs, where the values MUST be unsigned integers, or in -// the special case of head, "none" or "varint" -func parseTag(tag string) fieldOptions { - opts := fieldOptions{} - for _, token := range strings.Split(tag, ",") { - parts := strings.Split(token, "=") - - // Handle name-only entries - if len(parts) == 1 { - switch parts[0] { - case varintOption: - opts.varint = true - case optionalOption: - opts.optional = true - case omitOption: - opts.omit = true - default: - // XXX(rlb): Ignoring unknown fields - } - continue - } - - if len(parts) != 2 || len(parts[0]) == 0 || len(parts[1]) == 0 { - panic(fmt.Errorf("Malformed tag")) - } - - // Handle name=value entries - switch parts[0] { - case "head": - switch { - case parts[1] == headOptionNone: - opts.omitHeader = true - case parts[1] == headOptionVarint: - opts.varintHeader = true - default: - opts.headerSize = atoi(parts[1]) - } - - case "min": - opts.minSize = atoi(parts[1]) - - case "max": - opts.maxSize = atoi(parts[1]) - - default: - // XXX(rlb): Ignoring unknown fields - } - } - - if !opts.Consistent() { - panic(fmt.Errorf("Inconsistent options")) - } - - return opts -} diff --git a/syntax/tags_test.go b/syntax/tags_test.go deleted file mode 100644 index 3a29bb4..0000000 --- a/syntax/tags_test.go +++ /dev/null @@ -1,129 +0,0 @@ -package syntax - -import ( - "reflect" - "runtime" - "testing" -) - -func TestTagParsing(t *testing.T) { - cases := []struct { - encoded string - opts fieldOptions - }{ - { - encoded: "head=2,min=3,max=60000", - opts: fieldOptions{ - headerSize: 2, - minSize: 3, - maxSize: 60000, - }, - }, - { - encoded: "head=varint,min=3,max=60000", - opts: fieldOptions{ - varintHeader: true, - minSize: 3, - maxSize: 60000, - }, - }, - { - encoded: "head=none,min=3,max=60000", - opts: fieldOptions{ - omitHeader: true, - minSize: 3, - maxSize: 60000, - }, - }, - { - encoded: "varint", - opts: fieldOptions{varint: true}, - }, - { - encoded: "optional", - opts: fieldOptions{optional: true}, - }, - { - encoded: "omit", - opts: fieldOptions{omit: true}, - }, - } - - for _, c := range cases { - parsed := parseTag(c.encoded) - if !reflect.DeepEqual(parsed, c.opts) { - t.Fatalf("Incorrect options parsing: [%+v] != [%+v]", parsed, c.opts) - } - } -} - -func TestTagConsistency(t *testing.T) { - - cases := []string{ - "head=3,head=none", - "head=none,head=varint", - "head=varint,head=3", - "min=4,max=2", - "head=3,varint", - "varint,optional", - "optional,head=3", - "omit,varint", - } - - tryToParse := func(opts string) (err error) { - defer func() { - if r := recover(); r != nil { - if _, ok := r.(runtime.Error); ok { - panic(r) - } - if s, ok := r.(string); ok { - panic(s) - } - err = r.(error) - } - }() - parseTag(opts) - return nil - } - - for _, opts := range cases { - err := tryToParse(opts) - if err == nil { - t.Fatalf("Incorrectly allowed inconsistent options: [%s]", opts) - } - } -} - -func TestTagValidity(t *testing.T) { - sliceTags := parseTag("head=2") - uintTags := parseTag("varint") - ptrTags := parseTag("optional") - - sliceType := reflect.TypeOf([]byte{}) - uintType := reflect.TypeOf(uint8(0)) - ptrType := reflect.TypeOf(new(uint8)) - - if !sliceTags.ValidForType(sliceType) { - t.Fatalf("Rejected valid tags for slice") - } - - if !uintTags.ValidForType(uintType) { - t.Fatalf("Rejected valid tags for uint") - } - - if !ptrTags.ValidForType(ptrType) { - t.Fatalf("Rejected valid tags for ptr") - } - - if uintTags.ValidForType(sliceType) { - t.Fatalf("Accepted invalid tags for slice: %v", uintTags) - } - - if ptrTags.ValidForType(uintType) { - t.Fatalf("Accepted invalid tags for uint: %v", ptrTags) - } - - if sliceTags.ValidForType(ptrType) { - t.Fatalf("Accepted invalid tags for ptr: %v", sliceTags) - } -} diff --git a/syntax/tls_test.go b/syntax/tls_test.go deleted file mode 100644 index 50a0155..0000000 --- a/syntax/tls_test.go +++ /dev/null @@ -1,128 +0,0 @@ -package syntax - -import ( - "bytes" - "encoding/hex" - "fmt" - "reflect" - "testing" -) - -type ProtocolVersion uint16 - -type ExtensionType uint16 -type Extension struct { - ExtensionType ExtensionType - ExtensionData []byte `tls:"head=2"` -} - -type Random [32]byte -type CipherSuite uint16 - -type ClientHello struct { - LegacyVersion ProtocolVersion - Random Random - LegacySessionID []byte `tls:"head=1,max=32"` - CipherSuites []CipherSuite `tls:"head=2,min=2"` - LegacyCompressionMethods []byte `tls:"head=1,min=1"` - Extensions []Extension `tls:"head=2"` -} - -type ServerHello struct { - Version ProtocolVersion - Random Random - CipherSuite CipherSuite - Extensions []Extension `tls:"head=2"` -} - -var ( - extValidIn = Extension{ - ExtensionType: ExtensionType(0x000a), - ExtensionData: []byte{0xf0, 0xf1, 0xf2, 0xf3, 0xf4}, - } - extEmptyIn = Extension{ - ExtensionType: ExtensionType(0x000a), - ExtensionData: []byte{}, - } - extListValidIn = []Extension{extValidIn, extEmptyIn} - extListValidHex = "000d000a0005f0f1f2f3f4000a0000" - - helloRandom = [32]byte{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, - 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, - 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, - 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37} - chValidIn = ClientHello{ - LegacyVersion: 0x0303, - Random: helloRandom, - LegacySessionID: []byte{}, - CipherSuites: []CipherSuite{0x0001, 0x0002, 0x0003}, - LegacyCompressionMethods: []byte{0}, - Extensions: extListValidIn, - } - chValidHex = "0303" + hex.EncodeToString(helloRandom[:]) + "00" + - "0006000100020003" + "0100" + extListValidHex - - shValidIn = ServerHello{ - Version: 0x7f12, - Random: helloRandom, - CipherSuite: CipherSuite(0x0001), - Extensions: extListValidIn, - } - shValidHex = "7f12" + hex.EncodeToString(helloRandom[:]) + "0001" + extListValidHex -) - -func TestTLSMarshal(t *testing.T) { - chValid, _ := hex.DecodeString(chValidHex) - shValid, _ := hex.DecodeString(shValidHex) - - // ClientHello marshal - out, err := Marshal(chValidIn) - if err != nil { - t.Fatalf("Failed to marshal a valid ClientHello [%v]", err) - } - if !bytes.Equal(out, chValid) { - t.Fatalf("Failed to marshal a valid ClientHello [%x] != [%x]", out, chValid) - } - - // ServerHello marshal - out, err = Marshal(shValidIn) - if err != nil { - t.Fatalf("Failed to marshal a valid ServerHello [%v]", err) - } - if !bytes.Equal(out, shValid) { - t.Fatalf("Failed to marshal a valid ServerHello [%x] != [%x]", out, shValid) - } -} - -func TestTLSUnmarshal(t *testing.T) { - chValid, _ := hex.DecodeString(chValidHex) - shValid, _ := hex.DecodeString(shValidHex) - - // ClientHello marshal - var ch ClientHello - read, err := Unmarshal(chValid, &ch) - if err != nil || read != len(chValid) { - t.Fatalf("Failed to unmarshal a valid ClientHello [%v]", err) - } - if !reflect.DeepEqual(ch, chValidIn) { - fmt.Println("LegacyVersion", reflect.DeepEqual(ch.LegacyVersion, chValidIn.LegacyVersion)) - fmt.Println("Random", reflect.DeepEqual(ch.Random, chValidIn.Random)) - fmt.Println("LegacySessionID") - fmt.Printf(" %+v\n", ch.LegacySessionID == nil) - fmt.Printf(" %+v\n", chValidIn.LegacySessionID == nil) - fmt.Println("CipherSuites", reflect.DeepEqual(ch.CipherSuites, chValidIn.CipherSuites)) - fmt.Println("LegacyCompressionMethods", reflect.DeepEqual(ch.LegacyCompressionMethods, chValidIn.LegacyCompressionMethods)) - fmt.Println("Extensions", reflect.DeepEqual(ch.Extensions, chValidIn.Extensions)) - t.Errorf("Failed to unmarshal a valid ClientHello [%+v] [%+v]", ch, chValidIn) - } - - // ServerHello marshal - var sh ServerHello - read, err = Unmarshal(shValid, &sh) - if err != nil || read != len(shValid) { - t.Fatalf("Failed to unmarshal a valid ServerHello [%v]", err) - } - if !reflect.DeepEqual(sh, shValidIn) { - t.Errorf("Failed to unmarshal a valid ServerHello") - } -}