encoding.mx raw
1 // Package mls implements MLS RFC 9420 cipher suite 0x0003.
2 //
3 // Wire-width discipline (see CLAUDE.md "Integer widths on the wire"):
4 // every function here that touches the wire uses explicit-width integer
5 // types (uint8/uint16/uint32/uint64). Bare int is avoided on all wire
6 // paths. Varints decode into uint32 — MLS §5.1.3 caps varint values at
7 // 2^30-1 so uint32 is the exact upper bound, not a best-guess.
8 package mls
9
10 // MLS wire encoding (RFC 9420 §6).
11 // Replaces cryptobyte.Builder/String from the Go version with direct
12 // byte manipulation suitable for Moxie's string=[]byte model.
13
14 import "errors"
15
16 var (
17 errUnexpectedEOF = errors.New("mls: unexpected EOF")
18 errVarintOverflow = errors.New("mls: varint exceeds 30 bits")
19 errExcessBytes = errors.New("mls: excess bytes after unmarshal")
20 )
21
22 // --- Reader ---
23
24 // Reader consumes bytes sequentially from a buffer.
25 type Reader struct {
26 data []byte
27 pos int
28 }
29
30 func newReader(data []byte) Reader {
31 return Reader{data: data}
32 }
33
34 func (r *Reader) empty() bool {
35 return r.pos >= len(r.data)
36 }
37
38 func (r *Reader) remaining() int {
39 return len(r.data) - r.pos
40 }
41
42 // readByte reads one byte.
43 func (r *Reader) readByte() (byte, bool) {
44 if r.pos >= len(r.data) {
45 return 0, false
46 }
47 b := r.data[r.pos]
48 r.pos++
49 return b, true
50 }
51
52 // readN reads n bytes as a slice of the underlying buffer (no copy).
53 func (r *Reader) readN(n int) ([]byte, bool) {
54 if r.remaining() < n {
55 return nil, false
56 }
57 out := r.data[r.pos : r.pos+n]
58 r.pos += n
59 return out, true
60 }
61
62 // readUint16 reads a big-endian uint16.
63 func (r *Reader) readUint16() (uint16, bool) {
64 if r.remaining() < 2 {
65 return 0, false
66 }
67 v := uint16(r.data[r.pos])<<8 | uint16(r.data[r.pos+1])
68 r.pos += 2
69 return v, true
70 }
71
72 // readUint32 reads a big-endian uint32.
73 func (r *Reader) readUint32() (uint32, bool) {
74 if r.remaining() < 4 {
75 return 0, false
76 }
77 v := uint32(r.data[r.pos])<<24 | uint32(r.data[r.pos+1])<<16 |
78 uint32(r.data[r.pos+2])<<8 | uint32(r.data[r.pos+3])
79 r.pos += 4
80 return v, true
81 }
82
83 // readUint64 reads a big-endian uint64.
84 func (r *Reader) readUint64() (uint64, bool) {
85 if r.remaining() < 8 {
86 return 0, false
87 }
88 v := uint64(r.data[r.pos])<<56 | uint64(r.data[r.pos+1])<<48 |
89 uint64(r.data[r.pos+2])<<40 | uint64(r.data[r.pos+3])<<32 |
90 uint64(r.data[r.pos+4])<<24 | uint64(r.data[r.pos+5])<<16 |
91 uint64(r.data[r.pos+6])<<8 | uint64(r.data[r.pos+7])
92 r.pos += 8
93 return v, true
94 }
95
96 // readVarint reads a QUIC-style variable-length integer (RFC 9000 §16).
97 // 2-bit prefix encodes length: 00=1byte/6bits, 01=2bytes/14bits, 10=4bytes/30bits.
98 // MLS §5.1.3 caps values at 2^30-1, which fits in uint32 exactly — the
99 // return type is the tight upper bound, not a convenience cast.
100 func (r *Reader) readVarint() (uint32, bool) {
101 b, ok := r.readByte()
102 if !ok {
103 return 0, false
104 }
105 prefix := b >> 6
106 if prefix == 3 {
107 return 0, false
108 }
109 n := int(1) << uint(prefix) // total bytes: 1, 2, or 4
110 v := uint32(b & 0x3F)
111 for i := 0; i < n-1; i++ {
112 b, ok = r.readByte()
113 if !ok {
114 return 0, false
115 }
116 v = (v << 8) | uint32(b)
117 }
118 // reject non-minimal encodings
119 if prefix >= 1 && v < uint32(1)<<uint32(8*(n/2)-2) {
120 return 0, false
121 }
122 return v, true
123 }
124
125 // readOpaqueVec reads a varint-length-prefixed byte vector (copy).
126 func (r *Reader) readOpaqueVec() ([]byte, bool) {
127 n, ok := r.readVarint()
128 if !ok {
129 return nil, false
130 }
131 src, ok := r.readN(int(n))
132 if !ok {
133 return nil, false
134 }
135 dst := []byte{:int(n)}
136 copy(dst, src)
137 return dst, true
138 }
139
140 // readVector reads a varint-length-prefixed sequence of encoded elements,
141 // calling f for each element until the sub-buffer is exhausted.
142 func (r *Reader) readVector(f func(r *Reader) error) error {
143 n, ok := r.readVarint()
144 if !ok {
145 return errUnexpectedEOF
146 }
147 data, ok := r.readN(int(n))
148 if !ok {
149 return errUnexpectedEOF
150 }
151 sub := newReader(data)
152 for !sub.empty() {
153 if err := f(&sub); err != nil {
154 return err
155 }
156 }
157 return nil
158 }
159
160 // readOptional reads a 1-byte presence flag (0 or 1).
161 func (r *Reader) readOptional() (bool, bool) {
162 b, ok := r.readByte()
163 if !ok {
164 return false, false
165 }
166 switch b {
167 case 0:
168 return false, true
169 case 1:
170 return true, true
171 default:
172 return false, false
173 }
174 }
175
176 // --- Writer ---
177
178 // Writer accumulates serialized bytes with deferred error checking.
179 // Errors are recorded but do not halt writes; check via bytes().
180 type Writer struct {
181 buf []byte
182 err error
183 }
184
185 func (w *Writer) setError(err error) {
186 if w.err == nil {
187 w.err = err
188 }
189 }
190
191 func (w *Writer) addByte(v byte) {
192 if w.err != nil {
193 return
194 }
195 w.buf = append(w.buf, v)
196 }
197
198 func (w *Writer) addBytes(v []byte) {
199 if w.err != nil {
200 return
201 }
202 w.buf = append(w.buf, v...)
203 }
204
205 func (w *Writer) addUint16(v uint16) {
206 if w.err != nil {
207 return
208 }
209 w.buf = append(w.buf, byte(v>>8), byte(v))
210 }
211
212 func (w *Writer) addUint32(v uint32) {
213 if w.err != nil {
214 return
215 }
216 w.buf = append(w.buf, byte(v>>24), byte(v>>16), byte(v>>8), byte(v))
217 }
218
219 func (w *Writer) addUint64(v uint64) {
220 if w.err != nil {
221 return
222 }
223 w.buf = append(w.buf, byte(v>>56), byte(v>>48), byte(v>>40), byte(v>>32),
224 byte(v>>24), byte(v>>16), byte(v>>8), byte(v))
225 }
226
227 // writeVarint encodes a QUIC-style variable-length integer.
228 // MLS §5.1.3 caps values at 2^30-1; larger values yield errVarintOverflow.
229 func (w *Writer) writeVarint(n uint32) {
230 if w.err != nil {
231 return
232 }
233 switch {
234 case n < 1<<6:
235 w.buf = append(w.buf, byte(n))
236 case n < 1<<14:
237 w.buf = append(w.buf, 0x40|byte(n>>8), byte(n))
238 case n < 1<<30:
239 w.buf = append(w.buf, 0x80|byte(n>>24), byte(n>>16), byte(n>>8), byte(n))
240 default:
241 w.setError(errVarintOverflow)
242 }
243 }
244
245 // writeOpaqueVec writes a varint-length-prefixed byte vector.
246 func (w *Writer) writeOpaqueVec(data []byte) {
247 if w.err != nil {
248 return
249 }
250 w.writeVarint(uint32(len(data)))
251 w.addBytes(data)
252 }
253
254 // writeVector encodes n elements into a varint-length-prefixed block.
255 // Each element is written by calling f(child, i).
256 func (w *Writer) writeVector(n int, f func(w *Writer, i int)) {
257 if w.err != nil {
258 return
259 }
260 var child Writer
261 for i := 0; i < n; i++ {
262 f(&child, i)
263 if child.err != nil {
264 w.setError(child.err)
265 return
266 }
267 }
268 w.writeOpaqueVec(child.buf)
269 }
270
271 // writeOptional writes a 1-byte presence flag.
272 func (w *Writer) writeOptional(present bool) {
273 if present {
274 w.addByte(1)
275 } else {
276 w.addByte(0)
277 }
278 }
279
280 func (w *Writer) bytes() ([]byte, error) {
281 return w.buf, w.err
282 }
283
284 // --- Marshal / Unmarshal interfaces ---
285
286 type unmarshaler interface {
287 unmarshal(r *Reader) error
288 }
289
290 type marshaler interface {
291 marshal(w *Writer)
292 }
293
294 func unmarshalRaw(raw []byte, v unmarshaler) error {
295 r := newReader(raw)
296 if err := v.unmarshal(&r); err != nil {
297 return err
298 }
299 if !r.empty() {
300 return errExcessBytes
301 }
302 return nil
303 }
304
305 func marshalRaw(v marshaler) ([]byte, error) {
306 var w Writer
307 v.marshal(&w)
308 return w.bytes()
309 }
310