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  }