github.com/icodeface/tls@v0.0.0-20230910023335-34df9250cd12/internal/x/crypto/cryptobyte/builder.go (about) 1 // Copyright 2017 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package cryptobyte 6 7 import ( 8 "errors" 9 "fmt" 10 ) 11 12 // A Builder builds byte strings from fixed-length and length-prefixed values. 13 // Builders either allocate space as needed, or are ‘fixed’, which means that 14 // they write into a given buffer and produce an error if it's exhausted. 15 // 16 // The zero value is a usable Builder that allocates space as needed. 17 // 18 // Simple values are marshaled and appended to a Builder using methods on the 19 // Builder. Length-prefixed values are marshaled by providing a 20 // BuilderContinuation, which is a function that writes the inner contents of 21 // the value to a given Builder. See the documentation for BuilderContinuation 22 // for details. 23 type Builder struct { 24 err error 25 result []byte 26 fixedSize bool 27 child *Builder 28 offset int 29 pendingLenLen int 30 pendingIsASN1 bool 31 inContinuation *bool 32 } 33 34 // NewBuilder creates a Builder that appends its output to the given buffer. 35 // Like append(), the slice will be reallocated if its capacity is exceeded. 36 // Use Bytes to get the final buffer. 37 func NewBuilder(buffer []byte) *Builder { 38 return &Builder{ 39 result: buffer, 40 } 41 } 42 43 // NewFixedBuilder creates a Builder that appends its output into the given 44 // buffer. This builder does not reallocate the output buffer. Writes that 45 // would exceed the buffer's capacity are treated as an error. 46 func NewFixedBuilder(buffer []byte) *Builder { 47 return &Builder{ 48 result: buffer, 49 fixedSize: true, 50 } 51 } 52 53 // Bytes returns the bytes written by the builder or an error if one has 54 // occurred during during building. 55 func (b *Builder) Bytes() ([]byte, error) { 56 if b.err != nil { 57 return nil, b.err 58 } 59 return b.result[b.offset:], nil 60 } 61 62 // BytesOrPanic returns the bytes written by the builder or panics if an error 63 // has occurred during building. 64 func (b *Builder) BytesOrPanic() []byte { 65 if b.err != nil { 66 panic(b.err) 67 } 68 return b.result[b.offset:] 69 } 70 71 // AddUint8 appends an 8-bit value to the byte string. 72 func (b *Builder) AddUint8(v uint8) { 73 b.add(byte(v)) 74 } 75 76 // AddUint16 appends a big-endian, 16-bit value to the byte string. 77 func (b *Builder) AddUint16(v uint16) { 78 b.add(byte(v>>8), byte(v)) 79 } 80 81 // AddUint24 appends a big-endian, 24-bit value to the byte string. The highest 82 // byte of the 32-bit input value is silently truncated. 83 func (b *Builder) AddUint24(v uint32) { 84 b.add(byte(v>>16), byte(v>>8), byte(v)) 85 } 86 87 // AddUint32 appends a big-endian, 32-bit value to the byte string. 88 func (b *Builder) AddUint32(v uint32) { 89 b.add(byte(v>>24), byte(v>>16), byte(v>>8), byte(v)) 90 } 91 92 // AddBytes appends a sequence of bytes to the byte string. 93 func (b *Builder) AddBytes(v []byte) { 94 b.add(v...) 95 } 96 97 // BuilderContinuation is continuation-passing interface for building 98 // length-prefixed byte sequences. Builder methods for length-prefixed 99 // sequences (AddUint8LengthPrefixed etc) will invoke the BuilderContinuation 100 // supplied to them. The child builder passed to the continuation can be used 101 // to build the content of the length-prefixed sequence. For example: 102 // 103 // parent := cryptobyte.NewBuilder() 104 // parent.AddUint8LengthPrefixed(func (child *Builder) { 105 // child.AddUint8(42) 106 // child.AddUint8LengthPrefixed(func (grandchild *Builder) { 107 // grandchild.AddUint8(5) 108 // }) 109 // }) 110 // 111 // It is an error to write more bytes to the child than allowed by the reserved 112 // length prefix. After the continuation returns, the child must be considered 113 // invalid, i.e. users must not store any copies or references of the child 114 // that outlive the continuation. 115 // 116 // If the continuation panics with a value of type BuildError then the inner 117 // error will be returned as the error from Bytes. If the child panics 118 // otherwise then Bytes will repanic with the same value. 119 type BuilderContinuation func(child *Builder) 120 121 // BuildError wraps an error. If a BuilderContinuation panics with this value, 122 // the panic will be recovered and the inner error will be returned from 123 // Builder.Bytes. 124 type BuildError struct { 125 Err error 126 } 127 128 // AddUint8LengthPrefixed adds a 8-bit length-prefixed byte sequence. 129 func (b *Builder) AddUint8LengthPrefixed(f BuilderContinuation) { 130 b.addLengthPrefixed(1, false, f) 131 } 132 133 // AddUint16LengthPrefixed adds a big-endian, 16-bit length-prefixed byte sequence. 134 func (b *Builder) AddUint16LengthPrefixed(f BuilderContinuation) { 135 b.addLengthPrefixed(2, false, f) 136 } 137 138 // AddUint24LengthPrefixed adds a big-endian, 24-bit length-prefixed byte sequence. 139 func (b *Builder) AddUint24LengthPrefixed(f BuilderContinuation) { 140 b.addLengthPrefixed(3, false, f) 141 } 142 143 // AddUint32LengthPrefixed adds a big-endian, 32-bit length-prefixed byte sequence. 144 func (b *Builder) AddUint32LengthPrefixed(f BuilderContinuation) { 145 b.addLengthPrefixed(4, false, f) 146 } 147 148 func (b *Builder) callContinuation(f BuilderContinuation, arg *Builder) { 149 if !*b.inContinuation { 150 *b.inContinuation = true 151 152 defer func() { 153 *b.inContinuation = false 154 155 r := recover() 156 if r == nil { 157 return 158 } 159 160 if buildError, ok := r.(BuildError); ok { 161 b.err = buildError.Err 162 } else { 163 panic(r) 164 } 165 }() 166 } 167 168 f(arg) 169 } 170 171 func (b *Builder) addLengthPrefixed(lenLen int, isASN1 bool, f BuilderContinuation) { 172 // Subsequent writes can be ignored if the builder has encountered an error. 173 if b.err != nil { 174 return 175 } 176 177 offset := len(b.result) 178 b.add(make([]byte, lenLen)...) 179 180 if b.inContinuation == nil { 181 b.inContinuation = new(bool) 182 } 183 184 b.child = &Builder{ 185 result: b.result, 186 fixedSize: b.fixedSize, 187 offset: offset, 188 pendingLenLen: lenLen, 189 pendingIsASN1: isASN1, 190 inContinuation: b.inContinuation, 191 } 192 193 b.callContinuation(f, b.child) 194 b.flushChild() 195 if b.child != nil { 196 panic("cryptobyte: internal error") 197 } 198 } 199 200 func (b *Builder) flushChild() { 201 if b.child == nil { 202 return 203 } 204 b.child.flushChild() 205 child := b.child 206 b.child = nil 207 208 if child.err != nil { 209 b.err = child.err 210 return 211 } 212 213 length := len(child.result) - child.pendingLenLen - child.offset 214 215 if length < 0 { 216 panic("cryptobyte: internal error") // result unexpectedly shrunk 217 } 218 219 if child.pendingIsASN1 { 220 // For ASN.1, we reserved a single byte for the length. If that turned out 221 // to be incorrect, we have to move the contents along in order to make 222 // space. 223 if child.pendingLenLen != 1 { 224 panic("cryptobyte: internal error") 225 } 226 var lenLen, lenByte uint8 227 if int64(length) > 0xfffffffe { 228 b.err = errors.New("pending ASN.1 child too long") 229 return 230 } else if length > 0xffffff { 231 lenLen = 5 232 lenByte = 0x80 | 4 233 } else if length > 0xffff { 234 lenLen = 4 235 lenByte = 0x80 | 3 236 } else if length > 0xff { 237 lenLen = 3 238 lenByte = 0x80 | 2 239 } else if length > 0x7f { 240 lenLen = 2 241 lenByte = 0x80 | 1 242 } else { 243 lenLen = 1 244 lenByte = uint8(length) 245 length = 0 246 } 247 248 // Insert the initial length byte, make space for successive length bytes, 249 // and adjust the offset. 250 child.result[child.offset] = lenByte 251 extraBytes := int(lenLen - 1) 252 if extraBytes != 0 { 253 child.add(make([]byte, extraBytes)...) 254 childStart := child.offset + child.pendingLenLen 255 copy(child.result[childStart+extraBytes:], child.result[childStart:]) 256 } 257 child.offset++ 258 child.pendingLenLen = extraBytes 259 } 260 261 l := length 262 for i := child.pendingLenLen - 1; i >= 0; i-- { 263 child.result[child.offset+i] = uint8(l) 264 l >>= 8 265 } 266 if l != 0 { 267 b.err = fmt.Errorf("cryptobyte: pending child length %d exceeds %d-byte length prefix", length, child.pendingLenLen) 268 return 269 } 270 271 if !b.fixedSize { 272 b.result = child.result // In case child reallocated result. 273 } 274 } 275 276 func (b *Builder) add(bytes ...byte) { 277 if b.err != nil { 278 return 279 } 280 if b.child != nil { 281 panic("attempted write while child is pending") 282 } 283 if len(b.result)+len(bytes) < len(bytes) { 284 b.err = errors.New("cryptobyte: length overflow") 285 } 286 if b.fixedSize && len(b.result)+len(bytes) > cap(b.result) { 287 b.err = errors.New("cryptobyte: Builder is exceeding its fixed-size buffer") 288 return 289 } 290 b.result = append(b.result, bytes...) 291 } 292 293 // A MarshalingValue marshals itself into a Builder. 294 type MarshalingValue interface { 295 // Marshal is called by Builder.AddValue. It receives a pointer to a builder 296 // to marshal itself into. It may return an error that occurred during 297 // marshaling, such as unset or invalid values. 298 Marshal(b *Builder) error 299 } 300 301 // AddValue calls Marshal on v, passing a pointer to the builder to append to. 302 // If Marshal returns an error, it is set on the Builder so that subsequent 303 // appends don't have an effect. 304 func (b *Builder) AddValue(v MarshalingValue) { 305 err := v.Marshal(b) 306 if err != nil { 307 b.err = err 308 } 309 }