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 error
18 errVarintOverflow error
19 errExcessBytes error
20 )
21
22 // --- Reader ---
23
24 // Reader consumes bytes sequentially from a buffer.
25 type Reader struct {
26 data []byte
27 pos int32
28 }
29
30 func newReader(data []byte) (r Reader) {
31 return Reader{data: data}
32 }
33
34 func (r *Reader) empty() (ok bool) {
35 return r.pos >= len(r.data)
36 }
37
38 func (r *Reader) remaining() (n int32) {
39 return len(r.data) - r.pos
40 }
41
42 // readByte reads one byte.
43 func (r *Reader) readByte() (n byte, ok 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 int32) (out2 []byte, ok 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() (n uint16, ok 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() (n uint32, ok 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() (n uint64, ok 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() (n2 uint32, ok2 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 := int32(1) << uint32(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() (out []byte, ok2 bool) {
127 n, ok := r.readVarint()
128 if !ok {
129 return nil, false
130 }
131 src, ok := r.readN(int32(n))
132 if !ok {
133 return nil, false
134 }
135 dst := []byte{:int32(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) (err error) {
143 n, ok := r.readVarint()
144 if !ok {
145 return errUnexpectedEOF
146 }
147 data, ok := r.readN(int32(n))
148 if !ok {
149 return errUnexpectedEOF
150 }
151 sub := newReader(data)
152 for !sub.empty() {
153 if e := f(&sub); e != nil {
154 return e
155 }
156 }
157 return nil
158 }
159
160 // readOptional reads a 1-byte presence flag (0 or 1).
161 func (r *Reader) readOptional() (ok2 bool, ok3 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 = push(w.buf, v)
196 }
197
198 func (w *Writer) addBytes(v []byte) {
199 if w.err != nil {
200 return
201 }
202 w.buf = w.buf | v
203 }
204
205 func (w *Writer) addUint16(v uint16) {
206 if w.err != nil {
207 return
208 }
209 w.buf = push(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 = push(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 = push(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 = push(w.buf, byte(n))
236 case n < 1<<14:
237 w.buf = push(w.buf, 0x40|byte(n>>8), byte(n))
238 case n < 1<<30:
239 w.buf = push(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 int32, f func(w *Writer, i int32)) {
257 if w.err != nil {
258 return
259 }
260 var child Writer
261 for i := int32(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() (out []byte, err2 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) (err error) {
295 r := newReader(raw)
296 if e := v.unmarshal(&r); e != nil {
297 return e
298 }
299 if !r.empty() {
300 return errExcessBytes
301 }
302 return nil
303 }
304
305 func marshalRaw(v marshaler) (out []byte, err error) {
306 var w Writer
307 v.marshal(&w)
308 return w.bytes()
309 }
310
311 func init() {
312 errInvalidKeySize = errors.New("mls: invalid key size")
313 errAEADOpenFailed = errors.New("mls: AEAD open failed")
314 errHPKEDecryptFailed = errors.New("mls: HPKE decryption failed")
315 kemSuiteID = []byte("KEM\x00\x20")
316 errUnexpectedEOF = errors.New("mls: unexpected EOF")
317 errVarintOverflow = errors.New("mls: varint exceeds 30 bits")
318 errExcessBytes = errors.New("mls: excess bytes after unmarshal")
319 errInvalidContentType = errors.New("mls: invalid content type")
320 errInvalidSenderType = errors.New("mls: invalid sender type")
321 errInvalidWireFormat = errors.New("mls: invalid wire format")
322 errInvalidVersion = errors.New("mls: invalid protocol version")
323 errNonZeroPadding = errors.New("mls: padding contains non-zero bytes")
324 errGroupSecretsNotFound = errors.New("mls: group secrets not found for key package ref")
325 errKeyPackageVersionMismatch = errors.New("mls: key package version mismatch")
326 errKeyPackageSuiteMismatch = errors.New("mls: key package cipher suite mismatch")
327 errKeyPackageBadSource = errors.New("mls: key package leaf node invalid source")
328 errKeyPackageBadSignature = errors.New("mls: invalid key package signature")
329 errKeyPackageKeysMatch = errors.New("mls: key package init key equals encryption key")
330 errInvalidPSKType = errors.New("mls: invalid PSK type")
331 errInvalidPSKUsage = errors.New("mls: invalid resumption PSK usage")
332 errInvalidProposalOrRefType = errors.New("mls: invalid proposal or ref type")
333 secretLabelInit = []byte("init")
334 secretLabelSenderData = []byte("sender data")
335 secretLabelEncryption = []byte("encryption")
336 secretLabelExporter = []byte("exporter")
337 secretLabelExternal = []byte("external")
338 secretLabelConfirm = []byte("confirm")
339 secretLabelMembership = []byte("membership")
340 secretLabelResumption = []byte("resumption")
341 secretLabelAuthentication = []byte("authentication")
342 errInvalidProposalType = errors.New("mls: invalid proposal type")
343 errDupAddKey = errors.New("mls: duplicate add proposal signature key")
344 errUpdateByCommitter = errors.New("mls: update proposal from committer")
345 errDupUpdateOrRemove = errors.New("mls: duplicate update/remove on same leaf")
346 errRemoveCommitter = errors.New("mls: remove proposal targets committer")
347 errDupPSK = errors.New("mls: duplicate PSK proposal")
348 errDupGroupContextExts = errors.New("mls: duplicate group context extensions proposal")
349 errReinitWithOther = errors.New("mls: reinit with other proposals")
350 errExternalInitNotAllowed = errors.New("mls: external init not allowed")
351 ratchetLabelHandshake = []byte("handshake")
352 ratchetLabelApplication = []byte("application")
353 errInvalidLeafNodeSource = errors.New("mls: invalid leaf node source")
354 errInvalidNodeType = errors.New("mls: invalid node type")
355 errNodeKeyMismatch = errors.New("mls: node public key mismatch")
356 errInvalidCredentialType = errors.New("mls: invalid credential type")
357 }
358