-
Notifications
You must be signed in to change notification settings - Fork 0
/
extension.go
205 lines (171 loc) · 5.33 KB
/
extension.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
package esni
import (
"bytes"
"encoding"
"encoding/binary"
"fmt"
"strings"
"github.com/pkg/errors"
)
const (
// mandatoryExtensionMask is used in an
// AND bitwise operation to check if the
// highest bit is set
mandatoryExtensionMask uint16 = 4096
)
var (
// ErrUnsupportedExtensionType is returned by
// UnmarshalBinary on ExtensionList if it
// encounters an extension type it is unable
// to unmarshal.
//
// The ESNI specification states that clients
// MUST fail if they encounter an unsupported
// extension type, this error is to ensure the
// ESNI record stops unmarshalling when this occurs.
ErrUnsupportedExtensionType = errors.New("unsupported extension type")
// ExtensionType_generator defines a map of
// extension types to their respective generator
// function
ExtensionType_generator = map[ExtensionType]func() Extension{}
// ExtensionType_name defines a map of extension
// types to their respective string representation
ExtensionType_name = map[ExtensionType]string{}
)
// ExtensionType represents the unique
// identifier of a specific ESNI extension
type ExtensionType uint16
// RegisterExtensionType will register the
// name and generator function for a specific
// extension type
func RegisterExtensionType(extType ExtensionType, name string, generator func() Extension) {
if _, exists := ExtensionType_generator[extType]; exists {
panic("extension type already registered")
}
ExtensionType_name[extType] = name
ExtensionType_generator[extType] = generator
}
// Mandatory returns if the inclusion,
// or use, of an extension is mandatory
// in the preparation of a ClientHello.
//
// An extension type is classified as
// mandatory if the highest bit is set
// to 1.
func (extType ExtensionType) Mandatory() bool {
return uint16(extType)&mandatoryExtensionMask == mandatoryExtensionMask
}
// String attempts to return the string
// representation of the ExtensionType based
// on those specified in ExtensionType_name,
// if no match is found "UNKNOWN" is returned
func (extType ExtensionType) String() string {
if name, ok := ExtensionType_name[extType]; ok {
return name
}
return "UNKNOWN"
}
// Generator attempts to return the generator
// function for the ExtensionType based on those
// specified in ExtensionType_generator, if no
// match is found, nil is returned.
//
// The generator function can be used to create
// a new instance of the extension for the purpose
// of unmarshalling.
func (extType ExtensionType) Generator() func() Extension {
if gen, ok := ExtensionType_generator[extType]; ok {
return gen
}
return nil
}
// Extension specifies the methods a
// structure must implement to be treated
// as a ESNI extension
type Extension interface {
// Type must return the unique type
// identifier for the extension
Type() ExtensionType
// Size must return the number of bytes
// that marshalling the extension to binary
// would produce
Size() uint16
// The extension must provide the ability
// to marshal and unmarshal itself from
// binary data
encoding.BinaryMarshaler
encoding.BinaryUnmarshaler
// The extension must provide a String()
// method that produces a log friendly
// representation of the extension data
fmt.Stringer
}
// ExtensionList represents a list of
// ESNI extensions present in a ESNI
// Keys record
type ExtensionList []Extension
// String will produce a friendly representation
// of the extension list and the extensions contained
// within
func (list ExtensionList) String() string {
var builder strings.Builder
builder.WriteString("[")
for i := range list {
if i > 0 {
builder.WriteString(", ")
}
_, _ = fmt.Fprintf(&builder, "{Type:%s, Mandatory:%t, Value:%s}", list[i].Type(), list[i].Type().Mandatory(), list[i])
}
builder.WriteString("]")
return builder.String()
}
// Size returns the number of bytes that
// marshalling the extension to its binary
// format would produce
func (list ExtensionList) Size() (size uint16) {
for i := range list {
size += 2
size += list[i].Size()
}
return
}
// MarshalBinary marshals the list of ESNI
// extensions into a binary format of each
// extension type and their respective marshaled
// format
func (list ExtensionList) MarshalBinary() ([]byte, error) {
buffer := bytes.NewBuffer(make([]byte, list.Size()))
for i := range list {
if err := binary.Write(buffer, binary.BigEndian, list[i].Type()); err != nil {
return nil, errors.Wrap(err, "write extension type")
}
extData, err := list[i].MarshalBinary()
if err != nil {
return nil, errors.Wrap(err, "marshal extension")
}
if _, err := buffer.Write(extData); err != nil {
return nil, errors.Wrap(err, "write extension data")
}
}
return buffer.Bytes(), nil
}
// UnmarshalBinary unmarshals an extension list
// from the provided data buffer, for each extension
// type read, the respective extension implementation
// will be called to be unmarshaled
func (list *ExtensionList) UnmarshalBinary(data []byte) error {
for pos := 0; pos < len(data); {
extType := ExtensionType(binary.BigEndian.Uint16(data[pos:]))
gen := extType.Generator()
if gen == nil {
return errors.Wrapf(ErrUnsupportedExtensionType, "extension_type(%d)", extType)
}
ext := gen()
if err := ext.UnmarshalBinary(data[pos+2:]); err != nil {
return errors.Wrap(err, "unmarshal extension")
}
*list = append(*list, ext)
pos += int(ext.Size()) + 2
}
return nil
}