Repository: fxamacker/cbor (go)

Issue:
bug: decoding should ignore CBOR tag number 55799 when it is a prefix
### What version of fxamacker/cbor are you using?

v2.2.0

### Does this issue reproduce with the latest release?

yes

### What OS and CPU architecture are you using (`go env`)?

<details><summary><code>go env</code> Output</summary><br><pre>
$ go env

</pre></details>

### What did you do?
<!--
If possible, provide steps and/or code to reproduce the problem. 
-->

Decode CBOR tag item with tag number 55799 to a Go interface{} (empty interface).

### What did you expect to see?

The empty interface populated by the tag content.

### What did you see instead?

The empty interface populated by a `cbor.Tag` struct which contains both tag number and tag content.

This is appropriate behavior for all unrecognized CBOR tags, but tag 55799 is a special case.  Ignoring it and simply decoding the tag content into the empty interface would be the correct default behavior.

### Notes

When decoding CBOR tag item with tag number 55799 to Unmarshaler, UnmarshalCBOR should receive tag content only.  

UnmarshalCBOR should receive the same CBOR data whether it is tagged with tag number 55799 or not.

see also: #227 and https://github.com/cbor-wg/CBORbis/issues/183. 


Pull request description:
Strip CBOR tag number 55799 when it is a prefix
Tag number 55799 is normally prefixed to data to indicate that it is CBOR encoded.

This library strips tag number 55799 (when it is a prefix) to provide the same decoding behavior as if CBOR data is not tagged.

* When decoding to empty interface or other Go types, tag number 55799 is skipped and tag content is decoded.

* When decoding to Unmarshaler, tag number 55799 is skipped and only the tag content is provided to UnmarshalCBOR.

Closes #228



Patch:
diff --git a/decode.go b/decode.go
index 6be91a2..1099544 100644
--- a/decode.go
+++ b/decode.go
@@ -514,6 +514,10 @@ func (t cborType) String() string {
 	}
 }
 
+const (
+	selfDescribedCBORTagNum = 55799
+)
+
 // parseToValue assumes data is well-formed, and does not perform bounds checking.
 // This function is complicated because it's the main function that decodes CBOR data to reflect.Value.
 func (d *decodeState) parseToValue(v reflect.Value, tInfo *typeInfo) error { //nolint:gocyclo
@@ -531,6 +535,16 @@ func (d *decodeState) parseToValue(v reflect.Value, tInfo *typeInfo) error { //n
 		}
 	}
 
+	// Strip self-described CBOR tag number.
+	for d.nextCBORType() == cborTypeTag {
+		off := d.off
+		_, _, tagNum := d.getHead()
+		if tagNum != selfDescribedCBORTagNum {
+			d.off = off
+			break
+		}
+	}
+
 	// Check validity of supported built-in tags.
 	if d.nextCBORType() == cborTypeTag {
 		off := d.off
@@ -545,7 +559,7 @@ func (d *decodeState) parseToValue(v reflect.Value, tInfo *typeInfo) error { //n
 	if tInfo.spclType != specialTypeNone {
 		switch tInfo.spclType {
 		case specialTypeEmptyIface:
-			iv, err := d.parse()
+			iv, err := d.parse(false) // Skipped self-described CBOR tag number already.
 			if iv != nil {
 				v.Set(reflect.ValueOf(iv))
 			}
@@ -663,7 +677,7 @@ func (d *decodeState) parseToTag(v reflect.Value) error {
 	_, _, num := d.getHead()
 
 	// Unmarshal tag content
-	content, err := d.parse()
+	content, err := d.parse(false)
 	if err != nil {
 		return err
 	}
@@ -701,7 +715,7 @@ func (d *decodeState) parseToTime() (tm time.Time, err error) {
 	}
 
 	var content interface{}
-	content, err = d.parse()
+	content, err = d.parse(false)
 	if err != nil {
 		return
 	}
@@ -753,7 +767,19 @@ func (d *decodeState) parseToUnmarshaler(v reflect.Value) error {
 }
 
 // parse assumes data is well-formed, and does not perform bounds checking.
-func (d *decodeState) parse() (interface{}, error) {
+func (d *decodeState) parse(skipSelfDescribedTag bool) (interface{}, error) {
+	// Strip self-described CBOR tag number.
+	if skipSelfDescribedTag {
+		for d.nextCBORType() == cborTypeTag {
+			off := d.off
+			_, _, tagNum := d.getHead()
+			if tagNum != selfDescribedCBORTagNum {
+				d.off = off
+				break
+			}
+		}
+	}
+
 	t := d.nextCBORType()
 	switch t {
 	case cborTypePositiveInt:
@@ -819,7 +845,7 @@ func (d *decodeState) parse() (interface{}, error) {
 
 		// Parse tag content
 		d.off = contentOff
-		content, err := d.parse()
+		content, err := d.parse(false)
 		if err != nil {
 			return nil, err
 		}
@@ -917,7 +943,7 @@ func (d *decodeState) parseArray() ([]interface{}, error) {
 	var e interface{}
 	var err, lastErr error
 	for i := 0; (hasSize && i < count) || (!hasSize && !d.foundBreak()); i++ {
-		if e, lastErr = d.parse(); lastErr != nil {
+		if e, lastErr = d.parse(true); lastErr != nil {
 			if err == nil {
 				err = lastErr
 			}
@@ -993,7 +1019,7 @@ func (d *decodeState) parseMap() (map[interface{}]interface{}, error) {
 	keyCount := 0
 	for i := 0; (hasSize && i < count) || (!hasSize && !d.foundBreak()); i++ {
 		// Parse CBOR map key.
-		if k, lastErr = d.parse(); lastErr != nil {
+		if k, lastErr = d.parse(true); lastErr != nil {
 			if err == nil {
 				err = lastErr
 			}
@@ -1015,7 +1041,7 @@ func (d *decodeState) parseMap() (map[interface{}]interface{}, error) {
 		}
 
 		// Parse CBOR map value.
-		if e, lastErr = d.parse(); lastErr != nil {
+		if e, lastErr = d.parse(true); lastErr != nil {
 			if err == nil {
 				err = lastErr
 			}
@@ -1331,7 +1357,7 @@ func (d *decodeState) parseMapToStruct(v reflect.Value, tInfo *typeInfo) error {
 			}
 			if d.dm.dupMapKey == DupMapKeyEnforcedAPF {
 				// parse key
-				k, lastErr = d.parse()
+				k, lastErr = d.parse(true)
 				if lastErr != nil {
 					d.skip() // skip value
 					continue
diff --git a/tag.go b/tag.go
index ddfacb0..ea89813 100644
--- a/tag.go
+++ b/tag.go
@@ -249,6 +249,9 @@ func newTagItem(opts TagOptions, contentType reflect.Type, num uint64, nestedNum
 	if num == 0 || num == 1 {
 		return nil, errors.New("cbor: cannot add tag number 0 or 1 to TagSet, use EncOptions.TimeTag and DecOptions.TimeTag instead")
 	}
+	if num == selfDescribedCBORTagNum {
+		return nil, errors.New("cbor: cannot add tag number 55799 to TagSet, it's built-in and ignored automatically")
+	}
 	//if reflect.PtrTo(contentType).Implements(typeMarshaler) && opts.EncTag != EncTagNone {
 	//return nil, errors.New("cbor: cannot add cbor.Marshaler to TagSet with EncTag != EncTagNone")
 	//}


Test patch:
diff --git a/decode_test.go b/decode_test.go
index 01537df..963e6e9 100644
--- a/decode_test.go
+++ b/decode_test.go
@@ -4471,3 +4471,336 @@ func TestStreamExtraErrorCondUnknowField(t *testing.T) {
 		t.Errorf("Decode() return %v (%T), want %v (%T)", v2, v2, wantObj, wantObj)
 	}
 }
+
+// TestUnmarshalTagNum55799 is identical to TestUnmarshal,
+// except that CBOR test data is prefixed with tag number 55799 (0xd9d9f7).
+func TestUnmarshalTagNum55799(t *testing.T) {
+	tagNum55799 := hexDecode("d9d9f7")
+
+	for _, tc := range unmarshalTests {
+		// Prefix tag number 55799 to CBOR test data
+		cborData := make([]byte, len(tc.cborData)+6)
+		copy(cborData, tagNum55799)
+		copy(cborData[3:], tagNum55799)
+		copy(cborData[6:], tc.cborData)
+
+		// Test unmarshalling CBOR into empty interface.
+		var v interface{}
+		if err := Unmarshal(cborData, &v); err != nil {
+			t.Errorf("Unmarshal(0x%x) returned error %v", cborData, err)
+		} else {
+			if tm, ok := tc.emptyInterfaceValue.(time.Time); ok {
+				if vt, ok := v.(time.Time); !ok || !tm.Equal(vt) {
+					t.Errorf("Unmarshal(0x%x) = %v (%T), want %v (%T)", cborData, v, v, tc.emptyInterfaceValue, tc.emptyInterfaceValue)
+				}
+			} else if !reflect.DeepEqual(v, tc.emptyInterfaceValue) {
+				t.Errorf("Unmarshal(0x%x) = %v (%T), want %v (%T)", cborData, v, v, tc.emptyInterfaceValue, tc.emptyInterfaceValue)
+			}
+		}
+
+		// Test unmarshalling CBOR into RawMessage.
+		var r RawMessage
+		if err := Unmarshal(cborData, &r); err != nil {
+			t.Errorf("Unmarshal(0x%x) returned error %v", cborData, err)
+		} else if !bytes.Equal(r, tc.cborData) {
+			t.Errorf("Unmarshal(0x%x) returned RawMessage %v, want %v", cborData, r, tc.cborData)
+		}
+
+		// Test unmarshalling CBOR into compatible data types.
+		for _, value := range tc.values {
+			v := reflect.New(reflect.TypeOf(value))
+			vPtr := v.Interface()
+			if err := Unmarshal(cborData, vPtr); err != nil {
+				t.Errorf("Unmarshal(0x%x) returned error %v", cborData, err)
+			} else {
+				if tm, ok := value.(time.Time); ok {
+					if vt, ok := v.Elem().Interface().(time.Time); !ok || !tm.Equal(vt) {
+						t.Errorf("Unmarshal(0x%x) = %v (%T), want %v (%T)", cborData, v.Elem().Interface(), v.Elem().Interface(), value, value)
+					}
+				} else if !reflect.DeepEqual(v.Elem().Interface(), value) {
+					t.Errorf("Unmarshal(0x%x) = %v (%T), want %v (%T)", cborData, v.Elem().Interface(), v.Elem().Interface(), value, value)
+				}
+			}
+		}
+
+		// Test unmarshalling CBOR into incompatible data types.
+		for _, typ := range tc.wrongTypes {
+			v := reflect.New(typ)
+			vPtr := v.Interface()
+			if err := Unmarshal(cborData, vPtr); err == nil {
+				t.Errorf("Unmarshal(0x%x, %s) didn't return an error", cborData, typ.String())
+			} else if _, ok := err.(*UnmarshalTypeError); !ok {
+				t.Errorf("Unmarshal(0x%x) returned wrong error type %T, want (*UnmarshalTypeError)", cborData, err)
+			} else if !strings.Contains(err.Error(), "cannot unmarshal") {
+				t.Errorf("Unmarshal(0x%x) returned error %q, want error containing %q", cborData, err.Error(), "cannot unmarshal")
+			}
+		}
+	}
+}
+
+// TestUnmarshalFloatWithTagNum55799 is identical to TestUnmarshalFloat,
+// except that CBOR test data is prefixed with tag number 55799 (0xd9d9f7).
+func TestUnmarshalFloatWithTagNum55799(t *testing.T) {
+	tagNum55799 := hexDecode("d9d9f7")
+
+	for _, tc := range unmarshalFloatTests {
+		// Prefix tag number 55799 to CBOR test data
+		cborData := make([]byte, len(tc.cborData)+3)
+		copy(cborData, tagNum55799)
+		copy(cborData[3:], tc.cborData)
+
+		// Test unmarshalling CBOR into empty interface.
+		var v interface{}
+		if err := Unmarshal(tc.cborData, &v); err != nil {
+			t.Errorf("Unmarshal(0x%x) returned error %v", tc.cborData, err)
+		} else {
+			testFloat(t, tc.cborData, v, tc.emptyInterfaceValue, tc.equalityThreshold)
+		}
+
+		// Test unmarshalling CBOR into RawMessage.
+		var r RawMessage
+		if err := Unmarshal(tc.cborData, &r); err != nil {
+			t.Errorf("Unmarshal(0x%x) returned error %v", tc.cborData, err)
+		} else if !bytes.Equal(r, tc.cborData) {
+			t.Errorf("Unmarshal(0x%x) returned RawMessage %v, want %v", tc.cborData, r, tc.cborData)
+		}
+
+		// Test unmarshalling CBOR into compatible data types.
+		for _, value := range tc.values {
+			v := reflect.New(reflect.TypeOf(value))
+			vPtr := v.Interface()
+			if err := Unmarshal(tc.cborData, vPtr); err != nil {
+				t.Errorf("Unmarshal(0x%x) returned error %v", tc.cborData, err)
+			} else {
+				testFloat(t, tc.cborData, v.Elem().Interface(), value, tc.equalityThreshold)
+			}
+		}
+
+		// Test unmarshalling CBOR into incompatible data types.
+		for _, typ := range tc.wrongTypes {
+			v := reflect.New(typ)
+			vPtr := v.Interface()
+			if err := Unmarshal(tc.cborData, vPtr); err == nil {
+				t.Errorf("Unmarshal(0x%x) didn't return an error", tc.cborData)
+			} else if _, ok := err.(*UnmarshalTypeError); !ok {
+				t.Errorf("Unmarshal(0x%x) returned wrong error type %T, want (*UnmarshalTypeError)", tc.cborData, err)
+			} else if !strings.Contains(err.Error(), "cannot unmarshal") {
+				t.Errorf("Unmarshal(0x%x) returned error %q, want error containing %q", tc.cborData, err.Error(), "cannot unmarshal")
+			}
+		}
+	}
+}
+
+func TestUnmarshalTagNum55799AsElement(t *testing.T) {
+	testCases := []struct {
+		name                string
+		cborData            []byte
+		emptyInterfaceValue interface{}
+		values              []interface{}
+		wrongTypes          []reflect.Type
+	}{
+		{
+			"array",
+			hexDecode("d9d9f783d9d9f701d9d9f702d9d9f703"), // 55799([55799(1), 55799(2), 55799(3)])
+			[]interface{}{uint64(1), uint64(2), uint64(3)},
+			[]interface{}{[]interface{}{uint64(1), uint64(2), uint64(3)}, []byte{1, 2, 3}, []int{1, 2, 3}, []uint{1, 2, 3}, [0]int{}, [1]int{1}, [3]int{1, 2, 3}, [5]int{1, 2, 3, 0, 0}, []float32{1, 2, 3}, []float64{1, 2, 3}},
+			[]reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeFloat32, typeFloat64, typeString, typeBool, typeStringSlice, typeMapStringInt, reflect.TypeOf([3]string{}), typeTag, typeRawTag},
+		},
+		{
+			"map",
+			hexDecode("d9d9f7a2d9d9f701d9d9f702d9d9f703d9d9f704"), // 55799({55799(1): 55799(2), 55799(3): 55799(4)})
+			map[interface{}]interface{}{uint64(1): uint64(2), uint64(3): uint64(4)},
+			[]interface{}{map[interface{}]interface{}{uint64(1): uint64(2), uint64(3): uint64(4)}, map[uint]int{1: 2, 3: 4}, map[int]uint{1: 2, 3: 4}},
+			[]reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeFloat32, typeFloat64, typeByteSlice, typeByteArray, typeString, typeBool, typeIntSlice, typeMapStringInt, typeTag, typeRawTag},
+		},
+	}
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			// Test unmarshalling CBOR into empty interface.
+			var v interface{}
+			if err := Unmarshal(tc.cborData, &v); err != nil {
+				t.Errorf("Unmarshal(0x%x) returned error %v", tc.cborData, err)
+			} else {
+				if tm, ok := tc.emptyInterfaceValue.(time.Time); ok {
+					if vt, ok := v.(time.Time); !ok || !tm.Equal(vt) {
+						t.Errorf("Unmarshal(0x%x) = %v (%T), want %v (%T)", tc.cborData, v, v, tc.emptyInterfaceValue, tc.emptyInterfaceValue)
+					}
+				} else if !reflect.DeepEqual(v, tc.emptyInterfaceValue) {
+					t.Errorf("Unmarshal(0x%x) = %v (%T), want %v (%T)", tc.cborData, v, v, tc.emptyInterfaceValue, tc.emptyInterfaceValue)
+				}
+			}
+
+			// Test unmarshalling CBOR into compatible data types.
+			for _, value := range tc.values {
+				v := reflect.New(reflect.TypeOf(value))
+				vPtr := v.Interface()
+				if err := Unmarshal(tc.cborData, vPtr); err != nil {
+					t.Errorf("Unmarshal(0x%x) returned error %v", tc.cborData, err)
+				} else {
+					if tm, ok := value.(time.Time); ok {
+						if vt, ok := v.Elem().Interface().(time.Time); !ok || !tm.Equal(vt) {
+							t.Errorf("Unmarshal(0x%x) = %v (%T), want %v (%T)", tc.cborData, v.Elem().Interface(), v.Elem().Interface(), value, value)
+						}
+					} else if !reflect.DeepEqual(v.Elem().Interface(), value) {
+						t.Errorf("Unmarshal(0x%x) = %v (%T), want %v (%T)", tc.cborData, v.Elem().Interface(), v.Elem().Interface(), value, value)
+					}
+				}
+			}
+
+			// Test unmarshalling CBOR into incompatible data types.
+			for _, typ := range tc.wrongTypes {
+				v := reflect.New(typ)
+				vPtr := v.Interface()
+				if err := Unmarshal(tc.cborData, vPtr); err == nil {
+					t.Errorf("Unmarshal(0x%x, %s) didn't return an error", tc.cborData, typ.String())
+				} else if _, ok := err.(*UnmarshalTypeError); !ok {
+					t.Errorf("Unmarshal(0x%x) returned wrong error type %T, want (*UnmarshalTypeError)", tc.cborData, err)
+				} else if !strings.Contains(err.Error(), "cannot unmarshal") {
+					t.Errorf("Unmarshal(0x%x) returned error %q, want error containing %q", tc.cborData, err.Error(), "cannot unmarshal")
+				}
+			}
+		})
+	}
+}
+
+func TestUnmarshalTagNum55799ToBinaryUnmarshaler(t *testing.T) {
+	cborData := hexDecode("d9d9f74800000000499602d2") // 55799(h'00000000499602D2')
+	wantObj := number(1234567890)
+
+	var v number
+	if err := Unmarshal(cborData, &v); err != nil {
+		t.Errorf("Unmarshal(0x%x) returned error %v", cborData, err)
+	} else if !reflect.DeepEqual(v, wantObj) {
+		t.Errorf("Unmarshal(0x%x) = %v (%T), want %v (%T)", cborData, v, v, wantObj, wantObj)
+	}
+}
+
+func TestUnmarshalTagNum55799ToUnmarshaler(t *testing.T) {
+	cborData := hexDecode("d9d9f7d864a1636e756d01") // 55799(100({"num": 1}))
+	wantObj := number3(1)
+
+	var v number3
+	if err := Unmarshal(cborData, &v); err != nil {
+		t.Errorf("Unmarshal(0x%x) returned error %v", cborData, err)
+	} else if !reflect.DeepEqual(v, wantObj) {
+		t.Errorf("Unmarshal(0x%x) = %v (%T), want %v (%T)", cborData, v, v, wantObj, wantObj)
+	}
+}
+
+func TestUnmarshalTagNum55799ToRegisteredGoType(t *testing.T) {
+	type myInt int
+	typ := reflect.TypeOf(myInt(0))
+
+	tags := NewTagSet()
+	if err := tags.Add(TagOptions{EncTag: EncTagRequired, DecTag: DecTagRequired}, typ, 125); err != nil {
+		t.Fatalf("TagSet.Add(%s, %v) returned error %v", typ, 125, err)
+	}
+
+	dm, _ := DecOptions{}.DecModeWithTags(tags)
+
+	cborData := hexDecode("d9d9f7d87d01") // 55799(125(1))
+	wantObj := myInt(1)
+
+	var v myInt
+	if err := dm.Unmarshal(cborData, &v); err != nil {
+		t.Errorf("Unmarshal(0x%x) returned error %v", cborData, err)
+	} else if !reflect.DeepEqual(v, wantObj) {
+		t.Errorf("Unmarshal(0x%x) = %v (%T), want %v (%T)", cborData, v, v, wantObj, wantObj)
+	}
+}
+
+// TODO: wait for clarification from 7049bis https://github.com/cbor-wg/CBORbis/issues/183
+// Nested tag number 55799 may be stripeed as well depending on 7049bis clarification.
+func TestUnmarshalNestedTagNum55799ToEmptyInterface(t *testing.T) {
+	cborData := hexDecode("d864d9d9f701") // 100(55799(1))
+	wantObj := Tag{100, Tag{55799, uint64(1)}}
+
+	var v interface{}
+	if err := Unmarshal(cborData, &v); err != nil {
+		t.Errorf("Unmarshal(0x%x) returned error %v", cborData, err)
+	} else if !reflect.DeepEqual(v, wantObj) {
+		t.Errorf("Unmarshal(0x%x) = %v (%T), want %v (%T)", cborData, v, v, wantObj, wantObj)
+	}
+}
+
+func TestUnmarshalNestedTagNum55799ToValue(t *testing.T) {
+	cborData := hexDecode("d864d9d9f701") // 100(55799(1))
+	wantObj := 1
+
+	var v int
+	if err := Unmarshal(cborData, &v); err != nil {
+		t.Errorf("Unmarshal(0x%x) returned error %v", cborData, err)
+	} else if !reflect.DeepEqual(v, wantObj) {
+		t.Errorf("Unmarshal(0x%x) = %v (%T), want %v (%T)", cborData, v, v, wantObj, wantObj)
+	}
+}
+
+func TestUnmarshalNestedTagNum55799ToTag(t *testing.T) {
+	cborData := hexDecode("d864d9d9f701") // 100(55799(1))
+	wantObj := Tag{100, Tag{55799, uint64(1)}}
+
+	var v Tag
+	if err := Unmarshal(cborData, &v); err != nil {
+		t.Errorf("Unmarshal(0x%x) returned error %v", cborData, err)
+	} else if !reflect.DeepEqual(v, wantObj) {
+		t.Errorf("Unmarshal(0x%x) = %v (%T), want %v (%T)", cborData, v, v, wantObj, wantObj)
+	}
+}
+
+func TestUnmarshalNestedTagNum55799ToTime(t *testing.T) {
+	cborData := hexDecode("c0d9d9f774323031332d30332d32315432303a30343a30305a") // 0(55799("2013-03-21T20:04:00Z"))
+	wantErrorMsg := "tag number 0 must be followed by text string, got tag"
+
+	var v time.Time
+	if err := Unmarshal(cborData, &v); err == nil {
+		t.Errorf("Unmarshal(0x%x) didn't return error", cborData)
+	} else if !strings.Contains(err.Error(), wantErrorMsg) {
+		t.Errorf("Unmarshal(0x%x) returned error %s, want %s", cborData, err.Error(), wantErrorMsg)
+	}
+}
+
+func TestUnmarshalNestedTagNum55799ToBinaryUnmarshaler(t *testing.T) {
+	cborData := hexDecode("d864d9d9f74800000000499602d2") // 100(55799(h'00000000499602D2'))
+	wantObj := number(1234567890)
+
+	var v number
+	if err := Unmarshal(cborData, &v); err != nil {
+		t.Errorf("Unmarshal(0x%x) returned error %v", cborData, err)
+	} else if !reflect.DeepEqual(v, wantObj) {
+		t.Errorf("Unmarshal(0x%x) = %v (%T), want %v (%T)", cborData, v, v, wantObj, wantObj)
+	}
+}
+
+func TestUnmarshalNestedTagNum55799ToUnmarshaler(t *testing.T) {
+	cborData := hexDecode("d864d9d9f7a1636e756d01") // 100(55799({"num": 1}))
+	wantErrorMsg := "wrong tag content type"
+
+	var v number3
+	if err := Unmarshal(cborData, &v); err == nil {
+		t.Errorf("Unmarshal(0x%x) didn't return error", cborData)
+	} else if !strings.Contains(err.Error(), wantErrorMsg) {
+		t.Errorf("Unmarshal(0x%x) returned error %s, want %s", cborData, err.Error(), wantErrorMsg)
+	}
+}
+
+func TestUnmarshalNestedTagNum55799ToRegisteredGoType(t *testing.T) {
+	type myInt int
+	typ := reflect.TypeOf(myInt(0))
+
+	tags := NewTagSet()
+	if err := tags.Add(TagOptions{EncTag: EncTagRequired, DecTag: DecTagRequired}, typ, 125); err != nil {
+		t.Fatalf("TagSet.Add(%s, %v) returned error %v", typ, 125, err)
+	}
+
+	dm, _ := DecOptions{}.DecModeWithTags(tags)
+
+	cborData := hexDecode("d87dd9d9f701") // 125(55799(1))
+	wantErrorMsg := "cbor: wrong tag number for cbor.myInt, got [125 55799], expected [125]"
+
+	var v myInt
+	if err := dm.Unmarshal(cborData, &v); err == nil {
+		t.Errorf("Unmarshal() didn't return error")
+	} else if !strings.Contains(err.Error(), wantErrorMsg) {
+		t.Errorf("Unmarshal(0x%x) returned error %s, want %s", cborData, err.Error(), wantErrorMsg)
+	}
+}
diff --git a/tag_test.go b/tag_test.go
index c90f444..1dfe94e 100644
--- a/tag_test.go
+++ b/tag_test.go
@@ -363,6 +363,13 @@ func TestAddTagError(t *testing.T) {
 			opts:         TagOptions{DecTag: DecTagRequired, EncTag: EncTagRequired},
 			wantErrorMsg: "cbor: cannot add tag number 0 or 1 to TagSet, use EncOptions.TimeTag and DecOptions.TimeTag instead",
 		},
+		{
+			name:         "tag number 55799",
+			typ:          reflect.TypeOf(myInt(0)),
+			num:          55799,
+			opts:         TagOptions{DecTag: DecTagRequired, EncTag: EncTagRequired},
+			wantErrorMsg: "cbor: cannot add tag number 55799 to TagSet, it's built-in and ignored automatically",
+		},
 	}
 	tags := NewTagSet()
 	for _, tc := range testCases {
@@ -1252,7 +1259,11 @@ func (n *number3) UnmarshalCBOR(data []byte) (err error) {
 	}
 
 	if rawTag.Number != 100 {
-		return fmt.Errorf("wrong tag number %d, want %d", rawTag.Number, 101)
+		return fmt.Errorf("wrong tag number %d, want %d", rawTag.Number, 100)
+	}
+
+	if rawTag.Content[0]&0xe0 != 0xa0 {
+		return fmt.Errorf("wrong tag content type, want map")
 	}
 
 	var v map[string]uint64