Repository: fxamacker/cbor
Pull request #149 (2020-02-13 20:15:50)

v2.1 CBOR tags, faster CBOR decoding & less memory
Support CBOR tags, faster CBOR decoding & less memory

- [x] #44 - Add support for CBOR tags (major type 6)
- [x] #147 - Decode "keyasint" structs faster and with less memory
- [x] #125 - Add encoding option to tag or not tag time values, and decoding option to validate tags.
- [x] #151 - Encode tag-required zero time as CBOR Undefined
- [x] #47 - Improve decoding speed (optimizations already identified during v1)
- [x] Cleanup and fix reported lint issues for #44, #47, #125, #147 changes
- [x] Basic optimization for CBOR tags feature, more can be done in later releases
- [x] Decided to combine v2.1 and v2.2 to reduce overhead (fuzzing, docs, benchmarks, charts, etc.)
- [x] Decided (Un)MarshalerWithMode isn't needed in v2.1.
- [x] Get feedback on proposed v2.1 API and decide what features to add/modify/remove. See #136. 
  - [x] Will add EncModeWithSharedTags (there will be 3 funcs to create EncMode, see table below)
  - [x] Will make sure EncMode, DecMode, and TagSet can be initialized before init() 

CBOR tag support includes:
- API to use TagSet to register user-defined Go type with CBOR tag number.
- API to encode Go value with CBOR tag number without registering.
- API to extract CBOR tag number with Tag or RawTag structs.
- API to use immutable TagSet or mutable shared TagSet (shared tag registry).
- EncOptions/DecOptions settings to tag or not tag time.Time values.

Improved decoding speed and memory for "keyasint" struct (COSE, CWT, etc.):
- speed is faster by 24% - 28%.
- mem allocs is reduced by 44% - 61%.
- mem bytes is reduced by 13% - 26%.

Encoding is a bit slower around 2% - 4% because of added features.  Optimization of encoding can be tackled in future releases because it is still very fast.

TagSet is a Go interface to register tag support for user-defined Go types.

EncMode and DecMode are reusable interfaces that are safe for concurrency.  They export codec functions with identical signatures to encoding/json.

There will be three ways to create EncMode (all are reusable and concurrency safe interface.)

|Function (returns EncMode)               | Encoding Mode                                                                |
|---------------------------------------------------|------------------------------------------------------------------------------|
|EncOptions{...}.EncMode() | immutable options, no tags
|:new: EncOptions{...}.EncModeWithTags(ts) | both options & tags are immutable
|:new: EncOptions{...}.EncModeWithSharedTags(ts) | immutable options & mutable shared tags |

There will be three ways to create DecMode (all are reusable and concurrency safe interface.)

|Function (returns DecMode)               | Decoding Mode                                                                |
|---------------------------------------------------|------------------------------------------------------------------------------|
|DecOptions{...}.DecMode() | immutable options, no tags
|:new: DecOptions{...}.DecModeWithTags(ts) | both options & tags are immutable
|:new: DecOptions{...}.DecModeWithSharedTags(ts) | immutable options & mutable shared tags |

Tag and RawTag are structs used to encode/decode CBOR tag number with tag content.

Updates: #136
Updates: #118

Closed: #44
Closed: #47
Closed: #125
Closed: #147
Closed: #151

x448: @fxamacker, issue #121 is missing from the checklist.
x448: Maybe new error messages should replace "can't" with "cannot" to get rid of single quote.
x448: The comment for methods that create and return EncMode or DecMode should add a sentence like:
* Returned EncMode has immutable options and no tags (safe for concurrency)
* Returned EncMode has options and tags that are both immutable (safe for concurrency)
* Returned EncMode has immutable options and mutable shared tags (safe for concurrency)

Similar for DecMode.

x448: @fxamacker, if you want, I think you can merge this to @master.  Then do separate PR for duplidate map key option.  

And also maybe another separate PR for README.md and benchmark changes which can happen after fuzzing begins but need to finish before release tag.

Patch:
diff --git a/.golangci.yml b/.golangci.yml
index 227fc40..0448042 100644
--- a/.golangci.yml
+++ b/.golangci.yml
@@ -70,8 +70,15 @@ issues:
   exclude-rules:
     - path: _test\.go
       linters:
+        - goconst
+        - dupl
         - gomnd
         - lll        
+    - path: doc\.go
+      linters:
+        - goimports
+        - gomnd
+        - lll
 
 # golangci.com configuration
 # https://github.com/golangci/golangci/wiki/Configuration
diff --git a/cache.go b/cache.go
index d90f7c7..ace1669 100644
--- a/cache.go
+++ b/cache.go
@@ -1,113 +1,11 @@
 // Copyright (c) Faye Amacker. All rights reserved.
 // Licensed under the MIT License. See LICENSE in the project root for license information.
 
-/*
-Package cbor provides a fuzz-tested CBOR encoder and decoder with full support
-for float16, Canonical CBOR, CTAP2 Canonical CBOR, and custom settings.
-
-CBOR encoding options allow "preferred serialization" by encoding integers and floats
-to their smallest forms (like float16) when values fit.
-
-Struct tags like "keyasint", "toarray" and "omitempty" makes CBOR data smaller.
-
-For example, "toarray" makes struct fields encode to array elements.  And "keyasint"
-makes struct fields encode to elements of CBOR map with int keys.
-
-Basics
-
-Function signatures identical to encoding/json include:
-
-    Marshal, Unmarshal, NewEncoder, NewDecoder, encoder.Encode, decoder.Decode.
-
-Codec functions are available at package-level (using defaults) or by creating modes
-from options at runtime.
-
-"Mode" in this API means definite way of encoding or decoding. Specifically, EncMode or DecMode.
-
-EncMode and DecMode interfaces are created from EncOptions or DecOptions structs.  For example,
-
-    em := cbor.EncOptions{...}.EncMode()
-    em := cbor.CanonicalEncOptions().EncMode()
-    em := cbor.CTAP2EncOptions().EncMode()
-
-Modes use immutable options to avoid side-effects and simplify concurrency. Behavior of modes
-won't accidentally change at runtime after they're created.
-
-Modes are intended to be reused and are safe for concurrent use.
-
-EncMode and DecMode Interfaces
-
-    // EncMode interface uses immutable options and is safe for concurrent use.
-    type EncMode interface {
-	Marshal(v interface{}) ([]byte, error)
-	NewEncoder(w io.Writer) *Encoder
-	EncOptions() EncOptions  // returns copy of options
-    }
-
-    // DecMode interface uses immutable options and is safe for concurrent use.
-    type DecMode interface {
-	Unmarshal(data []byte, v interface{}) error
-	NewDecoder(r io.Reader) *Decoder
-	DecOptions() DecOptions  // returns copy of options
-    }
-
-Using Default Encoding Mode
-
-    b, err := cbor.Marshal(v)
-
-    encoder := cbor.NewEncoder(w)
-    err = encoder.Encode(v)
-
-Using Default Decoding Mode
-
-    err := cbor.Unmarshal(b, &v)
-
-    decoder := cbor.NewDecoder(r)
-    err = decoder.Decode(&v)
-
-Creating and Using Encoding Modes
-
-    // Create EncOptions using either struct literal or a function.
-    opts := cbor.CanonicalEncOptions()
-
-    // If needed, modify encoding options
-    opts.Time = cbor.TimeUnix
-
-    // Create reusable EncMode interface with immutable options, safe for concurrent use.
-    em, err := opts.EncMode()
-
-    // Use EncMode like encoding/json, with same function signatures.
-    b, err := em.Marshal(v)
-    // or
-    encoder := em.NewEncoder(w)
-    err := encoder.Encode(v)
-
-Default Options
-
-Default encoding options are listed at https://github.com/fxamacker/cbor#api
-
-Struct Tags
-
-Struct tags like `cbor:"name,omitempty"` and `json:"name,omitempty"` work as expected.
-If both struct tags are specified then `cbor` is used.
-
-Struct tags like "keyasint", "toarray", and "omitempty" make it easy to use
-very compact formats like COSE and CWT (CBOR Web Tokens) with structs.
-
-For example, "toarray" makes struct fields encode to array elements.  And "keyasint"
-makes struct fields encode to elements of CBOR map with int keys.
-
-https://raw.githubusercontent.com/fxamacker/images/master/cbor/v2.0.0/cbor_easy_api.png
-
-Tests and Fuzzing
-
-Over 375 tests are included in this package. Cover-guided fuzzing is handled by a separate package:
-fxamacker/cbor-fuzz.
-*/
 package cbor
 
 import (
 	"bytes"
+	"errors"
 	"reflect"
 	"sort"
 	"strconv"
@@ -119,10 +17,65 @@ var (
 	decodingStructTypeCache sync.Map // map[reflect.Type]*decodingStructType
 	encodingStructTypeCache sync.Map // map[reflect.Type]*encodingStructType
 	encodeFuncCache         sync.Map // map[reflect.Type]encodeFunc
+	typeInfoCache           sync.Map // map[reflect.Type]*typeInfo
 )
 
+type specialType int
+
+const (
+	specialTypeNone specialType = iota
+	specialTypeUnmarshalerIface
+	specialTypeEmptyIface
+	specialTypeTag
+	specialTypeTime
+)
+
+type typeInfo struct {
+	elemTypeInfo *typeInfo
+	keyTypeInfo  *typeInfo
+	typ          reflect.Type
+	kind         reflect.Kind
+	nonPtrType   reflect.Type
+	nonPtrKind   reflect.Kind
+	spclType     specialType
+}
+
+func newTypeInfo(t reflect.Type) *typeInfo {
+	tInfo := typeInfo{typ: t, kind: t.Kind()}
+
+	for t.Kind() == reflect.Ptr {
+		t = t.Elem()
+	}
+
+	k := t.Kind()
+
+	tInfo.nonPtrType = t
+	tInfo.nonPtrKind = k
+
+	if k == reflect.Interface && t.NumMethod() == 0 {
+		tInfo.spclType = specialTypeEmptyIface
+	} else if t == typeTag {
+		tInfo.spclType = specialTypeTag
+	} else if t == typeTime {
+		tInfo.spclType = specialTypeTime
+	} else if reflect.PtrTo(t).Implements(typeUnmarshaler) {
+		tInfo.spclType = specialTypeUnmarshalerIface
+	}
+
+	switch k {
+	case reflect.Array, reflect.Slice:
+		tInfo.elemTypeInfo = getTypeInfo(t.Elem())
+	case reflect.Map:
+		tInfo.keyTypeInfo = getTypeInfo(t.Key())
+		tInfo.elemTypeInfo = getTypeInfo(t.Elem())
+	}
+
+	return &tInfo
+}
+
 type decodingStructType struct {
 	fields  fields
+	err     error
 	toArray bool
 }
 
@@ -135,11 +88,21 @@ func getDecodingStructType(t reflect.Type) *decodingStructType {
 
 	toArray := hasToArrayOption(structOptions)
 
+	var err error
 	for i := 0; i < len(flds); i++ {
-		flds[i].isUnmarshaler = implementsUnmarshaler(flds[i].typ)
+		if flds[i].keyAsInt {
+			nameAsInt, numErr := strconv.Atoi(flds[i].name)
+			if numErr != nil {
+				err = errors.New("cbor: failed to parse field name \"" + flds[i].name + "\" to int (" + numErr.Error() + ")")
+				break
+			}
+			flds[i].nameAsInt = int64(nameAsInt)
+		}
+
+		flds[i].typInfo = getTypeInfo(flds[i].typ)
 	}
 
-	structType := &decodingStructType{fields: flds, toArray: toArray}
+	structType := &decodingStructType{fields: flds, err: err, toArray: toArray}
 	decodingStructTypeCache.Store(t, structType)
 	return structType
 }
@@ -228,9 +191,10 @@ func getEncodingStructType(t reflect.Type) *encodingStructType {
 		if flds[i].keyAsInt {
 			nameAsInt, numErr := strconv.Atoi(flds[i].name)
 			if numErr != nil {
-				err = numErr
+				err = errors.New("cbor: failed to parse field name \"" + flds[i].name + "\" to int (" + numErr.Error() + ")")
 				break
 			}
+			flds[i].nameAsInt = int64(nameAsInt)
 			if nameAsInt >= 0 {
 				encodeHead(e, byte(cborTypePositiveInt), uint64(nameAsInt))
 			} else {
@@ -328,6 +292,15 @@ func getEncodeFunc(t reflect.Type) encodeFunc {
 	return f
 }
 
+func getTypeInfo(t reflect.Type) *typeInfo {
+	if v, _ := typeInfoCache.Load(t); v != nil {
+		return v.(*typeInfo)
+	}
+	tInfo := newTypeInfo(t)
+	typeInfoCache.Store(t, tInfo)
+	return tInfo
+}
+
 func hasToArrayOption(tag string) bool {
 	s := ",toarray"
 	idx := strings.Index(tag, s)
diff --git a/decode.go b/decode.go
index d6a2a2a..641fe94 100644
--- a/decode.go
+++ b/decode.go
@@ -144,11 +144,63 @@ func (e *UnmarshalTypeError) Error() string {
 
 // DecOptions specifies decoding options.
 type DecOptions struct {
+	// TimeTag specifies whether to check validity of time.Time (e.g. valid tag number and tag content type).
+	// For now, valid tag number means 0 or 1 as specified in RFC 7049 if the Go type is time.Time.
+	TimeTag DecTagMode
 }
 
-// DecMode returns a DecMode interface from DecOptions.
+// DecMode returns DecMode with immutable options and no tags (safe for concurrency).
 func (opts DecOptions) DecMode() (DecMode, error) {
-	dm := decMode{}
+	return opts.decMode()
+}
+
+// DecModeWithTags returns DecMode with options and tags that are both immutable (safe for concurrency).
+func (opts DecOptions) DecModeWithTags(tags TagSet) (DecMode, error) {
+	if tags == nil {
+		return nil, errors.New("cbor: cannot create DecMode with nil value as TagSet")
+	}
+
+	dm, err := opts.decMode()
+	if err != nil {
+		return nil, err
+	}
+
+	// Copy tags
+	ts := tagSet(make(map[reflect.Type]*tagItem))
+	syncTags := tags.(*syncTagSet)
+	syncTags.RLock()
+	for contentType, tag := range syncTags.t {
+		if tag.opts.DecTag != DecTagIgnored {
+			ts[contentType] = tag
+		}
+	}
+	syncTags.RUnlock()
+
+	if len(ts) > 0 {
+		dm.tags = ts
+	}
+
+	return dm, nil
+}
+
+// DecModeWithSharedTags returns DecMode with immutable options and mutable shared tags (safe for concurrency).
+func (opts DecOptions) DecModeWithSharedTags(tags TagSet) (DecMode, error) {
+	if tags == nil {
+		return nil, errors.New("cbor: cannot create DecMode with nil value as TagSet")
+	}
+	dm, err := opts.decMode()
+	if err != nil {
+		return nil, err
+	}
+	dm.tags = tags
+	return dm, nil
+}
+
+func (opts DecOptions) decMode() (*decMode, error) {
+	if !opts.TimeTag.valid() {
+		return nil, errors.New("cbor: invalid TimeTag " + strconv.Itoa(int(opts.TimeTag)))
+	}
+	dm := decMode{timeTag: opts.TimeTag}
 	return &dm, nil
 }
 
@@ -160,13 +212,15 @@ type DecMode interface {
 }
 
 type decMode struct {
+	tags    tagProvider
+	timeTag DecTagMode
 }
 
 var defaultDecMode = &decMode{}
 
 // DecOptions returns user specified options used to create this DecMode.
 func (dm *decMode) DecOptions() DecOptions {
-	return DecOptions{}
+	return DecOptions{TimeTag: dm.timeTag}
 }
 
 // Unmarshal parses the CBOR-encoded data and stores the result in the value
@@ -203,7 +257,7 @@ func (d *decodeState) value(v interface{}) error {
 	rv = rv.Elem()
 
 	if rv.Kind() == reflect.Interface && rv.NumMethod() == 0 {
-		// Fast path to decode to empty interface without calling implementsUnmarshaler.
+		// Fast path to decode to empty interface without retrieving typeInfo.
 		iv, err := d.parse()
 		if iv != nil {
 			rv.Set(reflect.ValueOf(iv))
@@ -211,7 +265,7 @@ func (d *decodeState) value(v interface{}) error {
 		return err
 	}
 
-	return d.parseToValue(rv, implementsUnmarshaler(rv.Type()))
+	return d.parseToValue(rv, getTypeInfo(rv.Type()))
 }
 
 type cborType uint8
@@ -252,19 +306,7 @@ func (t cborType) String() string {
 
 // 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, isUnmarshaler bool) error { //nolint:gocyclo
-	if isUnmarshaler {
-		return d.parseToUnmarshaler(v)
-	}
-
-	if v.Kind() == reflect.Interface && v.NumMethod() == 0 {
-		iv, err := d.parse()
-		if iv != nil {
-			v.Set(reflect.ValueOf(iv))
-		}
-		return err
-	}
-
+func (d *decodeState) parseToValue(v reflect.Value, tInfo *typeInfo) error { //nolint:gocyclo
 	// Create new value for the pointer v to point to if CBOR value is not nil/undefined.
 	if !d.nextCBORNil() {
 		for v.Kind() == reflect.Ptr {
@@ -279,7 +321,49 @@ func (d *decodeState) parseToValue(v reflect.Value, isUnmarshaler bool) error {
 		}
 	}
 
+	if tInfo.spclType != specialTypeNone {
+		switch tInfo.spclType {
+		case specialTypeEmptyIface:
+			iv, err := d.parse()
+			if iv != nil {
+				v.Set(reflect.ValueOf(iv))
+			}
+			return err
+		case specialTypeTag:
+			return d.parseToTag(v)
+		case specialTypeTime:
+			return d.parseToTime(v)
+		case specialTypeUnmarshalerIface:
+			return d.parseToUnmarshaler(v)
+		}
+	}
+
+	// Check registered tag number
+	if tagItem := d.getRegisteredTagItem(tInfo.nonPtrType); tagItem != nil {
+		t := d.nextCBORType()
+		if t != cborTypeTag {
+			if tagItem.opts.DecTag == DecTagRequired {
+				d.skip() // Required tag number is absent, skip entire tag
+				return &UnmarshalTypeError{Value: t.String(), Type: tInfo.typ, errMsg: "expect CBOR tag value"}
+			}
+		} else if err := d.validRegisteredTagNums(tInfo.nonPtrType, tagItem.num); err != nil {
+			d.skip() // Skip tag content
+			return err
+		}
+	}
+
 	t := d.nextCBORType()
+
+	// Skip tag number(s) here to avoid recursion
+	if t == cborTypeTag {
+		d.getHead()
+		t = d.nextCBORType()
+		for t == cborTypeTag {
+			d.getHead()
+			t = d.nextCBORType()
+		}
+	}
+
 	switch t {
 	case cborTypePositiveInt:
 		_, _, val := d.getHead()
@@ -287,7 +371,11 @@ func (d *decodeState) parseToValue(v reflect.Value, isUnmarshaler bool) error {
 	case cborTypeNegativeInt:
 		_, _, val := d.getHead()
 		if val > math.MaxInt64 {
-			return &UnmarshalTypeError{Value: t.String(), Type: v.Type(), errMsg: "-1-" + strconv.FormatUint(val, 10) + " overflows Go's int64"}
+			return &UnmarshalTypeError{
+				Value:  t.String(),
+				Type:   tInfo.nonPtrType,
+				errMsg: "-1-" + strconv.FormatUint(val, 10) + " overflows Go's int64",
+			}
 		}
 		nValue := int64(-1) ^ int64(val)
 		return fillNegativeInt(t, nValue, v)
@@ -300,9 +388,6 @@ func (d *decodeState) parseToValue(v reflect.Value, isUnmarshaler bool) error {
 			return err
 		}
 		return fillTextString(t, b, v)
-	case cborTypeTag:
-		d.getHead() // Skip tag number
-		return d.parseToValue(v, isUnmarshaler)
 	case cborTypePrimitives:
 		_, ai, val := d.getHead()
 		if ai < 20 || ai == 24 {
@@ -324,41 +409,153 @@ func (d *decodeState) parseToValue(v reflect.Value, isUnmarshaler bool) error {
 			return fillFloat(t, f, v)
 		}
 	case cborTypeArray:
-		if v.Kind() == reflect.Slice {
-			return d.parseArrayToSlice(v)
-		} else if v.Kind() == reflect.Array {
-			return d.parseArrayToArray(v)
-		} else if v.Kind() == reflect.Struct {
-			return d.parseArrayToStruct(v)
+		if tInfo.nonPtrKind == reflect.Slice {
+			return d.parseArrayToSlice(v, tInfo)
+		} else if tInfo.nonPtrKind == reflect.Array {
+			return d.parseArrayToArray(v, tInfo)
+		} else if tInfo.nonPtrKind == reflect.Struct {
+			return d.parseArrayToStruct(v, tInfo)
 		}
 		d.skip()
-		return &UnmarshalTypeError{Value: t.String(), Type: v.Type()}
+		return &UnmarshalTypeError{Value: t.String(), Type: tInfo.nonPtrType}
 	case cborTypeMap:
-		if v.Kind() == reflect.Struct {
-			return d.parseMapToStruct(v)
-		} else if v.Kind() == reflect.Map {
-			return d.parseMapToMap(v)
+		if tInfo.nonPtrKind == reflect.Struct {
+			return d.parseMapToStruct(v, tInfo)
+		} else if tInfo.nonPtrKind == reflect.Map {
+			return d.parseMapToMap(v, tInfo)
 		}
 		d.skip()
-		return &UnmarshalTypeError{Value: t.String(), Type: v.Type()}
+		return &UnmarshalTypeError{Value: t.String(), Type: tInfo.nonPtrType}
 	}
 	return nil
 }
 
-// parseToUnmarshaler assumes data is well-formed, and does not perform bounds checking.
-func (d *decodeState) parseToUnmarshaler(v reflect.Value) error {
-	if !d.nextCBORNil() {
-		for v.Kind() == reflect.Ptr {
-			if v.IsNil() {
-				if !v.CanSet() {
+func (d *decodeState) parseToTag(v reflect.Value) error {
+	t := d.nextCBORType()
+	if t != cborTypeTag {
+		d.skip()
+		return &UnmarshalTypeError{Value: t.String(), Type: typeTag}
+	}
+
+	// Unmarshal tag number
+	_, _, num := d.getHead()
+
+	// Unmarshal tag content
+	content, err := d.parse()
+	if err != nil {
+		return err
+	}
+
+	v.Set(reflect.ValueOf(Tag{num, content}))
+	return nil
+}
+
+func (d *decodeState) parseToTime(v reflect.Value) error {
+	t := d.nextCBORType()
+
+	// Verify that tag number or absent of tag number is acceptable to specified timeTag.
+	if t == cborTypeTag {
+		if d.dm.timeTag == DecTagIgnored {
+			// Skip tag number
+			d.getHead()
+			t = d.nextCBORType()
+			for t == cborTypeTag {
+				d.getHead()
+				t = d.nextCBORType()
+			}
+		} else {
+			// Read tag number
+			_, _, tagNum := d.getHead()
+
+			// Verify tag number (0 or 1) is followed by appropriate tag content type.
+			t = d.nextCBORType()
+			switch tagNum {
+			case 0:
+				// Tag content (date/time text string in RFC 3339 format) must be string type.
+				if t != cborTypeTextString {
 					d.skip()
-					return errors.New("cbor: cannot set new value for " + v.Type().String())
+					return errors.New("cbor: tag number 0 must be followed by text string, got " + t.String())
 				}
-				v.Set(reflect.New(v.Type().Elem()))
+			case 1:
+				// Tag content (epoch date/time) must be uint, int, or float type.
+				if t != cborTypePositiveInt && t != cborTypeNegativeInt && (d.data[d.off] < 0xf9 || d.data[d.off] > 0xfb) {
+					d.skip()
+					return errors.New("cbor: tag number 1 must be followed by integer or floating-point number, got " + t.String())
+				}
+			default:
+				d.skip()
+				return errors.New("cbor: wrong tag number for time.Time, got " + strconv.Itoa(int(tagNum)) + ", expect 0 or 1")
 			}
-			v = v.Elem()
 		}
-	} else if v.Kind() == reflect.Ptr && v.IsNil() {
+	} else {
+		if d.dm.timeTag == DecTagRequired {
+			d.skip()
+			return &UnmarshalTypeError{Value: t.String(), Type: typeTime, errMsg: "expect CBOR tag value"}
+		}
+	}
+
+	switch t {
+	case cborTypePositiveInt:
+		_, _, val := d.getHead()
+		tm := time.Unix(int64(val), 0)
+		v.Set(reflect.ValueOf(tm))
+		return nil
+	case cborTypeNegativeInt:
+		_, _, val := d.getHead()
+		if val > math.MaxInt64 {
+			return &UnmarshalTypeError{
+				Value:  t.String(),
+				Type:   typeTime,
+				errMsg: "-1-" + strconv.FormatUint(val, 10) + " overflows Go's int64",
+			}
+		}
+		nValue := int64(-1) ^ int64(val)
+		tm := time.Unix(nValue, 0)
+		v.Set(reflect.ValueOf(tm))
+		return nil
+	case cborTypeTextString:
+		b, err := d.parseTextString()
+		if err != nil {
+			return err
+		}
+		tm, err := time.Parse(time.RFC3339, string(b))
+		if err != nil {
+			return errors.New("cbor: cannot set " + string(b) + " for time.Time: " + err.Error())
+		}
+		v.Set(reflect.ValueOf(tm))
+		return nil
+	case cborTypePrimitives:
+		_, ai, val := d.getHead()
+		var f float64
+		switch ai {
+		case 22, 23:
+			v.Set(reflect.ValueOf(time.Time{}))
+			return nil
+		case 25:
+			f = float64(float16.Frombits(uint16(val)).Float32())
+		case 26:
+			f = float64(math.Float32frombits(uint32(val)))
+		case 27:
+			f = math.Float64frombits(val)
+		default:
+			return &UnmarshalTypeError{Value: t.String(), Type: typeTime}
+		}
+		if math.IsNaN(f) || math.IsInf(f, 0) {
+			v.Set(reflect.ValueOf(time.Time{}))
+			return nil
+		}
+		f1, f2 := math.Modf(f)
+		tm := time.Unix(int64(f1), int64(f2*1e9))
+		v.Set(reflect.ValueOf(tm))
+		return nil
+	}
+	d.skip()
+	return &UnmarshalTypeError{Value: t.String(), Type: typeTime}
+}
+
+// parseToUnmarshaler assumes data is well-formed, and does not perform bounds checking.
+func (d *decodeState) parseToUnmarshaler(v reflect.Value) error {
+	if d.nextCBORNil() && v.Kind() == reflect.Ptr && v.IsNil() {
 		d.skip()
 		return nil
 	}
@@ -402,8 +599,39 @@ func (d *decodeState) parse() (interface{}, error) {
 		}
 		return string(b), nil
 	case cborTypeTag:
-		d.getHead() // Skip tag number
-		return d.parse()
+		_, _, tagNum := d.getHead()
+		nt := d.nextCBORType()
+		content, err := d.parse()
+		if err != nil {
+			return nil, err
+		}
+		switch tagNum {
+		case 0:
+			// Tag content should be date/time text string in RFC 3339 format.
+			s, ok := content.(string)
+			if !ok {
+				return nil, errors.New("cbor: tag number 0 must be followed by text string, got " + nt.String())
+			}
+			tm, err := time.Parse(time.RFC3339, s)
+			if err != nil {
+				return nil, errors.New("cbor: cannot set " + s + " for time.Time: " + err.Error())
+			}
+			return tm, nil
+		case 1:
+			// Tag content should be epoch date/time.
+			switch content := content.(type) {
+			case uint64:
+				return time.Unix(int64(content), 0), nil
+			case int64:
+				return time.Unix(content, 0), nil
+			case float64:
+				f1, f2 := math.Modf(content)
+				return time.Unix(int64(f1), int64(f2*1e9)), nil
+			default:
+				return nil, errors.New("cbor: tag number 1 must be followed by integer or floating-point number, got " + nt.String())
+			}
+		}
+		return Tag{tagNum, content}, nil
 	case cborTypePrimitives:
 		_, ai, val := d.getHead()
 		if ai < 20 || ai == 24 {
@@ -513,7 +741,7 @@ func (d *decodeState) parseArray() ([]interface{}, error) {
 	return v, err
 }
 
-func (d *decodeState) parseArrayToSlice(v reflect.Value) error {
+func (d *decodeState) parseArrayToSlice(v reflect.Value, tInfo *typeInfo) error {
 	_, ai, val := d.getHead()
 	hasSize := (ai != 31)
 	count := int(val)
@@ -521,16 +749,15 @@ func (d *decodeState) parseArrayToSlice(v reflect.Value) error {
 		count = d.numOfItemsUntilBreak() // peek ahead to get array size to preallocate slice for better performance
 	}
 	if count == 0 {
-		v.Set(reflect.MakeSlice(v.Type(), 0, 0))
+		v.Set(reflect.MakeSlice(tInfo.nonPtrType, 0, 0))
 	}
 	if v.IsNil() || v.Cap() < count {
-		v.Set(reflect.MakeSlice(v.Type(), count, count))
+		v.Set(reflect.MakeSlice(tInfo.nonPtrType, count, count))
 	}
 	v.SetLen(count)
-	elemIsUnmarshaler := implementsUnmarshaler(v.Type().Elem())
 	var err error
 	for i := 0; (hasSize && i < count) || (!hasSize && !d.foundBreak()); i++ {
-		if lastErr := d.parseToValue(v.Index(i), elemIsUnmarshaler); lastErr != nil {
+		if lastErr := d.parseToValue(v.Index(i), tInfo.elemTypeInfo); lastErr != nil {
 			if err == nil {
 				err = lastErr
 			}
@@ -539,15 +766,14 @@ func (d *decodeState) parseArrayToSlice(v reflect.Value) error {
 	return err
 }
 
-func (d *decodeState) parseArrayToArray(v reflect.Value) error {
+func (d *decodeState) parseArrayToArray(v reflect.Value, tInfo *typeInfo) error {
 	_, ai, val := d.getHead()
 	hasSize := (ai != 31)
 	count := int(val)
-	elemIsUnmarshaler := implementsUnmarshaler(v.Type().Elem())
 	i := 0
 	var err error
 	for ; i < v.Len() && ((hasSize && i < count) || (!hasSize && !d.foundBreak())); i++ {
-		if lastErr := d.parseToValue(v.Index(i), elemIsUnmarshaler); lastErr != nil {
+		if lastErr := d.parseToValue(v.Index(i), tInfo.elemTypeInfo); lastErr != nil {
 			if err == nil {
 				err = lastErr
 			}
@@ -555,7 +781,7 @@ func (d *decodeState) parseArrayToArray(v reflect.Value) error {
 	}
 	// Set remaining Go array elements to zero values.
 	if i < v.Len() {
-		zeroV := reflect.Zero(v.Type().Elem())
+		zeroV := reflect.Zero(tInfo.elemTypeInfo.typ)
 		for ; i < v.Len(); i++ {
 			v.Index(i).Set(zeroV)
 		}
@@ -583,6 +809,9 @@ func (d *decodeState) parseMap() (map[interface{}]interface{}, error) {
 			continue
 		}
 		kkind := reflect.ValueOf(k).Kind()
+		if tag, ok := k.(Tag); ok {
+			kkind = tag.contentKind()
+		}
 		if !isHashableKind(kkind) {
 			if err == nil {
 				err = errors.New("cbor: invalid map key type: " + kkind.String())
@@ -601,7 +830,7 @@ func (d *decodeState) parseMap() (map[interface{}]interface{}, error) {
 	return m, err
 }
 
-func (d *decodeState) parseMapToMap(v reflect.Value) error {
+func (d *decodeState) parseMapToMap(v reflect.Value, tInfo *typeInfo) error {
 	_, ai, val := d.getHead()
 	hasSize := (ai != 31)
 	count := int(val)
@@ -610,13 +839,11 @@ func (d *decodeState) parseMapToMap(v reflect.Value) error {
 		if !hasSize {
 			mapsize = 0
 		}
-		v.Set(reflect.MakeMapWithSize(v.Type(), mapsize))
+		v.Set(reflect.MakeMapWithSize(tInfo.nonPtrType, mapsize))
 	}
-	keyType, eleType := v.Type().Key(), v.Type().Elem()
-	reuseKey, reuseEle := isImmutableKind(keyType.Kind()), isImmutableKind(eleType.Kind())
+	keyType, eleType := tInfo.keyTypeInfo.typ, tInfo.elemTypeInfo.typ
+	reuseKey, reuseEle := isImmutableKind(tInfo.keyTypeInfo.kind), isImmutableKind(tInfo.elemTypeInfo.kind)
 	var keyValue, eleValue, zeroKeyValue, zeroEleValue reflect.Value
-	keyIsUnmarshaler := implementsUnmarshaler(v.Type().Key())
-	elemIsUnmarshaler := implementsUnmarshaler(v.Type().Elem())
 	keyIsInterfaceType := keyType == typeIntf // If key type is interface{}, need to check if key value is hashable.
 	var err, lastErr error
 	for i := 0; (hasSize && i < count) || (!hasSize && !d.foundBreak()); i++ {
@@ -628,19 +855,27 @@ func (d *decodeState) parseMapToMap(v reflect.Value) error {
 			}
 			keyValue.Set(zeroKeyValue)
 		}
-		if lastErr = d.parseToValue(keyValue, keyIsUnmarshaler); lastErr != nil {
+		if lastErr = d.parseToValue(keyValue, tInfo.keyTypeInfo); lastErr != nil {
 			if err == nil {
 				err = lastErr
 			}
 			d.skip()
 			continue
 		}
-		if keyIsInterfaceType && !isHashableKind(keyValue.Elem().Kind()) {
-			if err == nil {
-				err = errors.New("cbor: invalid map key type: " + keyValue.Elem().Kind().String())
+		if keyIsInterfaceType {
+			kkind := keyValue.Elem().Kind()
+			if keyValue.Elem().IsValid() {
+				if tag, ok := keyValue.Elem().Interface().(Tag); ok {
+					kkind = tag.contentKind()
+				}
+			}
+			if !isHashableKind(kkind) {
+				if err == nil {
+					err = errors.New("cbor: invalid map key type: " + kkind.String())
+				}
+				d.skip()
+				continue
 			}
-			d.skip()
-			continue
 		}
 
 		if !eleValue.IsValid() {
@@ -651,7 +886,7 @@ func (d *decodeState) parseMapToMap(v reflect.Value) error {
 			}
 			eleValue.Set(zeroEleValue)
 		}
-		if lastErr := d.parseToValue(eleValue, elemIsUnmarshaler); lastErr != nil {
+		if lastErr := d.parseToValue(eleValue, tInfo.elemTypeInfo); lastErr != nil {
 			if err == nil {
 				err = lastErr
 			}
@@ -663,36 +898,42 @@ func (d *decodeState) parseMapToMap(v reflect.Value) error {
 	return err
 }
 
-func (d *decodeState) parseArrayToStruct(v reflect.Value) error {
-	structType := getDecodingStructType(v.Type())
+func (d *decodeState) parseArrayToStruct(v reflect.Value, tInfo *typeInfo) error {
+	structType := getDecodingStructType(tInfo.nonPtrType)
+	if structType.err != nil {
+		return structType.err
+	}
+
 	if !structType.toArray {
 		t := d.nextCBORType()
 		d.skip()
 		return &UnmarshalTypeError{
 			Value:  t.String(),
-			Type:   v.Type(),
+			Type:   tInfo.nonPtrType,
 			errMsg: "cannot decode CBOR array to struct without toarray option",
 		}
 	}
+
 	start := d.off
 	t, ai, val := d.getHead()
 	hasSize := (ai != 31)
 	count := int(val)
 	if !hasSize {
-		count = d.numOfItemsUntilBreak() // peek ahead to get array size to preallocate slice for better performance
+		count = d.numOfItemsUntilBreak() // peek ahead to get array size
 	}
 	if count != len(structType.fields) {
 		d.off = start
 		d.skip()
 		return &UnmarshalTypeError{
 			Value:  t.String(),
-			Type:   v.Type(),
+			Type:   tInfo.typ,
 			errMsg: "cannot decode CBOR array to struct with different number of elements",
 		}
 	}
 	var err error
 	for i := 0; (hasSize && i < count) || (!hasSize && !d.foundBreak()); i++ {
-		fv, lastErr := fieldByIndex(v, structType.fields[i].idx)
+		f := structType.fields[i]
+		fv, lastErr := fieldByIndex(v, f.idx)
 		if lastErr != nil {
 			if err == nil {
 				err = lastErr
@@ -700,11 +941,11 @@ func (d *decodeState) parseArrayToStruct(v reflect.Value) error {
 			d.skip()
 			continue
 		}
-		if lastErr := d.parseToValue(fv, structType.fields[i].isUnmarshaler); lastErr != nil {
+		if lastErr := d.parseToValue(fv, f.typInfo); lastErr != nil {
 			if err == nil {
 				if typeError, ok := lastErr.(*UnmarshalTypeError); ok {
-					typeError.Struct = v.Type().String()
-					typeError.Field = structType.fields[i].name
+					typeError.Struct = tInfo.typ.String()
+					typeError.Field = f.name
 					err = typeError
 				} else {
 					err = lastErr
@@ -716,26 +957,33 @@ func (d *decodeState) parseArrayToStruct(v reflect.Value) error {
 }
 
 // parseMapToStruct needs to be fast so gocyclo can be ignored for now.
-func (d *decodeState) parseMapToStruct(v reflect.Value) error { //nolint:gocyclo
-	structType := getDecodingStructType(v.Type())
+func (d *decodeState) parseMapToStruct(v reflect.Value, tInfo *typeInfo) error { //nolint:gocyclo
+	structType := getDecodingStructType(tInfo.nonPtrType)
+	if structType.err != nil {
+		return structType.err
+	}
+
 	if structType.toArray {
 		t := d.nextCBORType()
 		d.skip()
 		return &UnmarshalTypeError{
 			Value:  t.String(),
-			Type:   v.Type(),
+			Type:   tInfo.nonPtrType,
 			errMsg: "cannot decode CBOR map to struct with toarray option",
 		}
 	}
+
 	foundFldIdx := make([]bool, len(structType.fields))
 	_, ai, val := d.getHead()
 	hasSize := (ai != 31)
 	count := int(val)
 	var err, lastErr error
 	for i := 0; (hasSize && i < count) || (!hasSize && !d.foundBreak()); i++ {
-		var keyBytes []byte
+		var f *field
+
 		t := d.nextCBORType()
 		if t == cborTypeTextString {
+			var keyBytes []byte
 			keyBytes, lastErr = d.parseTextString()
 			if lastErr != nil {
 				if err == nil {
@@ -744,55 +992,72 @@ func (d *decodeState) parseMapToStruct(v reflect.Value) error { //nolint:gocyclo
 				d.skip() // skip value
 				continue
 			}
-		} else if t == cborTypePositiveInt || t == cborTypeNegativeInt {
-			iv, lastErr := d.parse()
-			if lastErr != nil {
-				if err == nil {
-					err = lastErr
+
+			keyLen := len(keyBytes)
+			for i := 0; i < len(structType.fields); i++ {
+				// Find field with exact match
+				fld := structType.fields[i]
+				if !foundFldIdx[i] && len(fld.name) == keyLen && fld.name == string(keyBytes) {
+					f = fld
+					foundFldIdx[i] = true
+					break
 				}
-				d.skip() // skip value
-				continue
 			}
-			switch n := iv.(type) {
-			case int64:
-				keyBytes = []byte(strconv.Itoa(int(n)))
-			case uint64:
-				keyBytes = []byte(strconv.Itoa(int(n)))
+			if f == nil {
+				keyString := string(keyBytes)
+				for i := 0; i < len(structType.fields); i++ {
+					// Find field with case-insensitive match
+					fld := structType.fields[i]
+					if !foundFldIdx[i] && len(fld.name) == keyLen && strings.EqualFold(fld.name, keyString) {
+						f = fld
+						foundFldIdx[i] = true
+						break
+					}
+				}
+			}
+		} else if t <= cborTypeNegativeInt { // uint/int
+			var nameAsInt int64
+
+			if t == cborTypePositiveInt {
+				_, _, val := d.getHead()
+				nameAsInt = int64(val)
+			} else {
+				_, _, val := d.getHead()
+				if val > math.MaxInt64 {
+					if err == nil {
+						err = &UnmarshalTypeError{
+							Value:  t.String(),
+							Type:   reflect.TypeOf(int64(0)),
+							errMsg: "-1-" + strconv.FormatUint(val, 10) + " overflows Go's int64",
+						}
+					}
+					d.skip() // skip value
+					continue
+				}
+				nameAsInt = int64(-1) ^ int64(val)
+			}
+
+			for i := 0; i < len(structType.fields); i++ {
+				fld := structType.fields[i]
+				if !foundFldIdx[i] && fld.keyAsInt && fld.nameAsInt == nameAsInt {
+					f = fld
+					foundFldIdx[i] = true
+					break
+				}
 			}
 		} else {
 			if err == nil {
 				err = &UnmarshalTypeError{
 					Value:  t.String(),
 					Type:   reflect.TypeOf(""),
-					errMsg: "map key is of type " + t.String() + " and cannot be used to match struct " + v.Type().String() + " field name",
+					errMsg: "map key is of type " + t.String() + " and cannot be used to match struct field name",
 				}
 			}
 			d.skip() // skip key
 			d.skip() // skip value
 			continue
 		}
-		keyLen := len(keyBytes)
 
-		var f *field
-		for i := 0; i < len(structType.fields); i++ {
-			// Find field with exact match
-			if !foundFldIdx[i] && len(structType.fields[i].name) == keyLen && structType.fields[i].name == string(keyBytes) {
-				f = &structType.fields[i]
-				foundFldIdx[i] = true
-				break
-			}
-		}
-		if f == nil {
-			keyString := string(keyBytes)
-			for i := 0; i < len(structType.fields); i++ {
-				// Find field with case-insensitive match
-				if !foundFldIdx[i] && len(structType.fields[i].name) == keyLen && strings.EqualFold(structType.fields[i].name, keyString) {
-					f = &structType.fields[i]
-					foundFldIdx[i] = true
-					break
-				}
-			}
-		}
 		if f == nil {
 			d.skip()
 			continue
@@ -807,10 +1072,10 @@ func (d *decodeState) parseMapToStruct(v reflect.Value) error { //nolint:gocyclo
 			d.skip()
 			continue
 		}
-		if lastErr = d.parseToValue(fv, f.isUnmarshaler); lastErr != nil {
+		if lastErr = d.parseToValue(fv, f.typInfo); lastErr != nil {
 			if err == nil {
 				if typeError, ok := lastErr.(*UnmarshalTypeError); ok {
-					typeError.Struct = v.Type().String()
+					typeError.Struct = tInfo.nonPtrType.String()
 					typeError.Field = f.name
 					err = typeError
 				} else {
@@ -822,6 +1087,35 @@ func (d *decodeState) parseMapToStruct(v reflect.Value) error { //nolint:gocyclo
 	return err
 }
 
+// validRegisteredTagNums verifies that tag numbers match registered tag numbers of type t.
+// validRegisteredTagNums assumes next CBOR data type is tag.  It scans all tag numbers, and stops at tag content.
+func (d *decodeState) validRegisteredTagNums(t reflect.Type, registeredTagNums []uint64) error {
+	// Scan until next cbor data is tag content.
+	tagNums := make([]uint64, 0, 1)
+	for d.nextCBORType() == cborTypeTag {
+		_, _, val := d.getHead()
+		tagNums = append(tagNums, val)
+	}
+
+	// Verify that tag numbers match registered tag numbers of type t
+	if len(tagNums) != len(registeredTagNums) {
+		return &WrongTagError{t, registeredTagNums, tagNums}
+	}
+	for i, n := range registeredTagNums {
+		if n != tagNums[i] {
+			return &WrongTagError{t, registeredTagNums, tagNums}
+		}
+	}
+	return nil
+}
+
+func (d *decodeState) getRegisteredTagItem(vt reflect.Type) *tagItem {
+	if d.dm.tags != nil {
+		return d.dm.tags.get(vt)
+	}
+	return nil
+}
+
 // skip moves data offset to the next item.  skip assumes data is well-formed,
 // and does not perform bounds checking.
 func (d *decodeState) skip() {
@@ -863,19 +1157,28 @@ func (d *decodeState) getHead() (t cborType, ai byte, val uint64) {
 	val = uint64(ai)
 	d.off++
 
-	switch ai {
-	case 24:
+	if ai < 24 {
+		return
+	}
+	if ai == 24 {
 		val = uint64(d.data[d.off])
 		d.off++
-	case 25:
+		return
+	}
+	if ai == 25 {
 		val = uint64(binary.BigEndian.Uint16(d.data[d.off : d.off+2]))
 		d.off += 2
-	case 26:
+		return
+	}
+	if ai == 26 {
 		val = uint64(binary.BigEndian.Uint32(d.data[d.off : d.off+4]))
 		d.off += 4
-	case 27:
+		return
+	}
+	if ai == 27 {
 		val = binary.BigEndian.Uint64(d.data[d.off : d.off+8])
 		d.off += 8
+		return
 	}
 	return
 }
@@ -926,10 +1229,6 @@ func fillNil(t cborType, v reflect.Value) error {
 		v.Set(reflect.Zero(v.Type()))
 		return nil
 	}
-	if v.Type() == typeTime {
-		v.Set(reflect.ValueOf(time.Time{}))
-		return nil
-	}
 	return nil
 }
 
@@ -955,11 +1254,6 @@ func fillPositiveInt(t cborType, val uint64, v reflect.Value) error {
 		v.SetFloat(f)
 		return nil
 	}
-	if v.Type() == typeTime {
-		tm := time.Unix(int64(val), 0)
-		v.Set(reflect.ValueOf(tm))
-		return nil
-	}
 	return &UnmarshalTypeError{Value: t.String(), Type: v.Type()}
 }
 
@@ -976,11 +1270,6 @@ func fillNegativeInt(t cborType, val int64, v reflect.Value) error {
 		v.SetFloat(f)
 		return nil
 	}
-	if v.Type() == typeTime {
-		tm := time.Unix(val, 0)
-		v.Set(reflect.ValueOf(tm))
-		return nil
-	}
 	return &UnmarshalTypeError{Value: t.String(), Type: v.Type()}
 }
 
@@ -1005,21 +1294,11 @@ func fillFloat(t cborType, val float64, v reflect.Value) error {
 		v.SetFloat(val)
 		return nil
 	}
-	if v.Type() == typeTime {
-		if math.IsNaN(val) || math.IsInf(val, 0) {
-			v.Set(reflect.ValueOf(time.Time{}))
-			return nil
-		}
-		f1, f2 := math.Modf(val)
-		tm := time.Unix(int64(f1), int64(f2*1e9))
-		v.Set(reflect.ValueOf(tm))
-		return nil
-	}
 	return &UnmarshalTypeError{Value: t.String(), Type: v.Type()}
 }
 
 func fillByteString(t cborType, val []byte, v reflect.Value) error {
-	if v.Type() != typeTime && reflect.PtrTo(v.Type()).Implements(typeBinaryUnmarshaler) {
+	if reflect.PtrTo(v.Type()).Implements(typeBinaryUnmarshaler) {
 		if v.CanAddr() {
 			v = v.Addr()
 			if u, ok := v.Interface().(encoding.BinaryUnmarshaler); ok {
@@ -1040,14 +1319,6 @@ func fillTextString(t cborType, val []byte, v reflect.Value) error {
 		v.SetString(string(val))
 		return nil
 	}
-	if v.Type() == typeTime {
-		tm, err := time.Parse(time.RFC3339, string(val))
-		if err != nil {
-			return errors.New("cbor: cannot set " + string(val) + " for time.Time")
-		}
-		v.Set(reflect.ValueOf(tm))
-		return nil
-	}
 	return &UnmarshalTypeError{Value: t.String(), Type: v.Type()}
 }
 
@@ -1073,13 +1344,6 @@ func isHashableKind(k reflect.Kind) bool {
 	}
 }
 
-func implementsUnmarshaler(t reflect.Type) bool {
-	for t.Kind() == reflect.Ptr {
-		t = t.Elem()
-	}
-	return reflect.PtrTo(t).Implements(typeUnmarshaler)
-}
-
 // fieldByIndex returns the nested field corresponding to the index.  It
 // allocates pointer to struct field if it is nil and settable.
 // reflect.Value.FieldByIndex() panics at nil pointer to unexported anonymous
diff --git a/doc.go b/doc.go
new file mode 100644
index 0000000..0fd94b8
--- /dev/null
+++ b/doc.go
@@ -0,0 +1,107 @@
+// Copyright (c) Faye Amacker. All rights reserved.
+// Licensed under the MIT License. See LICENSE in the project root for license information.
+
+/*
+Package cbor provides a fuzz-tested CBOR encoder and decoder with full support
+for float16, Canonical CBOR, CTAP2 Canonical CBOR, and custom settings.
+
+CBOR encoding options allow "preferred serialization" by encoding integers and floats
+to their smallest forms (like float16) when values fit.
+
+Struct tags like "keyasint", "toarray" and "omitempty" makes CBOR data smaller.
+
+For example, "toarray" makes struct fields encode to array elements.  And "keyasint"
+makes struct fields encode to elements of CBOR map with int keys.
+
+Basics
+
+Function signatures identical to encoding/json include:
+
+    Marshal, Unmarshal, NewEncoder, NewDecoder, encoder.Encode, decoder.Decode.
+
+Codec functions are available at package-level (using defaults) or by creating modes
+from options at runtime.
+
+"Mode" in this API means definite way of encoding or decoding. Specifically, EncMode or DecMode.
+
+EncMode and DecMode interfaces are created from EncOptions or DecOptions structs.  For example,
+
+    em := cbor.EncOptions{...}.EncMode()
+    em := cbor.CanonicalEncOptions().EncMode()
+    em := cbor.CTAP2EncOptions().EncMode()
+
+Modes use immutable options to avoid side-effects and simplify concurrency. Behavior of modes
+won't accidentally change at runtime after they're created.
+
+Modes are intended to be reused and are safe for concurrent use.
+
+EncMode and DecMode Interfaces
+
+    // EncMode interface uses immutable options and is safe for concurrent use.
+    type EncMode interface {
+	Marshal(v interface{}) ([]byte, error)
+	NewEncoder(w io.Writer) *Encoder
+	EncOptions() EncOptions  // returns copy of options
+    }
+
+    // DecMode interface uses immutable options and is safe for concurrent use.
+    type DecMode interface {
+	Unmarshal(data []byte, v interface{}) error
+	NewDecoder(r io.Reader) *Decoder
+	DecOptions() DecOptions  // returns copy of options
+    }
+
+Using Default Encoding Mode
+
+    b, err := cbor.Marshal(v)
+
+    encoder := cbor.NewEncoder(w)
+    err = encoder.Encode(v)
+
+Using Default Decoding Mode
+
+    err := cbor.Unmarshal(b, &v)
+
+    decoder := cbor.NewDecoder(r)
+    err = decoder.Decode(&v)
+
+Creating and Using Encoding Modes
+
+    // Create EncOptions using either struct literal or a function.
+    opts := cbor.CanonicalEncOptions()
+
+    // If needed, modify encoding options
+    opts.Time = cbor.TimeUnix
+
+    // Create reusable EncMode interface with immutable options, safe for concurrent use.
+    em, err := opts.EncMode()
+
+    // Use EncMode like encoding/json, with same function signatures.
+    b, err := em.Marshal(v)
+    // or
+    encoder := em.NewEncoder(w)
+    err := encoder.Encode(v)
+
+Default Options
+
+Default encoding options are listed at https://github.com/fxamacker/cbor#api
+
+Struct Tags
+
+Struct tags like `cbor:"name,omitempty"` and `json:"name,omitempty"` work as expected.
+If both struct tags are specified then `cbor` is used.
+
+Struct tags like "keyasint", "toarray", and "omitempty" make it easy to use
+very compact formats like COSE and CWT (CBOR Web Tokens) with structs.
+
+For example, "toarray" makes struct fields encode to array elements.  And "keyasint"
+makes struct fields encode to elements of CBOR map with int keys.
+
+https://raw.githubusercontent.com/fxamacker/images/master/cbor/v2.0.0/cbor_easy_api.png
+
+Tests and Fuzzing
+
+Over 375 tests are included in this package. Cover-guided fuzzing is handled by a separate package:
+fxamacker/cbor-fuzz.
+*/
+package cbor
diff --git a/encode.go b/encode.go
index 9fd644c..3b712c7 100644
--- a/encode.go
+++ b/encode.go
@@ -253,6 +253,10 @@ type EncOptions struct {
 	// Time specifies how to encode time.Time.
 	Time TimeMode
 
+	// TimeTag allows time.Time to be encoded with a tag number.
+	// RFC3339 format gets tag number 0, and numeric epoch time tag number 1.
+	TimeTag EncTagMode
+
 	disableIndefiniteLength bool
 }
 
@@ -349,8 +353,50 @@ func PreferredUnsortedEncOptions() EncOptions {
 	}
 }
 
-// EncMode returns an EncMode interface from EncOptions.
+// EncMode returns EncMode with immutable options and no tags (safe for concurrency).
 func (opts EncOptions) EncMode() (EncMode, error) {
+	return opts.encMode()
+}
+
+// EncModeWithTags returns EncMode with options and tags that are both immutable (safe for concurrency).
+func (opts EncOptions) EncModeWithTags(tags TagSet) (EncMode, error) {
+	if tags == nil {
+		return nil, errors.New("cbor: cannot create EncMode with nil value as TagSet")
+	}
+	em, err := opts.encMode()
+	if err != nil {
+		return nil, err
+	}
+	// Copy tags
+	ts := tagSet(make(map[reflect.Type]*tagItem))
+	syncTags := tags.(*syncTagSet)
+	syncTags.RLock()
+	for contentType, tag := range syncTags.t {
+		if tag.opts.EncTag != EncTagIgnored {
+			ts[contentType] = tag
+		}
+	}
+	syncTags.RUnlock()
+	if len(ts) > 0 {
+		em.tags = ts
+	}
+	return em, nil
+}
+
+// EncModeWithSharedTags returns EncMode with immutable options and mutable shared tags (safe for concurrency).
+func (opts EncOptions) EncModeWithSharedTags(tags TagSet) (EncMode, error) {
+	if tags == nil {
+		return nil, errors.New("cbor: cannot create EncMode with nil value as TagSet")
+	}
+	em, err := opts.encMode()
+	if err != nil {
+		return nil, err
+	}
+	em.tags = tags
+	return em, nil
+}
+
+func (opts EncOptions) encMode() (*encMode, error) {
 	if !opts.Sort.valid() {
 		return nil, errors.New("cbor: invalid SortMode " + strconv.Itoa(int(opts.Sort)))
 	}
@@ -366,12 +412,16 @@ func (opts EncOptions) EncMode() (EncMode, error) {
 	if !opts.Time.valid() {
 		return nil, errors.New("cbor: invalid TimeMode " + strconv.Itoa(int(opts.Time)))
 	}
+	if !opts.TimeTag.valid() {
+		return nil, errors.New("cbor: invalid TimeTag " + strconv.Itoa(int(opts.TimeTag)))
+	}
 	em := encMode{
 		sort:                    opts.Sort,
 		shortestFloat:           opts.ShortestFloat,
 		nanConvert:              opts.NaNConvert,
 		infConvert:              opts.InfConvert,
 		time:                    opts.Time,
+		timeTag:                 opts.TimeTag,
 		disableIndefiniteLength: opts.disableIndefiniteLength,
 	}
 	return &em, nil
@@ -385,11 +435,13 @@ type EncMode interface {
 }
 
 type encMode struct {
+	tags                    tagProvider
 	sort                    SortMode
 	shortestFloat           ShortestFloatMode
 	nanConvert              NaNConvertMode
 	infConvert              InfConvertMode
 	time                    TimeMode
+	timeTag                 EncTagMode
 	disableIndefiniteLength bool
 }
 
@@ -403,18 +455,27 @@ func (em *encMode) EncOptions() EncOptions {
 		NaNConvert:              em.nanConvert,
 		InfConvert:              em.infConvert,
 		Time:                    em.time,
+		TimeTag:                 em.timeTag,
 		disableIndefiniteLength: em.disableIndefiniteLength,
 	}
 }
 
-// Marshal returns the CBOR encoding of v using em EncMode.
+func (em *encMode) encTagBytes(t reflect.Type) []byte {
+	if em.tags != nil {
+		if tagItem := em.tags.get(t); tagItem != nil {
+			return tagItem.cborTagNum
+		}
+	}
+	return nil
+}
+
+// Marshal returns the CBOR encoding of v using em encMode.
 //
 // See the documentation for Marshal for details.
 func (em *encMode) Marshal(v interface{}) ([]byte, error) {
 	e := getEncodeState()
 
-	_, err := encode(e, em, reflect.ValueOf(v))
-	if err != nil {
+	if err := encode(e, em, reflect.ValueOf(v)); err != nil {
 		putEncodeState(e)
 		return nil, err
 	}
@@ -456,52 +517,71 @@ func putEncodeState(e *encodeState) {
 	encodeStatePool.Put(e)
 }
 
-type encodeFunc func(e *encodeState, em *encMode, v reflect.Value) (int, error)
+type encodeFunc func(e *encodeState, em *encMode, v reflect.Value) error
 
 var (
 	cborFalse            = []byte{0xf4}
 	cborTrue             = []byte{0xf5}
 	cborNil              = []byte{0xf6}
+	cborUndefined        = []byte{0xf7}
 	cborNaN              = []byte{0xf9, 0x7e, 0x00}
 	cborPositiveInfinity = []byte{0xf9, 0x7c, 0x00}
 	cborNegativeInfinity = []byte{0xf9, 0xfc, 0x00}
 )
 
-func encode(e *encodeState, em *encMode, v reflect.Value) (int, error) {
+func encode(e *encodeState, em *encMode, v reflect.Value) error {
 	if !v.IsValid() {
 		// v is zero value
 		e.Write(cborNil)
-		return 1, nil
+		return nil
 	}
-	f := getEncodeFunc(v.Type())
+	vt := v.Type()
+	f := getEncodeFunc(vt)
 	if f == nil {
-		return 0, &UnsupportedTypeError{v.Type()}
+		return &UnsupportedTypeError{vt}
 	}
 
 	return f(e, em, v)
 }
 
-func encodeBool(e *encodeState, em *encMode, v reflect.Value) (int, error) {
+func encodeBool(e *encodeState, em *encMode, v reflect.Value) error {
+	if b := em.encTagBytes(v.Type()); b != nil {
+		e.Write(b)
+	}
+	b := cborFalse
 	if v.Bool() {
-		return e.Write(cborTrue)
+		b = cborTrue
 	}
-	return e.Write(cborFalse)
+	e.Write(b)
+	return nil
 }
 
-func encodeInt(e *encodeState, em *encMode, v reflect.Value) (int, error) {
+func encodeInt(e *encodeState, em *encMode, v reflect.Value) error {
+	if b := em.encTagBytes(v.Type()); b != nil {
+		e.Write(b)
+	}
 	i := v.Int()
 	if i >= 0 {
-		return encodeHead(e, byte(cborTypePositiveInt), uint64(i)), nil
+		encodeHead(e, byte(cborTypePositiveInt), uint64(i))
+		return nil
 	}
-	n := v.Int()*(-1) - 1
-	return encodeHead(e, byte(cborTypeNegativeInt), uint64(n)), nil
+	i = i*(-1) - 1
+	encodeHead(e, byte(cborTypeNegativeInt), uint64(i))
+	return nil
 }
 
-func encodeUint(e *encodeState, em *encMode, v reflect.Value) (int, error) {
-	return encodeHead(e, byte(cborTypePositiveInt), v.Uint()), nil
+func encodeUint(e *encodeState, em *encMode, v reflect.Value) error {
+	if b := em.encTagBytes(v.Type()); b != nil {
+		e.Write(b)
+	}
+	encodeHead(e, byte(cborTypePositiveInt), v.Uint())
+	return nil
 }
 
-func encodeFloat(e *encodeState, em *encMode, v reflect.Value) (int, error) {
+func encodeFloat(e *encodeState, em *encMode, v reflect.Value) error {
+	if b := em.encTagBytes(v.Type()); b != nil {
+		e.Write(b)
+	}
 	f64 := v.Float()
 	if math.IsNaN(f64) {
 		return encodeNaN(e, em, v)
@@ -515,7 +595,8 @@ func encodeFloat(e *encodeState, em *encMode, v reflect.Value) (int, error) {
 		// Don't use encodeFloat64() because it cannot be inlined.
 		e.scratch[0] = byte(cborTypePrimitives) | byte(27)
 		binary.BigEndian.PutUint64(e.scratch[1:], math.Float64bits(f64))
-		return e.Write(e.scratch[:9])
+		e.Write(e.scratch[:9])
+		return nil
 	}
 
 	f32 := float32(f64)
@@ -537,7 +618,8 @@ func encodeFloat(e *encodeState, em *encMode, v reflect.Value) (int, error) {
 			// Don't use encodeFloat16() because it cannot be inlined.
 			e.scratch[0] = byte(cborTypePrimitives) | byte(25)
 			binary.BigEndian.PutUint16(e.scratch[1:], uint16(f16))
-			return e.Write(e.scratch[:3])
+			e.Write(e.scratch[:3])
+			return nil
 		}
 	}
 
@@ -545,16 +627,19 @@ func encodeFloat(e *encodeState, em *encMode, v reflect.Value) (int, error) {
 	// Don't use encodeFloat32() because it cannot be inlined.
 	e.scratch[0] = byte(cborTypePrimitives) | byte(26)
 	binary.BigEndian.PutUint32(e.scratch[1:], math.Float32bits(f32))
-	return e.Write(e.scratch[:5])
+	e.Write(e.scratch[:5])
+	return nil
 }
 
-func encodeInf(e *encodeState, em *encMode, v reflect.Value) (int, error) {
+func encodeInf(e *encodeState, em *encMode, v reflect.Value) error {
 	f64 := v.Float()
 	if em.infConvert == InfConvertFloat16 {
 		if f64 > 0 {
-			return e.Write(cborPositiveInfinity)
+			e.Write(cborPositiveInfinity)
+		} else {
+			e.Write(cborNegativeInfinity)
 		}
-		return e.Write(cborNegativeInfinity)
+		return nil
 	}
 	if v.Kind() == reflect.Float64 {
 		return encodeFloat64(e, f64)
@@ -562,10 +647,11 @@ func encodeInf(e *encodeState, em *encMode, v reflect.Value) (int, error) {
 	return encodeFloat32(e, float32(f64))
 }
 
-func encodeNaN(e *encodeState, em *encMode, v reflect.Value) (int, error) {
+func encodeNaN(e *encodeState, em *encMode, v reflect.Value) error {
 	switch em.nanConvert {
 	case NaNConvert7e00:
-		return e.Write(cborNaN)
+		e.Write(cborNaN)
+		return nil
 
 	case NaNConvertNone:
 		if v.Kind() == reflect.Float64 {
@@ -619,108 +705,118 @@ func encodeNaN(e *encodeState, em *encMode, v reflect.Value) (int, error) {
 	}
 }
 
-func encodeFloat16(e *encodeState, f16 float16.Float16) (int, error) {
+func encodeFloat16(e *encodeState, f16 float16.Float16) error {
 	e.scratch[0] = byte(cborTypePrimitives) | byte(25)
 	binary.BigEndian.PutUint16(e.scratch[1:], uint16(f16))
-	return e.Write(e.scratch[:3])
+	e.Write(e.scratch[:3])
+	return nil
 }
 
-func encodeFloat32(e *encodeState, f32 float32) (int, error) {
+func encodeFloat32(e *encodeState, f32 float32) error {
 	e.scratch[0] = byte(cborTypePrimitives) | byte(26)
 	binary.BigEndian.PutUint32(e.scratch[1:], math.Float32bits(f32))
-	return e.Write(e.scratch[:5])
+	e.Write(e.scratch[:5])
+	return nil
 }
 
-func encodeFloat64(e *encodeState, f64 float64) (int, error) {
+func encodeFloat64(e *encodeState, f64 float64) error {
 	e.scratch[0] = byte(cborTypePrimitives) | byte(27)
 	binary.BigEndian.PutUint64(e.scratch[1:], math.Float64bits(f64))
-	return e.Write(e.scratch[:9])
+	e.Write(e.scratch[:9])
+	return nil
 }
 
-func encodeByteString(e *encodeState, em *encMode, v reflect.Value) (int, error) {
-	if v.Kind() == reflect.Slice && v.IsNil() {
-		return e.Write(cborNil)
+func encodeByteString(e *encodeState, em *encMode, v reflect.Value) error {
+	vk := v.Kind()
+	if vk == reflect.Slice && v.IsNil() {
+		e.Write(cborNil)
+		return nil
+	}
+	if b := em.encTagBytes(v.Type()); b != nil {
+		e.Write(b)
 	}
 	slen := v.Len()
 	if slen == 0 {
-		return 1, e.WriteByte(byte(cborTypeByteString))
+		return e.WriteByte(byte(cborTypeByteString))
 	}
-	n1 := encodeHead(e, byte(cborTypeByteString), uint64(slen))
-	if v.Kind() == reflect.Array {
+	encodeHead(e, byte(cborTypeByteString), uint64(slen))
+	if vk == reflect.Array {
 		for i := 0; i < slen; i++ {
 			e.WriteByte(byte(v.Index(i).Uint()))
 		}
-		return n1 + slen, nil
+		return nil
 	}
-	n2, _ := e.Write(v.Bytes())
-	return n1 + n2, nil
+	e.Write(v.Bytes())
+	return nil
 }
 
-func encodeString(e *encodeState, em *encMode, v reflect.Value) (int, error) {
+func encodeString(e *encodeState, em *encMode, v reflect.Value) error {
+	if b := em.encTagBytes(v.Type()); b != nil {
+		e.Write(b)
+	}
 	s := v.String()
-	n1 := encodeHead(e, byte(cborTypeTextString), uint64(len(s)))
-	n2, _ := e.WriteString(s)
-	return n1 + n2, nil
+	encodeHead(e, byte(cborTypeTextString), uint64(len(s)))
+	e.WriteString(s)
+	return nil
 }
 
+// Assuming that arrayEncoder.f != nil
 type arrayEncoder struct {
 	f encodeFunc
 }
 
-func (ae arrayEncoder) encodeArray(e *encodeState, em *encMode, v reflect.Value) (int, error) {
-	if ae.f == nil {
-		return 0, &UnsupportedTypeError{v.Type()}
-	}
+func (ae arrayEncoder) encodeArray(e *encodeState, em *encMode, v reflect.Value) error {
 	if v.Kind() == reflect.Slice && v.IsNil() {
-		return e.Write(cborNil)
+		e.Write(cborNil)
+		return nil
+	}
+	if b := em.encTagBytes(v.Type()); b != nil {
+		e.Write(b)
 	}
 	alen := v.Len()
 	if alen == 0 {
-		return 1, e.WriteByte(byte(cborTypeArray))
+		return e.WriteByte(byte(cborTypeArray))
 	}
-	n := encodeHead(e, byte(cborTypeArray), uint64(alen))
+	encodeHead(e, byte(cborTypeArray), uint64(alen))
 	for i := 0; i < alen; i++ {
-		n1, err := ae.f(e, em, v.Index(i))
-		if err != nil {
-			return 0, err
+		if err := ae.f(e, em, v.Index(i)); err != nil {
+			return err
 		}
-		n += n1
 	}
-	return n, nil
+	return nil
 }
 
+// Assuming that arrayEncoder.kf and arrayEncoder.ef are not nil
 type mapEncoder struct {
 	kf, ef encodeFunc
 }
 
-func (me mapEncoder) encodeMap(e *encodeState, em *encMode, v reflect.Value) (int, error) {
-	if em.sort != SortNone {
-		return me.encodeMapCanonical(e, em, v)
-	}
-	if me.kf == nil || me.ef == nil {
-		return 0, &UnsupportedTypeError{v.Type()}
-	}
+func (me mapEncoder) encodeMap(e *encodeState, em *encMode, v reflect.Value) error {
 	if v.IsNil() {
-		return e.Write(cborNil)
+		e.Write(cborNil)
+		return nil
+	}
+	if b := em.encTagBytes(v.Type()); b != nil {
+		e.Write(b)
 	}
 	mlen := v.Len()
 	if mlen == 0 {
-		return 1, e.WriteByte(byte(cborTypeMap))
+		return e.WriteByte(byte(cborTypeMap))
 	}
-	n := encodeHead(e, byte(cborTypeMap), uint64(mlen))
+	if em.sort != SortNone {
+		return me.encodeMapCanonical(e, em, v)
+	}
+	encodeHead(e, byte(cborTypeMap), uint64(mlen))
 	iter := v.MapRange()
 	for iter.Next() {
-		n1, err := me.kf(e, em, iter.Key())
-		if err != nil {
-			return 0, err
+		if err := me.kf(e, em, iter.Key()); err != nil {
+			return err
 		}
-		n2, err := me.ef(e, em, iter.Value())
-		if err != nil {
-			return 0, err
+		if err := me.ef(e, em, iter.Value()); err != nil {
+			return err
 		}
-		n += n1 + n2
 	}
-	return n, nil
+	return nil
 }
 
 type keyValue struct {
@@ -788,37 +884,27 @@ func putKeyValues(x *[]keyValue) {
 	keyValuePool.Put(x)
 }
 
-func (me mapEncoder) encodeMapCanonical(e *encodeState, em *encMode, v reflect.Value) (int, error) {
-	if me.kf == nil || me.ef == nil {
-		return 0, &UnsupportedTypeError{v.Type()}
-	}
-	if v.IsNil() {
-		return e.Write(cborNil)
-	}
-	if v.Len() == 0 {
-		return 1, e.WriteByte(byte(cborTypeMap))
-	}
-
+func (me mapEncoder) encodeMapCanonical(e *encodeState, em *encMode, v reflect.Value) error {
 	kve := getEncodeState()       // accumulated cbor encoded key-values
 	kvsp := getKeyValues(v.Len()) // for sorting keys
 	kvs := *kvsp
 	iter := v.MapRange()
 	for i := 0; iter.Next(); i++ {
-		n1, err := me.kf(kve, em, iter.Key())
-		if err != nil {
+		off := kve.Len()
+		if err := me.kf(kve, em, iter.Key()); err != nil {
 			putEncodeState(kve)
 			putKeyValues(kvsp)
-			return 0, err
+			return err
 		}
-		n2, err := me.ef(kve, em, iter.Value())
-		if err != nil {
+		n1 := kve.Len() - off
+		if err := me.ef(kve, em, iter.Value()); err != nil {
 			putEncodeState(kve)
 			putKeyValues(kvsp)
-			return 0, err
+			return err
 		}
-
+		n2 := kve.Len() - off
 		// Save key and keyvalue length to create slice later.
-		kvs[i] = keyValue{keyLen: n1, keyValueLen: n1 + n2}
+		kvs[i] = keyValue{keyLen: n1, keyValueLen: n2}
 	}
 
 	b := kve.Bytes()
@@ -834,23 +920,23 @@ func (me mapEncoder) encodeMapCanonical(e *encodeState, em *encMode, v reflect.V
 		sort.Sort(&lengthFirstKeyValueSorter{kvs})
 	}
 
-	n := encodeHead(e, byte(cborTypeMap), uint64(len(kvs)))
+	encodeHead(e, byte(cborTypeMap), uint64(len(kvs)))
 	for i := 0; i < len(kvs); i++ {
-		n1, _ := e.Write(kvs[i].keyValueCBORData)
-		n += n1
+		e.Write(kvs[i].keyValueCBORData)
 	}
 
 	putEncodeState(kve)
 	putKeyValues(kvsp)
-	return n, nil
+	return nil
 }
 
-func encodeStructToArray(e *encodeState, em *encMode, v reflect.Value, flds fields) (int, error) {
-	n := encodeHead(e, byte(cborTypeArray), uint64(len(flds)))
+func encodeStructToArray(e *encodeState, em *encMode, v reflect.Value, flds fields) error {
+	encodeHead(e, byte(cborTypeArray), uint64(len(flds)))
 FieldLoop:
 	for i := 0; i < len(flds); i++ {
+		f := flds[i]
 		fv := v
-		for k, n := range flds[i].idx {
+		for k, n := range f.idx {
 			if k > 0 {
 				if fv.Kind() == reflect.Ptr && fv.Type().Elem().Kind() == reflect.Struct {
 					if fv.IsNil() {
@@ -863,37 +949,38 @@ FieldLoop:
 			}
 			fv = fv.Field(n)
 		}
-		n1, err := flds[i].ef(e, em, fv)
-		if err != nil {
-			return 0, err
+		if err := f.ef(e, em, fv); err != nil {
+			return err
 		}
-		n += n1
 	}
-	return n, nil
+	return nil
 }
 
-func encodeFixedLengthStruct(e *encodeState, em *encMode, v reflect.Value, flds fields) (int, error) {
-	n := encodeHead(e, byte(cborTypeMap), uint64(len(flds)))
+func encodeFixedLengthStruct(e *encodeState, em *encMode, v reflect.Value, flds fields) error {
+	encodeHead(e, byte(cborTypeMap), uint64(len(flds)))
 
 	for i := 0; i < len(flds); i++ {
-		n1, _ := e.Write(flds[i].cborName)
+		f := flds[i]
+		e.Write(f.cborName)
 
-		fv := v.Field(flds[i].idx[0])
-		n2, err := flds[i].ef(e, em, fv)
-		if err != nil {
-			return 0, err
+		fv := v.Field(f.idx[0])
+		if err := f.ef(e, em, fv); err != nil {
+			return err
 		}
-
-		n += n1 + n2
 	}
 
-	return n, nil
+	return nil
 }
 
-func encodeStruct(e *encodeState, em *encMode, v reflect.Value) (int, error) {
-	structType := getEncodingStructType(v.Type())
+func encodeStruct(e *encodeState, em *encMode, v reflect.Value) error {
+	vt := v.Type()
+	structType := getEncodingStructType(vt)
 	if structType.err != nil {
-		return 0, structType.err
+		return structType.err
+	}
+
+	if b := em.encTagBytes(vt); b != nil {
+		e.Write(b)
 	}
 
 	if structType.toArray {
@@ -910,8 +997,9 @@ func encodeStruct(e *encodeState, em *encMode, v reflect.Value) (int, error) {
 	kvcount := 0
 FieldLoop:
 	for i := 0; i < len(flds); i++ {
+		f := flds[i]
 		fv := v
-		for k, n := range flds[i].idx {
+		for k, n := range f.idx {
 			if k > 0 {
 				if fv.Kind() == reflect.Ptr && fv.Type().Elem().Kind() == reflect.Struct {
 					if fv.IsNil() {
@@ -923,37 +1011,50 @@ FieldLoop:
 			}
 			fv = fv.Field(n)
 		}
-		if flds[i].omitEmpty && isEmptyValue(fv) {
+		if f.omitEmpty && isEmptyValue(fv) {
 			continue
 		}
 
-		kve.Write(flds[i].cborName)
+		kve.Write(f.cborName)
 
-		if _, err := flds[i].ef(kve, em, fv); err != nil {
+		if err := f.ef(kve, em, fv); err != nil {
 			putEncodeState(kve)
-			return 0, err
+			return err
 		}
 		kvcount++
 	}
 
-	n1 := encodeHead(e, byte(cborTypeMap), uint64(kvcount))
-	n2, err := e.Write(kve.Bytes())
+	encodeHead(e, byte(cborTypeMap), uint64(kvcount))
+	e.Write(kve.Bytes())
 
 	putEncodeState(kve)
-	return n1 + n2, err
+	return nil
 }
 
-func encodeIntf(e *encodeState, em *encMode, v reflect.Value) (int, error) {
+func encodeIntf(e *encodeState, em *encMode, v reflect.Value) error {
 	if v.IsNil() {
-		return e.Write(cborNil)
+		e.Write(cborNil)
+		return nil
 	}
 	return encode(e, em, v.Elem())
 }
 
-func encodeTime(e *encodeState, em *encMode, v reflect.Value) (int, error) {
+func encodeTime(e *encodeState, em *encMode, v reflect.Value) error {
 	t := v.Interface().(time.Time)
 	if t.IsZero() {
-		return e.Write(cborNil)
+		if em.timeTag == EncTagIgnored {
+			e.Write(cborNil)
+			return nil
+		}
+		e.Write(cborUndefined)
+		return nil
+	}
+	if em.timeTag == EncTagRequired {
+		tagNumber := 1
+		if em.time == TimeRFC3339 || em.time == TimeRFC3339Nano {
+			tagNumber = 0
+		}
+		encodeHead(e, byte(cborTypeTag), uint64(tagNumber))
 	}
 	switch em.time {
 	case TimeUnix:
@@ -974,29 +1075,33 @@ func encodeTime(e *encodeState, em *encMode, v reflect.Value) (int, error) {
 	case TimeRFC3339:
 		s := t.Format(time.RFC3339)
 		return encodeString(e, em, reflect.ValueOf(s))
-	default: // TimeRFC3339Nano:
+	default: // TimeRFC3339Nano
 		s := t.Format(time.RFC3339Nano)
 		return encodeString(e, em, reflect.ValueOf(s))
 	}
 }
 
-func encodeBinaryMarshalerType(e *encodeState, em *encMode, v reflect.Value) (int, error) {
+func encodeBinaryMarshalerType(e *encodeState, em *encMode, v reflect.Value) error {
+	vt := v.Type()
 	m, ok := v.Interface().(encoding.BinaryMarshaler)
 	if !ok {
-		pv := reflect.New(v.Type())
+		pv := reflect.New(vt)
 		pv.Elem().Set(v)
 		m = pv.Interface().(encoding.BinaryMarshaler)
 	}
 	data, err := m.MarshalBinary()
 	if err != nil {
-		return 0, err
+		return err
 	}
-	n1 := encodeHead(e, byte(cborTypeByteString), uint64(len(data)))
-	n2, _ := e.Write(data)
-	return n1 + n2, nil
+	if b := em.encTagBytes(vt); b != nil {
+		e.Write(b)
+	}
+	encodeHead(e, byte(cborTypeByteString), uint64(len(data)))
+	e.Write(data)
+	return nil
 }
 
-func encodeMarshalerType(e *encodeState, em *encMode, v reflect.Value) (int, error) {
+func encodeMarshalerType(e *encodeState, em *encMode, v reflect.Value) error {
 	m, ok := v.Interface().(Marshaler)
 	if !ok {
 		pv := reflect.New(v.Type())
@@ -1005,38 +1110,52 @@ func encodeMarshalerType(e *encodeState, em *encMode, v reflect.Value) (int, err
 	}
 	data, err := m.MarshalCBOR()
 	if err != nil {
-		return 0, err
+		return err
 	}
-	return e.Write(data)
+	e.Write(data)
+	return nil
 }
 
-func encodeHead(e *encodeState, t byte, n uint64) int {
+func encodeTag(e *encodeState, em *encMode, v reflect.Value) error {
+	t := v.Interface().(Tag)
+
+	// Marshal tag number
+	encodeHead(e, byte(cborTypeTag), t.Number)
+
+	// Marshal tag content
+	if err := encode(e, em, reflect.ValueOf(t.Content)); err != nil {
+		return err
+	}
+
+	return nil
+}
+
+func encodeHead(e *encodeState, t byte, n uint64) {
 	if n <= 23 {
 		e.WriteByte(t | byte(n))
-		return 1
+		return
 	}
 	if n <= math.MaxUint8 {
 		e.scratch[0] = t | byte(24)
 		e.scratch[1] = byte(n)
 		e.Write(e.scratch[:2])
-		return 2
+		return
 	}
 	if n <= math.MaxUint16 {
 		e.scratch[0] = t | byte(25)
 		binary.BigEndian.PutUint16(e.scratch[1:], uint16(n))
 		e.Write(e.scratch[:3])
-		return 3
+		return
 	}
 	if n <= math.MaxUint32 {
 		e.scratch[0] = t | byte(26)
 		binary.BigEndian.PutUint32(e.scratch[1:], uint32(n))
 		e.Write(e.scratch[:5])
-		return 5
+		return
 	}
 	e.scratch[0] = t | byte(27)
 	binary.BigEndian.PutUint64(e.scratch[1:], n)
 	e.Write(e.scratch[:9])
-	return 9
 }
 
 var (
@@ -1045,19 +1164,23 @@ var (
 )
 
 func getEncodeFuncInternal(t reflect.Type) encodeFunc {
-	if t.Kind() == reflect.Ptr {
+	k := t.Kind()
+	if k == reflect.Ptr {
 		return getEncodeIndirectValueFunc(t)
 	}
+	if t == typeTag {
+		return encodeTag
+	}
+	if t == typeTime {
+		return encodeTime
+	}
 	if reflect.PtrTo(t).Implements(typeMarshaler) {
 		return encodeMarshalerType
 	}
 	if reflect.PtrTo(t).Implements(typeBinaryMarshaler) {
-		if t == typeTime {
-			return encodeTime
-		}
 		return encodeBinaryMarshalerType
 	}
-	switch t.Kind() {
+	switch k {
 	case reflect.Bool:
 		return encodeBool
 	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
@@ -1072,9 +1195,17 @@ func getEncodeFuncInternal(t reflect.Type) encodeFunc {
 		if t.Elem().Kind() == reflect.Uint8 {
 			return encodeByteString
 		}
-		return arrayEncoder{f: getEncodeFunc(t.Elem())}.encodeArray
+		f := getEncodeFunc(t.Elem())
+		if f == nil {
+			return nil
+		}
+		return arrayEncoder{f: f}.encodeArray
 	case reflect.Map:
-		return mapEncoder{kf: getEncodeFunc(t.Key()), ef: getEncodeFunc(t.Elem())}.encodeMap
+		kf, ef := getEncodeFunc(t.Key()), getEncodeFunc(t.Elem())
+		if kf == nil || ef == nil {
+			return nil
+		}
+		return mapEncoder{kf: kf, ef: ef}.encodeMap
 	case reflect.Struct:
 		return encodeStruct
 	case reflect.Interface:
@@ -1091,12 +1222,13 @@ func getEncodeIndirectValueFunc(t reflect.Type) encodeFunc {
 	if f == nil {
 		return nil
 	}
-	return func(e *encodeState, em *encMode, v reflect.Value) (int, error) {
+	return func(e *encodeState, em *encMode, v reflect.Value) error {
 		for v.Kind() == reflect.Ptr && !v.IsNil() {
 			v = v.Elem()
 		}
 		if v.Kind() == reflect.Ptr && v.IsNil() {
-			return e.Write(cborNil)
+			e.Write(cborNil)
+			return nil
 		}
 		return f(e, em, v)
 	}
diff --git a/stream.go b/stream.go
index bdd52c5..aa0816a 100644
--- a/stream.go
+++ b/stream.go
@@ -107,7 +107,7 @@ func (enc *Encoder) Encode(v interface{}) error {
 		}
 	}
 
-	_, err := encode(enc.e, enc.em, reflect.ValueOf(v))
+	err := encode(enc.e, enc.em, reflect.ValueOf(v))
 	if err == nil {
 		_, err = enc.e.WriteTo(enc.w)
 	}
diff --git a/structfields.go b/structfields.go
index 86305c3..d73b719 100644
--- a/structfields.go
+++ b/structfields.go
@@ -10,18 +10,19 @@ import (
 )
 
 type field struct {
-	name          string
-	cborName      []byte
-	idx           []int
-	typ           reflect.Type
-	ef            encodeFunc
-	isUnmarshaler bool
-	tagged        bool // used to choose dominant field (at the same level tagged fields dominate untagged fields)
-	omitEmpty     bool // used to skip empty field
-	keyAsInt      bool // used to encode/decode field name as int
+	name      string
+	nameAsInt int64 // used to decoder to match field name with CBOR int
+	cborName  []byte
+	idx       []int
+	typ       reflect.Type
+	ef        encodeFunc
+	typInfo   *typeInfo // used to decoder to reuse type info
+	tagged    bool      // used to choose dominant field (at the same level tagged fields dominate untagged fields)
+	omitEmpty bool      // used to skip empty field
+	keyAsInt  bool      // used to encode/decode field name as int
 }
 
-type fields []field
+type fields []*field
 
 // indexFieldSorter sorts fields by field idx at each level, breaking ties with idx depth.
 type indexFieldSorter struct {
@@ -154,7 +155,7 @@ func getFields(typ reflect.Type) (flds fields, structOptions string) {
 				}
 
 				if !f.Anonymous || ft.Kind() != reflect.Struct || len(tagFieldName) > 0 {
-					flds = append(flds, field{name: fieldName, idx: idx, typ: f.Type, tagged: tagged, omitEmpty: omitempty, keyAsInt: keyasint})
+					flds = append(flds, &field{name: fieldName, idx: idx, typ: f.Type, tagged: tagged, omitEmpty: omitempty, keyAsInt: keyasint})
 					continue
 				}
 
diff --git a/tag.go b/tag.go
new file mode 100644
index 0000000..18bba47
--- /dev/null
+++ b/tag.go
@@ -0,0 +1,247 @@
+package cbor
+
+import (
+	"errors"
+	"fmt"
+	"reflect"
+	"sync"
+)
+
+// Tag represents CBOR tag data, including tag number and unmarshaled tag content.
+type Tag struct {
+	Number  uint64
+	Content interface{}
+}
+
+func (t Tag) contentKind() reflect.Kind {
+	c := t.Content
+	for {
+		t, ok := c.(Tag)
+		if !ok {
+			break
+		}
+		c = t.Content
+	}
+	return reflect.ValueOf(c).Kind()
+}
+
+// RawTag represents CBOR tag data, including tag number and raw tag content.
+// RawTag implements Unmarshaler and Marshaler interfaces.
+type RawTag struct {
+	Number  uint64
+	Content RawMessage
+}
+
+// UnmarshalCBOR sets *t with tag number and raw tag content copied from data.
+func (t *RawTag) UnmarshalCBOR(data []byte) error {
+	if t == nil {
+		return errors.New("cbor.RawTag: UnmarshalCBOR on nil pointer")
+	}
+
+	d := decodeState{data: data, dm: defaultDecMode}
+
+	// Unmarshal tag number.
+	typ, _, num := d.getHead()
+	if typ != cborTypeTag {
+		return &UnmarshalTypeError{Value: typ.String(), Type: typeRawTag}
+	}
+	t.Number = num
+
+	// Unmarshal tag content.
+	c := d.data[d.off:]
+	t.Content = make([]byte, len(c))
+	copy(t.Content, c)
+	return nil
+}
+
+// MarshalCBOR returns CBOR encoding of t.
+func (t RawTag) MarshalCBOR() ([]byte, error) {
+	e := getEncodeState()
+	encodeHead(e, byte(cborTypeTag), t.Number)
+
+	buf := make([]byte, len(e.Bytes())+len(t.Content))
+	n := copy(buf, e.Bytes())
+	copy(buf[n:], t.Content)
+
+	putEncodeState(e)
+	return buf, nil
+}
+
+// DecTagMode specifies how decoder handles tag number.
+type DecTagMode int
+
+const (
+	// DecTagIgnored makes decoder ignore tag number (skips if present).
+	DecTagIgnored DecTagMode = iota
+
+	// DecTagOptional makes decoder verify tag number if it's present.
+	DecTagOptional
+
+	// DecTagRequired makes decoder verify tag number and tag number must be present.
+	DecTagRequired
+
+	maxDecTagMode
+)
+
+func (dtm DecTagMode) valid() bool {
+	return dtm < maxDecTagMode
+}
+
+// EncTagMode specifies how encoder handles tag number.
+type EncTagMode int
+
+const (
+	// EncTagIgnored makes encoder not encode tag number.
+	EncTagIgnored EncTagMode = iota
+
+	// EncTagRequired makes encoder encode tag number.
+	EncTagRequired
+
+	maxEncTagMode
+)
+
+func (etm EncTagMode) valid() bool {
+	return etm < maxEncTagMode
+}
+
+// TagOptions specifies how encoder and decoder handle tag number.
+type TagOptions struct {
+	DecTag DecTagMode
+	EncTag EncTagMode
+}
+
+// TagSet is an interface to add and remove tag info.  It is used by EncMode and DecMode
+// to provide CBOR tag support.
+type TagSet interface {
+	// Add adds given tag number(s), content type, and tag options to TagSet.
+	Add(opts TagOptions, contentType reflect.Type, num uint64, nestedNum ...uint64) error
+
+	// Remove removes given tag content type from TagSet.
+	Remove(contentType reflect.Type)
+
+	tagProvider
+}
+
+type tagProvider interface {
+	get(t reflect.Type) *tagItem
+}
+
+type tagItem struct {
+	num         []uint64
+	cborTagNum  []byte
+	contentType reflect.Type
+	opts        TagOptions
+}
+
+type (
+	tagSet map[reflect.Type]*tagItem
+
+	syncTagSet struct {
+		sync.RWMutex
+		t tagSet
+	}
+)
+
+func (t tagSet) get(typ reflect.Type) *tagItem {
+	return t[typ]
+}
+
+// NewTagSet returns TagSet (safe for concurrency).
+func NewTagSet() TagSet {
+	return &syncTagSet{t: make(map[reflect.Type]*tagItem)}
+}
+
+// Add adds given tag number(s), content type, and tag options to TagSet.
+func (t *syncTagSet) Add(opts TagOptions, contentType reflect.Type, num uint64, nestedNum ...uint64) error {
+	if contentType == nil {
+		return errors.New("cbor: cannot add nil content type to TagSet")
+	}
+	for contentType.Kind() == reflect.Ptr {
+		contentType = contentType.Elem()
+	}
+	tag, err := newTagItem(opts, contentType, num, nestedNum...)
+	if err != nil {
+		return err
+	}
+	t.Lock()
+	defer t.Unlock()
+	if _, ok := t.t[contentType]; ok {
+		return errors.New("cbor: content type " + contentType.String() + " already exists in TagSet")
+	}
+	t.t[contentType] = tag
+	return nil
+}
+
+// Remove removes given tag content type from TagSet.
+func (t *syncTagSet) Remove(contentType reflect.Type) {
+	for contentType.Kind() == reflect.Ptr {
+		contentType = contentType.Elem()
+	}
+	t.Lock()
+	delete(t.t, contentType)
+	t.Unlock()
+}
+
+func (t *syncTagSet) get(typ reflect.Type) *tagItem {
+	t.RLock()
+	ti := t.t[typ]
+	t.RUnlock()
+	return ti
+}
+
+func newTagItem(opts TagOptions, contentType reflect.Type, num uint64, nestedNum ...uint64) (*tagItem, error) {
+	if opts.DecTag == DecTagIgnored && opts.EncTag == EncTagIgnored {
+		return nil, errors.New("cbor: cannot add tag with DecTagIgnored and EncTagIgnored options to TagSet")
+	}
+	if contentType.PkgPath() == "" || contentType.Kind() == reflect.Interface {
+		return nil, errors.New("cbor: can only add named types to TagSet, got " + contentType.String())
+	}
+	if contentType == typeTime {
+		return nil, errors.New("cbor: cannot add time.Time to TagSet, use EncOptions.TimeTag and DecOptions.TimeTag instead")
+	}
+	if contentType == typeTag {
+		return nil, errors.New("cbor: cannot add cbor.Tag to TagSet")
+	}
+	if contentType == typeRawTag {
+		return nil, errors.New("cbor: cannot add cbor.RawTag to TagSet")
+	}
+	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 reflect.PtrTo(contentType).Implements(typeMarshaler) && opts.EncTag != EncTagIgnored {
+		return nil, errors.New("cbor: cannot add cbor.Marshaler to TagSet with EncTag != EncTagIgnored")
+	}
+	if reflect.PtrTo(contentType).Implements(typeUnmarshaler) && opts.DecTag != DecTagIgnored {
+		return nil, errors.New("cbor: cannot add cbor.Unmarshaler to TagSet with DecTag != DecTagIgnored")
+	}
+
+	te := tagItem{num: []uint64{num}, opts: opts, contentType: contentType}
+	te.num = append(te.num, nestedNum...)
+
+	// Cache encoded tag numbers
+	e := getEncodeState()
+	for _, n := range te.num {
+		encodeHead(e, byte(cborTypeTag), n)
+	}
+	te.cborTagNum = make([]byte, e.Len())
+	copy(te.cborTagNum, e.Bytes())
+	putEncodeState(e)
+
+	return &te, nil
+}
+
+var (
+	typeTag    = reflect.TypeOf(Tag{})
+	typeRawTag = reflect.TypeOf(RawTag{})
+)
+
+// WrongTagError describes mismatch between CBOR tag and registered tag.
+type WrongTagError struct {
+	RegisteredType   reflect.Type
+	RegisteredTagNum []uint64
+	TagNum           []uint64
+}
+
+func (e *WrongTagError) Error() string {
+	return fmt.Sprintf("cbor: wrong tag number for %s, got %v, expected %v", e.RegisteredType.String(), e.TagNum, e.RegisteredTagNum)
+}
diff --git a/valid.go b/valid.go
index 46022f4..a0b5864 100644
--- a/valid.go
+++ b/valid.go
@@ -45,10 +45,12 @@ func validInternal(data []byte, off int, depth int) (int, int, error) {
 	if depth > maxNestingLevel {
 		return 0, 0, errors.New("cbor: reached max depth " + strconv.Itoa(maxNestingLevel))
 	}
+
 	off, t, ai, val, err := validHead(data, off)
 	if err != nil {
 		return 0, 0, err
 	}
+
 	if ai == 31 {
 		if t == cborTypeByteString || t == cborTypeTextString {
 			return validIndefiniteString(data, off, t, depth)
@@ -56,6 +58,8 @@ func validInternal(data []byte, off int, depth int) (int, int, error) {
 		return validIndefiniteArrOrMap(data, off, t, depth)
 	}
 
+	dataLen := len(data)
+
 	switch t {
 	case cborTypeByteString, cborTypeTextString:
 		valInt := int(val)
@@ -63,7 +67,7 @@ func validInternal(data []byte, off int, depth int) (int, int, error) {
 			// Detect integer overflow
 			return 0, 0, errors.New("cbor: " + t.String() + " length " + strconv.FormatUint(val, 10) + " is too large, causing integer overflow")
 		}
-		if len(data)-off < valInt { // valInt+off may overflow integer
+		if dataLen-off < valInt { // valInt+off may overflow integer
 			return 0, 0, io.ErrUnexpectedEOF
 		}
 		off += valInt
@@ -93,7 +97,7 @@ func validInternal(data []byte, off int, depth int) (int, int, error) {
 	case cborTypeTag:
 		// Scan nested tag numbers to avoid recursion.
 		for {
-			if len(data)-off < 1 { // Tag number must be followed by tag content.
+			if dataLen == off { // Tag number must be followed by tag content.
 				return 0, 0, io.ErrUnexpectedEOF
 			}
 			if cborType(data[off]&0xe0) != cborTypeTag {
@@ -105,9 +109,7 @@ func validInternal(data []byte, off int, depth int) (int, int, error) {
 			depth++
 		}
 		// Check tag content.
-		if off, depth, err = validInternal(data, off, depth); err != nil {
-			return 0, 0, err
-		}
+		return validInternal(data, off, depth)
 	}
 	return off, depth, nil
 }
@@ -115,8 +117,9 @@ func validInternal(data []byte, off int, depth int) (int, int, error) {
 // validIndefiniteString checks indefinite length byte/text string's well-formedness and returns data's next offset, max depth, and error.
 func validIndefiniteString(data []byte, off int, t cborType, depth int) (int, int, error) {
 	var err error
+	dataLen := len(data)
 	for {
-		if len(data)-off < 1 {
+		if dataLen == off {
 			return 0, 0, io.ErrUnexpectedEOF
 		}
 		if data[off] == 0xff {
@@ -142,9 +145,10 @@ func validIndefiniteString(data []byte, off int, t cborType, depth int) (int, in
 func validIndefiniteArrOrMap(data []byte, off int, t cborType, depth int) (int, int, error) {
 	var err error
 	maxDepth := depth
+	dataLen := len(data)
 	i := 0
 	for {
-		if len(data)-off < 1 {
+		if dataLen == off {
 			return 0, 0, io.ErrUnexpectedEOF
 		}
 		if data[off] == 0xff {
@@ -168,7 +172,7 @@ func validIndefiniteArrOrMap(data []byte, off int, t cborType, depth int) (int,
 
 func validHead(data []byte, off int) (_ int, t cborType, ai byte, val uint64, err error) {
 	dataLen := len(data) - off
-	if dataLen < 1 {
+	if dataLen == 0 {
 		return 0, 0, 0, 0, io.ErrUnexpectedEOF
 	}
 
@@ -177,8 +181,10 @@ func validHead(data []byte, off int) (_ int, t cborType, ai byte, val uint64, er
 	val = uint64(ai)
 	off++
 
-	switch ai {
-	case 24:
+	if ai < 24 {
+		return off, t, ai, val, nil
+	}
+	if ai == 24 {
 		if dataLen < 2 {
 			return 0, 0, 0, 0, io.ErrUnexpectedEOF
 		}
@@ -187,33 +193,41 @@ func validHead(data []byte, off int) (_ int, t cborType, ai byte, val uint64, er
 		if t == cborTypePrimitives && val < 32 {
 			return 0, 0, 0, 0, &SyntaxError{"cbor: invalid simple value " + strconv.Itoa(int(val)) + " for type " + t.String()}
 		}
-	case 25:
+		return off, t, ai, val, nil
+	}
+	if ai == 25 {
 		if dataLen < 3 {
 			return 0, 0, 0, 0, io.ErrUnexpectedEOF
 		}
 		val = uint64(binary.BigEndian.Uint16(data[off : off+2]))
 		off += 2
-	case 26:
+		return off, t, ai, val, nil
+	}
+	if ai == 26 {
 		if dataLen < 5 {
 			return 0, 0, 0, 0, io.ErrUnexpectedEOF
 		}
 		val = uint64(binary.BigEndian.Uint32(data[off : off+4]))
 		off += 4
-	case 27:
+		return off, t, ai, val, nil
+	}
+	if ai == 27 {
 		if dataLen < 9 {
 			return 0, 0, 0, 0, io.ErrUnexpectedEOF
 		}
 		val = binary.BigEndian.Uint64(data[off : off+8])
 		off += 8
-	case 28, 29, 30:
-		return 0, 0, 0, 0, &SyntaxError{"cbor: invalid additional information " + strconv.Itoa(int(ai)) + " for type " + t.String()}
-	case 31:
+		return off, t, ai, val, nil
+	}
+	if ai == 31 {
 		switch t {
 		case cborTypePositiveInt, cborTypeNegativeInt, cborTypeTag:
 			return 0, 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 validIndefinite().
 			return 0, 0, 0, 0, &SyntaxError{"cbor: unexpected \"break\" code"}
 		}
+		return off, t, ai, val, nil
 	}
-	return off, t, ai, val, nil
+	// ai == 28, 29, 30
+	return 0, 0, 0, 0, &SyntaxError{"cbor: invalid additional information " + strconv.Itoa(int(ai)) + " for type " + t.String()}
 }


Test patch:
diff --git a/decode_test.go b/decode_test.go
index bfe7c49..ec5fc01 100644
--- a/decode_test.go
+++ b/decode_test.go
@@ -36,7 +36,6 @@ var (
 	typeMapStringInt    = reflect.TypeOf(map[string]int{})
 	typeMapStringString = reflect.TypeOf(map[string]string{})
 	typeMapStringIntf   = reflect.TypeOf(map[string]interface{}{})
-	// typeIntf            = reflect.TypeOf([]interface{}(nil)).Elem()
 )
 
 type unmarshalTest struct {
@@ -53,92 +52,92 @@ var unmarshalTests = []unmarshalTest{
 		hexDecode("00"),
 		uint64(0),
 		[]interface{}{uint8(0), uint16(0), uint32(0), uint64(0), uint(0), int8(0), int16(0), int32(0), int64(0), int(0), float32(0), float64(0)},
-		[]reflect.Type{typeString, typeBool, typeIntSlice, typeMapStringInt},
+		[]reflect.Type{typeString, typeBool, typeIntSlice, typeMapStringInt, typeTag, typeRawTag},
 	},
 	{
 		hexDecode("01"),
 		uint64(1),
 		[]interface{}{uint8(1), uint16(1), uint32(1), uint64(1), uint(1), int8(1), int16(1), int32(1), int64(1), int(1), float32(1), float64(1)},
-		[]reflect.Type{typeString, typeBool, typeIntSlice, typeMapStringInt},
+		[]reflect.Type{typeString, typeBool, typeIntSlice, typeMapStringInt, typeTag, typeRawTag},
 	},
 	{
 		hexDecode("0a"),
 		uint64(10),
 		[]interface{}{uint8(10), uint16(10), uint32(10), uint64(10), uint(10), int8(10), int16(10), int32(10), int64(10), int(10), float32(10), float64(10)},
-		[]reflect.Type{typeString, typeBool, typeIntSlice, typeMapStringInt},
+		[]reflect.Type{typeString, typeBool, typeIntSlice, typeMapStringInt, typeTag, typeRawTag},
 	},
 	{
 		hexDecode("17"),
 		uint64(23),
 		[]interface{}{uint8(23), uint16(23), uint32(23), uint64(23), uint(23), int8(23), int16(23), int32(23), int64(23), int(23), float32(23), float64(23)},
-		[]reflect.Type{typeString, typeBool, typeIntSlice, typeMapStringInt},
+		[]reflect.Type{typeString, typeBool, typeIntSlice, typeMapStringInt, typeTag, typeRawTag},
 	},
 	{
 		hexDecode("1818"),
 		uint64(24),
 		[]interface{}{uint8(24), uint16(24), uint32(24), uint64(24), uint(24), int8(24), int16(24), int32(24), int64(24), int(24), float32(24), float64(24)},
-		[]reflect.Type{typeString, typeBool, typeIntSlice, typeMapStringInt},
+		[]reflect.Type{typeString, typeBool, typeIntSlice, typeMapStringInt, typeTag, typeRawTag},
 	},
 	{
 		hexDecode("1819"),
 		uint64(25),
 		[]interface{}{uint8(25), uint16(25), uint32(25), uint64(25), uint(25), int8(25), int16(25), int32(25), int64(25), int(25), float32(25), float64(25)},
-		[]reflect.Type{typeString, typeBool, typeIntSlice, typeMapStringInt},
+		[]reflect.Type{typeString, typeBool, typeIntSlice, typeMapStringInt, typeTag, typeRawTag},
 	},
 	{
 		hexDecode("1864"),
 		uint64(100),
 		[]interface{}{uint8(100), uint16(100), uint32(100), uint64(100), uint(100), int8(100), int16(100), int32(100), int64(100), int(100), float32(100), float64(100)},
-		[]reflect.Type{typeString, typeBool, typeIntSlice, typeMapStringInt},
+		[]reflect.Type{typeString, typeBool, typeIntSlice, typeMapStringInt, typeTag, typeRawTag},
 	},
 	{
 		hexDecode("1903e8"),
 		uint64(1000),
 		[]interface{}{uint16(1000), uint32(1000), uint64(1000), uint(1000), int16(1000), int32(1000), int64(1000), int(1000), float32(1000), float64(1000)},
-		[]reflect.Type{typeUint8, typeInt8, typeString, typeBool, typeIntSlice, typeMapStringInt},
+		[]reflect.Type{typeUint8, typeInt8, typeString, typeBool, typeIntSlice, typeMapStringInt, typeTag, typeRawTag},
 	},
 	{
 		hexDecode("1a000f4240"),
 		uint64(1000000),
 		[]interface{}{uint32(1000000), uint64(1000000), uint(1000000), int32(1000000), int64(1000000), int(1000000), float32(1000000), float64(1000000)},
-		[]reflect.Type{typeUint8, typeUint16, typeInt8, typeInt16, typeString, typeBool, typeIntSlice, typeMapStringInt},
+		[]reflect.Type{typeUint8, typeUint16, typeInt8, typeInt16, typeString, typeBool, typeIntSlice, typeMapStringInt, typeTag, typeRawTag},
 	},
 	{
 		hexDecode("1b000000e8d4a51000"),
 		uint64(1000000000000),
 		[]interface{}{uint64(1000000000000), uint(1000000000000), int64(1000000000000), int(1000000000000), float32(1000000000000), float64(1000000000000)},
-		[]reflect.Type{typeUint8, typeUint16, typeUint32, typeInt8, typeInt16, typeInt32, typeString, typeBool, typeIntSlice, typeMapStringInt},
+		[]reflect.Type{typeUint8, typeUint16, typeUint32, typeInt8, typeInt16, typeInt32, typeString, typeBool, typeIntSlice, typeMapStringInt, typeTag, typeRawTag},
 	},
 	{
 		hexDecode("1bffffffffffffffff"),
 		uint64(18446744073709551615),
 		[]interface{}{uint64(18446744073709551615), uint(18446744073709551615), float32(18446744073709551615), float64(18446744073709551615)},
-		[]reflect.Type{typeUint8, typeUint16, typeUint32, typeInt8, typeInt16, typeInt32, typeString, typeBool, typeIntSlice, typeMapStringInt},
+		[]reflect.Type{typeUint8, typeUint16, typeUint32, typeInt8, typeInt16, typeInt32, typeString, typeBool, typeIntSlice, typeMapStringInt, typeTag, typeRawTag},
 	},
 	// negative integer
 	{
 		hexDecode("20"),
 		int64(-1),
 		[]interface{}{int8(-1), int16(-1), int32(-1), int64(-1), int(-1), float32(-1), float64(-1)},
-		[]reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeString, typeBool, typeIntSlice, typeMapStringInt},
+		[]reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeString, typeBool, typeIntSlice, typeMapStringInt, typeTag, typeRawTag},
 	},
 	{
 		hexDecode("29"),
 		int64(-10),
 		[]interface{}{int8(-10), int16(-10), int32(-10), int64(-10), int(-10), float32(-10), float64(-10)},
-		[]reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeString, typeBool, typeIntSlice, typeMapStringInt},
+		[]reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeString, typeBool, typeIntSlice, typeMapStringInt, typeTag, typeRawTag},
 	},
 	{
 		hexDecode("3863"),
 		int64(-100),
 		[]interface{}{int8(-100), int16(-100), int32(-100), int64(-100), int(-100), float32(-100), float64(-100)},
-		[]reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeString, typeBool, typeIntSlice, typeMapStringInt},
+		[]reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeString, typeBool, typeIntSlice, typeMapStringInt, typeTag, typeRawTag},
 	},
 	{
 		hexDecode("3903e7"),
 		int64(-1000),
 		[]interface{}{int16(-1000), int32(-1000), int64(-1000), int(-1000), float32(-1000), float64(-1000)},
-		[]reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeString, typeBool, typeIntSlice, typeMapStringInt},
+		[]reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeString, typeBool, typeIntSlice, typeMapStringInt, typeTag, typeRawTag},
 	},
 	// {"3bffffffffffffffff", int64(-18446744073709551616)}, // CBOR value -18446744073709551616 overflows Go's int64, see TestNegIntOverflow
 	// byte string
@@ -146,99 +145,99 @@ var unmarshalTests = []unmarshalTest{
 		hexDecode("40"),
 		[]byte{},
 		[]interface{}{[]byte{}},
-		[]reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeFloat32, typeFloat64, typeBool, typeIntSlice, typeMapStringInt},
+		[]reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeFloat32, typeFloat64, typeBool, typeIntSlice, typeMapStringInt, typeTag, typeRawTag},
 	},
 	{
 		hexDecode("4401020304"),
 		[]byte{1, 2, 3, 4},
 		[]interface{}{[]byte{1, 2, 3, 4}},
-		[]reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeFloat32, typeFloat64, typeBool, typeIntSlice, typeMapStringInt},
+		[]reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeFloat32, typeFloat64, typeBool, typeIntSlice, typeMapStringInt, typeTag, typeRawTag},
 	},
 	{
 		hexDecode("5f42010243030405ff"),
 		[]byte{1, 2, 3, 4, 5},
 		[]interface{}{[]byte{1, 2, 3, 4, 5}},
-		[]reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeFloat32, typeFloat64, typeBool, typeIntSlice, typeMapStringInt},
+		[]reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeFloat32, typeFloat64, typeBool, typeIntSlice, typeMapStringInt, typeTag, typeRawTag},
 	},
 	// text string
 	{
 		hexDecode("60"),
 		"",
 		[]interface{}{""},
-		[]reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeFloat32, typeFloat64, typeBool, typeIntSlice, typeMapStringInt},
+		[]reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeFloat32, typeFloat64, typeBool, typeIntSlice, typeMapStringInt, typeTag, typeRawTag},
 	},
 	{
 		hexDecode("6161"),
 		"a",
 		[]interface{}{"a"},
-		[]reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeFloat32, typeFloat64, typeBool, typeIntSlice, typeMapStringInt},
+		[]reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeFloat32, typeFloat64, typeBool, typeIntSlice, typeMapStringInt, typeTag, typeRawTag},
 	},
 	{
 		hexDecode("6449455446"),
 		"IETF",
 		[]interface{}{"IETF"},
-		[]reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeFloat32, typeFloat64, typeBool, typeIntSlice, typeMapStringInt},
+		[]reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeFloat32, typeFloat64, typeBool, typeIntSlice, typeMapStringInt, typeTag, typeRawTag},
 	},
 	{
 		hexDecode("62225c"),
 		"\"\\",
 		[]interface{}{"\"\\"},
-		[]reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeFloat32, typeFloat64, typeBool, typeIntSlice, typeMapStringInt},
+		[]reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeFloat32, typeFloat64, typeBool, typeIntSlice, typeMapStringInt, typeTag, typeRawTag},
 	},
 	{
 		hexDecode("62c3bc"),
 		"ü",
 		[]interface{}{"ü"},
-		[]reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeFloat32, typeFloat64, typeBool, typeIntSlice, typeMapStringInt},
+		[]reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeFloat32, typeFloat64, typeBool, typeIntSlice, typeMapStringInt, typeTag, typeRawTag},
 	},
 	{
 		hexDecode("63e6b0b4"),
 		"水",
 		[]interface{}{"水"},
-		[]reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeFloat32, typeFloat64, typeBool, typeIntSlice, typeMapStringInt},
+		[]reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeFloat32, typeFloat64, typeBool, typeIntSlice, typeMapStringInt, typeTag, typeRawTag},
 	},
 	{
 		hexDecode("64f0908591"),
 		"𐅑",
 		[]interface{}{"𐅑"},
-		[]reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeFloat32, typeFloat64, typeBool, typeIntSlice, typeMapStringInt},
+		[]reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeFloat32, typeFloat64, typeBool, typeIntSlice, typeMapStringInt, typeTag, typeRawTag},
 	},
 	{
 		hexDecode("7f657374726561646d696e67ff"),
 		"streaming",
 		[]interface{}{"streaming"},
-		[]reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeFloat32, typeFloat64, typeBool, typeIntSlice, typeMapStringInt},
+		[]reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeFloat32, typeFloat64, typeBool, typeIntSlice, typeMapStringInt, typeTag, typeRawTag},
 	},
 	// array
 	{
 		hexDecode("80"),
 		[]interface{}{},
 		[]interface{}{[]interface{}{}, []byte{}, []string{}, []int{}, [...]int{}, []float32{}, []float64{}},
-		[]reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeFloat32, typeFloat64, typeString, typeBool, typeMapStringInt},
+		[]reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeFloat32, typeFloat64, typeString, typeBool, typeMapStringInt, typeTag, typeRawTag},
 	},
 	{
 		hexDecode("83010203"),
 		[]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}, [...]int{1, 2, 3}, []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{})},
+		[]reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeFloat32, typeFloat64, typeString, typeBool, typeStringSlice, typeMapStringInt, reflect.TypeOf([3]string{}), typeTag, typeRawTag},
 	},
 	{
 		hexDecode("8301820203820405"),
 		[]interface{}{uint64(1), []interface{}{uint64(2), uint64(3)}, []interface{}{uint64(4), uint64(5)}},
 		[]interface{}{[]interface{}{uint64(1), []interface{}{uint64(2), uint64(3)}, []interface{}{uint64(4), uint64(5)}}, [...]interface{}{uint64(1), []interface{}{uint64(2), uint64(3)}, []interface{}{uint64(4), uint64(5)}}},
-		[]reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeFloat32, typeFloat64, typeString, typeBool, typeStringSlice, typeMapStringInt, reflect.TypeOf([3]string{})},
+		[]reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeFloat32, typeFloat64, typeString, typeBool, typeStringSlice, typeMapStringInt, reflect.TypeOf([3]string{}), typeTag, typeRawTag},
 	},
 	{
 		hexDecode("83018202039f0405ff"),
 		[]interface{}{uint64(1), []interface{}{uint64(2), uint64(3)}, []interface{}{uint64(4), uint64(5)}},
 		[]interface{}{[]interface{}{uint64(1), []interface{}{uint64(2), uint64(3)}, []interface{}{uint64(4), uint64(5)}}, [...]interface{}{uint64(1), []interface{}{uint64(2), uint64(3)}, []interface{}{uint64(4), uint64(5)}}},
-		[]reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeFloat32, typeFloat64, typeString, typeBool, typeStringSlice, typeMapStringInt, reflect.TypeOf([3]string{})},
+		[]reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeFloat32, typeFloat64, typeString, typeBool, typeStringSlice, typeMapStringInt, reflect.TypeOf([3]string{}), typeTag, typeRawTag},
 	},
 	{
 		hexDecode("83019f0203ff820405"),
 		[]interface{}{uint64(1), []interface{}{uint64(2), uint64(3)}, []interface{}{uint64(4), uint64(5)}},
 		[]interface{}{[]interface{}{uint64(1), []interface{}{uint64(2), uint64(3)}, []interface{}{uint64(4), uint64(5)}}, [...]interface{}{uint64(1), []interface{}{uint64(2), uint64(3)}, []interface{}{uint64(4), uint64(5)}}},
-		[]reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeFloat32, typeFloat64, typeString, typeBool, typeStringSlice, typeMapStringInt, reflect.TypeOf([3]string{})},
+		[]reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeFloat32, typeFloat64, typeString, typeBool, typeStringSlice, typeMapStringInt, reflect.TypeOf([3]string{}), typeTag, typeRawTag},
 	},
 	{
 		hexDecode("98190102030405060708090a0b0c0d0e0f101112131415161718181819"),
@@ -251,25 +250,25 @@ var unmarshalTests = []unmarshalTest{
 			[...]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25},
 			[]float32{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25},
 			[]float64{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25}},
-		[]reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeFloat32, typeFloat64, typeString, typeBool, typeStringSlice, typeMapStringInt, reflect.TypeOf([3]string{})},
+		[]reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeFloat32, typeFloat64, typeString, typeBool, typeStringSlice, typeMapStringInt, reflect.TypeOf([3]string{}), typeTag, typeRawTag},
 	},
 	{
 		hexDecode("9fff"),
 		[]interface{}{},
 		[]interface{}{[]interface{}{}, []byte{}, []string{}, []int{}, [...]int{}, []float32{}, []float64{}},
-		[]reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeFloat32, typeFloat64, typeString, typeBool, typeMapStringInt},
+		[]reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeFloat32, typeFloat64, typeString, typeBool, typeMapStringInt, typeTag, typeRawTag},
 	},
 	{
 		hexDecode("9f018202039f0405ffff"),
 		[]interface{}{uint64(1), []interface{}{uint64(2), uint64(3)}, []interface{}{uint64(4), uint64(5)}},
 		[]interface{}{[]interface{}{uint64(1), []interface{}{uint64(2), uint64(3)}, []interface{}{uint64(4), uint64(5)}}, [...]interface{}{uint64(1), []interface{}{uint64(2), uint64(3)}, []interface{}{uint64(4), uint64(5)}}},
-		[]reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeFloat32, typeFloat64, typeString, typeBool, typeStringSlice, typeMapStringInt, reflect.TypeOf([3]string{})},
+		[]reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeFloat32, typeFloat64, typeString, typeBool, typeStringSlice, typeMapStringInt, reflect.TypeOf([3]string{}), typeTag, typeRawTag},
 	},
 	{
 		hexDecode("9f01820203820405ff"),
 		[]interface{}{uint64(1), []interface{}{uint64(2), uint64(3)}, []interface{}{uint64(4), uint64(5)}},
 		[]interface{}{[]interface{}{uint64(1), []interface{}{uint64(2), uint64(3)}, []interface{}{uint64(4), uint64(5)}}, [...]interface{}{uint64(1), []interface{}{uint64(2), uint64(3)}, []interface{}{uint64(4), uint64(5)}}},
-		[]reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeFloat32, typeFloat64, typeString, typeBool, typeStringSlice, typeMapStringInt, reflect.TypeOf([3]string{})},
+		[]reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeFloat32, typeFloat64, typeString, typeBool, typeStringSlice, typeMapStringInt, reflect.TypeOf([3]string{}), typeTag, typeRawTag},
 	},
 	{
 		hexDecode("9f0102030405060708090a0b0c0d0e0f101112131415161718181819ff"),
@@ -282,39 +281,39 @@ var unmarshalTests = []unmarshalTest{
 			[...]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25},
 			[]float32{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25},
 			[]float64{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25}},
-		[]reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeFloat32, typeFloat64, typeString, typeBool, typeStringSlice, typeMapStringInt, reflect.TypeOf([3]string{})},
+		[]reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeFloat32, typeFloat64, typeString, typeBool, typeStringSlice, typeMapStringInt, reflect.TypeOf([3]string{}), typeTag, typeRawTag},
 	},
 	{
 		hexDecode("826161a161626163"),
 		[]interface{}{"a", map[interface{}]interface{}{"b": "c"}},
 		[]interface{}{[]interface{}{"a", map[interface{}]interface{}{"b": "c"}}, [...]interface{}{"a", map[interface{}]interface{}{"b": "c"}}},
-		[]reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeFloat32, typeFloat64, typeString, typeBool, typeStringSlice, typeMapStringInt, reflect.TypeOf([3]string{})},
+		[]reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeFloat32, typeFloat64, typeString, typeBool, typeStringSlice, typeMapStringInt, reflect.TypeOf([3]string{}), typeTag, typeRawTag},
 	},
 	{
 		hexDecode("826161bf61626163ff"),
 		[]interface{}{"a", map[interface{}]interface{}{"b": "c"}},
 		[]interface{}{[]interface{}{"a", map[interface{}]interface{}{"b": "c"}}, [...]interface{}{"a", map[interface{}]interface{}{"b": "c"}}},
-		[]reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeFloat32, typeFloat64, typeString, typeBool, typeStringSlice, typeMapStringInt, reflect.TypeOf([3]string{})},
+		[]reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeFloat32, typeFloat64, typeString, typeBool, typeStringSlice, typeMapStringInt, reflect.TypeOf([3]string{}), typeTag, typeRawTag},
 	},
 	// map
 	{
 		hexDecode("a0"),
 		map[interface{}]interface{}{},
 		[]interface{}{map[interface{}]interface{}{}, map[string]bool{}, map[string]int{}, map[int]string{}, map[int]bool{}},
-		[]reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeFloat32, typeFloat64, typeByteSlice, typeString, typeBool, typeIntSlice},
+		[]reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeFloat32, typeFloat64, typeByteSlice, typeString, typeBool, typeIntSlice, typeTag, typeRawTag},
 	},
 	{
 		hexDecode("a201020304"),
 		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, typeString, typeBool, typeIntSlice, typeMapStringInt},
+		[]reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeFloat32, typeFloat64, typeByteSlice, typeString, typeBool, typeIntSlice, typeMapStringInt, typeTag, typeRawTag},
 	},
 	{
 		hexDecode("a26161016162820203"),
 		map[interface{}]interface{}{"a": uint64(1), "b": []interface{}{uint64(2), uint64(3)}},
 		[]interface{}{map[interface{}]interface{}{"a": uint64(1), "b": []interface{}{uint64(2), uint64(3)}},
 			map[string]interface{}{"a": uint64(1), "b": []interface{}{uint64(2), uint64(3)}}},
-		[]reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeFloat32, typeFloat64, typeByteSlice, typeString, typeBool, typeIntSlice, typeMapStringInt},
+		[]reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeFloat32, typeFloat64, typeByteSlice, typeString, typeBool, typeIntSlice, typeMapStringInt, typeTag, typeRawTag},
 	},
 	{
 		hexDecode("a56161614161626142616361436164614461656145"),
@@ -322,69 +321,69 @@ var unmarshalTests = []unmarshalTest{
 		[]interface{}{map[interface{}]interface{}{"a": "A", "b": "B", "c": "C", "d": "D", "e": "E"},
 			map[string]interface{}{"a": "A", "b": "B", "c": "C", "d": "D", "e": "E"},
 			map[string]string{"a": "A", "b": "B", "c": "C", "d": "D", "e": "E"}},
-		[]reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeFloat32, typeFloat64, typeByteSlice, typeString, typeBool, typeIntSlice, typeMapStringInt},
+		[]reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeFloat32, typeFloat64, typeByteSlice, typeString, typeBool, typeIntSlice, typeMapStringInt, typeTag, typeRawTag},
 	},
 	{
 		hexDecode("bf61610161629f0203ffff"),
 		map[interface{}]interface{}{"a": uint64(1), "b": []interface{}{uint64(2), uint64(3)}},
 		[]interface{}{map[interface{}]interface{}{"a": uint64(1), "b": []interface{}{uint64(2), uint64(3)}},
 			map[string]interface{}{"a": uint64(1), "b": []interface{}{uint64(2), uint64(3)}}},
-		[]reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeFloat32, typeFloat64, typeByteSlice, typeString, typeBool, typeIntSlice, typeMapStringInt},
+		[]reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeFloat32, typeFloat64, typeByteSlice, typeString, typeBool, typeIntSlice, typeMapStringInt, typeTag, typeRawTag},
 	},
 	{
 		hexDecode("bf6346756ef563416d7421ff"),
 		map[interface{}]interface{}{"Fun": true, "Amt": int64(-2)},
 		[]interface{}{map[interface{}]interface{}{"Fun": true, "Amt": int64(-2)},
 			map[string]interface{}{"Fun": true, "Amt": int64(-2)}},
-		[]reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeFloat32, typeFloat64, typeByteSlice, typeString, typeBool, typeIntSlice, typeMapStringInt},
+		[]reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeFloat32, typeFloat64, typeByteSlice, typeString, typeBool, typeIntSlice, typeMapStringInt, typeTag, typeRawTag},
 	},
 	// tag
 	{
 		hexDecode("c074323031332d30332d32315432303a30343a30305a"),
-		"2013-03-21T20:04:00Z",
-		[]interface{}{"2013-03-21T20:04:00Z"},
+		time.Date(2013, 3, 21, 20, 4, 0, 0, time.UTC), // 2013-03-21 20:04:00 +0000 UTC
+		[]interface{}{"2013-03-21T20:04:00Z", time.Date(2013, 3, 21, 20, 4, 0, 0, time.UTC), Tag{0, "2013-03-21T20:04:00Z"}, RawTag{0, hexDecode("74323031332d30332d32315432303a30343a30305a")}},
 		[]reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeFloat32, typeFloat64, typeBool, typeIntSlice, typeMapStringInt},
 	}, // 0: standard date/time
 	{
 		hexDecode("c11a514b67b0"),
-		uint64(1363896240),
-		[]interface{}{uint32(1363896240), uint64(1363896240), int32(1363896240), int64(1363896240), float32(1363896240), float64(1363896240)},
+		time.Date(2013, 3, 21, 20, 4, 0, 0, time.UTC), // 2013-03-21 20:04:00 +0000 UTC
+		[]interface{}{uint32(1363896240), uint64(1363896240), int32(1363896240), int64(1363896240), float32(1363896240), float64(1363896240), time.Date(2013, 3, 21, 20, 4, 0, 0, time.UTC), Tag{1, uint64(1363896240)}, RawTag{1, hexDecode("1a514b67b0")}},
 		[]reflect.Type{typeUint8, typeUint16, typeInt8, typeInt16, typeByteSlice, typeString, typeBool, typeIntSlice, typeMapStringInt},
 	}, // 1: epoch-based date/time
 	{
 		hexDecode("c249010000000000000000"),
-		[]byte{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
-		[]interface{}{[]byte{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+		Tag{2, []byte{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+		[]interface{}{[]byte{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, Tag{2, []byte{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, RawTag{2, hexDecode("49010000000000000000")}},
 		[]reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeFloat32, typeFloat64, typeBool, typeIntSlice, typeMapStringInt},
 	}, // 2: positive bignum: 18446744073709551616
 	{
 		hexDecode("c349010000000000000000"),
-		[]byte{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
-		[]interface{}{[]byte{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+		Tag{3, []byte{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+		[]interface{}{[]byte{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, Tag{3, []byte{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, RawTag{3, hexDecode("49010000000000000000")}},
 		[]reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeFloat32, typeFloat64, typeBool, typeIntSlice, typeMapStringInt},
 	}, // 3: negative bignum: -18446744073709551617
 	{
 		hexDecode("c1fb41d452d9ec200000"),
-		float64(1363896240.5),
-		[]interface{}{float32(1363896240.5), float64(1363896240.5)},
+		time.Date(2013, 3, 21, 20, 4, 0, 500000000, time.UTC), // 2013-03-21 20:04:00.5 +0000 UTC
+		[]interface{}{float32(1363896240.5), float64(1363896240.5), time.Date(2013, 3, 21, 20, 4, 0, 500000000, time.UTC), Tag{1, float64(1363896240.5)}, RawTag{1, hexDecode("fb41d452d9ec200000")}},
 		[]reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeByteSlice, typeString, typeBool, typeIntSlice, typeMapStringInt},
 	}, // 1: epoch-based date/time
 	{
 		hexDecode("d74401020304"),
-		[]byte{0x01, 0x02, 0x03, 0x04},
-		[]interface{}{[]byte{0x01, 0x02, 0x03, 0x04}},
+		Tag{23, []byte{0x01, 0x02, 0x03, 0x04}},
+		[]interface{}{[]byte{0x01, 0x02, 0x03, 0x04}, Tag{23, []byte{0x01, 0x02, 0x03, 0x04}}, RawTag{23, hexDecode("4401020304")}},
 		[]reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeFloat32, typeFloat64, typeBool, typeIntSlice, typeMapStringInt},
 	}, // 23: expected conversion to base16 encoding
 	{
 		hexDecode("d818456449455446"),
-		[]byte{0x64, 0x49, 0x45, 0x54, 0x46},
-		[]interface{}{[]byte{0x64, 0x49, 0x45, 0x54, 0x46}},
+		Tag{24, []byte{0x64, 0x49, 0x45, 0x54, 0x46}},
+		[]interface{}{[]byte{0x64, 0x49, 0x45, 0x54, 0x46}, Tag{24, []byte{0x64, 0x49, 0x45, 0x54, 0x46}}, RawTag{24, hexDecode("456449455446")}},
 		[]reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeFloat32, typeFloat64, typeBool, typeIntSlice, typeMapStringInt},
 	}, // 24: encoded cborBytes data item
 	{
 		hexDecode("d82076687474703a2f2f7777772e6578616d706c652e636f6d"),
-		"http://www.example.com",
-		[]interface{}{"http://www.example.com"},
+		Tag{32, "http://www.example.com"},
+		[]interface{}{"http://www.example.com", Tag{32, "http://www.example.com"}, RawTag{32, hexDecode("76687474703a2f2f7777772e6578616d706c652e636f6d")}},
 		[]reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeFloat32, typeFloat64, typeBool, typeIntSlice, typeMapStringInt},
 	}, // 32: URI
 	// primitives
@@ -392,31 +391,31 @@ var unmarshalTests = []unmarshalTest{
 		hexDecode("f4"),
 		false,
 		[]interface{}{false},
-		[]reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeFloat32, typeFloat64, typeByteSlice, typeString, typeIntSlice, typeMapStringInt},
+		[]reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeFloat32, typeFloat64, typeByteSlice, typeString, typeIntSlice, typeMapStringInt, typeTag, typeRawTag},
 	},
 	{
 		hexDecode("f5"),
 		true,
 		[]interface{}{true},
-		[]reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeFloat32, typeFloat64, typeByteSlice, typeString, typeIntSlice, typeMapStringInt},
+		[]reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeFloat32, typeFloat64, typeByteSlice, typeString, typeIntSlice, typeMapStringInt, typeTag, typeRawTag},
 	},
 	{
 		hexDecode("f6"),
 		nil,
 		[]interface{}{[]byte(nil), []int(nil), []string(nil), map[string]int(nil)},
-		[]reflect.Type{},
+		[]reflect.Type{typeTag, typeRawTag},
 	},
 	{
 		hexDecode("f7"),
 		nil,
 		[]interface{}{[]byte(nil), []int(nil), []string(nil), map[string]int(nil)},
-		[]reflect.Type{},
+		[]reflect.Type{typeTag, typeRawTag},
 	},
 	{
 		hexDecode("f0"),
 		uint64(16),
 		[]interface{}{uint8(16), uint16(16), uint32(16), uint64(16), uint(16), int8(16), int16(16), int32(16), int64(16), int(16), float32(16), float64(16)},
-		[]reflect.Type{typeByteSlice, typeString, typeBool, typeIntSlice, typeMapStringInt},
+		[]reflect.Type{typeByteSlice, typeString, typeBool, typeIntSlice, typeMapStringInt, typeTag, typeRawTag},
 	},
 	// This example is not well-formed because Simple value (with 5-bit value 24) must be >= 32.
 	// See RFC 7049 section 2.3 for details, instead of the incorrect example in RFC 7049 Appendex A.
@@ -433,32 +432,45 @@ var unmarshalTests = []unmarshalTest{
 		hexDecode("f820"),
 		uint64(32),
 		[]interface{}{uint8(32), uint16(32), uint32(32), uint64(32), uint(32), int8(32), int16(32), int32(32), int64(32), int(32), float32(32), float64(32)},
-		[]reflect.Type{typeByteSlice, typeString, typeBool, typeIntSlice, typeMapStringInt},
+		[]reflect.Type{typeByteSlice, typeString, typeBool, typeIntSlice, typeMapStringInt, typeTag, typeRawTag},
 	},
 	{
 		hexDecode("f8ff"),
 		uint64(255),
 		[]interface{}{uint8(255), uint16(255), uint32(255), uint64(255), uint(255), int16(255), int32(255), int64(255), int(255), float32(255), float64(255)},
-		[]reflect.Type{typeByteSlice, typeString, typeBool, typeIntSlice, typeMapStringInt},
+		[]reflect.Type{typeByteSlice, typeString, typeBool, typeIntSlice, typeMapStringInt, typeTag, typeRawTag},
 	},
 	// More testcases not covered by https://tools.ietf.org/html/rfc7049#appendix-A.
 	{
 		hexDecode("5fff"), // empty indefinite length byte string
 		[]byte{},
 		[]interface{}{[]byte{}},
-		[]reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeFloat32, typeFloat64, typeBool, typeIntSlice, typeMapStringInt},
+		[]reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeFloat32, typeFloat64, typeBool, typeIntSlice, typeMapStringInt, typeTag, typeRawTag},
 	},
 	{
 		hexDecode("7fff"), // empty indefinite length text string
 		"",
 		[]interface{}{""},
-		[]reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeFloat32, typeFloat64, typeBool, typeIntSlice, typeMapStringInt},
+		[]reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeFloat32, typeFloat64, typeBool, typeIntSlice, typeMapStringInt, typeTag, typeRawTag},
 	},
 	{
 		hexDecode("bfff"), // empty indefinite length map
 		map[interface{}]interface{}{},
 		[]interface{}{map[interface{}]interface{}{}, map[string]bool{}, map[string]int{}, map[int]string{}, map[int]bool{}},
-		[]reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeFloat32, typeFloat64, typeByteSlice, typeString, typeBool, typeIntSlice},
+		[]reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeFloat32, typeFloat64, typeByteSlice, typeString, typeBool, typeIntSlice, typeTag, typeRawTag},
+	},
+	// More test data with tags
+	{
+		hexDecode("c13a0177f2cf"), // 1969-03-21T20:04:00Z, tag 1 with negative integer as epoch time
+		time.Date(1969, 3, 21, 20, 4, 0, 0, time.UTC),
+		[]interface{}{int32(-24638160), int64(-24638160), int32(-24638160), int64(-24638160), float32(-24638160), float64(-24638160), time.Date(1969, 3, 21, 20, 4, 0, 0, time.UTC), Tag{1, int64(-24638160)}, RawTag{1, hexDecode("3a0177f2cf")}},
+		[]reflect.Type{typeUint8, typeUint16, typeInt8, typeInt16, typeByteSlice, typeString, typeBool, typeIntSlice, typeMapStringInt},
+	},
+	{
+		hexDecode("d83dd183010203"), // 61(17([1, 2, 3])), nested tags 61 and 17
+		Tag{61, Tag{17, []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}, [...]int{1, 2, 3}, []float32{1, 2, 3}, []float64{1, 2, 3}, Tag{61, Tag{17, []interface{}{uint64(1), uint64(2), uint64(3)}}}, RawTag{61, hexDecode("d183010203")}},
+		[]reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeFloat32, typeFloat64, typeString, typeBool, typeStringSlice, typeMapStringInt, reflect.TypeOf([3]string{})},
 	},
 }
 
@@ -480,77 +492,77 @@ var unmarshalFloatTests = []unmarshalFloatTest{
 		hexDecode("f90000"),
 		float64(0.0),
 		[]interface{}{float32(0.0), float64(0.0)},
-		[]reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeByteSlice, typeString, typeBool, typeIntSlice, typeMapStringInt},
+		[]reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeByteSlice, typeString, typeBool, typeIntSlice, typeMapStringInt, typeTag, typeRawTag},
 		0.0,
 	},
 	{
 		hexDecode("f98000"),
 		float64(-0.0),
 		[]interface{}{float32(-0.0), float64(-0.0)},
-		[]reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeByteSlice, typeString, typeBool, typeIntSlice, typeMapStringInt},
+		[]reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeByteSlice, typeString, typeBool, typeIntSlice, typeMapStringInt, typeTag, typeRawTag},
 		0.0,
 	},
 	{
 		hexDecode("f93c00"),
 		float64(1.0),
 		[]interface{}{float32(1.0), float64(1.0)},
-		[]reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeByteSlice, typeString, typeBool, typeIntSlice, typeMapStringInt},
+		[]reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeByteSlice, typeString, typeBool, typeIntSlice, typeMapStringInt, typeTag, typeRawTag},
 		0.0,
 	},
 	{
 		hexDecode("f93e00"),
 		float64(1.5),
 		[]interface{}{float32(1.5), float64(1.5)},
-		[]reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeByteSlice, typeString, typeBool, typeIntSlice, typeMapStringInt},
+		[]reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeByteSlice, typeString, typeBool, typeIntSlice, typeMapStringInt, typeTag, typeRawTag},
 		0.0,
 	},
 	{
 		hexDecode("f97bff"),
 		float64(65504.0),
 		[]interface{}{float32(65504.0), float64(65504.0)},
-		[]reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeByteSlice, typeString, typeBool, typeIntSlice, typeMapStringInt},
+		[]reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeByteSlice, typeString, typeBool, typeIntSlice, typeMapStringInt, typeTag, typeRawTag},
 		0.0,
 	},
 	{
 		hexDecode("f90001"), // float16 subnormal value
 		float64(5.960464477539063e-08),
 		[]interface{}{float32(5.960464477539063e-08), float64(5.960464477539063e-08)},
-		[]reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeByteSlice, typeString, typeBool, typeIntSlice, typeMapStringInt},
+		[]reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeByteSlice, typeString, typeBool, typeIntSlice, typeMapStringInt, typeTag, typeRawTag},
 		1e-16,
 	},
 	{
 		hexDecode("f90400"),
 		float64(6.103515625e-05),
 		[]interface{}{float32(6.103515625e-05), float64(6.103515625e-05)},
-		[]reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeByteSlice, typeString, typeBool, typeIntSlice, typeMapStringInt},
+		[]reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeByteSlice, typeString, typeBool, typeIntSlice, typeMapStringInt, typeTag, typeRawTag},
 		1e-16,
 	},
 	{
 		hexDecode("f9c400"),
 		float64(-4.0),
 		[]interface{}{float32(-4.0), float64(-4.0)},
-		[]reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeByteSlice, typeString, typeBool, typeIntSlice, typeMapStringInt},
+		[]reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeByteSlice, typeString, typeBool, typeIntSlice, typeMapStringInt, typeTag, typeRawTag},
 		0.0,
 	},
 	{
 		hexDecode("f97c00"),
 		math.Inf(1),
 		[]interface{}{math.Float32frombits(0x7f800000), math.Inf(1)},
-		[]reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeByteSlice, typeString, typeBool, typeIntSlice, typeMapStringInt},
+		[]reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeByteSlice, typeString, typeBool, typeIntSlice, typeMapStringInt, typeTag, typeRawTag},
 		0.0,
 	},
 	{
 		hexDecode("f97e00"),
 		math.NaN(),
 		[]interface{}{math.Float32frombits(0x7fc00000), math.NaN()},
-		[]reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeByteSlice, typeString, typeBool, typeIntSlice, typeMapStringInt},
+		[]reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeByteSlice, typeString, typeBool, typeIntSlice, typeMapStringInt, typeTag, typeRawTag},
 		0.0,
 	},
 	{
 		hexDecode("f9fc00"),
 		math.Inf(-1),
 		[]interface{}{math.Float32frombits(0xff800000), math.Inf(-1)},
-		[]reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeByteSlice, typeString, typeBool, typeIntSlice, typeMapStringInt},
+		[]reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeByteSlice, typeString, typeBool, typeIntSlice, typeMapStringInt, typeTag, typeRawTag},
 		0.0,
 	},
 	// float32
@@ -558,35 +570,35 @@ var unmarshalFloatTests = []unmarshalFloatTest{
 		hexDecode("fa47c35000"),
 		float64(100000.0),
 		[]interface{}{float32(100000.0), float64(100000.0)},
-		[]reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeByteSlice, typeString, typeBool, typeIntSlice, typeMapStringInt},
+		[]reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeByteSlice, typeString, typeBool, typeIntSlice, typeMapStringInt, typeTag, typeRawTag},
 		0.0,
 	},
 	{
 		hexDecode("fa7f7fffff"),
 		float64(3.4028234663852886e+38),
 		[]interface{}{float32(3.4028234663852886e+38), float64(3.4028234663852886e+38)},
-		[]reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeByteSlice, typeString, typeBool, typeIntSlice, typeMapStringInt},
+		[]reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeByteSlice, typeString, typeBool, typeIntSlice, typeMapStringInt, typeTag, typeRawTag},
 		1e-9,
 	},
 	{
 		hexDecode("fa7f800000"),
 		math.Inf(1),
 		[]interface{}{math.Float32frombits(0x7f800000), math.Inf(1)},
-		[]reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeByteSlice, typeString, typeBool, typeIntSlice, typeMapStringInt},
+		[]reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeByteSlice, typeString, typeBool, typeIntSlice, typeMapStringInt, typeTag, typeRawTag},
 		0.0,
 	},
 	{
 		hexDecode("fa7fc00000"),
 		math.NaN(),
 		[]interface{}{math.Float32frombits(0x7fc00000), math.NaN()},
-		[]reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeByteSlice, typeString, typeBool, typeIntSlice, typeMapStringInt},
+		[]reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeByteSlice, typeString, typeBool, typeIntSlice, typeMapStringInt, typeTag, typeRawTag},
 		0.0,
 	},
 	{
 		hexDecode("faff800000"),
 		math.Inf(-1),
 		[]interface{}{math.Float32frombits(0xff800000), math.Inf(-1)},
-		[]reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeByteSlice, typeString, typeBool, typeIntSlice, typeMapStringInt},
+		[]reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeByteSlice, typeString, typeBool, typeIntSlice, typeMapStringInt, typeTag, typeRawTag},
 		0.0,
 	},
 	// float64
@@ -594,42 +606,42 @@ var unmarshalFloatTests = []unmarshalFloatTest{
 		hexDecode("fb3ff199999999999a"),
 		float64(1.1),
 		[]interface{}{float32(1.1), float64(1.1)},
-		[]reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeByteSlice, typeString, typeBool, typeIntSlice, typeMapStringInt},
+		[]reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeByteSlice, typeString, typeBool, typeIntSlice, typeMapStringInt, typeTag, typeRawTag},
 		1e-9,
 	},
 	{
 		hexDecode("fb7e37e43c8800759c"),
 		float64(1.0e+300),
 		[]interface{}{float64(1.0e+300)},
-		[]reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeFloat32, typeByteSlice, typeString, typeBool, typeIntSlice, typeMapStringInt},
+		[]reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeFloat32, typeByteSlice, typeString, typeBool, typeIntSlice, typeMapStringInt, typeTag, typeRawTag},
 		1e-9,
 	},
 	{
 		hexDecode("fbc010666666666666"),
 		float64(-4.1),
 		[]interface{}{float32(-4.1), float64(-4.1)},
-		[]reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeByteSlice, typeString, typeBool, typeIntSlice, typeMapStringInt},
+		[]reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeByteSlice, typeString, typeBool, typeIntSlice, typeMapStringInt, typeTag, typeRawTag},
 		1e-9,
 	},
 	{
 		hexDecode("fb7ff0000000000000"),
 		math.Inf(1),
 		[]interface{}{math.Float32frombits(0x7f800000), math.Inf(1)},
-		[]reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeByteSlice, typeString, typeBool, typeIntSlice, typeMapStringInt},
+		[]reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeByteSlice, typeString, typeBool, typeIntSlice, typeMapStringInt, typeTag, typeRawTag},
 		0.0,
 	},
 	{
 		hexDecode("fb7ff8000000000000"),
 		math.NaN(),
 		[]interface{}{math.Float32frombits(0x7fc00000), math.NaN()},
-		[]reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeByteSlice, typeString, typeBool, typeIntSlice, typeMapStringInt},
+		[]reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeByteSlice, typeString, typeBool, typeIntSlice, typeMapStringInt, typeTag, typeRawTag},
 		0.0,
 	},
 	{
 		hexDecode("fbfff0000000000000"),
 		math.Inf(-1),
 		[]interface{}{math.Float32frombits(0xff800000), math.Inf(-1)},
-		[]reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeByteSlice, typeString, typeBool, typeIntSlice, typeMapStringInt},
+		[]reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeByteSlice, typeString, typeBool, typeIntSlice, typeMapStringInt, typeTag, typeRawTag},
 		0.0,
 	},
 
@@ -638,28 +650,28 @@ var unmarshalFloatTests = []unmarshalFloatTest{
 		hexDecode("f903ff"),
 		float64(0.000060976),
 		[]interface{}{float32(0.000060976), float64(0.000060976)},
-		[]reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeByteSlice, typeString, typeBool, typeIntSlice, typeMapStringInt},
+		[]reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeByteSlice, typeString, typeBool, typeIntSlice, typeMapStringInt, typeTag, typeRawTag},
 		1e-9,
 	},
 	{
 		hexDecode("f93bff"),
 		float64(0.999511719),
 		[]interface{}{float32(0.999511719), float64(0.999511719)},
-		[]reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeByteSlice, typeString, typeBool, typeIntSlice, typeMapStringInt},
+		[]reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeByteSlice, typeString, typeBool, typeIntSlice, typeMapStringInt, typeTag, typeRawTag},
 		1e-9,
 	},
 	{
 		hexDecode("f93c01"),
 		float64(1.000976563),
 		[]interface{}{float32(1.000976563), float64(1.000976563)},
-		[]reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeByteSlice, typeString, typeBool, typeIntSlice, typeMapStringInt},
+		[]reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeByteSlice, typeString, typeBool, typeIntSlice, typeMapStringInt, typeTag, typeRawTag},
 		1e-9,
 	},
 	{
 		hexDecode("f93555"),
 		float64(0.333251953125),
 		[]interface{}{float32(0.333251953125), float64(0.333251953125)},
-		[]reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeByteSlice, typeString, typeBool, typeIntSlice, typeMapStringInt},
+		[]reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeByteSlice, typeString, typeBool, typeIntSlice, typeMapStringInt, typeTag, typeRawTag},
 		1e-9,
 	},
 	// CBOR test data "canonNums" are from https://github.com/cbor-wg/cbor-test-vectors
@@ -667,70 +679,70 @@ var unmarshalFloatTests = []unmarshalFloatTest{
 		hexDecode("f9bd00"),
 		float64(-1.25),
 		[]interface{}{float32(-1.25), float64(-1.25)},
-		[]reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeByteSlice, typeString, typeBool, typeIntSlice, typeMapStringInt},
+		[]reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeByteSlice, typeString, typeBool, typeIntSlice, typeMapStringInt, typeTag, typeRawTag},
 		0.0,
 	},
 	{
 		hexDecode("f93e00"),
 		float64(1.5),
 		[]interface{}{float32(1.5), float64(1.5)},
-		[]reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeByteSlice, typeString, typeBool, typeIntSlice, typeMapStringInt},
+		[]reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeByteSlice, typeString, typeBool, typeIntSlice, typeMapStringInt, typeTag, typeRawTag},
 		0.0,
 	},
 	{
 		hexDecode("fb4024333333333333"),
 		float64(10.1),
 		[]interface{}{float32(10.1), float64(10.1)},
-		[]reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeByteSlice, typeString, typeBool, typeIntSlice, typeMapStringInt},
+		[]reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeByteSlice, typeString, typeBool, typeIntSlice, typeMapStringInt, typeTag, typeRawTag},
 		0.0,
 	},
 	{
 		hexDecode("f90001"),
 		float64(5.960464477539063e-8),
 		[]interface{}{float32(5.960464477539063e-8), float64(5.960464477539063e-8)},
-		[]reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeByteSlice, typeString, typeBool, typeIntSlice, typeMapStringInt},
+		[]reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeByteSlice, typeString, typeBool, typeIntSlice, typeMapStringInt, typeTag, typeRawTag},
 		0.0,
 	},
 	{
 		hexDecode("fa7f7fffff"),
 		float64(3.4028234663852886e+38),
 		[]interface{}{float32(3.4028234663852886e+38), float64(3.4028234663852886e+38)},
-		[]reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeByteSlice, typeString, typeBool, typeIntSlice, typeMapStringInt},
+		[]reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeByteSlice, typeString, typeBool, typeIntSlice, typeMapStringInt, typeTag, typeRawTag},
 		0.0,
 	},
 	{
 		hexDecode("f90400"),
 		float64(0.00006103515625),
 		[]interface{}{float32(0.00006103515625), float64(0.00006103515625)},
-		[]reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeByteSlice, typeString, typeBool, typeIntSlice, typeMapStringInt},
+		[]reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeByteSlice, typeString, typeBool, typeIntSlice, typeMapStringInt, typeTag, typeRawTag},
 		0.0,
 	},
 	{
 		hexDecode("f933ff"),
 		float64(0.2498779296875),
 		[]interface{}{float32(0.2498779296875), float64(0.2498779296875)},
-		[]reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeByteSlice, typeString, typeBool, typeIntSlice, typeMapStringInt},
+		[]reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeByteSlice, typeString, typeBool, typeIntSlice, typeMapStringInt, typeTag, typeRawTag},
 		0.0,
 	},
 	{
 		hexDecode("fa33000000"),
 		float64(2.9802322387695312e-8),
 		[]interface{}{float32(2.9802322387695312e-8), float64(2.9802322387695312e-8)},
-		[]reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeByteSlice, typeString, typeBool, typeIntSlice, typeMapStringInt},
+		[]reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeByteSlice, typeString, typeBool, typeIntSlice, typeMapStringInt, typeTag, typeRawTag},
 		0.0,
 	},
 	{
 		hexDecode("fa33333866"),
 		float64(4.1727979294137185e-8),
 		[]interface{}{float32(4.1727979294137185e-8), float64(4.1727979294137185e-8)},
-		[]reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeByteSlice, typeString, typeBool, typeIntSlice, typeMapStringInt},
+		[]reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeByteSlice, typeString, typeBool, typeIntSlice, typeMapStringInt, typeTag, typeRawTag},
 		0.0,
 	},
 	{
 		hexDecode("fa37002000"),
 		float64(0.000007636845111846924),
 		[]interface{}{float32(0.000007636845111846924), float64(0.000007636845111846924)},
-		[]reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeByteSlice, typeString, typeBool, typeIntSlice, typeMapStringInt},
+		[]reflect.Type{typeUint8, typeUint16, typeUint32, typeUint64, typeInt8, typeInt16, typeInt32, typeInt64, typeByteSlice, typeString, typeBool, typeIntSlice, typeMapStringInt, typeTag, typeRawTag},
 		0.0,
 	},
 }
@@ -751,8 +763,14 @@ func TestUnmarshal(t *testing.T) {
 		var v interface{}
 		if err := Unmarshal(tc.cborData, &v); err != nil {
 			t.Errorf("Unmarshal(0x%x) returned error %v", tc.cborData, err)
-		} 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)
+		} 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 RawMessage.
 		var r RawMessage
@@ -767,8 +785,14 @@ func TestUnmarshal(t *testing.T) {
 			vPtr := v.Interface()
 			if err := Unmarshal(tc.cborData, vPtr); err != nil {
 				t.Errorf("Unmarshal(0x%x) returned error %v", tc.cborData, err)
-			} 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)
+			} 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.
@@ -776,7 +800,7 @@ func TestUnmarshal(t *testing.T) {
 			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)
+				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") {
@@ -885,9 +909,10 @@ func TestNegIntOverflow(t *testing.T) {
 	}
 }
 
-func TestUnmarshalIntoPointer(t *testing.T) {
-	cborDataInt := []byte{0x18, 0x18}                                                                      // 24
-	cborDataString := []byte{0x7f, 0x65, 0x73, 0x74, 0x72, 0x65, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x67, 0xff} // "streaming"
+func TestUnmarshalIntoPtrPrimitives(t *testing.T) {
+	cborDataInt := hexDecode("1818")                          // 24
+	cborDataString := hexDecode("7f657374726561646d696e67ff") // "streaming"
+
 	const wantInt = 24
 	const wantString = "streaming"
 
@@ -924,13 +949,13 @@ func TestUnmarshalIntoPointer(t *testing.T) {
 	if err := Unmarshal(cborDataString, &pps); err != nil {
 		t.Errorf("Unmarshal(0x%x) returned error %v", cborDataString, err)
 	} else if s != wantString {
-		t.Errorf("Unmarshal(0x%x) = %v (%T), want %q", cborDataString, s, s, wantString)
+		t.Errorf("Unmarshal(0x%x) = %v (%T), want %v", cborDataString, s, s, wantString)
 	}
 	// Unmarshal CBOR string into a nil pointer.
 	if err := Unmarshal(cborDataString, &p2); err != nil {
 		t.Errorf("Unmarshal(0x%x) returned error %v", cborDataString, err)
 	} else if *p2 != wantString {
-		t.Errorf("Unmarshal(0x%x) = %v (%T), want %q", cborDataString, *p2, p2, wantString)
+		t.Errorf("Unmarshal(0x%x) = %v (%T), want %v", cborDataString, *p2, p2, wantString)
 	}
 
 	// Unmarshal CBOR string into a non-nil RawMessage.
@@ -947,6 +972,94 @@ func TestUnmarshalIntoPointer(t *testing.T) {
 	}
 }
 
+func TestUnmarshalIntoPtrArrayPtrElem(t *testing.T) {
+	cborData := hexDecode("83010203") // []int{1, 2, 3}
+
+	n1, n2, n3 := 1, 2, 3
+
+	wantArray := []*int{&n1, &n2, &n3}
+
+	var p *[]*int
+
+	var slc []*int
+	pslc := &slc
+	ppslc := &pslc
+
+	// Unmarshal CBOR array into a non-nil pointer.
+	if err := Unmarshal(cborData, &ppslc); err != nil {
+		t.Errorf("Unmarshal(0x%x, %s) returned error %v", cborData, reflect.TypeOf(ppslc), err)
+	} else if !reflect.DeepEqual(slc, wantArray) {
+		t.Errorf("Unmarshal(0x%x) = %v (%T), want %v", cborData, slc, slc, wantArray)
+	}
+	// Unmarshal CBOR array into a nil pointer.
+	if err := Unmarshal(cborData, &p); err != nil {
+		t.Errorf("Unmarshal(0x%x) returned error %v", cborData, err)
+	} else if !reflect.DeepEqual(*p, wantArray) {
+		t.Errorf("Unmarshal(0x%x) = %v (%T), want %v", cborData, *p, p, wantArray)
+	}
+}
+
+func TestUnmarshalIntoPtrMapPtrElem(t *testing.T) {
+	cborData := hexDecode("a201020304") // {1: 2, 3: 4}
+
+	n1, n2, n3, n4 := 1, 2, 3, 4
+
+	wantMap := map[int]*int{n1: &n2, n3: &n4}
+
+	var p *map[int]*int
+
+	var m map[int]*int
+	pm := &m
+	ppm := &pm
+
+	// Unmarshal CBOR map into a non-nil pointer.
+	if err := Unmarshal(cborData, &ppm); err != nil {
+		t.Errorf("Unmarshal(0x%x, %s) returned error %v", cborData, reflect.TypeOf(ppm), err)
+	} else if !reflect.DeepEqual(m, wantMap) {
+		t.Errorf("Unmarshal(0x%x) = %v (%T), want %v", cborData, m, m, wantMap)
+	}
+	// Unmarshal CBOR map into a nil pointer.
+	if err := Unmarshal(cborData, &p); err != nil {
+		t.Errorf("Unmarshal(0x%x) returned error %v", cborData, err)
+	} else if !reflect.DeepEqual(*p, wantMap) {
+		t.Errorf("Unmarshal(0x%x) = %v (%T), want %v", cborData, *p, p, wantMap)
+	}
+}
+
+func TestUnmarshalIntoPtrStructPtrElem(t *testing.T) {
+	type s1 struct {
+		A *string `cbor:"a"`
+		B *string `cbor:"b"`
+		C *string `cbor:"c"`
+		D *string `cbor:"d"`
+		E *string `cbor:"e"`
+	}
+
+	cborData := hexDecode("a56161614161626142616361436164614461656145") // map[string]string{"a": "A", "b": "B", "c": "C", "d": "D", "e": "E"}
+
+	a, b, c, d, e := "A", "B", "C", "D", "E"
+	wantObj := s1{A: &a, B: &b, C: &c, D: &d, E: &e}
+
+	var p *s1
+
+	var s s1
+	ps := &s
+	pps := &ps
+
+	// Unmarshal CBOR map into a non-nil pointer.
+	if err := Unmarshal(cborData, &pps); err != nil {
+		t.Errorf("Unmarshal(0x%x, %s) returned error %v", cborData, reflect.TypeOf(pps), err)
+	} else if !reflect.DeepEqual(s, wantObj) {
+		t.Errorf("Unmarshal(0x%x) = %v (%T), want %v", cborData, s, s, wantObj)
+	}
+	// Unmarshal CBOR map into a nil pointer.
+	if err := Unmarshal(cborData, &p); err != nil {
+		t.Errorf("Unmarshal(0x%x) returned error %v", cborData, err)
+	} else if !reflect.DeepEqual(*p, wantObj) {
+		t.Errorf("Unmarshal(0x%x) = %v (%T), want %v", cborData, *p, p, wantObj)
+	}
+}
+
 func TestUnmarshalIntoArray(t *testing.T) {
 	cborData := hexDecode("83010203") // []int{1, 2, 3}
 
@@ -1584,11 +1697,17 @@ func TestDecodeTime(t *testing.T) {
 		wantTime        time.Time
 	}{
 		{
-			name:            "zero time",
+			name:            "zero time", // decode CBOR null to zero time
 			cborRFC3339Time: hexDecode("f6"),
 			cborUnixTime:    hexDecode("f6"),
 			wantTime:        time.Time{},
 		},
+		{
+			name:            "zero time", // decode CBOR undefined to zero time
+			cborRFC3339Time: hexDecode("f7"),
+			cborUnixTime:    hexDecode("f7"),
+			wantTime:        time.Time{},
+		},
 		{
 			name:            "NaN",
 			cborRFC3339Time: hexDecode("f97e00"),
@@ -1613,6 +1732,12 @@ func TestDecodeTime(t *testing.T) {
 			cborUnixTime:    hexDecode("1a514b67b0"),
 			wantTime:        parseTime(time.RFC3339Nano, "2013-03-21T20:04:00Z"),
 		},
+		{
+			name:            "time with fractional seconds", // float
+			cborRFC3339Time: hexDecode("7819313937302d30312d30315432313a34363a34302d30363a3030"),
+			cborUnixTime:    hexDecode("fa47c35000"),
+			wantTime:        parseTime(time.RFC3339Nano, "1970-01-01T21:46:40-06:00"),
+		},
 		{
 			name:            "time with fractional seconds", // float
 			cborRFC3339Time: hexDecode("76323031332d30332d32315432303a30343a30302e355a"),
@@ -1644,6 +1769,63 @@ func TestDecodeTime(t *testing.T) {
 	}
 }
 
+func TestDecodeTimeWithTag(t *testing.T) {
+	testCases := []struct {
+		name            string
+		cborRFC3339Time []byte
+		cborUnixTime    []byte
+		wantTime        time.Time
+	}{
+		{
+			name:            "time without fractional seconds", // positive integer
+			cborRFC3339Time: hexDecode("c074323031332d30332d32315432303a30343a30305a"),
+			cborUnixTime:    hexDecode("c11a514b67b0"),
+			wantTime:        parseTime(time.RFC3339Nano, "2013-03-21T20:04:00Z"),
+		},
+		{
+			name:            "time with fractional seconds", // float
+			cborRFC3339Time: hexDecode("c076323031332d30332d32315432303a30343a30302e355a"),
+			cborUnixTime:    hexDecode("c1fb41d452d9ec200000"),
+			wantTime:        parseTime(time.RFC3339Nano, "2013-03-21T20:04:00.5Z"),
+		},
+		{
+			name:            "time before January 1, 1970 UTC without fractional seconds", // negative integer
+			cborRFC3339Time: hexDecode("c074313936392d30332d32315432303a30343a30305a"),
+			cborUnixTime:    hexDecode("c13a0177f2cf"),
+			wantTime:        parseTime(time.RFC3339Nano, "1969-03-21T20:04:00Z"),
+		},
+	}
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			tm := time.Now()
+			if err := Unmarshal(tc.cborRFC3339Time, &tm); err != nil {
+				t.Errorf("Unmarshal(0x%x) returned error %v", tc.cborRFC3339Time, err)
+			} else if !tc.wantTime.Equal(tm) {
+				t.Errorf("Unmarshal(0x%x) = %v (%T), want %v (%T)", tc.cborRFC3339Time, tm, tm, tc.wantTime, tc.wantTime)
+			}
+			tm = time.Now()
+			if err := Unmarshal(tc.cborUnixTime, &tm); err != nil {
+				t.Errorf("Unmarshal(0x%x) returned error %v", tc.cborUnixTime, err)
+			} else if !tc.wantTime.Equal(tm) {
+				t.Errorf("Unmarshal(0x%x) = %v (%T), want %v (%T)", tc.cborUnixTime, tm, tm, tc.wantTime, tc.wantTime)
+			}
+
+			var v interface{}
+			if err := Unmarshal(tc.cborRFC3339Time, &v); err != nil {
+				t.Errorf("Unmarshal(0x%x) returned error %v", tc.cborRFC3339Time, err)
+			} else if tm, ok := v.(time.Time); !ok || !tc.wantTime.Equal(tm) {
+				t.Errorf("Unmarshal(0x%x) = %v (%T), want %v (%T)", tc.cborRFC3339Time, v, v, tc.wantTime, tc.wantTime)
+			}
+			v = nil
+			if err := Unmarshal(tc.cborUnixTime, &v); err != nil {
+				t.Errorf("Unmarshal(0x%x) returned error %v", tc.cborUnixTime, err)
+			} else if tm, ok := v.(time.Time); !ok || !tc.wantTime.Equal(tm) {
+				t.Errorf("Unmarshal(0x%x) = %v (%T), want %v (%T)", tc.cborUnixTime, v, v, tc.wantTime, tc.wantTime)
+			}
+		})
+	}
+}
+
 func TestDecodeTimeError(t *testing.T) {
 	testCases := []struct {
 		name         string
@@ -1660,19 +1842,293 @@ func TestDecodeTimeError(t *testing.T) {
 			cborData:     hexDecode("4f013030303030303030e03031ed3030"),
 			wantErrorMsg: "cbor: cannot unmarshal byte string into Go value of type time.Time",
 		},
+		{
+			name:         "bool cannot be decoded into time.Time",
+			cborData:     hexDecode("f4"),
+			wantErrorMsg: "cbor: cannot unmarshal primitives into Go value of type time.Time",
+		},
+		{
+			name:         "invalid UTF-8 string",
+			cborData:     hexDecode("7f62e6b061b4ff"),
+			wantErrorMsg: "cbor: invalid UTF-8 string",
+		},
+		{
+			name:         "negative integer overflow",
+			cborData:     hexDecode("3bffffffffffffffff"),
+			wantErrorMsg: "cbor: cannot unmarshal negative integer into Go value of type time.Time",
+		},
 	}
 	for _, tc := range testCases {
 		t.Run(tc.name, func(t *testing.T) {
 			tm := time.Now()
 			if err := Unmarshal(tc.cborData, &tm); err == nil {
 				t.Errorf("Unmarshal(0x%x) didn't return an error, want error msg %q", tc.cborData, tc.wantErrorMsg)
-			} else if err.Error() != tc.wantErrorMsg {
+			} else if !strings.Contains(err.Error(), tc.wantErrorMsg) {
 				t.Errorf("Unmarshal(0x%x) returned error %q, want %q", tc.cborData, err.Error(), tc.wantErrorMsg)
 			}
 		})
 	}
 }
 
+func TestDecodeTimeWithTagError(t *testing.T) {
+	testCases := []struct {
+		name         string
+		cborData     []byte
+		wantErrorMsg string
+	}{
+		{
+			name:         "Tag 0 with invalid RFC3339 time string",
+			cborData:     hexDecode("c07f657374726561646d696e67ff"),
+			wantErrorMsg: "cbor: cannot set streaming for time.Time",
+		},
+		{
+			name:         "Tag 0 with invalid UTF-8 string",
+			cborData:     hexDecode("c07f62e6b061b4ff"),
+			wantErrorMsg: "cbor: invalid UTF-8 string",
+		},
+		{
+			name:         "Tag 0 with integer content",
+			cborData:     hexDecode("c01a514b67b0"),
+			wantErrorMsg: "cbor: tag number 0 must be followed by text string, got positive integer",
+		},
+		{
+			name:         "Tag 0 with byte string content",
+			cborData:     hexDecode("c04f013030303030303030e03031ed3030"),
+			wantErrorMsg: "cbor: tag number 0 must be followed by text string, got byte string",
+		},
+		{
+			name:         "Tag 1 with negative integer overflow",
+			cborData:     hexDecode("c13bffffffffffffffff"),
+			wantErrorMsg: "cbor: cannot unmarshal negative integer into Go value",
+		},
+		{
+			name:         "Tag 1 with string content",
+			cborData:     hexDecode("c174323031332d30332d32315432303a30343a30305a"),
+			wantErrorMsg: "cbor: tag number 1 must be followed by integer or floating-point number, got UTF-8 text string",
+		},
+		{
+			name:         "Tag 1 with simple value",
+			cborData:     hexDecode("d801f6"), // 1(null)
+			wantErrorMsg: "cbor: tag number 1 must be followed by integer or floating-point number, got primitive",
+		},
+	}
+	dm, _ := DecOptions{TimeTag: DecTagOptional}.DecMode()
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			tm := time.Now()
+			if err := dm.Unmarshal(tc.cborData, &tm); err == nil {
+				t.Errorf("Unmarshal(0x%x) didn't return error, want error msg %q", tc.cborData, tc.wantErrorMsg)
+			} else if !strings.Contains(err.Error(), tc.wantErrorMsg) {
+				t.Errorf("Unmarshal(0x%x) returned error %q, want %q", tc.cborData, err, tc.wantErrorMsg)
+			}
+
+			var v interface{}
+			if err := dm.Unmarshal(tc.cborData, &v); err == nil {
+				t.Errorf("Unmarshal(0x%x) didn't return error, want error msg %q", tc.cborData, tc.wantErrorMsg)
+			} else if !strings.Contains(err.Error(), tc.wantErrorMsg) {
+				t.Errorf("Unmarshal(0x%x) returned error %q, want %q", tc.cborData, err, tc.wantErrorMsg)
+			}
+		})
+	}
+}
+
+func TestDecodeTimeStreaming(t *testing.T) {
+	// Decoder decodes from mixed invalid and valid time.
+	testCases := []struct {
+		cborData     []byte
+		wantErrorMsg string
+		wantObj      time.Time
+	}{
+		{
+			cborData:     hexDecode("c07f62e6b061b4ff"),
+			wantErrorMsg: "cbor: invalid UTF-8 string",
+		},
+		{
+			cborData: hexDecode("c074323031332d30332d32315432303a30343a30305a"),
+			wantObj:  time.Date(2013, 3, 21, 20, 4, 0, 0, time.UTC),
+		},
+		{
+			cborData:     hexDecode("c01a514b67b0"),
+			wantErrorMsg: "cbor: tag number 0 must be followed by text string, got positive integer",
+		},
+		{
+			cborData: hexDecode("c074323031332d30332d32315432303a30343a30305a"),
+			wantObj:  time.Date(2013, 3, 21, 20, 4, 0, 0, time.UTC),
+		},
+		{
+			cborData:     hexDecode("c13bffffffffffffffff"),
+			wantErrorMsg: "-1-18446744073709551615 overflows Go's int64",
+		},
+		{
+			cborData: hexDecode("c11a514b67b0"),
+			wantObj:  time.Date(2013, 3, 21, 20, 4, 0, 0, time.UTC),
+		},
+		{
+			cborData:     hexDecode("c174323031332d30332d32315432303a30343a30305a"),
+			wantErrorMsg: "tag number 1 must be followed by integer or floating-point number, got UTF-8 text string",
+		},
+		{
+			cborData: hexDecode("c11a514b67b0"),
+			wantObj:  time.Date(2013, 3, 21, 20, 4, 0, 0, time.UTC),
+		},
+	}
+	// Data is a mixed stream of valid and invalid time data
+	var cborData []byte
+	for _, tc := range testCases {
+		cborData = append(cborData, tc.cborData...)
+	}
+	dm, _ := DecOptions{TimeTag: DecTagOptional}.DecMode()
+	dec := dm.NewDecoder(bytes.NewReader(cborData))
+	for _, tc := range testCases {
+		var v interface{}
+		err := dec.Decode(&v)
+		if tc.wantErrorMsg != "" {
+			if err == nil {
+				t.Errorf("Unmarshal(0x%x) didn't return error, want error msg %q", tc.cborData, tc.wantErrorMsg)
+			} else if !strings.Contains(err.Error(), tc.wantErrorMsg) {
+				t.Errorf("Unmarshal(0x%x) returned error msg %q, want %q", tc.cborData, err, tc.wantErrorMsg)
+			}
+		} else {
+			tm, ok := v.(time.Time)
+			if !ok {
+				t.Errorf("Unmarshal(0x%x) returned %s (%T), want time.Time", tc.cborData, v, v)
+			}
+			if !tc.wantObj.Equal(tm) {
+				t.Errorf("Unmarshal(0x%x) returned %s, want %s", tc.cborData, tm, tc.wantObj)
+			}
+		}
+	}
+	dec = dm.NewDecoder(bytes.NewReader(cborData))
+	for _, tc := range testCases {
+		var tm time.Time
+		err := dec.Decode(&tm)
+		if tc.wantErrorMsg != "" {
+			if err == nil {
+				t.Errorf("Unmarshal(0x%x) did't return error, want error msg %q", tc.cborData, tc.wantErrorMsg)
+			} else if !strings.Contains(err.Error(), tc.wantErrorMsg) {
+				t.Errorf("Unmarshal(0x%x) returned error msg %q, want %q", tc.cborData, err, tc.wantErrorMsg)
+			}
+		} else {
+			if !tc.wantObj.Equal(tm) {
+				t.Errorf("Unmarshal(0x%x) returned %s, want %s", tc.cborData, tm, tc.wantObj)
+			}
+		}
+	}
+}
+
+func TestDecTimeTagOption(t *testing.T) {
+	timeTagIgnoredDecMode, _ := DecOptions{TimeTag: DecTagIgnored}.DecMode()
+	timeTagOptionalDecMode, _ := DecOptions{TimeTag: DecTagOptional}.DecMode()
+	timeTagRequiredDecMode, _ := DecOptions{TimeTag: DecTagRequired}.DecMode()
+
+	testCases := []struct {
+		name            string
+		cborRFC3339Time []byte
+		cborUnixTime    []byte
+		decMode         DecMode
+		wantTime        time.Time
+		wantErrorMsg    string
+	}{
+		// not-tagged time CBOR data
+		{
+			name:            "not-tagged data with DecTagIgnored option",
+			cborRFC3339Time: hexDecode("74323031332d30332d32315432303a30343a30305a"),
+			cborUnixTime:    hexDecode("1a514b67b0"),
+			decMode:         timeTagIgnoredDecMode,
+			wantTime:        parseTime(time.RFC3339Nano, "2013-03-21T20:04:00Z"),
+		},
+		{
+			name:            "not-tagged data with timeTagOptionalDecMode option",
+			cborRFC3339Time: hexDecode("74323031332d30332d32315432303a30343a30305a"),
+			cborUnixTime:    hexDecode("1a514b67b0"),
+			decMode:         timeTagOptionalDecMode,
+			wantTime:        parseTime(time.RFC3339Nano, "2013-03-21T20:04:00Z"),
+		},
+		{
+			name:            "not-tagged data with timeTagRequiredDecMode option",
+			cborRFC3339Time: hexDecode("74323031332d30332d32315432303a30343a30305a"),
+			cborUnixTime:    hexDecode("1a514b67b0"),
+			decMode:         timeTagRequiredDecMode,
+			wantErrorMsg:    "expect CBOR tag value",
+		},
+		// tagged time CBOR data
+		{
+			name:            "tagged data with timeTagIgnoredDecMode option",
+			cborRFC3339Time: hexDecode("c074323031332d30332d32315432303a30343a30305a"),
+			cborUnixTime:    hexDecode("c11a514b67b0"),
+			decMode:         timeTagIgnoredDecMode,
+			wantTime:        parseTime(time.RFC3339Nano, "2013-03-21T20:04:00Z"),
+		},
+		{
+			name:            "tagged data with timeTagOptionalDecMode option",
+			cborRFC3339Time: hexDecode("c074323031332d30332d32315432303a30343a30305a"),
+			cborUnixTime:    hexDecode("c11a514b67b0"),
+			decMode:         timeTagOptionalDecMode,
+			wantTime:        parseTime(time.RFC3339Nano, "2013-03-21T20:04:00Z"),
+		},
+		{
+			name:            "tagged data with timeTagRequiredDecMode option",
+			cborRFC3339Time: hexDecode("c074323031332d30332d32315432303a30343a30305a"),
+			cborUnixTime:    hexDecode("c11a514b67b0"),
+			decMode:         timeTagRequiredDecMode,
+			wantTime:        parseTime(time.RFC3339Nano, "2013-03-21T20:04:00Z"),
+		},
+		// mis-tagged time CBOR data
+		{
+			name:            "mis-tagged data with timeTagIgnoredDecMode option",
+			cborRFC3339Time: hexDecode("c8c974323031332d30332d32315432303a30343a30305a"),
+			cborUnixTime:    hexDecode("c8c91a514b67b0"),
+			decMode:         timeTagIgnoredDecMode,
+			wantTime:        parseTime(time.RFC3339Nano, "2013-03-21T20:04:00Z"),
+		},
+		{
+			name:            "mis-tagged data with timeTagOptionalDecMode option",
+			cborRFC3339Time: hexDecode("c8c974323031332d30332d32315432303a30343a30305a"),
+			cborUnixTime:    hexDecode("c8c91a514b67b0"),
+			decMode:         timeTagOptionalDecMode,
+			wantErrorMsg:    "cbor: wrong tag number for time.Time, got 8, expect 0 or 1",
+		},
+		{
+			name:            "mis-tagged data with timeTagRequiredDecMode option",
+			cborRFC3339Time: hexDecode("c8c974323031332d30332d32315432303a30343a30305a"),
+			cborUnixTime:    hexDecode("c8c91a514b67b0"),
+			decMode:         timeTagRequiredDecMode,
+			wantErrorMsg:    "cbor: wrong tag number for time.Time, got 8, expect 0 or 1",
+		},
+	}
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			tm := time.Now()
+			err := tc.decMode.Unmarshal(tc.cborRFC3339Time, &tm)
+			if tc.wantErrorMsg != "" {
+				if err == nil {
+					t.Errorf("Unmarshal(0x%x) didn't return error", tc.cborRFC3339Time)
+				} else if !strings.Contains(err.Error(), tc.wantErrorMsg) {
+					t.Errorf("Unmarshal(0x%x) returned error %q, want error containing %q", tc.cborRFC3339Time, err.Error(), tc.wantErrorMsg)
+				}
+			} else {
+				if !tc.wantTime.Equal(tm) {
+					t.Errorf("Unmarshal(0x%x) = %v (%T), want %v (%T)", tc.cborRFC3339Time, tm, tm, tc.wantTime, tc.wantTime)
+				}
+			}
+
+			tm = time.Now()
+			err = tc.decMode.Unmarshal(tc.cborUnixTime, &tm)
+			if tc.wantErrorMsg != "" {
+				if err == nil {
+					t.Errorf("Unmarshal(0x%x) didn't return error", tc.cborRFC3339Time)
+				} else if !strings.Contains(err.Error(), tc.wantErrorMsg) {
+					t.Errorf("Unmarshal(0x%x) returned error %q, want error containing %q", tc.cborRFC3339Time, err.Error(), tc.wantErrorMsg)
+				}
+			} else {
+				if !tc.wantTime.Equal(tm) {
+					t.Errorf("Unmarshal(0x%x) = %v (%T), want %v (%T)", tc.cborRFC3339Time, tm, tm, tc.wantTime, tc.wantTime)
+				}
+			}
+		})
+	}
+}
+
 func TestUnmarshalStructTag1(t *testing.T) {
 	type strc struct {
 		A string `cbor:"a"`
@@ -2415,7 +2871,7 @@ func TestDuplicateMapKeys(t *testing.T) {
 }
 
 func TestDecOptions(t *testing.T) {
-	opts1 := DecOptions{}
+	opts1 := DecOptions{TimeTag: DecTagRequired}
 	dm, err := opts1.DecMode()
 	if err != nil {
 		t.Errorf("DecMode() returned an error %v", err)
@@ -2453,3 +2909,52 @@ func testRoundTrip(t *testing.T, testCases []roundTripTest, em EncMode, dm DecMo
 		})
 	}
 }
+
+func TestDecModeInvalidTimeTag(t *testing.T) {
+	wantErrorMsg := "cbor: invalid TimeTag 101"
+	_, err := DecOptions{TimeTag: 101}.DecMode()
+	if err == nil {
+		t.Errorf("DecMode() didn't return an error")
+	} else if err.Error() != wantErrorMsg {
+		t.Errorf("DecMode() returned error %q, want %q", err.Error(), wantErrorMsg)
+	}
+}
+
+func TestUnmarshalStructKeyAsIntNumError(t *testing.T) {
+	type T1 struct {
+		F1 int `cbor:"a,keyasint"`
+	}
+	type T2 struct {
+		F1 int `cbor:"-18446744073709551616,keyasint"`
+	}
+	testCases := []struct {
+		name         string
+		cborData     []byte
+		obj          interface{}
+		wantErrorMsg string
+	}{
+		{
+			name:         "string as key",
+			cborData:     hexDecode("a1616101"),
+			obj:          T1{},
+			wantErrorMsg: "cbor: failed to parse field name \"a\" to int",
+		},
+		{
+			name:         "out of range int as key",
+			cborData:     hexDecode("a13bffffffffffffffff01"),
+			obj:          T2{},
+			wantErrorMsg: "cbor: failed to parse field name \"-18446744073709551616\" to int",
+		},
+	}
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			v := reflect.New(reflect.TypeOf(tc.obj))
+			err := Unmarshal(tc.cborData, v.Interface())
+			if err == nil {
+				t.Errorf("Unmarshal(0x%x) didn't return an error, want error %q", tc.cborData, tc.wantErrorMsg)
+			} else if !strings.Contains(err.Error(), tc.wantErrorMsg) {
+				t.Errorf("Unmarshal(0x%x) error %v, want %v", tc.cborData, err.Error(), tc.wantErrorMsg)
+			}
+		})
+	}
+}
diff --git a/encode_test.go b/encode_test.go
index d290690..df36f2c 100644
--- a/encode_test.go
+++ b/encode_test.go
@@ -11,7 +11,6 @@ import (
 	"io/ioutil"
 	"math"
 	"reflect"
-	"strconv"
 	"strings"
 	"testing"
 	"time"
@@ -219,6 +218,39 @@ var marshalTests = []marshalTest{
 			map[interface{}]interface{}{"b": "B", "a": "A", "c": "C", "e": "E", "d": "D"},
 		},
 	},
+	// tag
+	{
+		hexDecode("c074323031332d30332d32315432303a30343a30305a"),
+		[]interface{}{Tag{0, "2013-03-21T20:04:00Z"}, RawTag{0, hexDecode("74323031332d30332d32315432303a30343a30305a")}},
+	}, // 0: standard date/time
+	{
+		hexDecode("c11a514b67b0"),
+		[]interface{}{Tag{1, uint64(1363896240)}, RawTag{1, hexDecode("1a514b67b0")}},
+	}, // 1: epoch-based date/time
+	{
+		hexDecode("c249010000000000000000"),
+		[]interface{}{Tag{2, []byte{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, RawTag{2, hexDecode("49010000000000000000")}},
+	}, // 2: positive bignum: 18446744073709551616
+	{
+		hexDecode("c349010000000000000000"),
+		[]interface{}{Tag{3, []byte{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, RawTag{3, hexDecode("49010000000000000000")}},
+	}, // 3: negative bignum: -18446744073709551617
+	{
+		hexDecode("c1fb41d452d9ec200000"),
+		[]interface{}{Tag{1, float64(1363896240.5)}, RawTag{1, hexDecode("fb41d452d9ec200000")}},
+	}, // 1: epoch-based date/time
+	{
+		hexDecode("d74401020304"),
+		[]interface{}{Tag{23, []byte{0x01, 0x02, 0x03, 0x04}}, RawTag{23, hexDecode("4401020304")}},
+	}, // 23: expected conversion to base16 encoding
+	{
+		hexDecode("d818456449455446"),
+		[]interface{}{Tag{24, []byte{0x64, 0x49, 0x45, 0x54, 0x46}}, RawTag{24, hexDecode("456449455446")}},
+	}, // 24: encoded cborBytes data item
+	{
+		hexDecode("d82076687474703a2f2f7777772e6578616d706c652e636f6d"),
+		[]interface{}{Tag{32, "http://www.example.com"}, RawTag{32, hexDecode("76687474703a2f2f7777772e6578616d706c652e636f6d")}},
+	}, // 32: URI
 	// primitives
 	{hexDecode("f4"), []interface{}{false}},
 	{hexDecode("f5"), []interface{}{true}},
@@ -234,6 +266,11 @@ var marshalTests = []marshalTest{
 	{hexDecode("fb3ff199999999999a"), []interface{}{float64(1.1)}},
 	{hexDecode("fb7e37e43c8800759c"), []interface{}{float64(1.0e+300)}},
 	{hexDecode("fbc010666666666666"), []interface{}{float64(-4.1)}},
+	// More testcases not covered by https://tools.ietf.org/html/rfc7049#appendix-A.
+	{
+		hexDecode("d83dd183010203"), // 61(17([1, 2, 3])), nested tags 61 and 17
+		[]interface{}{Tag{61, Tag{17, []interface{}{uint64(1), uint64(2), uint64(3)}}}, RawTag{61, hexDecode("d183010203")}},
+	},
 }
 
 var exMarshalTests = []marshalTest{
@@ -260,14 +297,14 @@ func TestInvalidTypeMarshal(t *testing.T) {
 		Chan chan bool
 	}
 	var marshalErrorTests = []marshalErrorTest{
-		{"channel can't be marshaled", make(chan bool), "cbor: unsupported type: chan bool"},
-		{"slice of channel can't be marshaled", make([]chan bool, 10), "cbor: unsupported type: []chan bool"},
-		{"slice of pointer to channel can't be marshaled", make([]*chan bool, 10), "cbor: unsupported type: []*chan bool"},
-		{"map of channel can't be marshaled", make(map[string]chan bool), "cbor: unsupported type: map[string]chan bool"},
-		{"struct of channel can't be marshaled", s1{}, "cbor: unsupported type: cbor.s1"},
-		{"struct of channel can't be marshaled", s2{}, "cbor: unsupported type: cbor.s2"},
-		{"function can't be marshaled", func(i int) int { return i * i }, "cbor: unsupported type: func(int) int"},
-		{"complex can't be marshaled", complex(100, 8), "cbor: unsupported type: complex128"},
+		{"channel cannot be marshaled", make(chan bool), "cbor: unsupported type: chan bool"},
+		{"slice of channel cannot be marshaled", make([]chan bool, 10), "cbor: unsupported type: []chan bool"},
+		{"slice of pointer to channel cannot be marshaled", make([]*chan bool, 10), "cbor: unsupported type: []*chan bool"},
+		{"map of channel cannot be marshaled", make(map[string]chan bool), "cbor: unsupported type: map[string]chan bool"},
+		{"struct of channel cannot be marshaled", s1{}, "cbor: unsupported type: cbor.s1"},
+		{"struct of channel cannot be marshaled", s2{}, "cbor: unsupported type: cbor.s2"},
+		{"function cannot be marshaled", func(i int) int { return i * i }, "cbor: unsupported type: func(int) int"},
+		{"complex cannot be marshaled", complex(100, 8), "cbor: unsupported type: complex128"},
 	}
 	em, err := EncOptions{Sort: SortCanonical}.EncMode()
 	if err != nil {
@@ -1280,6 +1317,159 @@ func TestEncodeTime(t *testing.T) {
 	}
 }
 
+func TestEncodeTimeWithTag(t *testing.T) {
+	timeUnixOpt := EncOptions{Time: TimeUnix, TimeTag: EncTagRequired}
+	timeUnixMicroOpt := EncOptions{Time: TimeUnixMicro, TimeTag: EncTagRequired}
+	timeUnixDynamicOpt := EncOptions{Time: TimeUnixDynamic, TimeTag: EncTagRequired}
+	timeRFC3339Opt := EncOptions{Time: TimeRFC3339, TimeTag: EncTagRequired}
+	timeRFC3339NanoOpt := EncOptions{Time: TimeRFC3339Nano, TimeTag: EncTagRequired}
+
+	type timeConvert struct {
+		opt          EncOptions
+		wantCborData []byte
+	}
+	testCases := []struct {
+		name    string
+		tm      time.Time
+		convert []timeConvert
+	}{
+		{
+			name: "zero time",
+			tm:   time.Time{},
+			convert: []timeConvert{
+				{
+					opt:          timeUnixOpt,
+					wantCborData: hexDecode("f7"), // encode as CBOR undefined
+				},
+				{
+					opt:          timeUnixMicroOpt,
+					wantCborData: hexDecode("f7"), // encode as CBOR undefined
+				},
+				{
+					opt:          timeUnixDynamicOpt,
+					wantCborData: hexDecode("f7"), // encode as CBOR undefined
+				},
+				{
+					opt:          timeRFC3339Opt,
+					wantCborData: hexDecode("f7"), // encode as CBOR undefined
+				},
+				{
+					opt:          timeRFC3339NanoOpt,
+					wantCborData: hexDecode("f7"), // encode as CBOR undefined
+				},
+			},
+		},
+		{
+			name: "time without fractional seconds",
+			tm:   parseTime(time.RFC3339Nano, "2013-03-21T20:04:00Z"),
+			convert: []timeConvert{
+				{
+					opt:          timeUnixOpt,
+					wantCborData: hexDecode("c11a514b67b0"), // 1363896240
+				},
+				{
+					opt:          timeUnixMicroOpt,
+					wantCborData: hexDecode("c1fb41d452d9ec000000"), // 1363896240.0
+				},
+				{
+					opt:          timeUnixDynamicOpt,
+					wantCborData: hexDecode("c11a514b67b0"), // 1363896240
+				},
+				{
+					opt:          timeRFC3339Opt,
+					wantCborData: hexDecode("c074323031332d30332d32315432303a30343a30305a"), // "2013-03-21T20:04:00Z"
+				},
+				{
+					opt:          timeRFC3339NanoOpt,
+					wantCborData: hexDecode("c074323031332d30332d32315432303a30343a30305a"), // "2013-03-21T20:04:00Z"
+				},
+			},
+		},
+		{
+			name: "time with fractional seconds",
+			tm:   parseTime(time.RFC3339Nano, "2013-03-21T20:04:00.5Z"),
+			convert: []timeConvert{
+				{
+					opt:          timeUnixOpt,
+					wantCborData: hexDecode("c11a514b67b0"), // 1363896240
+				},
+				{
+					opt:          timeUnixMicroOpt,
+					wantCborData: hexDecode("c1fb41d452d9ec200000"), // 1363896240.5
+				},
+				{
+					opt:          timeUnixDynamicOpt,
+					wantCborData: hexDecode("c1fb41d452d9ec200000"), // 1363896240.5
+				},
+				{
+					opt:          timeRFC3339Opt,
+					wantCborData: hexDecode("c074323031332d30332d32315432303a30343a30305a"), // "2013-03-21T20:04:00Z"
+				},
+				{
+					opt:          timeRFC3339NanoOpt,
+					wantCborData: hexDecode("c076323031332d30332d32315432303a30343a30302e355a"), // "2013-03-21T20:04:00.5Z"
+				},
+			},
+		},
+		{
+			name: "time before January 1, 1970 UTC without fractional seconds",
+			tm:   parseTime(time.RFC3339Nano, "1969-03-21T20:04:00Z"),
+			convert: []timeConvert{
+				{
+					opt:          timeUnixOpt,
+					wantCborData: hexDecode("c13a0177f2cf"), // -24638160
+				},
+				{
+					opt:          timeUnixMicroOpt,
+					wantCborData: hexDecode("c1fbc1777f2d00000000"), // -24638160.0
+				},
+				{
+					opt:          timeUnixDynamicOpt,
+					wantCborData: hexDecode("c13a0177f2cf"), // -24638160
+				},
+				{
+					opt:          timeRFC3339Opt,
+					wantCborData: hexDecode("c074313936392d30332d32315432303a30343a30305a"), // "1969-03-21T20:04:00Z"
+				},
+				{
+					opt:          timeRFC3339NanoOpt,
+					wantCborData: hexDecode("c074313936392d30332d32315432303a30343a30305a"), // "1969-03-21T20:04:00Z"
+				},
+			},
+		},
+	}
+	for _, tc := range testCases {
+		for _, convert := range tc.convert {
+			var convertName string
+			switch convert.opt.Time {
+			case TimeUnix:
+				convertName = "TimeUnix"
+			case TimeUnixMicro:
+				convertName = "TimeUnixMicro"
+			case TimeUnixDynamic:
+				convertName = "TimeUnixDynamic"
+			case TimeRFC3339:
+				convertName = "TimeRFC3339"
+			case TimeRFC3339Nano:
+				convertName = "TimeRFC3339Nano"
+			}
+			name := tc.name + " with " + convertName + " option"
+			t.Run(name, func(t *testing.T) {
+				em, err := convert.opt.EncMode()
+				if err != nil {
+					t.Errorf("EncMode() returned error %v", err)
+				}
+				b, err := em.Marshal(tc.tm)
+				if err != nil {
+					t.Errorf("Marshal(%+v) returned error %v", tc.tm, err)
+				} else if !bytes.Equal(b, convert.wantCborData) {
+					t.Errorf("Marshal(%+v) = 0x%x, want 0x%x", tc.tm, b, convert.wantCborData)
+				}
+			})
+		}
+	}
+}
+
 func parseTime(layout string, value string) time.Time {
 	tm, err := time.Parse(layout, value)
 	if err != nil {
@@ -1570,12 +1760,12 @@ func TestMarshalStructKeyAsIntNumError(t *testing.T) {
 		{
 			name:         "float as key",
 			obj:          T1{},
-			wantErrorMsg: "parsing \"2.0\": invalid syntax",
+			wantErrorMsg: "cbor: failed to parse field name \"2.0\" to int",
 		},
 		{
 			name:         "out of range int as key",
 			obj:          T2{},
-			wantErrorMsg: "parsing \"-18446744073709551616\": value out of range",
+			wantErrorMsg: "cbor: failed to parse field name \"-18446744073709551616\" to int",
 		},
 	}
 	for _, tc := range testCases {
@@ -1583,8 +1773,6 @@ func TestMarshalStructKeyAsIntNumError(t *testing.T) {
 			b, err := Marshal(tc.obj)
 			if err == nil {
 				t.Errorf("Marshal(%+v) didn't return an error, want error %q", tc.obj, tc.wantErrorMsg)
-			} else if _, ok := err.(*strconv.NumError); !ok {
-				t.Errorf("Marshal(%v) error type %T, want *strconv.NumError", tc.obj, err)
 			} else if !strings.Contains(err.Error(), tc.wantErrorMsg) {
 				t.Errorf("Marshal(%v) error %v, want %v", tc.obj, err.Error(), tc.wantErrorMsg)
 			} else if b != nil {
@@ -2696,7 +2884,14 @@ func TestPreferredUnsortedEncOptions(t *testing.T) {
 }
 
 func TestEncOptions(t *testing.T) {
-	opts1 := EncOptions{}
+	opts1 := EncOptions{
+		Sort:          SortBytewiseLexical,
+		ShortestFloat: ShortestFloat16,
+		NaNConvert:    NaNConvertPreserveSignal,
+		InfConvert:    InfConvertNone,
+		Time:          TimeRFC3339Nano,
+		TimeTag:       EncTagRequired,
+	}
 	em, err := opts1.EncMode()
 	if err != nil {
 		t.Errorf("EncMode() returned an error %v", err)
@@ -2707,3 +2902,13 @@ func TestEncOptions(t *testing.T) {
 		}
 	}
 }
+
+func TestEncModeInvalidTimeTag(t *testing.T) {
+	wantErrorMsg := "cbor: invalid TimeTag 100"
+	_, err := EncOptions{TimeTag: 100}.EncMode()
+	if err == nil {
+		t.Errorf("EncMode() didn't return an error")
+	} else if err.Error() != wantErrorMsg {
+		t.Errorf("EncMode() returned error %q, want %q", err.Error(), wantErrorMsg)
+	}
+}
diff --git a/stream_test.go b/stream_test.go
index 4a20c84..6469628 100644
--- a/stream_test.go
+++ b/stream_test.go
@@ -8,6 +8,7 @@ import (
 	"io"
 	"reflect"
 	"testing"
+	"time"
 )
 
 func TestDecoder(t *testing.T) {
@@ -25,7 +26,11 @@ func TestDecoder(t *testing.T) {
 			if err := decoder.Decode(&v); err != nil {
 				t.Fatalf("Decode() returned error %v", err)
 			}
-			if !reflect.DeepEqual(v, tc.emptyInterfaceValue) {
+			if tm, ok := tc.emptyInterfaceValue.(time.Time); ok {
+				if vt, ok := v.(time.Time); !ok || !tm.Equal(vt) {
+					t.Errorf("Decode() = %v (%T), want %v (%T)", v, v, tc.emptyInterfaceValue, tc.emptyInterfaceValue)
+				}
+			} else if !reflect.DeepEqual(v, tc.emptyInterfaceValue) {
 				t.Errorf("Decode() = %v (%T), want %v (%T)", v, v, tc.emptyInterfaceValue, tc.emptyInterfaceValue)
 			}
 			bytesRead += len(tc.cborData)
@@ -74,7 +79,11 @@ func TestDecoderUnmarshalTypeError(t *testing.T) {
 				if err := decoder.Decode(&vi); err != nil {
 					t.Errorf("Decode() returned error %v", err)
 				}
-				if !reflect.DeepEqual(vi, tc.emptyInterfaceValue) {
+				if tm, ok := tc.emptyInterfaceValue.(time.Time); ok {
+					if vt, ok := vi.(time.Time); !ok || !tm.Equal(vt) {
+						t.Errorf("Decode() = %v (%T), want %v (%T)", vi, vi, tc.emptyInterfaceValue, tc.emptyInterfaceValue)
+					}
+				} else if !reflect.DeepEqual(vi, tc.emptyInterfaceValue) {
 					t.Errorf("Decode() = %v (%T), want %v (%T)", vi, vi, tc.emptyInterfaceValue, tc.emptyInterfaceValue)
 				}
 				bytesRead += len(tc.cborData)
@@ -146,9 +155,9 @@ func TestEncoderError(t *testing.T) {
 		value        interface{}
 		wantErrorMsg string
 	}{
-		{"channel can't be marshaled", make(chan bool), "cbor: unsupported type: chan bool"},
-		{"function can't be marshaled", func(i int) int { return i * i }, "cbor: unsupported type: func(int) int"},
-		{"complex can't be marshaled", complex(100, 8), "cbor: unsupported type: complex128"},
+		{"channel cannot be marshaled", make(chan bool), "cbor: unsupported type: chan bool"},
+		{"function cannot be marshaled", func(i int) int { return i * i }, "cbor: unsupported type: func(int) int"},
+		{"complex cannot be marshaled", complex(100, 8), "cbor: unsupported type: complex128"},
 	}
 	var w bytes.Buffer
 	encoder := NewEncoder(&w)
@@ -403,3 +412,14 @@ func TestEmptyRawMessage(t *testing.T) {
 		t.Errorf("Marshal(%+v) = 0x%x, want 0x%x", r, b, wantCborData)
 	}
 }
+
+func TestNilRawMessageUnmarshalCBORError(t *testing.T) {
+	wantErrorMsg := "cbor.RawMessage: UnmarshalCBOR on nil pointer"
+	var r *RawMessage
+	cborData := hexDecode("01")
+	if err := r.UnmarshalCBOR(cborData); err == nil {
+		t.Errorf("UnmarshalCBOR() didn't return error")
+	} else if err.Error() != wantErrorMsg {
+		t.Errorf("UnmarshalCBOR() returned error %q, want %q", err.Error(), wantErrorMsg)
+	}
+}
diff --git a/tag_test.go b/tag_test.go
new file mode 100644
index 0000000..059745c
--- /dev/null
+++ b/tag_test.go
@@ -0,0 +1,1097 @@
+package cbor
+
+import (
+	"bytes"
+	"io"
+	"reflect"
+	"strings"
+	"testing"
+	"time"
+)
+
+func TestTagNewTypeWithBuiltinUnderlyingType(t *testing.T) {
+	type myBool bool
+	type myUint uint
+	type myUint8 uint8
+	type myUint16 uint16
+	type myUint32 uint32
+	type myUint64 uint64
+	type myInt int
+	type myInt8 int8
+	type myInt16 int16
+	type myInt32 int32
+	type myInt64 int64
+	type myFloat32 float32
+	type myFloat64 float64
+	type myString string
+	type myByteSlice []byte
+	type myIntSlice []int
+	type myIntArray [4]int
+	type myMapIntInt map[int]int
+
+	types := []reflect.Type{
+		reflect.TypeOf(myBool(false)),
+		reflect.TypeOf(myUint(0)),
+		reflect.TypeOf(myUint8(0)),
+		reflect.TypeOf(myUint16(0)),
+		reflect.TypeOf(myUint32(0)),
+		reflect.TypeOf(myUint64(0)),
+		reflect.TypeOf(myInt(0)),
+		reflect.TypeOf(myInt8(0)),
+		reflect.TypeOf(myInt16(0)),
+		reflect.TypeOf(myInt32(0)),
+		reflect.TypeOf(myInt64(0)),
+		reflect.TypeOf(myFloat32(0)),
+		reflect.TypeOf(myFloat64(0)),
+		reflect.TypeOf(myString("")),
+		reflect.TypeOf(myByteSlice([]byte{})),
+		reflect.TypeOf(myIntSlice([]int{})),
+		reflect.TypeOf(myIntArray([4]int{})),
+		reflect.TypeOf(myMapIntInt(map[int]int{})),
+	}
+
+	tags := NewTagSet()
+	for i, typ := range types {
+		tagNum := uint64(100 + i)
+		if err := tags.Add(TagOptions{EncTag: EncTagRequired, DecTag: DecTagRequired}, typ, tagNum); err != nil {
+			t.Fatalf("TagSet.Add(%s, %d) returned error %v", typ, tagNum, err)
+		}
+	}
+
+	em, _ := EncOptions{Sort: SortCanonical}.EncModeWithTags(tags)
+	dm, _ := DecOptions{}.DecModeWithTags(tags)
+
+	testCases := []roundTripTest{
+		{
+			name:         "bool",
+			obj:          myBool(true),
+			wantCborData: hexDecode("d864f5"),
+		},
+		{
+			name:         "uint",
+			obj:          myUint(0),
+			wantCborData: hexDecode("d86500"),
+		},
+		{
+			name:         "uint8",
+			obj:          myUint8(0),
+			wantCborData: hexDecode("d86600"),
+		},
+		{
+			name:         "uint16",
+			obj:          myUint16(1000),
+			wantCborData: hexDecode("d8671903e8"),
+		},
+		{
+			name:         "uint32",
+			obj:          myUint32(1000000),
+			wantCborData: hexDecode("d8681a000f4240"),
+		},
+		{
+			name:         "uint64",
+			obj:          myUint64(1000000000000),
+			wantCborData: hexDecode("d8691b000000e8d4a51000"),
+		},
+		{
+			name:         "int",
+			obj:          myInt(-1),
+			wantCborData: hexDecode("d86a20"),
+		},
+		{
+			name:         "int8",
+			obj:          myInt8(-1),
+			wantCborData: hexDecode("d86b20"),
+		},
+		{
+			name:         "int16",
+			obj:          myInt16(-1000),
+			wantCborData: hexDecode("d86c3903e7"),
+		},
+		{
+			name:         "int32",
+			obj:          myInt32(-1000),
+			wantCborData: hexDecode("d86d3903e7"),
+		},
+		{
+			name:         "int64",
+			obj:          myInt64(-1000),
+			wantCborData: hexDecode("d86e3903e7"),
+		},
+		{
+			name:         "float32",
+			obj:          myFloat32(100000.0),
+			wantCborData: hexDecode("d86ffa47c35000"),
+		},
+		{
+			name:         "float64",
+			obj:          myFloat64(1.1),
+			wantCborData: hexDecode("d870fb3ff199999999999a"),
+		},
+		{
+			name:         "string",
+			obj:          myString("a"),
+			wantCborData: hexDecode("d8716161"),
+		},
+		{
+			name:         "[]byte",
+			obj:          myByteSlice([]byte{1, 2, 3, 4}),
+			wantCborData: hexDecode("d8724401020304"),
+		},
+		{
+			name:         "[]int",
+			obj:          myIntSlice([]int{1, 2, 3, 4}),
+			wantCborData: hexDecode("d8738401020304"),
+		},
+		{
+			name:         "[4]int",
+			obj:          myIntArray([...]int{1, 2, 3, 4}),
+			wantCborData: hexDecode("d8748401020304"),
+		},
+		{
+			name:         "map[int]int",
+			obj:          myMapIntInt(map[int]int{1: 2, 3: 4}),
+			wantCborData: hexDecode("d875a201020304"),
+		},
+	}
+
+	testRoundTrip(t, testCases, em, dm)
+}
+
+func TestTagBinaryMarshalerUnmarshaler(t *testing.T) {
+	t1 := reflect.TypeOf((*number)(nil)) // Use *number for testing purpose
+	t2 := reflect.TypeOf(stru{})
+
+	tags := NewTagSet()
+	if err := tags.Add(TagOptions{EncTag: EncTagRequired, DecTag: DecTagRequired}, t1, 123); err != nil {
+		t.Fatalf("TagSet.Add(%s, %d) returned error %v", t1, 123, err)
+	}
+	if err := tags.Add(TagOptions{EncTag: EncTagRequired, DecTag: DecTagRequired}, t2, 124); err != nil {
+		t.Fatalf("TagSet.Add(%s, %d) returned error %v", t2, 124, err)
+	}
+
+	em, _ := EncOptions{}.EncModeWithTags(tags)
+	dm, _ := DecOptions{}.DecModeWithTags(tags)
+
+	testCases := []roundTripTest{
+		{
+			name:         "primitive obj",
+			obj:          number(1234567890),
+			wantCborData: hexDecode("d87b4800000000499602d2"),
+		},
+		{
+			name:         "struct obj",
+			obj:          stru{a: "a", b: "b", c: "c"},
+			wantCborData: hexDecode("d87c45612C622C63"),
+		},
+	}
+
+	testRoundTrip(t, testCases, em, dm)
+}
+
+func TestTagStruct(t *testing.T) {
+	type coseHeader struct {
+		Alg int    `cbor:"1,keyasint,omitempty"`
+		Kid []byte `cbor:"4,keyasint,omitempty"`
+		IV  []byte `cbor:"5,keyasint,omitempty"`
+	}
+	type signedCWT struct {
+		_           struct{} `cbor:",toarray"`
+		Protected   []byte
+		Unprotected coseHeader
+		Payload     []byte
+		Signature   []byte
+	}
+
+	t1 := reflect.TypeOf(signedCWT{})
+
+	tags := NewTagSet()
+	if err := tags.Add(TagOptions{EncTag: EncTagRequired, DecTag: DecTagRequired}, t1, 18); err != nil {
+		t.Fatalf("TagSet.Add(%s, %d) returned error %v", t1, 18, err)
+	}
+
+	em, _ := EncOptions{}.EncModeWithTags(tags)
+	dm, _ := DecOptions{}.DecModeWithTags(tags)
+
+	// Data from https://tools.ietf.org/html/rfc8392#appendix-A section A.3
+	cborData := hexDecode("d28443a10126a104524173796d6d657472696345434453413235365850a70175636f61703a2f2f61732e6578616d706c652e636f6d02656572696b77037818636f61703a2f2f6c696768742e6578616d706c652e636f6d041a5612aeb0051a5610d9f0061a5610d9f007420b7158405427c1ff28d23fbad1f29c4c7c6a555e601d6fa29f9179bc3d7438bacaca5acd08c8d4d4f96131680c429a01f85951ecee743a52b9b63632c57209120e1c9e30")
+	var v signedCWT
+	if err := dm.Unmarshal(cborData, &v); err != nil {
+		t.Errorf("Unmarshal() returned error %v", err)
+	}
+	b, err := em.Marshal(v)
+	if err != nil {
+		t.Errorf("Marshal(%+v) returned error %v", v, err)
+	}
+	if !bytes.Equal(b, cborData) {
+		t.Errorf("Marshal(%+v) = 0x%x, want 0x%x", v, b, cborData)
+	}
+}
+
+func TestNestedTagStruct(t *testing.T) {
+	type coseHeader struct {
+		Alg int    `cbor:"1,keyasint,omitempty"`
+		Kid []byte `cbor:"4,keyasint,omitempty"`
+		IV  []byte `cbor:"5,keyasint,omitempty"`
+	}
+	type macedCOSE struct {
+		_           struct{} `cbor:",toarray"`
+		Protected   []byte
+		Unprotected coseHeader
+		Payload     []byte
+		Tag         []byte
+	}
+
+	t1 := reflect.TypeOf(macedCOSE{})
+
+	// Register tag CBOR Web Token (CWT) 61 and COSE_Mac0 17 with macedCOSE type
+	tags := NewTagSet()
+	if err := tags.Add(TagOptions{EncTag: EncTagRequired, DecTag: DecTagRequired}, t1, 61, 17); err != nil {
+		t.Fatalf("TagSet.Add(%s, %d, %v) returned error %v", t1, 61, 17, err)
+	}
+
+	em, _ := EncOptions{}.EncModeWithTags(tags)
+	dm, _ := DecOptions{}.DecModeWithTags(tags)
+
+	// Data from https://tools.ietf.org/html/rfc8392#appendix-A section A.4
+	cborData := hexDecode("d83dd18443a10104a1044c53796d6d65747269633235365850a70175636f61703a2f2f61732e6578616d706c652e636f6d02656572696b77037818636f61703a2f2f6c696768742e6578616d706c652e636f6d041a5612aeb0051a5610d9f0061a5610d9f007420b7148093101ef6d789200")
+	var v macedCOSE
+	if err := dm.Unmarshal(cborData, &v); err != nil {
+		t.Errorf("Unmarshal() returned error %v", err)
+	}
+	b, err := em.Marshal(v)
+	if err != nil {
+		t.Errorf("Marshal(%+v) returned error %v", v, err)
+	}
+	if !bytes.Equal(b, cborData) {
+		t.Errorf("Marshal(%+v) = 0x%x, want 0x%x", v, b, cborData)
+	}
+}
+
+func TestAddTagError(t *testing.T) {
+	type myInt int
+	testCases := []struct {
+		name         string
+		typ          reflect.Type
+		num          uint64
+		opts         TagOptions
+		wantErrorMsg string
+	}{
+		{
+			name:         "nil type",
+			typ:          nil,
+			num:          100,
+			opts:         TagOptions{DecTag: DecTagRequired, EncTag: EncTagRequired},
+			wantErrorMsg: "cbor: cannot add nil content type to TagSet",
+		},
+		{
+			name:         "DecTag is DecTagIgnored && EncTag is EncTagIgnored",
+			typ:          reflect.TypeOf(myInt(0)),
+			num:          100,
+			opts:         TagOptions{DecTag: DecTagIgnored, EncTag: EncTagIgnored},
+			wantErrorMsg: "cbor: cannot add tag with DecTagIgnored and EncTagIgnored options to TagSet",
+		},
+		{
+			name:         "time.Time",
+			typ:          reflect.TypeOf(time.Time{}),
+			num:          101,
+			opts:         TagOptions{DecTag: DecTagRequired, EncTag: EncTagRequired},
+			wantErrorMsg: "cbor: cannot add time.Time to TagSet, use EncOptions.TimeTag and DecOptions.TimeTag instead",
+		},
+		{
+			name:         "builtin type string",
+			typ:          reflect.TypeOf(""),
+			num:          102,
+			opts:         TagOptions{DecTag: DecTagRequired, EncTag: EncTagRequired},
+			wantErrorMsg: "cbor: can only add named types to TagSet, got string",
+		},
+		{
+			name:         "unnamed type struct{}",
+			typ:          reflect.TypeOf(struct{}{}),
+			num:          103,
+			opts:         TagOptions{DecTag: DecTagRequired, EncTag: EncTagRequired},
+			wantErrorMsg: "cbor: can only add named types to TagSet, got struct {}",
+		},
+		{
+			name:         "interface",
+			typ:          reflect.TypeOf((*io.Reader)(nil)).Elem(),
+			num:          104,
+			opts:         TagOptions{DecTag: DecTagRequired, EncTag: EncTagRequired},
+			wantErrorMsg: "cbor: can only add named types to TagSet, got io.Reader",
+		},
+		{
+			name:         "cbor.Tag",
+			typ:          reflect.TypeOf(Tag{}),
+			num:          105,
+			opts:         TagOptions{DecTag: DecTagRequired, EncTag: EncTagRequired},
+			wantErrorMsg: "cbor: cannot add cbor.Tag to TagSet",
+		},
+		{
+			name:         "cbor.RawTag",
+			typ:          reflect.TypeOf(RawTag{}),
+			num:          106,
+			opts:         TagOptions{DecTag: DecTagRequired, EncTag: EncTagRequired},
+			wantErrorMsg: "cbor: cannot add cbor.RawTag to TagSet",
+		},
+		{
+			name:         "cbor.Unmarshaler",
+			typ:          reflect.TypeOf(number2(0)),
+			num:          107,
+			opts:         TagOptions{DecTag: DecTagRequired, EncTag: EncTagIgnored},
+			wantErrorMsg: "cbor: cannot add cbor.Unmarshaler to TagSet with DecTag != DecTagIgnored",
+		},
+		{
+			name:         "cbor.Marshaler",
+			typ:          reflect.TypeOf(number2(0)),
+			num:          108,
+			opts:         TagOptions{DecTag: DecTagRequired, EncTag: EncTagRequired},
+			wantErrorMsg: "cbor: cannot add cbor.Marshaler to TagSet with EncTag != EncTagIgnored",
+		},
+		{
+			name:         "tag number 0",
+			typ:          reflect.TypeOf(myInt(0)),
+			num:          0,
+			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 1",
+			typ:          reflect.TypeOf(myInt(0)),
+			num:          1,
+			opts:         TagOptions{DecTag: DecTagRequired, EncTag: EncTagRequired},
+			wantErrorMsg: "cbor: cannot add tag number 0 or 1 to TagSet, use EncOptions.TimeTag and DecOptions.TimeTag instead",
+		},
+	}
+	tags := NewTagSet()
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			if err := tags.Add(tc.opts, tc.typ, tc.num); err == nil {
+				t.Errorf("TagSet.Add(%s, %d) didn't return an error", tc.typ.String(), tc.num)
+			} else if err.Error() != tc.wantErrorMsg {
+				var typeString string
+				if tc.typ == nil {
+					typeString = "nil"
+				} else {
+					typeString = tc.typ.String()
+				}
+				t.Errorf("TagSet.Add(%s, %d) returned error msg %q, want %q", typeString, tc.num, err, tc.wantErrorMsg)
+			}
+		})
+	}
+}
+
+func TestAddDuplicateTagError(t *testing.T) {
+	type myInt int
+	myIntType := reflect.TypeOf(myInt(0))
+	wantErrorMsg := "cbor: content type cbor.myInt already exists in TagSet"
+
+	tags := NewTagSet()
+	// Add myIntType and 100 to tags
+	if err := tags.Add(TagOptions{DecTag: DecTagRequired, EncTag: EncTagRequired}, myIntType, 100); err != nil {
+		t.Errorf("TagSet.Add(%s, %d) returned error %v", myIntType.String(), 100, err)
+	}
+	// Add myIntType and 101 to tags
+	if err := tags.Add(TagOptions{DecTag: DecTagRequired, EncTag: EncTagRequired}, myIntType, 101); err == nil {
+		t.Errorf("TagSet.Add(%s, %d) didn't return an error", myIntType.String(), 101)
+	} else if err.Error() != wantErrorMsg {
+		t.Errorf("TagSet.Add(%s, %d) returned error msg %q, want %q", myIntType, 100, err, wantErrorMsg)
+	}
+}
+
+func TestAddRemoveTag(t *testing.T) {
+	type myInt int
+	type myFloat float64
+	myIntType := reflect.TypeOf(myInt(0))
+	myFloatType := reflect.TypeOf(myFloat(0.0))
+	pMyIntType := reflect.TypeOf((*myInt)(nil))
+	pMyFloatType := reflect.TypeOf((*myFloat)(nil))
+
+	tags := NewTagSet()
+	stags := tags.(*syncTagSet)
+	if err := tags.Add(TagOptions{DecTag: DecTagRequired, EncTag: EncTagRequired}, myIntType, 100); err != nil {
+		t.Errorf("TagSet.Add(%s, %d) returned error %v", myIntType.String(), 100, err)
+	}
+	if err := tags.Add(TagOptions{DecTag: DecTagRequired, EncTag: EncTagRequired}, myFloatType, 101); err != nil {
+		t.Errorf("TagSet.Add(%s, %d) returned error %v", myFloatType.String(), 101, err)
+	}
+	if err := tags.Add(TagOptions{DecTag: DecTagRequired, EncTag: EncTagRequired}, pMyIntType, 102); err == nil {
+		t.Errorf("TagSet.Add(%s, %d) didn't return an error", pMyIntType.String(), 102)
+	}
+	if err := tags.Add(TagOptions{DecTag: DecTagRequired, EncTag: EncTagRequired}, pMyFloatType, 103); err == nil {
+		t.Errorf("TagSet.Add(%s, %d) didn't return an error", pMyFloatType.String(), 103)
+	}
+	if len(stags.t) != 2 {
+		t.Errorf("TagSet len is %d, want %d", len(stags.t), 2)
+	}
+	tags.Remove(pMyIntType)
+	if len(stags.t) != 1 {
+		t.Errorf("TagSet len is %d, want %d", len(stags.t), 1)
+	}
+	tags.Remove(pMyFloatType)
+	if len(stags.t) != 0 {
+		t.Errorf("TagSet len is %d, want %d", len(stags.t), 0)
+	}
+	tags.Remove(myIntType)
+	tags.Remove(myFloatType)
+}
+
+func TestAddTagTypeAliasError(t *testing.T) {
+	type myBool = bool
+	type myUint = uint
+	type myUint8 = uint8
+	type myUint16 = uint16
+	type myUint32 = uint32
+	type myUint64 = uint64
+	type myInt = int
+	type myInt8 = int8
+	type myInt16 = int16
+	type myInt32 = int32
+	type myInt64 = int64
+	type myFloat32 = float32
+	type myFloat64 = float64
+	type myString = string
+	type myByteSlice = []byte
+	type myIntSlice = []int
+	type myIntArray = [4]int
+	type myMapIntInt = map[int]int
+
+	testCases := []struct {
+		name         string
+		typ          reflect.Type
+		wantErrorMsg string
+	}{
+		{
+			name:         "bool",
+			typ:          reflect.TypeOf(myBool(false)),
+			wantErrorMsg: "cbor: can only add named types to TagSet, got bool",
+		},
+		{
+			name:         "uint",
+			typ:          reflect.TypeOf(myUint(0)),
+			wantErrorMsg: "cbor: can only add named types to TagSet, got uint",
+		},
+		{
+			name:         "uint8",
+			typ:          reflect.TypeOf(myUint8(0)),
+			wantErrorMsg: "cbor: can only add named types to TagSet, got uint8",
+		},
+		{
+			name:         "uint16",
+			typ:          reflect.TypeOf(myUint16(0)),
+			wantErrorMsg: "cbor: can only add named types to TagSet, got uint16",
+		},
+		{
+			name:         "uint32",
+			typ:          reflect.TypeOf(myUint32(0)),
+			wantErrorMsg: "cbor: can only add named types to TagSet, got uint32",
+		},
+		{
+			name:         "uint64",
+			typ:          reflect.TypeOf(myUint64(0)),
+			wantErrorMsg: "cbor: can only add named types to TagSet, got uint64",
+		},
+		{
+			name:         "int",
+			typ:          reflect.TypeOf(myInt(0)),
+			wantErrorMsg: "cbor: can only add named types to TagSet, got int",
+		},
+		{
+			name:         "int8",
+			typ:          reflect.TypeOf(myInt8(0)),
+			wantErrorMsg: "cbor: can only add named types to TagSet, got int8",
+		},
+		{
+			name:         "int16",
+			typ:          reflect.TypeOf(myInt16(0)),
+			wantErrorMsg: "cbor: can only add named types to TagSet, got int16",
+		},
+		{
+			name:         "int32",
+			typ:          reflect.TypeOf(myInt32(0)),
+			wantErrorMsg: "cbor: can only add named types to TagSet, got int32",
+		},
+		{
+			name:         "int64",
+			typ:          reflect.TypeOf(myInt64(0)),
+			wantErrorMsg: "cbor: can only add named types to TagSet, got int64",
+		},
+		{
+			name:         "float32",
+			typ:          reflect.TypeOf(myFloat32(0.0)),
+			wantErrorMsg: "cbor: can only add named types to TagSet, got float32",
+		},
+		{
+			name:         "float64",
+			typ:          reflect.TypeOf(myFloat64(0.0)),
+			wantErrorMsg: "cbor: can only add named types to TagSet, got float64",
+		},
+		{
+			name:         "string",
+			typ:          reflect.TypeOf(myString("")),
+			wantErrorMsg: "cbor: can only add named types to TagSet, got string",
+		},
+		{
+			name:         "[]byte",
+			typ:          reflect.TypeOf(myByteSlice([]byte{})), //nolint:unconvert
+			wantErrorMsg: "cbor: can only add named types to TagSet, got []uint8",
+		},
+		{
+			name:         "[]int",
+			typ:          reflect.TypeOf(myIntSlice([]int{})), //nolint:unconvert
+			wantErrorMsg: "cbor: can only add named types to TagSet, got []int",
+		},
+		{
+			name:         "[4]int",
+			typ:          reflect.TypeOf(myIntArray([4]int{})), //nolint:unconvert
+			wantErrorMsg: "cbor: can only add named types to TagSet, got [4]int",
+		},
+		{
+			name:         "map[int]int",
+			typ:          reflect.TypeOf(myMapIntInt(map[int]int{})), //nolint:unconvert
+			wantErrorMsg: "cbor: can only add named types to TagSet, got map[int]int",
+		},
+	}
+
+	tags := NewTagSet()
+	for i, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			if err := tags.Add(TagOptions{EncTag: EncTagRequired, DecTag: DecTagRequired}, tc.typ, uint64(100+i)); err == nil {
+				t.Errorf("TagSet.Add(%s, %d) didn't return an error", tc.typ.String(), 0)
+			} else if err.Error() != tc.wantErrorMsg {
+				t.Errorf("TagSet.Add(%s, %d) returned error msg %q, want %q", tc.typ.String(), 0, err, tc.wantErrorMsg)
+			}
+		})
+	}
+}
+
+// TestDecodeTag decodes tag data with DecTagRequired/EncTagOptional/EncTagIgnored options.
+func TestDecodeTagData(t *testing.T) {
+	type myInt int
+	type s struct {
+		A string `cbor:"a"`
+		B string `cbor:"b"`
+		C string `cbor:"c"`
+	}
+
+	type tagInfo struct {
+		t reflect.Type
+		n []uint64
+	}
+	tagInfos := []tagInfo{
+		{reflect.TypeOf((*number)(nil)), []uint64{123}}, // BinaryMarshaler *number
+		{reflect.TypeOf(stru{}), []uint64{124}},         // BinaryMarshaler stru
+		{reflect.TypeOf(myInt(0)), []uint64{125}},       // non-struct type
+		{reflect.TypeOf(s{}), []uint64{126}},            // struct type
+	}
+
+	tagsDecRequired := NewTagSet()
+	tagsDecOptional := NewTagSet()
+	tagsDecIgnored := NewTagSet()
+	for _, tag := range tagInfos {
+		if err := tagsDecRequired.Add(TagOptions{EncTag: EncTagRequired, DecTag: DecTagRequired}, tag.t, tag.n[0], tag.n[1:]...); err != nil {
+			t.Fatalf("TagSet.Add(%s, %v) returned error %v", tag.t, tag.n, err)
+		}
+		if err := tagsDecOptional.Add(TagOptions{EncTag: EncTagRequired, DecTag: DecTagOptional}, tag.t, tag.n[0], tag.n[1:]...); err != nil {
+			t.Fatalf("TagSet.Add(%s, %v) returned error %v", tag.t, tag.n, err)
+		}
+		if err := tagsDecIgnored.Add(TagOptions{EncTag: EncTagRequired, DecTag: DecTagIgnored}, tag.t, tag.n[0], tag.n[1:]...); err != nil {
+			t.Fatalf("TagSet.Add(%s, %v) returned error %v", tag.t, tag.n, err)
+		}
+	}
+
+	type tag struct {
+		name   string
+		tagSet TagSet
+	}
+	tags := []tag{
+		{"EncTagRequired_DecTagRequired", tagsDecRequired},
+		{"EncTagRequired_DecTagOptional", tagsDecOptional},
+		{"EncTagRequired_DecTagIgnored", tagsDecIgnored},
+	}
+
+	testCases := []roundTripTest{
+		{
+			name:         "BinaryMarshaler non-struct",
+			obj:          number(1234567890),
+			wantCborData: hexDecode("d87b4800000000499602d2"),
+		},
+		{
+			name:         "BinaryMarshaler struct",
+			obj:          stru{a: "a", b: "b", c: "c"},
+			wantCborData: hexDecode("d87c45612C622C63"),
+		},
+		{
+			name:         "non-struct",
+			obj:          myInt(1),
+			wantCborData: hexDecode("d87d01"),
+		},
+		{
+			name:         "struct",
+			obj:          s{A: "A", B: "B", C: "C"},
+			wantCborData: hexDecode("d87ea3616161416162614261636143"), // {"a":"A", "b":"B", "c":"C"}
+		},
+	}
+	for _, tag := range tags {
+		t.Run(tag.name, func(t *testing.T) {
+			em, _ := EncOptions{}.EncModeWithTags(tag.tagSet)
+			dm, _ := DecOptions{}.DecModeWithTags(tag.tagSet)
+			testRoundTrip(t, testCases, em, dm)
+		})
+	}
+}
+
+// TestDecodeNoTag decodes no-tag data with DecTagRequired/EncTagOptional/EncTagIgnored options
+func TestDecodeNoTagData(t *testing.T) {
+	type myInt int
+	type s struct {
+		A string `cbor:"a"`
+		B string `cbor:"b"`
+		C string `cbor:"c"`
+	}
+
+	type tagInfo struct {
+		t reflect.Type
+		n []uint64
+	}
+	tagInfos := []tagInfo{
+		{reflect.TypeOf((*number)(nil)), []uint64{123}}, // BinaryMarshaler *number
+		{reflect.TypeOf(stru{}), []uint64{124}},         // BinaryMarshaler stru
+		{reflect.TypeOf(myInt(0)), []uint64{125}},       // non-struct type
+		{reflect.TypeOf(s{}), []uint64{126}},            // struct type
+	}
+
+	tagsDecRequired := NewTagSet()
+	tagsDecOptional := NewTagSet()
+	for _, tag := range tagInfos {
+		if err := tagsDecRequired.Add(TagOptions{EncTag: EncTagIgnored, DecTag: DecTagRequired}, tag.t, tag.n[0], tag.n[1:]...); err != nil {
+			t.Fatalf("TagSet.Add(%s, %v) returned error %v", tag.t, tag.n, err)
+		}
+		if err := tagsDecOptional.Add(TagOptions{EncTag: EncTagIgnored, DecTag: DecTagOptional}, tag.t, tag.n[0], tag.n[1:]...); err != nil {
+			t.Fatalf("TagSet.Add(%s, %v) returned error %v", tag.t, tag.n, err)
+		}
+	}
+
+	type tag struct {
+		name   string
+		tagSet TagSet
+	}
+	tags := []tag{
+		{"EncTagIgnored_DecTagOptional", tagsDecOptional},
+	}
+
+	testCases := []roundTripTest{
+		{
+			name:         "BinaryMarshaler non-struct",
+			obj:          number(1234567890),
+			wantCborData: hexDecode("4800000000499602d2"),
+		},
+		{
+			name:         "BinaryMarshaler struct",
+			obj:          stru{a: "a", b: "b", c: "c"},
+			wantCborData: hexDecode("45612C622C63"),
+		},
+		{
+			name:         "non-struct",
+			obj:          myInt(1),
+			wantCborData: hexDecode("01"),
+		},
+		{
+			name:         "struct",
+			obj:          s{A: "A", B: "B", C: "C"},
+			wantCborData: hexDecode("a3616161416162614261636143"), // {"a":"A", "b":"B", "c":"C"}
+		},
+	}
+
+	for _, tag := range tags {
+		t.Run(tag.name, func(t *testing.T) {
+			em, _ := EncOptions{}.EncModeWithTags(tag.tagSet)
+			dm, _ := DecOptions{}.DecModeWithTags(tag.tagSet)
+			testRoundTrip(t, testCases, em, dm)
+		})
+	}
+
+	// Decode non-tag data with DecTagRequired option returns UnmarshalTypeError
+	for _, tc := range testCases {
+		name := "EncTagIgnored_DecTagRequired " + tc.name
+		t.Run(name, func(t *testing.T) {
+			dm, _ := DecOptions{}.DecModeWithTags(tagsDecRequired)
+			v := reflect.New(reflect.TypeOf(tc.obj))
+			if err := dm.Unmarshal(tc.wantCborData, v.Interface()); err == nil {
+				t.Errorf("Unmarshal(0x%x) didn't return an error", tc.wantCborData)
+			} else {
+				if _, ok := err.(*UnmarshalTypeError); !ok {
+					t.Errorf("Unmarshal(0x%x) returned wrong type of error %T, want (*UnmarshalTypeError)", tc.wantCborData, err)
+				} else if !strings.Contains(err.Error(), "expect CBOR tag value") {
+					t.Errorf("Unmarshal(0x%x) returned error %q, want error containing %q", tc.wantCborData, err.Error(), "expect CBOR tag value")
+				}
+			}
+		})
+	}
+}
+
+// TestDecodeWrongTag decodes wrong tag data with DecTagRequired/EncTagOptional/EncTagIgnored options
+func TestDecodeWrongTag(t *testing.T) {
+	type myInt int
+	type s struct {
+		A string `cbor:"a"`
+		B string `cbor:"b"`
+		C string `cbor:"c"`
+	}
+
+	type tagInfo struct {
+		t reflect.Type
+		n []uint64
+	}
+	tagInfos := []tagInfo{
+		{reflect.TypeOf((*number)(nil)), []uint64{123}}, // BinaryMarshaler *number
+		{reflect.TypeOf(stru{}), []uint64{124}},         // BinaryMarshaler stru
+		{reflect.TypeOf(myInt(0)), []uint64{100}},       // non-struct type
+		{reflect.TypeOf(s{}), []uint64{101, 102}},       // struct type
+	}
+
+	tagsDecRequired := NewTagSet()
+	tagsDecOptional := NewTagSet()
+	tagsDecIgnored := NewTagSet()
+	for _, tag := range tagInfos {
+		if err := tagsDecRequired.Add(TagOptions{EncTag: EncTagRequired, DecTag: DecTagRequired}, tag.t, tag.n[0], tag.n[1:]...); err != nil {
+			t.Fatalf("TagSet.Add(%s, %v) returned error %v", tag.t, tag.n, err)
+		}
+		if err := tagsDecOptional.Add(TagOptions{EncTag: EncTagRequired, DecTag: DecTagOptional}, tag.t, tag.n[0], tag.n[1:]...); err != nil {
+			t.Fatalf("TagSet.Add(%s, %v) returned error %v", tag.t, tag.n, err)
+		}
+		if err := tagsDecIgnored.Add(TagOptions{EncTag: EncTagRequired, DecTag: DecTagIgnored}, tag.t, tag.n[0], tag.n[1:]...); err != nil {
+			t.Fatalf("TagSet.Add(%s, %v) returned error %v", tag.t, tag.n, err)
+		}
+	}
+	type tag struct {
+		name   string
+		tagSet TagSet
+	}
+	tags := []tag{
+		{"EncTagRequired_DecTagRequired", tagsDecRequired},
+		{"EncTagRequired_DecTagOptional", tagsDecOptional},
+	}
+
+	testCases := []struct {
+		name         string
+		obj          interface{}
+		cborData     []byte
+		wantErrorMsg string
+	}{
+		{
+			name:         "BinaryMarshaler non-struct",
+			obj:          number(1234567890),
+			cborData:     hexDecode("d87d4800000000499602d2"),
+			wantErrorMsg: "cbor: wrong tag number for cbor.number, got [125], expected [123]",
+		},
+		{
+			name:         "BinaryMarshaler struct",
+			obj:          stru{a: "a", b: "b", c: "c"},
+			cborData:     hexDecode("d87d45612C622C63"),
+			wantErrorMsg: "cbor: wrong tag number for cbor.stru, got [125], expected [124]",
+		},
+		{
+			name:         "non-struct",
+			obj:          myInt(1),
+			cborData:     hexDecode("d87d01"),
+			wantErrorMsg: "cbor: wrong tag number for cbor.myInt, got [125], expected [100]",
+		},
+		{
+			name:         "struct",
+			obj:          s{A: "A", B: "B", C: "C"},
+			cborData:     hexDecode("d87ea3616161416162614261636143"), // {"a":"A", "b":"B", "c":"C"}
+			wantErrorMsg: "cbor: wrong tag number for cbor.s, got [126], expected [101 102]",
+		},
+	}
+
+	for _, tc := range testCases {
+		for _, tag := range tags {
+			name := tag.name + " " + tc.name
+			t.Run(name, func(t *testing.T) {
+				dm, _ := DecOptions{}.DecModeWithTags(tag.tagSet)
+				v := reflect.New(reflect.TypeOf(tc.obj))
+				if err := dm.Unmarshal(tc.cborData, v.Interface()); err == nil {
+					t.Errorf("Unmarshal(0x%x) didn't return an error", tc.cborData)
+				} else {
+					if _, ok := err.(*WrongTagError); !ok {
+						t.Errorf("Unmarshal(0x%x) returned wrong type of error %T, want (*WrongTagError)", tc.cborData, err)
+					} else if err.Error() != tc.wantErrorMsg {
+						t.Errorf("Unmarshal(0x%x) returned error %q, want error %q", tc.cborData, err.Error(), tc.wantErrorMsg)
+					}
+				}
+			})
+		}
+	}
+
+	// Decode wrong tag data with DecTagIgnored option returns no error.
+	for _, tc := range testCases {
+		name := "EncTagRequired_DecTagIgnored " + tc.name
+		t.Run(name, func(t *testing.T) {
+			dm, _ := DecOptions{}.DecModeWithTags(tagsDecIgnored)
+			v := reflect.New(reflect.TypeOf(tc.obj))
+			if err := dm.Unmarshal(tc.cborData, v.Interface()); err != nil {
+				t.Errorf("Unmarshal() returned error %v", err)
+			}
+			if !reflect.DeepEqual(tc.obj, v.Elem().Interface()) {
+				t.Errorf("Marshal-Unmarshal returned different values: %v, %v", tc.obj, v.Elem().Interface())
+			}
+		})
+	}
+}
+
+func TestEncodeSharedTag(t *testing.T) {
+	type myInt int
+
+	myIntType := reflect.TypeOf(myInt(0))
+
+	sharedTagSet := NewTagSet()
+
+	em, err := EncOptions{}.EncModeWithSharedTags(sharedTagSet)
+	if err != nil {
+		t.Errorf("EncModeWithSharedTags() returned error %v", err)
+	}
+
+	// Register myInt type with tag number 123
+	if err = sharedTagSet.Add(TagOptions{EncTag: EncTagRequired, DecTag: DecTagRequired}, myIntType, 123); err != nil {
+		t.Fatalf("TagSet.Add(%s, %v) returned error %v", myIntType, 100, err)
+	}
+
+	// Encode myInt with tag number 123
+	v := myInt(1)
+	wantCborData := hexDecode("d87b01")
+	b, err := em.Marshal(v)
+	if err != nil {
+		t.Errorf("Marshal(%v) returned error %v", v, err)
+	}
+	if !bytes.Equal(b, wantCborData) {
+		t.Errorf("Marshal(%v) = 0x%x, want 0x%x", v, b, wantCborData)
+	}
+
+	// Unregister myInt type
+	sharedTagSet.Remove(myIntType)
+
+	// Encode myInt without tag number 123
+	v = myInt(2)
+	wantCborData = hexDecode("02")
+	b, err = em.Marshal(v)
+	if err != nil {
+		t.Errorf("Marshal(%v) returned error %v", v, err)
+	}
+	if !bytes.Equal(b, wantCborData) {
+		t.Errorf("Marshal(%v) = 0x%x, want 0x%x", v, b, wantCborData)
+	}
+
+	// Register myInt type with tag number 234
+	if err = sharedTagSet.Add(TagOptions{EncTag: EncTagRequired, DecTag: DecTagRequired}, myIntType, 234); err != nil {
+		t.Fatalf("TagSet.Add(%s, %v) returned error %v", myIntType, 100, err)
+	}
+
+	// Encode myInt with tag number 234
+	v = myInt(3)
+	wantCborData = hexDecode("d8ea03")
+	b, err = em.Marshal(v)
+	if err != nil {
+		t.Errorf("Marshal(%v) returned error %v", v, err)
+	}
+	if !bytes.Equal(b, wantCborData) {
+		t.Errorf("Marshal(%v) = 0x%x, want 0x%x", v, b, wantCborData)
+	}
+
+}
+
+func TestDecodeSharedTag(t *testing.T) {
+	type myInt int
+
+	myIntType := reflect.TypeOf(myInt(0))
+
+	sharedTagSet := NewTagSet()
+
+	dm, err := DecOptions{}.DecModeWithSharedTags(sharedTagSet)
+	if err != nil {
+		t.Errorf("DecModeWithSharedTags() returned error %v", err)
+	}
+
+	// Register myInt type with tag number 123
+	if err = sharedTagSet.Add(TagOptions{EncTag: EncTagRequired, DecTag: DecTagRequired}, myIntType, 123); err != nil {
+		t.Fatalf("TagSet.Add(%s, %v) returned error %v", myIntType, 100, err)
+	}
+
+	// Decode myInt with tag number 123
+	var v myInt
+	wantV := myInt(1)
+	cborData := hexDecode("d87b01")
+	if err = dm.Unmarshal(cborData, &v); err != nil {
+		t.Errorf("Unmarshal(0x%x) returned error %v", cborData, err)
+	}
+	if !reflect.DeepEqual(v, wantV) {
+		t.Errorf("Unmarshal(0x%x) = %v (%T), want %v (%T)", cborData, v, v, wantV, wantV)
+	}
+
+	// Unregister myInt type
+	sharedTagSet.Remove(myIntType)
+
+	// Decode myInt without tag number
+	wantV = myInt(2)
+	cborData = hexDecode("02")
+	if err := dm.Unmarshal(cborData, &v); err != nil {
+		t.Errorf("Unmarshal(0x%x) returned error %v", cborData, err)
+	}
+	if !reflect.DeepEqual(v, wantV) {
+		t.Errorf("Unmarshal(0x%x) = %v (%T), want %v (%T)", cborData, v, v, wantV, wantV)
+	}
+
+	// Register myInt type with tag number 234
+	if err := sharedTagSet.Add(TagOptions{EncTag: EncTagRequired, DecTag: DecTagRequired}, myIntType, 234); err != nil {
+		t.Fatalf("TagSet.Add(%s, %v) returned error %v", myIntType, 100, err)
+	}
+
+	// Decode myInt with tag number 234
+	wantV = myInt(3)
+	cborData = hexDecode("d8ea03")
+	if err := dm.Unmarshal(cborData, &v); err != nil {
+		t.Errorf("Unmarshal(0x%x) returned error %v", cborData, err)
+	}
+	if !reflect.DeepEqual(v, wantV) {
+		t.Errorf("Unmarshal(0x%x) = %v (%T), want %v (%T)", cborData, v, v, wantV, wantV)
+	}
+}
+
+func TestDecModeWithTagsError(t *testing.T) {
+	// Create DecMode with nil as TagSet
+	wantErrorMsg := "cbor: cannot create DecMode with nil value as TagSet"
+	dm, err := DecOptions{}.DecModeWithTags(nil)
+	if dm != nil {
+		t.Errorf("DecModeWithTags(nil) returned %v", dm)
+	}
+	if err.Error() != wantErrorMsg {
+		t.Errorf("DecModeWithTags(nil) returned error %q, want %q", err.Error(), wantErrorMsg)
+	}
+	dm, err = DecOptions{}.DecModeWithSharedTags(nil)
+	if dm != nil {
+		t.Errorf("DecModeWithSharedTags(nil) returned %v", dm)
+	}
+	if err.Error() != wantErrorMsg {
+		t.Errorf("DecModeWithSharedTags(nil) returned error %q, want %q", err.Error(), wantErrorMsg)
+	}
+
+	// Create DecMode with invalid EncOptions
+	wantErrorMsg = "cbor: invalid TimeTag 100"
+	dm, err = DecOptions{TimeTag: 100}.DecModeWithTags(NewTagSet())
+	if dm != nil {
+		t.Errorf("DecModeWithTags() returned %v", dm)
+	}
+	if err.Error() != wantErrorMsg {
+		t.Errorf("DecModeWithTags() returned error %q, want %q", err.Error(), wantErrorMsg)
+	}
+	dm, err = DecOptions{TimeTag: 100}.DecModeWithSharedTags(NewTagSet())
+	if dm != nil {
+		t.Errorf("DecModeWithSharedTags() returned %v", dm)
+	}
+	if err.Error() != wantErrorMsg {
+		t.Errorf("DecModeWithSharedTags() returned error %q, want %q", err.Error(), wantErrorMsg)
+	}
+}
+
+func TestEncModeWithTagsError(t *testing.T) {
+	// Create EncMode with nil as TagSet
+	wantErrorMsg := "cbor: cannot create EncMode with nil value as TagSet"
+	em, err := EncOptions{}.EncModeWithTags(nil)
+	if em != nil {
+		t.Errorf("EncModeWithTags(nil) returned %v", em)
+	}
+	if err.Error() != wantErrorMsg {
+		t.Errorf("EncModeWithTags(nil) returned error %q, want %q", err.Error(), wantErrorMsg)
+	}
+	em, err = EncOptions{}.EncModeWithSharedTags(nil)
+	if em != nil {
+		t.Errorf("EncModeWithSharedTags(nil) returned %v", em)
+	}
+	if err.Error() != wantErrorMsg {
+		t.Errorf("EncModeWithSharedTags(nil) returned error %q, want %q", err.Error(), wantErrorMsg)
+	}
+
+	// Create EncMode with invalid EncOptions
+	wantErrorMsg = "cbor: invalid TimeTag 100"
+	em, err = EncOptions{TimeTag: 100}.EncModeWithTags(NewTagSet())
+	if em != nil {
+		t.Errorf("EncModeWithTags() returned %v", em)
+	}
+	if err.Error() != wantErrorMsg {
+		t.Errorf("EncModeWithTags() returned error %q, want %q", err.Error(), wantErrorMsg)
+	}
+	em, err = EncOptions{TimeTag: 100}.EncModeWithSharedTags(NewTagSet())
+	if em != nil {
+		t.Errorf("EncModeWithSharedTags() returned %v", em)
+	}
+	if err.Error() != wantErrorMsg {
+		t.Errorf("EncModeWithSharedTags() returned error %q, want %q", err.Error(), wantErrorMsg)
+	}
+}
+
+func TestNilRawTagUnmarshalCBORError(t *testing.T) {
+	wantErrorMsg := "cbor.RawTag: UnmarshalCBOR on nil pointer"
+	var tag *RawTag
+	cborData := hexDecode("c249010000000000000000")
+	if err := tag.UnmarshalCBOR(cborData); err == nil {
+		t.Errorf("UnmarshalCBOR() didn't return error")
+	} else if err.Error() != wantErrorMsg {
+		t.Errorf("UnmarshalCBOR() returned error %q, want %q", err.Error(), wantErrorMsg)
+	}
+}
+
+func TestTagUnmarshalError(t *testing.T) {
+	cborData := hexDecode("d87b61fe") // invalid UTF-8 string
+	var tag Tag
+	if err := Unmarshal(cborData, &tag); err == nil {
+		t.Errorf("Unmarshal(0x%x) didn't return error", cborData)
+	} else if err.Error() != invalidUTF8ErrorMsg {
+		t.Errorf("Unmarshal(0x%x) returned error %q, want %q", cborData, err.Error(), invalidUTF8ErrorMsg)
+	}
+}
+
+func TestTagMarshalError(t *testing.T) {
+	wantErrorMsg := "cbor: unsupported type: chan bool"
+	tag := Tag{
+		Number:  123,
+		Content: make(chan bool),
+	}
+	if _, err := Marshal(tag); err == nil {
+		t.Errorf("Marshal() didn't return error")
+	} else if err.Error() != wantErrorMsg {
+		t.Errorf("Marshal() returned error %q, want %q", err.Error(), wantErrorMsg)
+	}
+}
+
+func TestTagMarshal(t *testing.T) {
+	m := make(map[interface{}]bool)
+	m[10] = true
+	m[100] = true
+	m[-1] = true
+	m["z"] = true
+	m["aa"] = true
+	m[[1]int{100}] = true
+	m[[1]int{-1}] = true
+	m[false] = true
+
+	v := Tag{100, m}
+
+	lenFirstSortedCborData := hexDecode("d864a80af520f5f4f51864f5617af58120f5626161f5811864f5") // tag number: 100, value: map with sorted keys: 10, -1, false, 100, "z", [-1], "aa", [100]
+	bytewiseSortedCborData := hexDecode("d864a80af51864f520f5617af5626161f5811864f58120f5f4f5") // tag number: 100, value: map with sorted keys: 10, 100, -1, "z", "aa", [100], [-1], false
+
+	em, _ := EncOptions{Sort: SortLengthFirst}.EncMode()
+	b, err := em.Marshal(v)
+	if err != nil {
+		t.Errorf("Marshal(%v) returned error %v", v, err)
+	}
+	if !bytes.Equal(b, lenFirstSortedCborData) {
+		t.Errorf("Marshal(%v) = 0x%x, want 0x%x", v, b, lenFirstSortedCborData)
+	}
+
+	em, _ = EncOptions{Sort: SortBytewiseLexical}.EncMode()
+	b, err = em.Marshal(v)
+	if err != nil {
+		t.Errorf("Marshal(%v) returned error %v", v, err)
+	}
+	if !bytes.Equal(b, bytewiseSortedCborData) {
+		t.Errorf("Marshal(%v) = 0x%x, want 0x%x", v, b, bytewiseSortedCborData)
+	}
+}