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 5package cryptobyte 6 7import ( 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. 23type 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. 37func 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. 46func NewFixedBuilder(buffer []byte) *Builder { 47 return &Builder{ 48 result: buffer, 49 fixedSize: true, 50 } 51} 52 53// SetError sets the value to be returned as the error from Bytes. Writes 54// performed after calling SetError are ignored. 55func (b *Builder) SetError(err error) { 56 b.err = err 57} 58 59// Bytes returns the bytes written by the builder or an error if one has 60// occurred during building. 61func (b *Builder) Bytes() ([]byte, error) { 62 if b.err != nil { 63 return nil, b.err 64 } 65 return b.result[b.offset:], nil 66} 67 68// BytesOrPanic returns the bytes written by the builder or panics if an error 69// has occurred during building. 70func (b *Builder) BytesOrPanic() []byte { 71 if b.err != nil { 72 panic(b.err) 73 } 74 return b.result[b.offset:] 75} 76 77// AddUint8 appends an 8-bit value to the byte string. 78func (b *Builder) AddUint8(v uint8) { 79 b.add(byte(v)) 80} 81 82// AddUint16 appends a big-endian, 16-bit value to the byte string. 83func (b *Builder) AddUint16(v uint16) { 84 b.add(byte(v>>8), byte(v)) 85} 86 87// AddUint24 appends a big-endian, 24-bit value to the byte string. The highest 88// byte of the 32-bit input value is silently truncated. 89func (b *Builder) AddUint24(v uint32) { 90 b.add(byte(v>>16), byte(v>>8), byte(v)) 91} 92 93// AddUint32 appends a big-endian, 32-bit value to the byte string. 94func (b *Builder) AddUint32(v uint32) { 95 b.add(byte(v>>24), byte(v>>16), byte(v>>8), byte(v)) 96} 97 98// AddBytes appends a sequence of bytes to the byte string. 99func (b *Builder) AddBytes(v []byte) { 100 b.add(v...) 101} 102 103// BuilderContinuation is a continuation-passing interface for building 104// length-prefixed byte sequences. Builder methods for length-prefixed 105// sequences (AddUint8LengthPrefixed etc) will invoke the BuilderContinuation 106// supplied to them. The child builder passed to the continuation can be used 107// to build the content of the length-prefixed sequence. For example: 108// 109// parent := cryptobyte.NewBuilder() 110// parent.AddUint8LengthPrefixed(func (child *Builder) { 111// child.AddUint8(42) 112// child.AddUint8LengthPrefixed(func (grandchild *Builder) { 113// grandchild.AddUint8(5) 114// }) 115// }) 116// 117// It is an error to write more bytes to the child than allowed by the reserved 118// length prefix. After the continuation returns, the child must be considered 119// invalid, i.e. users must not store any copies or references of the child 120// that outlive the continuation. 121// 122// If the continuation panics with a value of type BuildError then the inner 123// error will be returned as the error from Bytes. If the child panics 124// otherwise then Bytes will repanic with the same value. 125type BuilderContinuation func(child *Builder) 126 127// BuildError wraps an error. If a BuilderContinuation panics with this value, 128// the panic will be recovered and the inner error will be returned from 129// Builder.Bytes. 130type BuildError struct { 131 Err error 132} 133 134// AddUint8LengthPrefixed adds a 8-bit length-prefixed byte sequence. 135func (b *Builder) AddUint8LengthPrefixed(f BuilderContinuation) { 136 b.addLengthPrefixed(1, false, f) 137} 138 139// AddUint16LengthPrefixed adds a big-endian, 16-bit length-prefixed byte sequence. 140func (b *Builder) AddUint16LengthPrefixed(f BuilderContinuation) { 141 b.addLengthPrefixed(2, false, f) 142} 143 144// AddUint24LengthPrefixed adds a big-endian, 24-bit length-prefixed byte sequence. 145func (b *Builder) AddUint24LengthPrefixed(f BuilderContinuation) { 146 b.addLengthPrefixed(3, false, f) 147} 148 149// AddUint32LengthPrefixed adds a big-endian, 32-bit length-prefixed byte sequence. 150func (b *Builder) AddUint32LengthPrefixed(f BuilderContinuation) { 151 b.addLengthPrefixed(4, false, f) 152} 153 154func (b *Builder) callContinuation(f BuilderContinuation, arg *Builder) { 155 if !*b.inContinuation { 156 *b.inContinuation = true 157 158 defer func() { 159 *b.inContinuation = false 160 161 r := recover() 162 if r == nil { 163 return 164 } 165 166 if buildError, ok := r.(BuildError); ok { 167 b.err = buildError.Err 168 } else { 169 panic(r) 170 } 171 }() 172 } 173 174 f(arg) 175} 176 177func (b *Builder) addLengthPrefixed(lenLen int, isASN1 bool, f BuilderContinuation) { 178 // Subsequent writes can be ignored if the builder has encountered an error. 179 if b.err != nil { 180 return 181 } 182 183 offset := len(b.result) 184 b.add(make([]byte, lenLen)...) 185 186 if b.inContinuation == nil { 187 b.inContinuation = new(bool) 188 } 189 190 b.child = &Builder{ 191 result: b.result, 192 fixedSize: b.fixedSize, 193 offset: offset, 194 pendingLenLen: lenLen, 195 pendingIsASN1: isASN1, 196 inContinuation: b.inContinuation, 197 } 198 199 b.callContinuation(f, b.child) 200 b.flushChild() 201 if b.child != nil { 202 panic("cryptobyte: internal error") 203 } 204} 205 206func (b *Builder) flushChild() { 207 if b.child == nil { 208 return 209 } 210 b.child.flushChild() 211 child := b.child 212 b.child = nil 213 214 if child.err != nil { 215 b.err = child.err 216 return 217 } 218 219 length := len(child.result) - child.pendingLenLen - child.offset 220 221 if length < 0 { 222 panic("cryptobyte: internal error") // result unexpectedly shrunk 223 } 224 225 if child.pendingIsASN1 { 226 // For ASN.1, we reserved a single byte for the length. If that turned out 227 // to be incorrect, we have to move the contents along in order to make 228 // space. 229 if child.pendingLenLen != 1 { 230 panic("cryptobyte: internal error") 231 } 232 var lenLen, lenByte uint8 233 if int64(length) > 0xfffffffe { 234 b.err = errors.New("pending ASN.1 child too long") 235 return 236 } else if length > 0xffffff { 237 lenLen = 5 238 lenByte = 0x80 | 4 239 } else if length > 0xffff { 240 lenLen = 4 241 lenByte = 0x80 | 3 242 } else if length > 0xff { 243 lenLen = 3 244 lenByte = 0x80 | 2 245 } else if length > 0x7f { 246 lenLen = 2 247 lenByte = 0x80 | 1 248 } else { 249 lenLen = 1 250 lenByte = uint8(length) 251 length = 0 252 } 253 254 // Insert the initial length byte, make space for successive length bytes, 255 // and adjust the offset. 256 child.result[child.offset] = lenByte 257 extraBytes := int(lenLen - 1) 258 if extraBytes != 0 { 259 child.add(make([]byte, extraBytes)...) 260 childStart := child.offset + child.pendingLenLen 261 copy(child.result[childStart+extraBytes:], child.result[childStart:]) 262 } 263 child.offset++ 264 child.pendingLenLen = extraBytes 265 } 266 267 l := length 268 for i := child.pendingLenLen - 1; i >= 0; i-- { 269 child.result[child.offset+i] = uint8(l) 270 l >>= 8 271 } 272 if l != 0 { 273 b.err = fmt.Errorf("cryptobyte: pending child length %d exceeds %d-byte length prefix", length, child.pendingLenLen) 274 return 275 } 276 277 if b.fixedSize && &b.result[0] != &child.result[0] { 278 panic("cryptobyte: BuilderContinuation reallocated a fixed-size buffer") 279 } 280 281 b.result = child.result 282} 283 284func (b *Builder) add(bytes ...byte) { 285 if b.err != nil { 286 return 287 } 288 if b.child != nil { 289 panic("cryptobyte: attempted write while child is pending") 290 } 291 if len(b.result)+len(bytes) < len(bytes) { 292 b.err = errors.New("cryptobyte: length overflow") 293 } 294 if b.fixedSize && len(b.result)+len(bytes) > cap(b.result) { 295 b.err = errors.New("cryptobyte: Builder is exceeding its fixed-size buffer") 296 return 297 } 298 b.result = append(b.result, bytes...) 299} 300 301// Unwrite rolls back n bytes written directly to the Builder. An attempt by a 302// child builder passed to a continuation to unwrite bytes from its parent will 303// panic. 304func (b *Builder) Unwrite(n int) { 305 if b.err != nil { 306 return 307 } 308 if b.child != nil { 309 panic("cryptobyte: attempted unwrite while child is pending") 310 } 311 length := len(b.result) - b.pendingLenLen - b.offset 312 if length < 0 { 313 panic("cryptobyte: internal error") 314 } 315 if n > length { 316 panic("cryptobyte: attempted to unwrite more than was written") 317 } 318 b.result = b.result[:len(b.result)-n] 319} 320 321// A MarshalingValue marshals itself into a Builder. 322type MarshalingValue interface { 323 // Marshal is called by Builder.AddValue. It receives a pointer to a builder 324 // to marshal itself into. It may return an error that occurred during 325 // marshaling, such as unset or invalid values. 326 Marshal(b *Builder) error 327} 328 329// AddValue calls Marshal on v, passing a pointer to the builder to append to. 330// If Marshal returns an error, it is set on the Builder so that subsequent 331// appends don't have an effect. 332func (b *Builder) AddValue(v MarshalingValue) { 333 err := v.Marshal(b) 334 if err != nil { 335 b.err = err 336 } 337} 338