| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394 |
- // Copyright (c) Faye Amacker. All rights reserved.
- // Licensed under the MIT License. See LICENSE in the project root for license information.
- package cbor
- import (
- "encoding/binary"
- "errors"
- "io"
- "math"
- "strconv"
- "github.com/x448/float16"
- )
- // SyntaxError is a description of a CBOR syntax error.
- type SyntaxError struct {
- msg string
- }
- func (e *SyntaxError) Error() string { return e.msg }
- // SemanticError is a description of a CBOR semantic error.
- type SemanticError struct {
- msg string
- }
- func (e *SemanticError) Error() string { return e.msg }
- // MaxNestedLevelError indicates exceeded max nested level of any combination of CBOR arrays/maps/tags.
- type MaxNestedLevelError struct {
- maxNestedLevels int
- }
- func (e *MaxNestedLevelError) Error() string {
- return "cbor: exceeded max nested level " + strconv.Itoa(e.maxNestedLevels)
- }
- // MaxArrayElementsError indicates exceeded max number of elements for CBOR arrays.
- type MaxArrayElementsError struct {
- maxArrayElements int
- }
- func (e *MaxArrayElementsError) Error() string {
- return "cbor: exceeded max number of elements " + strconv.Itoa(e.maxArrayElements) + " for CBOR array"
- }
- // MaxMapPairsError indicates exceeded max number of key-value pairs for CBOR maps.
- type MaxMapPairsError struct {
- maxMapPairs int
- }
- func (e *MaxMapPairsError) Error() string {
- return "cbor: exceeded max number of key-value pairs " + strconv.Itoa(e.maxMapPairs) + " for CBOR map"
- }
- // IndefiniteLengthError indicates found disallowed indefinite length items.
- type IndefiniteLengthError struct {
- t cborType
- }
- func (e *IndefiniteLengthError) Error() string {
- return "cbor: indefinite-length " + e.t.String() + " isn't allowed"
- }
- // TagsMdError indicates found disallowed CBOR tags.
- type TagsMdError struct {
- }
- func (e *TagsMdError) Error() string {
- return "cbor: CBOR tag isn't allowed"
- }
- // ExtraneousDataError indicates found extraneous data following well-formed CBOR data item.
- type ExtraneousDataError struct {
- numOfBytes int // number of bytes of extraneous data
- index int // location of extraneous data
- }
- func (e *ExtraneousDataError) Error() string {
- return "cbor: " + strconv.Itoa(e.numOfBytes) + " bytes of extraneous data starting at index " + strconv.Itoa(e.index)
- }
- // wellformed checks whether the CBOR data item is well-formed.
- // allowExtraData indicates if extraneous data is allowed after the CBOR data item.
- // - use allowExtraData = true when using Decoder.Decode()
- // - use allowExtraData = false when using Unmarshal()
- func (d *decoder) wellformed(allowExtraData bool, checkBuiltinTags bool) error {
- if len(d.data) == d.off {
- return io.EOF
- }
- _, err := d.wellformedInternal(0, checkBuiltinTags)
- if err == nil {
- if !allowExtraData && d.off != len(d.data) {
- err = &ExtraneousDataError{len(d.data) - d.off, d.off}
- }
- }
- return err
- }
- // wellformedInternal checks data's well-formedness and returns max depth and error.
- func (d *decoder) wellformedInternal(depth int, checkBuiltinTags bool) (int, error) { //nolint:gocyclo
- t, _, val, indefiniteLength, err := d.wellformedHeadWithIndefiniteLengthFlag()
- if err != nil {
- return 0, err
- }
- switch t {
- case cborTypeByteString, cborTypeTextString:
- if indefiniteLength {
- if d.dm.indefLength == IndefLengthForbidden {
- return 0, &IndefiniteLengthError{t}
- }
- return d.wellformedIndefiniteString(t, depth, checkBuiltinTags)
- }
- valInt := int(val)
- if valInt < 0 {
- // Detect integer overflow
- return 0, errors.New("cbor: " + t.String() + " length " + strconv.FormatUint(val, 10) + " is too large, causing integer overflow")
- }
- if len(d.data)-d.off < valInt { // valInt+off may overflow integer
- return 0, io.ErrUnexpectedEOF
- }
- d.off += valInt
- case cborTypeArray, cborTypeMap:
- depth++
- if depth > d.dm.maxNestedLevels {
- return 0, &MaxNestedLevelError{d.dm.maxNestedLevels}
- }
- if indefiniteLength {
- if d.dm.indefLength == IndefLengthForbidden {
- return 0, &IndefiniteLengthError{t}
- }
- return d.wellformedIndefiniteArrayOrMap(t, depth, checkBuiltinTags)
- }
- valInt := int(val)
- if valInt < 0 {
- // Detect integer overflow
- return 0, errors.New("cbor: " + t.String() + " length " + strconv.FormatUint(val, 10) + " is too large, it would cause integer overflow")
- }
- if t == cborTypeArray {
- if valInt > d.dm.maxArrayElements {
- return 0, &MaxArrayElementsError{d.dm.maxArrayElements}
- }
- } else {
- if valInt > d.dm.maxMapPairs {
- return 0, &MaxMapPairsError{d.dm.maxMapPairs}
- }
- }
- count := 1
- if t == cborTypeMap {
- count = 2
- }
- maxDepth := depth
- for j := 0; j < count; j++ {
- for i := 0; i < valInt; i++ {
- var dpt int
- if dpt, err = d.wellformedInternal(depth, checkBuiltinTags); err != nil {
- return 0, err
- }
- if dpt > maxDepth {
- maxDepth = dpt // Save max depth
- }
- }
- }
- depth = maxDepth
- case cborTypeTag:
- if d.dm.tagsMd == TagsForbidden {
- return 0, &TagsMdError{}
- }
- tagNum := val
- // Scan nested tag numbers to avoid recursion.
- for {
- if len(d.data) == d.off { // Tag number must be followed by tag content.
- return 0, io.ErrUnexpectedEOF
- }
- if checkBuiltinTags {
- err = validBuiltinTag(tagNum, d.data[d.off])
- if err != nil {
- return 0, err
- }
- }
- if d.dm.bignumTag == BignumTagForbidden && (tagNum == 2 || tagNum == 3) {
- return 0, &UnacceptableDataItemError{
- CBORType: cborTypeTag.String(),
- Message: "bignum",
- }
- }
- if getType(d.data[d.off]) != cborTypeTag {
- break
- }
- if _, _, tagNum, err = d.wellformedHead(); err != nil {
- return 0, err
- }
- depth++
- if depth > d.dm.maxNestedLevels {
- return 0, &MaxNestedLevelError{d.dm.maxNestedLevels}
- }
- }
- // Check tag content.
- return d.wellformedInternal(depth, checkBuiltinTags)
- }
- return depth, nil
- }
- // wellformedIndefiniteString checks indefinite length byte/text string's well-formedness and returns max depth and error.
- func (d *decoder) wellformedIndefiniteString(t cborType, depth int, checkBuiltinTags bool) (int, error) {
- var err error
- for {
- if len(d.data) == d.off {
- return 0, io.ErrUnexpectedEOF
- }
- if isBreakFlag(d.data[d.off]) {
- d.off++
- break
- }
- // Peek ahead to get next type and indefinite length status.
- nt, ai := parseInitialByte(d.data[d.off])
- if t != nt {
- return 0, &SyntaxError{"cbor: wrong element type " + nt.String() + " for indefinite-length " + t.String()}
- }
- if additionalInformation(ai).isIndefiniteLength() {
- return 0, &SyntaxError{"cbor: indefinite-length " + t.String() + " chunk is not definite-length"}
- }
- if depth, err = d.wellformedInternal(depth, checkBuiltinTags); err != nil {
- return 0, err
- }
- }
- return depth, nil
- }
- // wellformedIndefiniteArrayOrMap checks indefinite length array/map's well-formedness and returns max depth and error.
- func (d *decoder) wellformedIndefiniteArrayOrMap(t cborType, depth int, checkBuiltinTags bool) (int, error) {
- var err error
- maxDepth := depth
- i := 0
- for {
- if len(d.data) == d.off {
- return 0, io.ErrUnexpectedEOF
- }
- if isBreakFlag(d.data[d.off]) {
- d.off++
- break
- }
- var dpt int
- if dpt, err = d.wellformedInternal(depth, checkBuiltinTags); err != nil {
- return 0, err
- }
- if dpt > maxDepth {
- maxDepth = dpt
- }
- i++
- if t == cborTypeArray {
- if i > d.dm.maxArrayElements {
- return 0, &MaxArrayElementsError{d.dm.maxArrayElements}
- }
- } else {
- if i%2 == 0 && i/2 > d.dm.maxMapPairs {
- return 0, &MaxMapPairsError{d.dm.maxMapPairs}
- }
- }
- }
- if t == cborTypeMap && i%2 == 1 {
- return 0, &SyntaxError{"cbor: unexpected \"break\" code"}
- }
- return maxDepth, nil
- }
- func (d *decoder) wellformedHeadWithIndefiniteLengthFlag() (
- t cborType,
- ai byte,
- val uint64,
- indefiniteLength bool,
- err error,
- ) {
- t, ai, val, err = d.wellformedHead()
- if err != nil {
- return
- }
- indefiniteLength = additionalInformation(ai).isIndefiniteLength()
- return
- }
- func (d *decoder) wellformedHead() (t cborType, ai byte, val uint64, err error) {
- dataLen := len(d.data) - d.off
- if dataLen == 0 {
- return 0, 0, 0, io.ErrUnexpectedEOF
- }
- t, ai = parseInitialByte(d.data[d.off])
- val = uint64(ai)
- d.off++
- dataLen--
- if ai <= maxAdditionalInformationWithoutArgument {
- return t, ai, val, nil
- }
- if ai == additionalInformationWith1ByteArgument {
- const argumentSize = 1
- if dataLen < argumentSize {
- return 0, 0, 0, io.ErrUnexpectedEOF
- }
- val = uint64(d.data[d.off])
- d.off++
- if t == cborTypePrimitives && val < 32 {
- return 0, 0, 0, &SyntaxError{"cbor: invalid simple value " + strconv.Itoa(int(val)) + " for type " + t.String()}
- }
- return t, ai, val, nil
- }
- if ai == additionalInformationWith2ByteArgument {
- const argumentSize = 2
- if dataLen < argumentSize {
- return 0, 0, 0, io.ErrUnexpectedEOF
- }
- val = uint64(binary.BigEndian.Uint16(d.data[d.off : d.off+argumentSize]))
- d.off += argumentSize
- if t == cborTypePrimitives {
- if err := d.acceptableFloat(float64(float16.Frombits(uint16(val)).Float32())); err != nil {
- return 0, 0, 0, err
- }
- }
- return t, ai, val, nil
- }
- if ai == additionalInformationWith4ByteArgument {
- const argumentSize = 4
- if dataLen < argumentSize {
- return 0, 0, 0, io.ErrUnexpectedEOF
- }
- val = uint64(binary.BigEndian.Uint32(d.data[d.off : d.off+argumentSize]))
- d.off += argumentSize
- if t == cborTypePrimitives {
- if err := d.acceptableFloat(float64(math.Float32frombits(uint32(val)))); err != nil {
- return 0, 0, 0, err
- }
- }
- return t, ai, val, nil
- }
- if ai == additionalInformationWith8ByteArgument {
- const argumentSize = 8
- if dataLen < argumentSize {
- return 0, 0, 0, io.ErrUnexpectedEOF
- }
- val = binary.BigEndian.Uint64(d.data[d.off : d.off+argumentSize])
- d.off += argumentSize
- if t == cborTypePrimitives {
- if err := d.acceptableFloat(math.Float64frombits(val)); err != nil {
- return 0, 0, 0, err
- }
- }
- return t, ai, val, nil
- }
- if additionalInformation(ai).isIndefiniteLength() {
- switch t {
- case cborTypePositiveInt, cborTypeNegativeInt, cborTypeTag:
- return 0, 0, 0, &SyntaxError{"cbor: invalid additional information " + strconv.Itoa(int(ai)) + " for type " + t.String()}
- case cborTypePrimitives: // 0xff (break code) should not be outside wellformedIndefinite().
- return 0, 0, 0, &SyntaxError{"cbor: unexpected \"break\" code"}
- }
- return t, ai, val, nil
- }
- // ai == 28, 29, 30
- return 0, 0, 0, &SyntaxError{"cbor: invalid additional information " + strconv.Itoa(int(ai)) + " for type " + t.String()}
- }
- func (d *decoder) acceptableFloat(f float64) error {
- switch {
- case d.dm.nanDec == NaNDecodeForbidden && math.IsNaN(f):
- return &UnacceptableDataItemError{
- CBORType: cborTypePrimitives.String(),
- Message: "floating-point NaN",
- }
- case d.dm.infDec == InfDecodeForbidden && math.IsInf(f, 0):
- return &UnacceptableDataItemError{
- CBORType: cborTypePrimitives.String(),
- Message: "floating-point infinity",
- }
- }
- return nil
- }
|