Repository: fxamacker/cbor
Pull request #152 (2020-02-16 23:50:36)

Add CBOR duplicate map key options
This PR adds 2 options for duplicate map keys for CBOR decoding:

- DupMapKeyQuiet: Turn off detection of duplicate map keys. It tries to use a "keep fastest" method by choosing either "keep first" or "keep last" depending on the Go data type.

- DupMapKeyEnforcedAPF: Turn on detection and rejection of duplidate map keys. Decoding stops immediately and returns DupMapKeyError when the first duplicate key is detected. The error includes the duplicate map key and the index number.

APF suffix means "Allow Partial Fill" so the destination map or struct can contain some decoded values at the time of error. It is the caller's responsibility to respond to the DuplicateMapKeyErr by discarding the partially filled result if that's required by their protocol.

Detection of duplicate map keys relies on whether the CBOR map key would be a duplicate "key" when decoded and applied to the user-provided Go map or struct.


Patch:
diff --git a/decode.go b/decode.go
index 641fe94..9ec2902 100644
--- a/decode.go
+++ b/decode.go
@@ -7,6 +7,7 @@ import (
 	"encoding"
 	"encoding/binary"
 	"errors"
+	"fmt"
 	"io"
 	"math"
 	"reflect"
@@ -142,8 +143,44 @@ func (e *UnmarshalTypeError) Error() string {
 	return s
 }
 
+// DupMapKeyError describes detected duplicate map key in CBOR map.
+type DupMapKeyError struct {
+	Key   interface{}
+	Index int
+}
+
+func (e *DupMapKeyError) Error() string {
+	return fmt.Sprintf("cbor: found duplicate map key \"%v\" at map element index %d", e.Key, e.Index)
+}
+
+// DupMapKeyMode specifies how to enforce duplicate map key.
+type DupMapKeyMode int
+
+const (
+	// DupMapKeyQuiet doesn't enforce duplicate map key. Decoder quietly (no error)
+	// uses faster of "keep first" or "keep last" depending on Go data type and other factors.
+	DupMapKeyQuiet DupMapKeyMode = iota
+
+	// DupMapKeyEnforcedAPF enforces detection and rejection of duplicate map keys.
+	// APF means "Allow Partial Fill" and the destination map or struct can be partially filled.
+	// If a duplicate map key is detected, DupMapKeyError is returned without further decoding
+	// of the map. It's the caller's responsibility to respond to DupMapKeyError by
+	// discarding the partially filled result if their protocol requires it.
+	// WARNING: using DupMapKeyEnforcedAPF will decrease performance and increase memory use.
+	DupMapKeyEnforcedAPF
+
+	maxDupMapKeyMode
+)
+
+func (dmkm DupMapKeyMode) valid() bool {
+	return dmkm < maxDupMapKeyMode
+}
+
 // DecOptions specifies decoding options.
 type DecOptions struct {
+	// DupMapKey specifies whether to enforce duplicate map key.
+	DupMapKey DupMapKeyMode
+
 	// 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
@@ -197,10 +234,13 @@ func (opts DecOptions) DecModeWithSharedTags(tags TagSet) (DecMode, error) {
 }
 
 func (opts DecOptions) decMode() (*decMode, error) {
+	if !opts.DupMapKey.valid() {
+		return nil, errors.New("cbor: invalid DupMapKey " + strconv.Itoa(int(opts.DupMapKey)))
+	}
 	if !opts.TimeTag.valid() {
 		return nil, errors.New("cbor: invalid TimeTag " + strconv.Itoa(int(opts.TimeTag)))
 	}
-	dm := decMode{timeTag: opts.TimeTag}
+	dm := decMode{dupMapKey: opts.DupMapKey, timeTag: opts.TimeTag}
 	return &dm, nil
 }
 
@@ -212,15 +252,16 @@ type DecMode interface {
 }
 
 type decMode struct {
-	tags    tagProvider
-	timeTag DecTagMode
+	tags      tagProvider
+	dupMapKey DupMapKeyMode
+	timeTag   DecTagMode
 }
 
 var defaultDecMode = &decMode{}
 
 // DecOptions returns user specified options used to create this DecMode.
 func (dm *decMode) DecOptions() DecOptions {
-	return DecOptions{TimeTag: dm.timeTag}
+	return DecOptions{DupMapKey: dm.dupMapKey, TimeTag: dm.timeTag}
 }
 
 // Unmarshal parses the CBOR-encoded data and stores the result in the value
@@ -800,7 +841,9 @@ func (d *decodeState) parseMap() (map[interface{}]interface{}, error) {
 	m := make(map[interface{}]interface{})
 	var k, e interface{}
 	var err, lastErr error
+	keyCount := 0
 	for i := 0; (hasSize && i < count) || (!hasSize && !d.foundBreak()); i++ {
+		// Parse CBOR map key.
 		if k, lastErr = d.parse(); lastErr != nil {
 			if err == nil {
 				err = lastErr
@@ -808,6 +851,8 @@ func (d *decodeState) parseMap() (map[interface{}]interface{}, error) {
 			d.skip()
 			continue
 		}
+
+		// Detect if CBOR map key can be used as Go map key.
 		kkind := reflect.ValueOf(k).Kind()
 		if tag, ok := k.(Tag); ok {
 			kkind = tag.contentKind()
@@ -819,18 +864,39 @@ func (d *decodeState) parseMap() (map[interface{}]interface{}, error) {
 			d.skip()
 			continue
 		}
+
+		// Parse CBOR map value.
 		if e, lastErr = d.parse(); lastErr != nil {
 			if err == nil {
 				err = lastErr
 			}
 			continue
 		}
+
+		// Add key-value pair to Go map.
 		m[k] = e
+
+		// Detect duplicate map key.
+		if d.dm.dupMapKey == DupMapKeyEnforcedAPF {
+			newKeyCount := len(m)
+			if newKeyCount == keyCount {
+				m[k] = nil
+				err = &DupMapKeyError{k, i}
+				i++
+				// skip the rest of the map
+				for ; (hasSize && i < count) || (!hasSize && !d.foundBreak()); i++ {
+					d.skip() // Skip map key
+					d.skip() // Skip map value
+				}
+				return m, err
+			}
+			keyCount = newKeyCount
+		}
 	}
 	return m, err
 }
 
-func (d *decodeState) parseMapToMap(v reflect.Value, tInfo *typeInfo) error {
+func (d *decodeState) parseMapToMap(v reflect.Value, tInfo *typeInfo) error { //nolint:gocyclo
 	_, ai, val := d.getHead()
 	hasSize := (ai != 31)
 	count := int(val)
@@ -846,7 +912,19 @@ func (d *decodeState) parseMapToMap(v reflect.Value, tInfo *typeInfo) error {
 	var keyValue, eleValue, zeroKeyValue, zeroEleValue reflect.Value
 	keyIsInterfaceType := keyType == typeIntf // If key type is interface{}, need to check if key value is hashable.
 	var err, lastErr error
+	keyCount := v.Len()
+	var existingKeys map[interface{}]bool // Store existing map keys, used for detecting duplicate map key.
+	if d.dm.dupMapKey == DupMapKeyEnforcedAPF {
+		existingKeys = make(map[interface{}]bool, keyCount)
+		if keyCount > 0 {
+			vKeys := v.MapKeys()
+			for i := 0; i < len(vKeys); i++ {
+				existingKeys[vKeys[i].Interface()] = true
+			}
+		}
+	}
 	for i := 0; (hasSize && i < count) || (!hasSize && !d.foundBreak()); i++ {
+		// Parse CBOR map key.
 		if !keyValue.IsValid() {
 			keyValue = reflect.New(keyType).Elem()
 		} else if !reuseKey {
@@ -862,6 +940,8 @@ func (d *decodeState) parseMapToMap(v reflect.Value, tInfo *typeInfo) error {
 			d.skip()
 			continue
 		}
+
+		// Detect if CBOR map key can be used as Go map key.
 		if keyIsInterfaceType {
 			kkind := keyValue.Elem().Kind()
 			if keyValue.Elem().IsValid() {
@@ -878,6 +958,7 @@ func (d *decodeState) parseMapToMap(v reflect.Value, tInfo *typeInfo) error {
 			}
 		}
 
+		// Parse CBOR map value.
 		if !eleValue.IsValid() {
 			eleValue = reflect.New(eleType).Elem()
 		} else if !reuseEle {
@@ -893,7 +974,29 @@ func (d *decodeState) parseMapToMap(v reflect.Value, tInfo *typeInfo) error {
 			continue
 		}
 
+		// Add key-value pair to Go map.
 		v.SetMapIndex(keyValue, eleValue)
+
+		// Detect duplicate map key.
+		if d.dm.dupMapKey == DupMapKeyEnforcedAPF {
+			newKeyCount := v.Len()
+			if newKeyCount == keyCount {
+				kvi := keyValue.Interface()
+				if !existingKeys[kvi] {
+					v.SetMapIndex(keyValue, reflect.New(eleType).Elem())
+					err = &DupMapKeyError{kvi, i}
+					i++
+					// skip the rest of the map
+					for ; (hasSize && i < count) || (!hasSize && !d.foundBreak()); i++ {
+						d.skip() // skip map key
+						d.skip() // skip map value
+					}
+					return err
+				}
+				delete(existingKeys, kvi)
+			}
+			keyCount = newKeyCount
+		}
 	}
 	return err
 }
@@ -978,8 +1081,14 @@ func (d *decodeState) parseMapToStruct(v reflect.Value, tInfo *typeInfo) error {
 	hasSize := (ai != 31)
 	count := int(val)
 	var err, lastErr error
-	for i := 0; (hasSize && i < count) || (!hasSize && !d.foundBreak()); i++ {
+	keyCount := 0
+	var mapKeys map[interface{}]struct{} // Store map keys, used for detecting duplicate map key.
+	if d.dm.dupMapKey == DupMapKeyEnforcedAPF {
+		mapKeys = make(map[interface{}]struct{}, len(structType.fields))
+	}
+	for j := 0; (hasSize && j < count) || (!hasSize && !d.foundBreak()); j++ {
 		var f *field
+		var k interface{} // Used by duplicate map key detection
 
 		t := d.nextCBORType()
 		if t == cborTypeTextString {
@@ -994,8 +1103,8 @@ func (d *decodeState) parseMapToStruct(v reflect.Value, tInfo *typeInfo) error {
 			}
 
 			keyLen := len(keyBytes)
+			// Find field with exact match
 			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
@@ -1003,10 +1112,10 @@ func (d *decodeState) parseMapToStruct(v reflect.Value, tInfo *typeInfo) error {
 					break
 				}
 			}
+			// Find field with case-insensitive match
 			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
@@ -1015,6 +1124,10 @@ func (d *decodeState) parseMapToStruct(v reflect.Value, tInfo *typeInfo) error {
 					}
 				}
 			}
+
+			if d.dm.dupMapKey == DupMapKeyEnforcedAPF {
+				k = string(keyBytes)
+			}
 		} else if t <= cborTypeNegativeInt { // uint/int
 			var nameAsInt int64
 
@@ -1037,6 +1150,7 @@ func (d *decodeState) parseMapToStruct(v reflect.Value, tInfo *typeInfo) error {
 				nameAsInt = int64(-1) ^ int64(val)
 			}
 
+			// Find field
 			for i := 0; i < len(structType.fields); i++ {
 				fld := structType.fields[i]
 				if !foundFldIdx[i] && fld.keyAsInt && fld.nameAsInt == nameAsInt {
@@ -1045,6 +1159,10 @@ func (d *decodeState) parseMapToStruct(v reflect.Value, tInfo *typeInfo) error {
 					break
 				}
 			}
+
+			if d.dm.dupMapKey == DupMapKeyEnforcedAPF {
+				k = nameAsInt
+			}
 		} else {
 			if err == nil {
 				err = &UnmarshalTypeError{
@@ -1053,13 +1171,46 @@ func (d *decodeState) parseMapToStruct(v reflect.Value, tInfo *typeInfo) error {
 					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
+			if d.dm.dupMapKey == DupMapKeyEnforcedAPF {
+				// parse key
+				k, lastErr = d.parse()
+				if lastErr != nil {
+					d.skip() // skip value
+					continue
+				}
+				// Detect if CBOR map key can be used as Go map key.
+				kkind := reflect.ValueOf(k).Kind()
+				if tag, ok := k.(Tag); ok {
+					kkind = tag.contentKind()
+				}
+				if !isHashableKind(kkind) {
+					d.skip() // skip value
+					continue
+				}
+			} else {
+				d.skip() // skip key
+			}
+		}
+
+		if d.dm.dupMapKey == DupMapKeyEnforcedAPF {
+			mapKeys[k] = struct{}{}
+			newKeyCount := len(mapKeys)
+			if newKeyCount == keyCount {
+				err = &DupMapKeyError{k, j}
+				d.skip() // skip value
+				j++
+				// skip the rest of the map
+				for ; (hasSize && j < count) || (!hasSize && !d.foundBreak()); j++ {
+					d.skip()
+					d.skip()
+				}
+				return err
+			}
+			keyCount = newKeyCount
 		}
 
 		if f == nil {
-			d.skip()
+			d.skip() // Skip value
 			continue
 		}
 		// reflect.Value.FieldByIndex() panics at nil pointer to unexported
@@ -1091,7 +1242,7 @@ func (d *decodeState) parseMapToStruct(v reflect.Value, tInfo *typeInfo) error {
 // 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)
+	tagNums := make([]uint64, 0, 2)
 	for d.nextCBORType() == cborTypeTag {
 		_, _, val := d.getHead()
 		tagNums = append(tagNums, val)
diff --git a/encode.go b/encode.go
index 3b712c7..50621d6 100644
--- a/encode.go
+++ b/encode.go
@@ -372,7 +372,7 @@ func (opts EncOptions) EncModeWithTags(tags TagSet) (EncMode, error) {
 	syncTags := tags.(*syncTagSet)
 	syncTags.RLock()
 	for contentType, tag := range syncTags.t {
-		if tag.opts.EncTag != EncTagIgnored {
+		if tag.opts.EncTag != EncTagNone {
 			ts[contentType] = tag
 		}
 	}
@@ -523,7 +523,6 @@ 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}
@@ -1042,11 +1041,7 @@ func encodeIntf(e *encodeState, em *encMode, v reflect.Value) error {
 func encodeTime(e *encodeState, em *encMode, v reflect.Value) error {
 	t := v.Interface().(time.Time)
 	if t.IsZero() {
-		if em.timeTag == EncTagIgnored {
-			e.Write(cborNil)
-			return nil
-		}
-		e.Write(cborUndefined)
+		e.Write(cborNil) // Even if tag is required, encode as CBOR null.
 		return nil
 	}
 	if em.timeTag == EncTagRequired {
diff --git a/tag.go b/tag.go
index 18bba47..a812182 100644
--- a/tag.go
+++ b/tag.go
@@ -91,8 +91,8 @@ func (dtm DecTagMode) valid() bool {
 type EncTagMode int
 
 const (
-	// EncTagIgnored makes encoder not encode tag number.
-	EncTagIgnored EncTagMode = iota
+	// EncTagNone makes encoder not encode tag number.
+	EncTagNone EncTagMode = iota
 
 	// EncTagRequired makes encoder encode tag number.
 	EncTagRequired
@@ -190,8 +190,8 @@ func (t *syncTagSet) get(typ reflect.Type) *tagItem {
 }
 
 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 opts.DecTag == DecTagIgnored && opts.EncTag == EncTagNone {
+		return nil, errors.New("cbor: cannot add tag with DecTagIgnored and EncTagNone 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())
@@ -208,8 +208,8 @@ func newTagItem(opts TagOptions, contentType reflect.Type, num uint64, nestedNum
 	if num == 0 || num == 1 {
 		return nil, errors.New("cbor: cannot add tag number 0 or 1 to TagSet, use EncOptions.TimeTag and DecOptions.TimeTag instead")
 	}
-	if 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(typeMarshaler) && opts.EncTag != EncTagNone {
+		return nil, errors.New("cbor: cannot add cbor.Marshaler to TagSet with EncTag != EncTagNone")
 	}
 	if reflect.PtrTo(contentType).Implements(typeUnmarshaler) && opts.DecTag != DecTagIgnored {
 		return nil, errors.New("cbor: cannot add cbor.Unmarshaler to TagSet with DecTag != DecTagIgnored")


Test patch:
diff --git a/bench_test.go b/bench_test.go
index 564dd89..cd36ee4 100644
--- a/bench_test.go
+++ b/bench_test.go
@@ -22,6 +22,20 @@ type claims struct {
 	Cti []byte `cbor:"7,keyasint"`
 }
 
+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
+}
+
 type coseKey struct {
 	Kty       int        `cbor:"1,keyasint,omitempty"`
 	Kid       []byte     `cbor:"2,keyasint,omitempty"`
@@ -494,6 +508,18 @@ func BenchmarkUnmarshalCWTClaims(b *testing.B) {
 	}
 }
 
+func BenchmarkUnmarshalCWTClaimsWithDupMapKeyOpt(b *testing.B) {
+	// Data from https://tools.ietf.org/html/rfc8392#appendix-A section A.1
+	cborData := hexDecode("a70175636f61703a2f2f61732e6578616d706c652e636f6d02656572696b77037818636f61703a2f2f6c696768742e6578616d706c652e636f6d041a5612aeb0051a5610d9f0061a5610d9f007420b71")
+	dm, _ := DecOptions{DupMapKey: DupMapKeyEnforcedAPF}.DecMode()
+	for i := 0; i < b.N; i++ {
+		var v claims
+		if err := dm.Unmarshal(cborData, &v); err != nil {
+			b.Fatal("Unmarshal:", err)
+		}
+	}
+}
+
 func BenchmarkMarshalCWTClaims(b *testing.B) {
 	// Data from https://tools.ietf.org/html/rfc8392#appendix-A section A.1
 	cborData := hexDecode("a70175636f61703a2f2f61732e6578616d706c652e636f6d02656572696b77037818636f61703a2f2f6c696768742e6578616d706c652e636f6d041a5612aeb0051a5610d9f0061a5610d9f007420b71")
@@ -572,3 +598,77 @@ func BenchmarkMarshalWebAuthn(b *testing.B) {
 		}
 	}
 }
+
+func BenchmarkUnmarshalCOSEMAC(b *testing.B) {
+	// Data from https://tools.ietf.org/html/rfc8392#appendix-A section A.4
+	cborData := hexDecode("d83dd18443a10104a1044c53796d6d65747269633235365850a70175636f61703a2f2f61732e6578616d706c652e636f6d02656572696b77037818636f61703a2f2f6c696768742e6578616d706c652e636f6d041a5612aeb0051a5610d9f0061a5610d9f007420b7148093101ef6d789200")
+
+	for i := 0; i < b.N; i++ {
+		var v macedCOSE
+		if err := Unmarshal(cborData, &v); err != nil {
+			b.Fatal("Unmarshal:", err)
+		}
+	}
+}
+
+func BenchmarkUnmarshalCOSEMACWithTag(b *testing.B) {
+	// Data from https://tools.ietf.org/html/rfc8392#appendix-A section A.4
+	cborData := hexDecode("d83dd18443a10104a1044c53796d6d65747269633235365850a70175636f61703a2f2f61732e6578616d706c652e636f6d02656572696b77037818636f61703a2f2f6c696768742e6578616d706c652e636f6d041a5612aeb0051a5610d9f0061a5610d9f007420b7148093101ef6d789200")
+
+	// 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}, reflect.TypeOf(macedCOSE{}), 61, 17); err != nil {
+		b.Fatal("TagSet.Add:", err)
+	}
+
+	// Create DecMode with tags
+	dm, _ := DecOptions{}.DecModeWithTags(tags)
+
+	for i := 0; i < b.N; i++ {
+		var v macedCOSE
+		if err := dm.Unmarshal(cborData, &v); err != nil {
+			b.Fatal("Unmarshal:", err)
+		}
+	}
+}
+func BenchmarkMarshalCOSEMAC(b *testing.B) {
+	// Data from https://tools.ietf.org/html/rfc8392#appendix-A section A.4
+	cborData := hexDecode("d83dd18443a10104a1044c53796d6d65747269633235365850a70175636f61703a2f2f61732e6578616d706c652e636f6d02656572696b77037818636f61703a2f2f6c696768742e6578616d706c652e636f6d041a5612aeb0051a5610d9f0061a5610d9f007420b7148093101ef6d789200")
+
+	var v macedCOSE
+	if err := Unmarshal(cborData, &v); err != nil {
+		b.Fatal("Unmarshal():", err)
+	}
+
+	for i := 0; i < b.N; i++ {
+		if _, err := Marshal(v); err != nil {
+			b.Fatal("Marshal():", v, err)
+		}
+	}
+}
+
+func BenchmarkMarshalCOSEMACWithTag(b *testing.B) {
+	// Data from https://tools.ietf.org/html/rfc8392#appendix-A section A.4
+	cborData := hexDecode("d83dd18443a10104a1044c53796d6d65747269633235365850a70175636f61703a2f2f61732e6578616d706c652e636f6d02656572696b77037818636f61703a2f2f6c696768742e6578616d706c652e636f6d041a5612aeb0051a5610d9f0061a5610d9f007420b7148093101ef6d789200")
+
+	// 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}, reflect.TypeOf(macedCOSE{}), 61, 17); err != nil {
+		b.Fatal("TagSet.Add:", err)
+	}
+
+	// Create EncMode with tags.
+	dm, _ := DecOptions{}.DecModeWithTags(tags)
+	em, _ := EncOptions{}.EncModeWithTags(tags)
+
+	var v macedCOSE
+	if err := dm.Unmarshal(cborData, &v); err != nil {
+		b.Fatal("Unmarshal():", err)
+	}
+
+	for i := 0; i < b.N; i++ {
+		if _, err := em.Marshal(v); err != nil {
+			b.Fatal("Marshal():", v, err)
+		}
+	}
+}
diff --git a/decode_test.go b/decode_test.go
index ec5fc01..cc4a7ab 100644
--- a/decode_test.go
+++ b/decode_test.go
@@ -9,6 +9,7 @@ import (
 	"encoding/hex"
 	"errors"
 	"fmt"
+	"io"
 	"math"
 	"reflect"
 	"strings"
@@ -2852,26 +2853,8 @@ func TestUnmarshalToNotNilInterface(t *testing.T) {
 	}
 }
 
-func TestDuplicateMapKeys(t *testing.T) {
-	cborData := hexDecode("a6616161416162614261636143616461446165614561636146") // map with duplicate key "c": {"a": "A", "b": "B", "c": "C", "d": "D", "e": "E", "c": "F"}
-	wantV := map[interface{}]interface{}{"a": "A", "b": "B", "c": "F", "d": "D", "e": "E"}
-	wantM := map[string]string{"a": "A", "b": "B", "c": "F", "d": "D", "e": "E"}
-	var v interface{}
-	if err := Unmarshal(cborData, &v); err != nil {
-		t.Errorf("Unmarshal(0x%x) returned error %v", cborData, err)
-	} else if !reflect.DeepEqual(v, wantV) {
-		t.Errorf("Unmarshal(0x%x) = %v (%T), want %v (%T)", cborData, v, v, wantV, wantV)
-	}
-	var m map[string]string
-	if err := Unmarshal(cborData, &m); err != nil {
-		t.Errorf("Unmarshal(0x%x) returned error %v", cborData, err)
-	} else if !reflect.DeepEqual(m, wantM) {
-		t.Errorf("Unmarshal(0x%x) = %v (%T), want %v (%T)", cborData, m, m, wantM, wantM)
-	}
-}
-
 func TestDecOptions(t *testing.T) {
-	opts1 := DecOptions{TimeTag: DecTagRequired}
+	opts1 := DecOptions{TimeTag: DecTagRequired, DupMapKey: DupMapKeyEnforcedAPF}
 	dm, err := opts1.DecMode()
 	if err != nil {
 		t.Errorf("DecMode() returned an error %v", err)
@@ -2920,6 +2903,16 @@ func TestDecModeInvalidTimeTag(t *testing.T) {
 	}
 }
 
+func TestDecModeInvalidDuplicateMapKey(t *testing.T) {
+	wantErrorMsg := "cbor: invalid DupMapKey 101"
+	_, err := DecOptions{DupMapKey: 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"`
@@ -2958,3 +2951,925 @@ func TestUnmarshalStructKeyAsIntNumError(t *testing.T) {
 		})
 	}
 }
+
+func TestUnmarshalEmptyMapWithDupMapKeyOpt(t *testing.T) {
+	testCases := []struct {
+		name     string
+		cborData []byte
+		wantV    interface{}
+	}{
+		{
+			name:     "empty map",
+			cborData: hexDecode("a0"),
+			wantV:    map[interface{}]interface{}{},
+		},
+		{
+			name:     "indefinite empty map",
+			cborData: hexDecode("bfff"),
+			wantV:    map[interface{}]interface{}{},
+		},
+	}
+
+	dm, err := DecOptions{DupMapKey: DupMapKeyEnforcedAPF}.DecMode()
+	if err != nil {
+		t.Errorf("DecMode() returned error %v", err)
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			var v interface{}
+			if err := dm.Unmarshal(tc.cborData, &v); err != nil {
+				t.Errorf("Unmarshal(0x%x) returned error %v", tc.cborData, err)
+			}
+			if !reflect.DeepEqual(v, tc.wantV) {
+				t.Errorf("Unmarshal(0x%x) = %v (%T), want %v (%T)", tc.cborData, v, v, tc.wantV, tc.wantV)
+			}
+		})
+	}
+}
+
+func TestUnmarshalDupMapKeyToEmptyInterface(t *testing.T) {
+	cborData := hexDecode("a6616161416162614261636143616161466164614461656145") // {"a": "A", "b": "B", "c": "C", "a": "F", "d": "D", "e": "E"}
+
+	// Duplicate key overwrites previous value (default).
+	wantV := map[interface{}]interface{}{"a": "F", "b": "B", "c": "C", "d": "D", "e": "E"}
+	var v interface{}
+	if err := 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)
+	}
+
+	// Duplicate key triggers error.
+	wantV = map[interface{}]interface{}{"a": nil, "b": "B", "c": "C"}
+	wantErrorMsg := "cbor: found duplicate map key \"a\" at map element index 3"
+	dm, _ := DecOptions{DupMapKey: DupMapKeyEnforcedAPF}.DecMode()
+	var v2 interface{}
+	if err := dm.Unmarshal(cborData, &v2); err == nil {
+		t.Errorf("Unmarshal(0x%x) didn't return an error", cborData)
+	} else if _, ok := err.(*DupMapKeyError); !ok {
+		t.Errorf("Unmarshal(0x%x) returned wrong error type %T, want (*DupMapKeyError)", cborData, err)
+	} else if err.Error() != wantErrorMsg {
+		t.Errorf("Unmarshal(0x%x) returned error %q, want error containing %q", cborData, err.Error(), wantErrorMsg)
+	}
+	if !reflect.DeepEqual(v2, wantV) {
+		t.Errorf("Unmarshal(0x%x) = %v (%T), want %v (%T)", cborData, v2, v2, wantV, wantV)
+	}
+}
+
+func TestStreamDupMapKeyToEmptyInterface(t *testing.T) {
+	cborData := hexDecode("a6616161416162614261636143616161466164614461656145") // map with duplicate key "c": {"a": "A", "b": "B", "c": "C", "a": "F", "d": "D", "e": "E"}
+
+	var b []byte
+	for i := 0; i < 3; i++ {
+		b = append(b, cborData...)
+	}
+
+	// Duplicate key overwrites previous value (default).
+	wantV := map[interface{}]interface{}{"a": "F", "b": "B", "c": "C", "d": "D", "e": "E"}
+	dec := NewDecoder(bytes.NewReader(b))
+	for i := 0; i < 3; i++ {
+		var v1 interface{}
+		if err := dec.Decode(&v1); err != nil {
+			t.Errorf("Decode() returned error %v", err)
+		}
+		if !reflect.DeepEqual(v1, wantV) {
+			t.Errorf("Decode() = %v (%T), want %v (%T)", v1, v1, wantV, wantV)
+		}
+	}
+	var v interface{}
+	if err := dec.Decode(&v); err != io.EOF {
+		t.Errorf("Decode() returned error %v, want %v", err, io.EOF)
+	}
+
+	// Duplicate key triggers error.
+	wantV = map[interface{}]interface{}{"a": nil, "b": "B", "c": "C"}
+	wantErrorMsg := "cbor: found duplicate map key \"a\" at map element index 3"
+	dm, _ := DecOptions{DupMapKey: DupMapKeyEnforcedAPF}.DecMode()
+	dec = dm.NewDecoder(bytes.NewReader(b))
+	for i := 0; i < 3; i++ {
+		var v2 interface{}
+		if err := dec.Decode(&v2); err == nil {
+			t.Errorf("Decode() didn't return an error")
+		} else if _, ok := err.(*DupMapKeyError); !ok {
+			t.Errorf("Decode() returned wrong error type %T, want (*DupMapKeyError)", err)
+		} else if err.Error() != wantErrorMsg {
+			t.Errorf("Decode() returned error %q, want error containing %q", err.Error(), wantErrorMsg)
+		}
+		if !reflect.DeepEqual(v2, wantV) {
+			t.Errorf("Unmarshal(0x%x) = %v (%T), want %v (%T)", cborData, v2, v2, wantV, wantV)
+		}
+	}
+	if err := dec.Decode(&v); err != io.EOF {
+		t.Errorf("Decode() returned error %v, want %v", err, io.EOF)
+	}
+}
+
+func TestUnmarshalDupMapKeyToEmptyMap(t *testing.T) {
+	cborData := hexDecode("a6616161416162614261636143616161466164614461656145") // {"a": "A", "b": "B", "c": "C", "a": "F", "d": "D", "e": "E"}
+
+	// Duplicate key overwrites previous value (default).
+	wantM := map[string]string{"a": "F", "b": "B", "c": "C", "d": "D", "e": "E"}
+	var m map[string]string
+	if err := Unmarshal(cborData, &m); err != nil {
+		t.Errorf("Unmarshal(0x%x) returned error %v", cborData, err)
+	}
+	if !reflect.DeepEqual(m, wantM) {
+		t.Errorf("Unmarshal(0x%x) = %v (%T), want %v (%T)", cborData, m, m, wantM, wantM)
+	}
+
+	// Duplicate key triggers error.
+	wantM = map[string]string{"a": "", "b": "B", "c": "C"}
+	wantErrorMsg := "cbor: found duplicate map key \"a\" at map element index 3"
+	dm, _ := DecOptions{DupMapKey: DupMapKeyEnforcedAPF}.DecMode()
+	var m2 map[string]string
+	if err := dm.Unmarshal(cborData, &m2); err == nil {
+		t.Errorf("Unmarshal(0x%x) didn't return an error", cborData)
+	} else if _, ok := err.(*DupMapKeyError); !ok {
+		t.Errorf("Unmarshal(0x%x) returned wrong error type %T, want (*DupMapKeyError)", cborData, err)
+	} else if err.Error() != wantErrorMsg {
+		t.Errorf("Unmarshal(0x%x) returned error %q, want error containing %q", cborData, err.Error(), wantErrorMsg)
+	}
+	if !reflect.DeepEqual(m2, wantM) {
+		t.Errorf("Unmarshal(0x%x) = %v (%T), want %v (%T)", cborData, m2, m2, wantM, wantM)
+	}
+}
+
+func TestStreamDupMapKeyToEmptyMap(t *testing.T) {
+	cborData := hexDecode("a6616161416162614261636143616161466164614461656145") // {"a": "A", "b": "B", "c": "C", "a": "F", "d": "D", "e": "E"}
+
+	var b []byte
+	for i := 0; i < 3; i++ {
+		b = append(b, cborData...)
+	}
+
+	// Duplicate key overwrites previous value (default).
+	wantM := map[string]string{"a": "F", "b": "B", "c": "C", "d": "D", "e": "E"}
+	dec := NewDecoder(bytes.NewReader(b))
+	for i := 0; i < 3; i++ {
+		var m1 map[string]string
+		if err := dec.Decode(&m1); err != nil {
+			t.Errorf("Decode() returned error %v", err)
+		}
+		if !reflect.DeepEqual(m1, wantM) {
+			t.Errorf("Decode() = %v (%T), want %v (%T)", m1, m1, wantM, wantM)
+		}
+	}
+	var v interface{}
+	if err := dec.Decode(&v); err != io.EOF {
+		t.Errorf("Decode() returned error %v, want %v", err, io.EOF)
+	}
+
+	// Duplicate key triggers error.
+	wantM = map[string]string{"a": "", "b": "B", "c": "C"}
+	wantErrorMsg := "cbor: found duplicate map key \"a\" at map element index 3"
+	dm, _ := DecOptions{DupMapKey: DupMapKeyEnforcedAPF}.DecMode()
+	dec = dm.NewDecoder(bytes.NewReader(b))
+	for i := 0; i < 3; i++ {
+		var m2 map[string]string
+		if err := dec.Decode(&m2); err == nil {
+			t.Errorf("Decode() didn't return an error")
+		} else if _, ok := err.(*DupMapKeyError); !ok {
+			t.Errorf("Decode() returned wrong error type %T, want (*DupMapKeyError)", err)
+		} else if err.Error() != wantErrorMsg {
+			t.Errorf("Decode() returned error %q, want error containing %q", err.Error(), wantErrorMsg)
+		}
+		if !reflect.DeepEqual(m2, wantM) {
+			t.Errorf("Unmarshal(0x%x) = %v (%T), want %v (%T)", cborData, m2, m2, wantM, wantM)
+		}
+	}
+	if err := dec.Decode(&v); err != io.EOF {
+		t.Errorf("Decode() returned error %v, want %v", err, io.EOF)
+	}
+}
+
+func TestUnmarshalDupMapKeyToNotEmptyMap(t *testing.T) {
+	cborData := hexDecode("a6616161416162614261636143616161466164614461656145") // {"a": "A", "b": "B", "c": "C", "a": "F", "d": "D", "e": "E"}
+
+	// Duplicate key overwrites previous value (default).
+	m := map[string]string{"a": "Z", "b": "Z", "c": "Z", "d": "Z", "e": "Z", "f": "Z"}
+	wantM := map[string]string{"a": "F", "b": "B", "c": "C", "d": "D", "e": "E", "f": "Z"}
+	if err := Unmarshal(cborData, &m); err != nil {
+		t.Errorf("Unmarshal(0x%x) returned error %v", cborData, err)
+	}
+	if !reflect.DeepEqual(m, wantM) {
+		t.Errorf("Unmarshal(0x%x) = %v (%T), want %v (%T)", cborData, m, m, wantM, wantM)
+	}
+
+	// Duplicate key triggers error.
+	m2 := map[string]string{"a": "Z", "b": "Z", "c": "Z", "d": "Z", "e": "Z", "f": "Z"}
+	wantM = map[string]string{"a": "", "b": "B", "c": "C", "d": "Z", "e": "Z", "f": "Z"}
+	wantErrorMsg := "cbor: found duplicate map key \"a\" at map element index 3"
+	dm, _ := DecOptions{DupMapKey: DupMapKeyEnforcedAPF}.DecMode()
+	if err := dm.Unmarshal(cborData, &m2); err == nil {
+		t.Errorf("Unmarshal(0x%x) didn't return an error", cborData)
+	} else if _, ok := err.(*DupMapKeyError); !ok {
+		t.Errorf("Unmarshal(0x%x) returned wrong error type %T, want (*DupMapKeyError)", cborData, err)
+	} else if !strings.Contains(err.Error(), wantErrorMsg) {
+		t.Errorf("Unmarshal(0x%x) returned error %q, want error containing %q", cborData, err.Error(), wantErrorMsg)
+	}
+	if !reflect.DeepEqual(m2, wantM) {
+		t.Errorf("Unmarshal(0x%x) = %v (%T), want %v (%T)", cborData, m2, m2, wantM, wantM)
+	}
+}
+
+func TestStreamDupMapKeyToNotEmptyMap(t *testing.T) {
+	cborData := hexDecode("a6616161416162614261636143616161466164614461656145") // {"a": "A", "b": "B", "c": "C", "a": "F", "d": "D", "e": "E"}
+
+	var b []byte
+	for i := 0; i < 3; i++ {
+		b = append(b, cborData...)
+	}
+
+	// Duplicate key overwrites previous value (default).
+	wantM := map[string]string{"a": "F", "b": "B", "c": "C", "d": "D", "e": "E", "f": "Z"}
+	dec := NewDecoder(bytes.NewReader(b))
+	for i := 0; i < 3; i++ {
+		m1 := map[string]string{"a": "Z", "b": "Z", "c": "Z", "d": "Z", "e": "Z", "f": "Z"}
+		if err := dec.Decode(&m1); err != nil {
+			t.Errorf("Decode() returned error %v", err)
+		}
+		if !reflect.DeepEqual(m1, wantM) {
+			t.Errorf("Decode() = %v (%T), want %v (%T)", m1, m1, wantM, wantM)
+		}
+	}
+	var v interface{}
+	if err := dec.Decode(&v); err != io.EOF {
+		t.Errorf("Decode() returned error %v, want %v", err, io.EOF)
+	}
+
+	// Duplicate key triggers error.
+	wantM = map[string]string{"a": "", "b": "B", "c": "C", "d": "Z", "e": "Z", "f": "Z"}
+	wantErrorMsg := "cbor: found duplicate map key \"a\" at map element index 3"
+	dm, _ := DecOptions{DupMapKey: DupMapKeyEnforcedAPF}.DecMode()
+	dec = dm.NewDecoder(bytes.NewReader(b))
+	for i := 0; i < 3; i++ {
+		m2 := map[string]string{"a": "Z", "b": "Z", "c": "Z", "d": "Z", "e": "Z", "f": "Z"}
+		if err := dec.Decode(&m2); err == nil {
+			t.Errorf("Decode() didn't return an error")
+		} else if _, ok := err.(*DupMapKeyError); !ok {
+			t.Errorf("Decode() returned wrong error type %T, want (*DupMapKeyError)", err)
+		} else if err.Error() != wantErrorMsg {
+			t.Errorf("Decode() returned error %q, want error containing %q", err.Error(), wantErrorMsg)
+		}
+		if !reflect.DeepEqual(m2, wantM) {
+			t.Errorf("Unmarshal(0x%x) = %v (%T), want %v (%T)", cborData, m2, m2, wantM, wantM)
+		}
+	}
+	if err := dec.Decode(&v); err != io.EOF {
+		t.Errorf("Decode() returned error %v, want %v", err, io.EOF)
+	}
+}
+
+func TestUnmarshalDupMapKeyToStruct(t *testing.T) {
+	type s struct {
+		A string `cbor:"a"`
+		B string `cbor:"b"`
+		C string `cbor:"c"`
+		D string `cbor:"d"`
+		E string `cbor:"e"`
+	}
+	cborData := hexDecode("a6616161416162614261636143616161466164614461656145") // {"a": "A", "b": "B", "c": "C", "a": "F", "d": "D", "e": "E"}
+
+	// Duplicate key doesn't overwrite previous value (default).
+	wantS := s{A: "A", B: "B", C: "C", D: "D", E: "E"}
+	var s1 s
+	if err := Unmarshal(cborData, &s1); err != nil {
+		t.Errorf("Unmarshal(0x%x) returned error %v", cborData, err)
+	}
+	if !reflect.DeepEqual(s1, wantS) {
+		t.Errorf("Unmarshal(0x%x) = %+v (%T), want %+v (%T)", cborData, s1, s1, wantS, wantS)
+	}
+
+	// Duplicate key triggers error.
+	wantS = s{A: "A", B: "B", C: "C"}
+	wantErrorMsg := "cbor: found duplicate map key \"a\" at map element index 3"
+	dm, _ := DecOptions{DupMapKey: DupMapKeyEnforcedAPF}.DecMode()
+	var s2 s
+	if err := dm.Unmarshal(cborData, &s2); err == nil {
+		t.Errorf("Unmarshal(0x%x, %s) didn't return an error", cborData, reflect.TypeOf(s2))
+	} else if _, ok := err.(*DupMapKeyError); !ok {
+		t.Errorf("Unmarshal(0x%x) returned wrong error type %T, want (*DupMapKeyError)", cborData, err)
+	} else if !strings.Contains(err.Error(), wantErrorMsg) {
+		t.Errorf("Unmarshal(0x%x) returned error %q, want error containing %q", cborData, err.Error(), wantErrorMsg)
+	}
+	if !reflect.DeepEqual(s2, wantS) {
+		t.Errorf("Unmarshal(0x%x) = %+v (%T), want %+v (%T)", cborData, s2, s2, wantS, wantS)
+	}
+}
+
+func TestStreamDupMapKeyToStruct(t *testing.T) {
+	type s struct {
+		A string `cbor:"a"`
+		B string `cbor:"b"`
+		C string `cbor:"c"`
+		D string `cbor:"d"`
+		E string `cbor:"e"`
+	}
+	cborData := hexDecode("a6616161416162614261636143616161466164614461656145") // {"a": "A", "b": "B", "c": "C", "a": "F", "d": "D", "e": "E"}
+
+	var b []byte
+	for i := 0; i < 3; i++ {
+		b = append(b, cborData...)
+	}
+
+	// Duplicate key overwrites previous value (default).
+	wantS := s{A: "A", B: "B", C: "C", D: "D", E: "E"}
+	dec := NewDecoder(bytes.NewReader(b))
+	for i := 0; i < 3; i++ {
+		var s1 s
+		if err := dec.Decode(&s1); err != nil {
+			t.Errorf("Decode() returned error %v", err)
+		}
+		if !reflect.DeepEqual(s1, wantS) {
+			t.Errorf("Decode() = %v (%T), want %v (%T)", s1, s1, wantS, wantS)
+		}
+	}
+	var v interface{}
+	if err := dec.Decode(&v); err != io.EOF {
+		t.Errorf("Decode() returned error %v, want %v", err, io.EOF)
+	}
+
+	// Duplicate key triggers error.
+	wantS = s{A: "A", B: "B", C: "C"}
+	wantErrorMsg := "cbor: found duplicate map key \"a\" at map element index 3"
+	dm, _ := DecOptions{DupMapKey: DupMapKeyEnforcedAPF}.DecMode()
+	dec = dm.NewDecoder(bytes.NewReader(b))
+	for i := 0; i < 3; i++ {
+		var s2 s
+		if err := dec.Decode(&s2); err == nil {
+			t.Errorf("Decode() didn't return an error")
+		} else if _, ok := err.(*DupMapKeyError); !ok {
+			t.Errorf("Decode() returned wrong error type %T, want (*DupMapKeyError)", err)
+		} else if err.Error() != wantErrorMsg {
+			t.Errorf("Decode() returned error %q, want error containing %q", err.Error(), wantErrorMsg)
+		}
+		if !reflect.DeepEqual(s2, wantS) {
+			t.Errorf("Unmarshal(0x%x) = %+v (%T), want %+v (%T)", cborData, s2, s2, wantS, wantS)
+		}
+	}
+	if err := dec.Decode(&v); err != io.EOF {
+		t.Errorf("Decode() returned error %v, want %v", err, io.EOF)
+	}
+}
+
+// dupl map key is a struct field
+func TestUnmarshalDupMapKeyToStructKeyAsInt(t *testing.T) {
+	type s struct {
+		A int `cbor:"1,keyasint"`
+		B int `cbor:"3,keyasint"`
+		C int `cbor:"5,keyasint"`
+	}
+	cborData := hexDecode("a40102030401030506") // {1:2, 3:4, 1:3, 5:6}
+
+	// Duplicate key doesn't overwrite previous value (default).
+	wantS := s{A: 2, B: 4, C: 6}
+	var s1 s
+	if err := Unmarshal(cborData, &s1); err != nil {
+		t.Errorf("Unmarshal(0x%x) returned error %v", cborData, err)
+	}
+	if !reflect.DeepEqual(s1, wantS) {
+		t.Errorf("Unmarshal(0x%x) = %+v (%T), want %+v (%T)", cborData, s1, s1, wantS, wantS)
+	}
+
+	// Duplicate key triggers error.
+	wantS = s{A: 2, B: 4}
+	wantErrorMsg := "cbor: found duplicate map key \"1\" at map element index 2"
+	dm, _ := DecOptions{DupMapKey: DupMapKeyEnforcedAPF}.DecMode()
+	var s2 s
+	if err := dm.Unmarshal(cborData, &s2); err == nil {
+		t.Errorf("Unmarshal(0x%x, %s) didn't return an error", cborData, reflect.TypeOf(s2))
+	} else if _, ok := err.(*DupMapKeyError); !ok {
+		t.Errorf("Unmarshal(0x%x) returned wrong error type %T, want (*DupMapKeyError)", cborData, err)
+	} else if !strings.Contains(err.Error(), wantErrorMsg) {
+		t.Errorf("Unmarshal(0x%x) returned error %q, want error containing %q", cborData, err.Error(), wantErrorMsg)
+	}
+	if !reflect.DeepEqual(s2, wantS) {
+		t.Errorf("Unmarshal(0x%x) = %+v (%T), want %+v (%T)", cborData, s2, s2, wantS, wantS)
+	}
+}
+
+func TestStreamDupMapKeyToStructKeyAsInt(t *testing.T) {
+	type s struct {
+		A int `cbor:"1,keyasint"`
+		B int `cbor:"3,keyasint"`
+		C int `cbor:"5,keyasint"`
+	}
+	cborData := hexDecode("a40102030401030506") // {1:2, 3:4, 1:3, 5:6}
+
+	var b []byte
+	for i := 0; i < 3; i++ {
+		b = append(b, cborData...)
+	}
+
+	// Duplicate key overwrites previous value (default).
+	wantS := s{A: 2, B: 4, C: 6}
+	dec := NewDecoder(bytes.NewReader(b))
+	for i := 0; i < 3; i++ {
+		var s1 s
+		if err := dec.Decode(&s1); err != nil {
+			t.Errorf("Decode() returned error %v", err)
+		}
+		if !reflect.DeepEqual(s1, wantS) {
+			t.Errorf("Decode() = %v (%T), want %v (%T)", s1, s1, wantS, wantS)
+		}
+	}
+	var v interface{}
+	if err := dec.Decode(&v); err != io.EOF {
+		t.Errorf("Decode() returned error %v, want %v", err, io.EOF)
+	}
+
+	// Duplicate key triggers error.
+	wantS = s{A: 2, B: 4}
+	wantErrorMsg := "cbor: found duplicate map key \"1\" at map element index 2"
+	dm, _ := DecOptions{DupMapKey: DupMapKeyEnforcedAPF}.DecMode()
+	dec = dm.NewDecoder(bytes.NewReader(b))
+	for i := 0; i < 3; i++ {
+		var s2 s
+		if err := dec.Decode(&s2); err == nil {
+			t.Errorf("Decode() didn't return an error")
+		} else if _, ok := err.(*DupMapKeyError); !ok {
+			t.Errorf("Decode() returned wrong error type %T, want (*DupMapKeyError)", err)
+		} else if err.Error() != wantErrorMsg {
+			t.Errorf("Decode() returned error %q, want error containing %q", err.Error(), wantErrorMsg)
+		}
+		if !reflect.DeepEqual(s2, wantS) {
+			t.Errorf("Unmarshal(0x%x) = %+v (%T), want %+v (%T)", cborData, s2, s2, wantS, wantS)
+		}
+	}
+	if err := dec.Decode(&v); err != io.EOF {
+		t.Errorf("Decode() returned error %v, want %v", err, io.EOF)
+	}
+}
+
+func TestUnmarshalDupMapKeyToStructNoMatchingField(t *testing.T) {
+	type s struct {
+		B string `cbor:"b"`
+		C string `cbor:"c"`
+		D string `cbor:"d"`
+		E string `cbor:"e"`
+	}
+	cborData := hexDecode("a6616161416162614261636143616161466164614461656145") // {"a": "A", "b": "B", "c": "C", "a": "F", "d": "D", "e": "E"}
+
+	wantS := s{B: "B", C: "C", D: "D", E: "E"}
+	var s1 s
+	if err := Unmarshal(cborData, &s1); err != nil {
+		t.Errorf("Unmarshal(0x%x) returned error %v", cborData, err)
+	}
+	if !reflect.DeepEqual(s1, wantS) {
+		t.Errorf("Unmarshal(0x%x) = %+v (%T), want %+v (%T)", cborData, s1, s1, wantS, wantS)
+	}
+
+	// Duplicate key triggers error even though map key "a" doesn't have a corresponding struct field.
+	wantS = s{B: "B", C: "C"}
+	wantErrorMsg := "cbor: found duplicate map key \"a\" at map element index 3"
+	dm, _ := DecOptions{DupMapKey: DupMapKeyEnforcedAPF}.DecMode()
+	var s2 s
+	if err := dm.Unmarshal(cborData, &s2); err == nil {
+		t.Errorf("Unmarshal(0x%x, %s) didn't return an error", cborData, reflect.TypeOf(s2))
+	} else if _, ok := err.(*DupMapKeyError); !ok {
+		t.Errorf("Unmarshal(0x%x) returned wrong error type %T, want (*DupMapKeyError)", cborData, err)
+	} else if !strings.Contains(err.Error(), wantErrorMsg) {
+		t.Errorf("Unmarshal(0x%x) returned error %q, want error containing %q", cborData, err.Error(), wantErrorMsg)
+	}
+	if !reflect.DeepEqual(s2, wantS) {
+		t.Errorf("Unmarshal(0x%x) = %+v (%T), want %+v (%T)", cborData, s2, s2, wantS, wantS)
+	}
+}
+
+func TestStreamDupMapKeyToStructNoMatchingField(t *testing.T) {
+	type s struct {
+		B string `cbor:"b"`
+		C string `cbor:"c"`
+		D string `cbor:"d"`
+		E string `cbor:"e"`
+	}
+	cborData := hexDecode("a6616161416162614261636143616161466164614461656145") // {"a": "A", "b": "B", "c": "C", "a": "F", "d": "D", "e": "E"}
+
+	var b []byte
+	for i := 0; i < 3; i++ {
+		b = append(b, cborData...)
+	}
+
+	// Duplicate key overwrites previous value (default).
+	wantS := s{B: "B", C: "C", D: "D", E: "E"}
+	dec := NewDecoder(bytes.NewReader(b))
+	for i := 0; i < 3; i++ {
+		var s1 s
+		if err := dec.Decode(&s1); err != nil {
+			t.Errorf("Decode() returned error %v", err)
+		}
+		if !reflect.DeepEqual(s1, wantS) {
+			t.Errorf("Decode() = %v (%T), want %v (%T)", s1, s1, wantS, wantS)
+		}
+	}
+	var v interface{}
+	if err := dec.Decode(&v); err != io.EOF {
+		t.Errorf("Decode() returned error %v, want %v", err, io.EOF)
+	}
+
+	// Duplicate key triggers error.
+	wantS = s{B: "B", C: "C"}
+	wantErrorMsg := "cbor: found duplicate map key \"a\" at map element index 3"
+	dm, _ := DecOptions{DupMapKey: DupMapKeyEnforcedAPF}.DecMode()
+	dec = dm.NewDecoder(bytes.NewReader(b))
+	for i := 0; i < 3; i++ {
+		var s2 s
+		if err := dec.Decode(&s2); err == nil {
+			t.Errorf("Decode() didn't return an error")
+		} else if _, ok := err.(*DupMapKeyError); !ok {
+			t.Errorf("Decode() returned wrong error type %T, want (*DupMapKeyError)", err)
+		} else if err.Error() != wantErrorMsg {
+			t.Errorf("Decode() returned error %q, want error containing %q", err.Error(), wantErrorMsg)
+		}
+		if !reflect.DeepEqual(s2, wantS) {
+			t.Errorf("Decode() = %v (%T), want %v (%T)", s2, s2, wantS, wantS)
+		}
+	}
+	if err := dec.Decode(&v); err != io.EOF {
+		t.Errorf("Decode() returned error %v, want %v", err, io.EOF)
+	}
+}
+
+func TestUnmarshalDupMapKeyToStructKeyAsIntNoMatchingField(t *testing.T) {
+	type s struct {
+		B int `cbor:"3,keyasint"`
+		C int `cbor:"5,keyasint"`
+	}
+	cborData := hexDecode("a40102030401030506") // {1:2, 3:4, 1:3, 5:6}
+
+	wantS := s{B: 4, C: 6}
+	var s1 s
+	if err := Unmarshal(cborData, &s1); err != nil {
+		t.Errorf("Unmarshal(0x%x) returned error %v", cborData, err)
+	}
+	if !reflect.DeepEqual(s1, wantS) {
+		t.Errorf("Unmarshal(0x%x) = %+v (%T), want %+v (%T)", cborData, s1, s1, wantS, wantS)
+	}
+
+	// Duplicate key triggers error even though map key "a" doesn't have a corresponding struct field.
+	wantS = s{B: 4}
+	wantErrorMsg := "cbor: found duplicate map key \"1\" at map element index 2"
+	dm, _ := DecOptions{DupMapKey: DupMapKeyEnforcedAPF}.DecMode()
+	var s2 s
+	if err := dm.Unmarshal(cborData, &s2); err == nil {
+		t.Errorf("Unmarshal(0x%x, %s) didn't return an error", cborData, reflect.TypeOf(s2))
+	} else if _, ok := err.(*DupMapKeyError); !ok {
+		t.Errorf("Unmarshal(0x%x) returned wrong error type %T, want (*DupMapKeyError)", cborData, err)
+	} else if !strings.Contains(err.Error(), wantErrorMsg) {
+		t.Errorf("Unmarshal(0x%x) returned error %q, want error containing %q", cborData, err.Error(), wantErrorMsg)
+	}
+	if !reflect.DeepEqual(s2, wantS) {
+		t.Errorf("Unmarshal(0x%x) = %+v (%T), want %+v (%T)", cborData, s2, s2, wantS, wantS)
+	}
+}
+
+func TestStreamDupMapKeyToStructKeyAsIntNoMatchingField(t *testing.T) {
+	type s struct {
+		B int `cbor:"3,keyasint"`
+		C int `cbor:"5,keyasint"`
+	}
+	cborData := hexDecode("a40102030401030506") // {1:2, 3:4, 1:3, 5:6}
+
+	var b []byte
+	for i := 0; i < 3; i++ {
+		b = append(b, cborData...)
+	}
+
+	// Duplicate key overwrites previous value (default).
+	wantS := s{B: 4, C: 6}
+	dec := NewDecoder(bytes.NewReader(b))
+	for i := 0; i < 3; i++ {
+		var s1 s
+		if err := dec.Decode(&s1); err != nil {
+			t.Errorf("Decode() returned error %v", err)
+		}
+		if !reflect.DeepEqual(s1, wantS) {
+			t.Errorf("Decode() = %v (%T), want %v (%T)", s1, s1, wantS, wantS)
+		}
+	}
+	var v interface{}
+	if err := dec.Decode(&v); err != io.EOF {
+		t.Errorf("Decode() returned error %v, want %v", err, io.EOF)
+	}
+
+	// Duplicate key triggers error.
+	wantS = s{B: 4}
+	wantErrorMsg := "cbor: found duplicate map key \"1\" at map element index 2"
+	dm, _ := DecOptions{DupMapKey: DupMapKeyEnforcedAPF}.DecMode()
+	dec = dm.NewDecoder(bytes.NewReader(b))
+	for i := 0; i < 3; i++ {
+		var s2 s
+		if err := dec.Decode(&s2); err == nil {
+			t.Errorf("Decode() didn't return an error")
+		} else if _, ok := err.(*DupMapKeyError); !ok {
+			t.Errorf("Decode() returned wrong error type %T, want (*DupMapKeyError)", err)
+		} else if err.Error() != wantErrorMsg {
+			t.Errorf("Decode() returned error %q, want error containing %q", err.Error(), wantErrorMsg)
+		}
+		if !reflect.DeepEqual(s2, wantS) {
+			t.Errorf("Decode() = %v (%T), want %v (%T)", s2, s2, wantS, wantS)
+		}
+	}
+	if err := dec.Decode(&v); err != io.EOF {
+		t.Errorf("Decode() returned error %v, want %v", err, io.EOF)
+	}
+}
+
+func TestUnmarshalDupMapKeyToStructWrongType(t *testing.T) {
+	type s struct {
+		A string `cbor:"a"`
+		B string `cbor:"b"`
+		C string `cbor:"c"`
+		D string `cbor:"d"`
+		E string `cbor:"e"`
+	}
+	cborData := hexDecode("a861616141fa47c35000026162614261636143fa47c3500003616161466164614461656145") // {"a": "A", 100000.0:2, "b": "B", "c": "C", 100000.0:3, "a": "F", "d": "D", "e": "E"}
+
+	var s1 s
+	wantS := s{A: "A", B: "B", C: "C", D: "D", E: "E"}
+	wantErrorMsg := "cbor: cannot unmarshal"
+	if err := Unmarshal(cborData, &s1); err == nil {
+		t.Errorf("Unmarshal(0x%x) didn't return an error", cborData)
+	} else if _, ok := err.(*UnmarshalTypeError); !ok {
+		t.Errorf("Unmarshal(0x%x) returned wrong error type %T, want (*UnmarshalTypeError)", cborData, err)
+	} else if !strings.Contains(err.Error(), wantErrorMsg) {
+		t.Errorf("Unmarshal(0x%x) returned error %q, want error containing %q", cborData, err.Error(), wantErrorMsg)
+	}
+	if !reflect.DeepEqual(s1, wantS) {
+		t.Errorf("Unmarshal(0x%x) = %+v (%T), want %+v (%T)", cborData, s1, s1, wantS, wantS)
+	}
+
+	wantS = s{A: "A", B: "B", C: "C"}
+	wantErrorMsg = "cbor: found duplicate map key \"100000\" at map element index 4"
+	dm, _ := DecOptions{DupMapKey: DupMapKeyEnforcedAPF}.DecMode()
+	var s2 s
+	if err := dm.Unmarshal(cborData, &s2); err == nil {
+		t.Errorf("Unmarshal(0x%x, %s) didn't return an error", cborData, reflect.TypeOf(s2))
+	} else if _, ok := err.(*DupMapKeyError); !ok {
+		t.Errorf("Unmarshal(0x%x) returned wrong error type %T, want (*DupMapKeyError)", cborData, err)
+	} else if !strings.Contains(err.Error(), wantErrorMsg) {
+		t.Errorf("Unmarshal(0x%x) returned error %q, want error containing %q", cborData, err.Error(), wantErrorMsg)
+	}
+	if !reflect.DeepEqual(s2, wantS) {
+		t.Errorf("Unmarshal(0x%x) = %+v (%T), want %+v (%T)", cborData, s2, s2, wantS, wantS)
+	}
+}
+
+func TestStreamDupMapKeyToStructWrongType(t *testing.T) {
+	type s struct {
+		A string `cbor:"a"`
+		B string `cbor:"b"`
+		C string `cbor:"c"`
+		D string `cbor:"d"`
+		E string `cbor:"e"`
+	}
+	cborData := hexDecode("a861616141fa47c35000026162614261636143fa47c3500003616161466164614461656145") // {"a": "A", 100000.0:2, "b": "B", "c": "C", 100000.0:3, "a": "F", "d": "D", "e": "E"}
+
+	var b []byte
+	for i := 0; i < 3; i++ {
+		b = append(b, cborData...)
+	}
+
+	wantS := s{A: "A", B: "B", C: "C", D: "D", E: "E"}
+	wantErrorMsg := "cbor: cannot unmarshal"
+	dec := NewDecoder(bytes.NewReader(b))
+	for i := 0; i < 3; i++ {
+		var s1 s
+		if err := dec.Decode(&s1); err == nil {
+			t.Errorf("Unmarshal(0x%x) didn't return an error", cborData)
+		} else if _, ok := err.(*UnmarshalTypeError); !ok {
+			t.Errorf("Unmarshal(0x%x) returned wrong error type %T, want (*UnmarshalTypeError)", cborData, err)
+		} else if !strings.Contains(err.Error(), wantErrorMsg) {
+			t.Errorf("Unmarshal(0x%x) returned error %q, want error containing %q", cborData, err.Error(), wantErrorMsg)
+		}
+		if !reflect.DeepEqual(s1, wantS) {
+			t.Errorf("Unmarshal(0x%x) = %+v (%T), want %+v (%T)", cborData, s1, s1, wantS, wantS)
+		}
+	}
+	var v interface{}
+	if err := dec.Decode(&v); err != io.EOF {
+		t.Errorf("Decode() returned error %v, want %v", err, io.EOF)
+	}
+
+	// Duplicate key triggers error.
+	wantS = s{A: "A", B: "B", C: "C"}
+	wantErrorMsg = "cbor: found duplicate map key \"100000\" at map element index 4"
+	dm, _ := DecOptions{DupMapKey: DupMapKeyEnforcedAPF}.DecMode()
+	dec = dm.NewDecoder(bytes.NewReader(b))
+	for i := 0; i < 3; i++ {
+		var s2 s
+		if err := dec.Decode(&s2); err == nil {
+			t.Errorf("Decode() didn't return an error")
+		} else if _, ok := err.(*DupMapKeyError); !ok {
+			t.Errorf("Decode() returned wrong error type %T, want (*DupMapKeyError)", err)
+		} else if err.Error() != wantErrorMsg {
+			t.Errorf("Decode() returned error %q, want error containing %q", err.Error(), wantErrorMsg)
+		}
+		if !reflect.DeepEqual(s2, wantS) {
+			t.Errorf("Unmarshal(0x%x) = %+v (%T), want %+v (%T)", cborData, s2, s2, wantS, wantS)
+		}
+	}
+	if err := dec.Decode(&v); err != io.EOF {
+		t.Errorf("Decode() returned error %v, want %v", err, io.EOF)
+	}
+}
+
+func TestUnmarshalDupMapKeyToStructStringParseError(t *testing.T) {
+	type s struct {
+		A string `cbor:"a"`
+		B string `cbor:"b"`
+		C string `cbor:"c"`
+		D string `cbor:"d"`
+		E string `cbor:"e"`
+	}
+	cborData := hexDecode("a661fe6141616261426163614361fe61466164614461656145") // {"\xFE": "A", "b": "B", "c": "C", "\xFE": "F", "d": "D", "e": "E"}
+	wantS := s{A: "", B: "B", C: "C", D: "D", E: "E"}
+	wantErrorMsg := "cbor: invalid UTF-8 string"
+
+	// Duplicate key doesn't overwrite previous value (default).
+	var s1 s
+	if err := Unmarshal(cborData, &s1); err == nil {
+		t.Errorf("Unmarshal(0x%x) didn't return an error", cborData)
+	} else if _, ok := err.(*SemanticError); !ok {
+		t.Errorf("Unmarshal(0x%x) returned wrong error type %T, want (*SemanticError)", cborData, err)
+	} else if !strings.Contains(err.Error(), wantErrorMsg) {
+		t.Errorf("Unmarshal(0x%x) returned error %q, want error containing %q", cborData, err.Error(), wantErrorMsg)
+	}
+	if !reflect.DeepEqual(s1, wantS) {
+		t.Errorf("Unmarshal(0x%x) = %+v (%T), want %+v (%T)", cborData, s1, s1, wantS, wantS)
+	}
+
+	// Duplicate key triggers error.
+	dm, _ := DecOptions{DupMapKey: DupMapKeyEnforcedAPF}.DecMode()
+	var s2 s
+	if err := dm.Unmarshal(cborData, &s2); err == nil {
+		t.Errorf("Unmarshal(0x%x, %s) didn't return an error", cborData, reflect.TypeOf(s2))
+	} else if _, ok := err.(*SemanticError); !ok {
+		t.Errorf("Unmarshal(0x%x) returned wrong error type %T, want (*SemanticError)", cborData, err)
+	} else if !strings.Contains(err.Error(), wantErrorMsg) {
+		t.Errorf("Unmarshal(0x%x) returned error %q, want error containing %q", cborData, err.Error(), wantErrorMsg)
+	}
+	if !reflect.DeepEqual(s2, wantS) {
+		t.Errorf("Unmarshal(0x%x) = %+v (%T), want %+v (%T)", cborData, s2, s2, wantS, wantS)
+	}
+}
+
+func TestUnmarshalDupMapKeyToStructIntParseError(t *testing.T) {
+	type s struct {
+		A int `cbor:"1,keyasint"`
+		B int `cbor:"3,keyasint"`
+		C int `cbor:"5,keyasint"`
+	}
+	cborData := hexDecode("a43bffffffffffffffff0203043bffffffffffffffff030506") // {-18446744073709551616:2, 3:4, -18446744073709551616:3, 5:6}
+
+	// Duplicate key doesn't overwrite previous value (default).
+	wantS := s{B: 4, C: 6}
+	wantErrorMsg := "cbor: cannot unmarshal"
+	var s1 s
+	if err := Unmarshal(cborData, &s1); err == nil {
+		t.Errorf("Unmarshal(0x%x) didn't return an error", cborData)
+	} else if _, ok := err.(*UnmarshalTypeError); !ok {
+		t.Errorf("Unmarshal(0x%x) returned wrong error type %T, want (*UnmarshalTypeError)", cborData, err)
+	} else if !strings.Contains(err.Error(), wantErrorMsg) {
+		t.Errorf("Unmarshal(0x%x) returned error %q, want error containing %q", cborData, err.Error(), wantErrorMsg)
+	}
+	if !reflect.DeepEqual(s1, wantS) {
+		t.Errorf("Unmarshal(0x%x) = %+v (%T), want %+v (%T)", cborData, s1, s1, wantS, wantS)
+	}
+
+	// Duplicate key triggers error.
+	dm, _ := DecOptions{DupMapKey: DupMapKeyEnforcedAPF}.DecMode()
+	var s2 s
+	if err := dm.Unmarshal(cborData, &s2); err == nil {
+		t.Errorf("Unmarshal(0x%x, %s) didn't return an error", cborData, reflect.TypeOf(s2))
+	} else if _, ok := err.(*UnmarshalTypeError); !ok {
+		t.Errorf("Unmarshal(0x%x) returned wrong error type %T, want (*UnmarshalTypeError)", cborData, err)
+	} else if !strings.Contains(err.Error(), wantErrorMsg) {
+		t.Errorf("Unmarshal(0x%x) returned error %q, want error containing %q", cborData, err.Error(), wantErrorMsg)
+	}
+	if !reflect.DeepEqual(s2, wantS) {
+		t.Errorf("Unmarshal(0x%x) = %+v (%T), want %+v (%T)", cborData, s2, s2, wantS, wantS)
+	}
+}
+
+func TestUnmarshalDupMapKeyToStructWrongTypeParseError(t *testing.T) {
+	type s struct {
+		A string `cbor:"a"`
+		B string `cbor:"b"`
+		C string `cbor:"c"`
+		D string `cbor:"d"`
+		E string `cbor:"e"`
+	}
+	cborData := hexDecode("a68161fe614161626142616361438161fe61466164614461656145") // {["\xFE"]: "A", "b": "B", "c": "C", ["\xFE"]: "F", "d": "D", "e": "E"}
+
+	// Duplicate key doesn't overwrite previous value (default).
+	wantS := s{A: "", B: "B", C: "C", D: "D", E: "E"}
+	wantErrorMsg := "cbor: cannot unmarshal"
+	var s1 s
+	if err := Unmarshal(cborData, &s1); err == nil {
+		t.Errorf("Unmarshal(0x%x) didn't return an error", cborData)
+	} else if _, ok := err.(*UnmarshalTypeError); !ok {
+		t.Errorf("Unmarshal(0x%x) returned wrong error type %T, want (*UnmarshalTypeError)", cborData, err)
+	} else if !strings.Contains(err.Error(), wantErrorMsg) {
+		t.Errorf("Unmarshal(0x%x) returned error %q, want error containing %q", cborData, err.Error(), wantErrorMsg)
+	}
+	if !reflect.DeepEqual(s1, wantS) {
+		t.Errorf("Unmarshal(0x%x) = %+v (%T), want %+v (%T)", cborData, s1, s1, wantS, wantS)
+	}
+
+	// Duplicate key triggers error.
+	dm, _ := DecOptions{DupMapKey: DupMapKeyEnforcedAPF}.DecMode()
+	var s2 s
+	if err := dm.Unmarshal(cborData, &s2); err == nil {
+		t.Errorf("Unmarshal(0x%x, %s) didn't return an error", cborData, reflect.TypeOf(s2))
+	} else if _, ok := err.(*UnmarshalTypeError); !ok {
+		t.Errorf("Unmarshal(0x%x) returned wrong error type %T, want (*UnmarshalTypeError)", cborData, err)
+	} else if !strings.Contains(err.Error(), wantErrorMsg) {
+		t.Errorf("Unmarshal(0x%x) returned error %q, want error containing %q", cborData, err.Error(), wantErrorMsg)
+	}
+	if !reflect.DeepEqual(s2, wantS) {
+		t.Errorf("Unmarshal(0x%x) = %+v (%T), want %+v (%T)", cborData, s2, s2, wantS, wantS)
+	}
+}
+
+func TestUnmarshalDupMapKeyToStructWrongTypeUnhashableError(t *testing.T) {
+	type s struct {
+		A string `cbor:"a"`
+		B string `cbor:"b"`
+		C string `cbor:"c"`
+		D string `cbor:"d"`
+		E string `cbor:"e"`
+	}
+	cborData := hexDecode("a6810061416162614261636143810061466164614461656145") // {[0]: "A", "b": "B", "c": "C", [0]: "F", "d": "D", "e": "E"}
+	wantS := s{A: "", B: "B", C: "C", D: "D", E: "E"}
+
+	// Duplicate key doesn't overwrite previous value (default).
+	wantErrorMsg := "cbor: cannot unmarshal"
+	var s1 s
+	if err := Unmarshal(cborData, &s1); err == nil {
+		t.Errorf("Unmarshal(0x%x) didn't return an error", cborData)
+	} else if _, ok := err.(*UnmarshalTypeError); !ok {
+		t.Errorf("Unmarshal(0x%x) returned wrong error type %T, want (*UnmarshalTypeError)", cborData, err)
+	} else if !strings.Contains(err.Error(), wantErrorMsg) {
+		t.Errorf("Unmarshal(0x%x) returned error %q, want error containing %q", cborData, err.Error(), wantErrorMsg)
+	}
+	if !reflect.DeepEqual(s1, wantS) {
+		t.Errorf("Unmarshal(0x%x) = %+v (%T), want %+v (%T)", cborData, s1, s1, wantS, wantS)
+	}
+
+	// Duplicate key triggers error.
+	dm, _ := DecOptions{DupMapKey: DupMapKeyEnforcedAPF}.DecMode()
+	var s2 s
+	if err := dm.Unmarshal(cborData, &s2); err == nil {
+		t.Errorf("Unmarshal(0x%x, %s) didn't return an error", cborData, reflect.TypeOf(s2))
+	} else if _, ok := err.(*UnmarshalTypeError); !ok {
+		t.Errorf("Unmarshal(0x%x) returned wrong error type %T, want (*UnmarshalTypeError)", cborData, err)
+	} else if !strings.Contains(err.Error(), wantErrorMsg) {
+		t.Errorf("Unmarshal(0x%x) returned error %q, want error containing %q", cborData, err.Error(), wantErrorMsg)
+	}
+	if !reflect.DeepEqual(s2, wantS) {
+		t.Errorf("Unmarshal(0x%x) = %+v (%T), want %+v (%T)", cborData, s2, s2, wantS, wantS)
+	}
+}
+
+func TestUnmarshalDupMapKeyToStructTagTypeError(t *testing.T) {
+	type s struct {
+		A string `cbor:"a"`
+		B string `cbor:"b"`
+		C string `cbor:"c"`
+		D string `cbor:"d"`
+		E string `cbor:"e"`
+	}
+	cborData := hexDecode("a6c24901000000000000000061416162614261636143c24901000000000000000061466164614461656145") // {bignum(18446744073709551616): "A", "b": "B", "c": "C", bignum(18446744073709551616): "F", "d": "D", "e": "E"}
+	wantS := s{A: "", B: "B", C: "C", D: "D", E: "E"}
+
+	// Duplicate key doesn't overwrite previous value (default).
+	wantErrorMsg := "cbor: cannot unmarshal"
+	var s1 s
+	if err := Unmarshal(cborData, &s1); err == nil {
+		t.Errorf("Unmarshal(0x%x) didn't return an error", cborData)
+	} else if _, ok := err.(*UnmarshalTypeError); !ok {
+		t.Errorf("Unmarshal(0x%x) returned wrong error type %T, want (*UnmarshalTypeError)", cborData, err)
+	} else if !strings.Contains(err.Error(), wantErrorMsg) {
+		t.Errorf("Unmarshal(0x%x) returned error %q, want error containing %q", cborData, err.Error(), wantErrorMsg)
+	}
+	if !reflect.DeepEqual(s1, wantS) {
+		t.Errorf("Unmarshal(0x%x) = %+v (%T), want %+v (%T)", cborData, s1, s1, wantS, wantS)
+	}
+
+	// Duplicate key triggers error.
+	dm, _ := DecOptions{DupMapKey: DupMapKeyEnforcedAPF}.DecMode()
+	var s2 s
+	if err := dm.Unmarshal(cborData, &s2); err == nil {
+		t.Errorf("Unmarshal(0x%x, %s) didn't return an error", cborData, reflect.TypeOf(s2))
+	} else if _, ok := err.(*UnmarshalTypeError); !ok {
+		t.Errorf("Unmarshal(0x%x) returned wrong error type %T, want (*UnmarshalTypeError)", cborData, err)
+	} else if !strings.Contains(err.Error(), wantErrorMsg) {
+		t.Errorf("Unmarshal(0x%x) returned error %q, want error containing %q", cborData, err.Error(), wantErrorMsg)
+	}
+	if !reflect.DeepEqual(s2, wantS) {
+		t.Errorf("Unmarshal(0x%x) = %+v (%T), want %+v (%T)", cborData, s2, s2, wantS, wantS)
+	}
+}
diff --git a/encode_test.go b/encode_test.go
index df36f2c..fd6e2c6 100644
--- a/encode_test.go
+++ b/encode_test.go
@@ -1339,23 +1339,23 @@ func TestEncodeTimeWithTag(t *testing.T) {
 			convert: []timeConvert{
 				{
 					opt:          timeUnixOpt,
-					wantCborData: hexDecode("f7"), // encode as CBOR undefined
+					wantCborData: hexDecode("f6"), // encode as CBOR null
 				},
 				{
 					opt:          timeUnixMicroOpt,
-					wantCborData: hexDecode("f7"), // encode as CBOR undefined
+					wantCborData: hexDecode("f6"), // encode as CBOR null
 				},
 				{
 					opt:          timeUnixDynamicOpt,
-					wantCborData: hexDecode("f7"), // encode as CBOR undefined
+					wantCborData: hexDecode("f6"), // encode as CBOR null
 				},
 				{
 					opt:          timeRFC3339Opt,
-					wantCborData: hexDecode("f7"), // encode as CBOR undefined
+					wantCborData: hexDecode("f6"), // encode as CBOR null
 				},
 				{
 					opt:          timeRFC3339NanoOpt,
-					wantCborData: hexDecode("f7"), // encode as CBOR undefined
+					wantCborData: hexDecode("f6"), // encode as CBOR null
 				},
 			},
 		},
diff --git a/tag_test.go b/tag_test.go
index 059745c..2184473 100644
--- a/tag_test.go
+++ b/tag_test.go
@@ -284,11 +284,11 @@ func TestAddTagError(t *testing.T) {
 			wantErrorMsg: "cbor: cannot add nil content type to TagSet",
 		},
 		{
-			name:         "DecTag is DecTagIgnored && EncTag is EncTagIgnored",
+			name:         "DecTag is DecTagIgnored && EncTag is EncTagNone",
 			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",
+			opts:         TagOptions{DecTag: DecTagIgnored, EncTag: EncTagNone},
+			wantErrorMsg: "cbor: cannot add tag with DecTagIgnored and EncTagNone options to TagSet",
 		},
 		{
 			name:         "time.Time",
@@ -336,7 +336,7 @@ func TestAddTagError(t *testing.T) {
 			name:         "cbor.Unmarshaler",
 			typ:          reflect.TypeOf(number2(0)),
 			num:          107,
-			opts:         TagOptions{DecTag: DecTagRequired, EncTag: EncTagIgnored},
+			opts:         TagOptions{DecTag: DecTagRequired, EncTag: EncTagNone},
 			wantErrorMsg: "cbor: cannot add cbor.Unmarshaler to TagSet with DecTag != DecTagIgnored",
 		},
 		{
@@ -344,7 +344,7 @@ func TestAddTagError(t *testing.T) {
 			typ:          reflect.TypeOf(number2(0)),
 			num:          108,
 			opts:         TagOptions{DecTag: DecTagRequired, EncTag: EncTagRequired},
-			wantErrorMsg: "cbor: cannot add cbor.Marshaler to TagSet with EncTag != EncTagIgnored",
+			wantErrorMsg: "cbor: cannot add cbor.Marshaler to TagSet with EncTag != EncTagNone",
 		},
 		{
 			name:         "tag number 0",
@@ -563,7 +563,7 @@ func TestAddTagTypeAliasError(t *testing.T) {
 	}
 }
 
-// TestDecodeTag decodes tag data with DecTagRequired/EncTagOptional/EncTagIgnored options.
+// TestDecodeTag decodes tag data with DecTagRequired/EncTagOptional/EncTagNone options.
 func TestDecodeTagData(t *testing.T) {
 	type myInt int
 	type s struct {
@@ -639,7 +639,7 @@ func TestDecodeTagData(t *testing.T) {
 	}
 }
 
-// TestDecodeNoTag decodes no-tag data with DecTagRequired/EncTagOptional/EncTagIgnored options
+// TestDecodeNoTag decodes no-tag data with DecTagRequired/EncTagOptional/EncTagNone options
 func TestDecodeNoTagData(t *testing.T) {
 	type myInt int
 	type s struct {
@@ -662,10 +662,10 @@ func TestDecodeNoTagData(t *testing.T) {
 	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 {
+		if err := tagsDecRequired.Add(TagOptions{EncTag: EncTagNone, 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 {
+		if err := tagsDecOptional.Add(TagOptions{EncTag: EncTagNone, 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)
 		}
 	}
@@ -728,7 +728,7 @@ func TestDecodeNoTagData(t *testing.T) {
 	}
 }
 
-// TestDecodeWrongTag decodes wrong tag data with DecTagRequired/EncTagOptional/EncTagIgnored options
+// TestDecodeWrongTag decodes wrong tag data with DecTagRequired/EncTagOptional/EncTagNone options
 func TestDecodeWrongTag(t *testing.T) {
 	type myInt int
 	type s struct {