Disable conflicting encode options when marshaling cbor.Tag.

Encode options, especially those that control the mapping from Go type to CBOR type, can result in
output containing tag validity errors. For tag numbers that are built in, it's possible to "do the
right thing" and override those options on a case-by-case basis. This can't and does not prevent tag
validity errors for unrecognized tag numbers.

Signed-off-by: Ben Luddy <bluddy@redhat.com>
This commit is contained in:
Ben Luddy 2024-05-31 16:03:22 -04:00
parent 69326f1c86
commit 0cabb4afed
No known key found for this signature in database
GPG Key ID: A6551E73A5974C30
3 changed files with 63 additions and 2 deletions

View File

@ -1648,8 +1648,21 @@ func encodeTag(e *bytes.Buffer, em *encMode, v reflect.Value) error {
// Marshal tag number
encodeHead(e, byte(cborTypeTag), t.Number)
vem := *em // shallow copy
// For built-in tags, disable settings that may introduce tag validity errors when
// marshaling certain Content values.
switch t.Number {
case tagNumRFC3339Time:
vem.stringType = StringToTextString
vem.stringMajorType = cborTypeTextString
case tagNumUnsignedBignum, tagNumNegativeBignum:
vem.byteSlice = ByteSliceToByteString
vem.byteSliceEncodingTag = 0
}
// Marshal tag content
return encode(e, em, reflect.ValueOf(t.Content))
return encode(e, &vem, reflect.ValueOf(t.Content))
}
// encodeHead writes CBOR head of specified type t and returns number of bytes written.

4
tag.go
View File

@ -7,7 +7,9 @@ import (
"sync"
)
// Tag represents CBOR tag data, including tag number and unmarshaled tag content.
// Tag represents CBOR tag data, including tag number and unmarshaled tag content. Marshaling and
// unmarshaling of tag content is subject to any encode and decode options that would apply to
// enclosed data item if it were to appear outside of a tag.
type Tag struct {
Number uint64
Content interface{}

View File

@ -1490,3 +1490,49 @@ func TestMarshalRawTagContainingMalformedCBORData(t *testing.T) {
})
}
}
// TestEncodeBuiltinTag tests that marshaling a value of type Tag "does the right thing" when
// marshaling the enclosed data item of a built-in tag number.
func TestEncodeBuiltinTag(t *testing.T) {
for _, tc := range []struct {
name string
tag Tag
opts EncOptions
want []byte
}{
{
name: "unsigned bignum content not enclosed in expected encoding tag",
tag: Tag{Number: tagNumUnsignedBignum, Content: []byte{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
opts: EncOptions{ByteSlice: ByteSliceExpectedEncodingBase16},
want: []byte{0xc2, 0x49, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
},
{
name: "negative bignum content not enclosed in expected encoding tag",
tag: Tag{Number: tagNumNegativeBignum, Content: []byte{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
opts: EncOptions{ByteSlice: ByteSliceExpectedEncodingBase16},
want: []byte{0xc3, 0x49, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
},
{
name: "rfc 3339 content is not encoded as byte string",
tag: Tag{Number: tagNumRFC3339Time, Content: "2013-03-21T20:04:00Z"},
opts: EncOptions{String: StringToByteString},
want: hexDecode("c074323031332d30332d32315432303a30343a30305a"),
},
} {
t.Run(tc.name, func(t *testing.T) {
em, err := tc.opts.EncMode()
if err != nil {
t.Fatal(err)
}
got, err := em.Marshal(tc.tag)
if err != nil {
t.Fatal(err)
}
if !bytes.Equal(got, tc.want) {
t.Errorf("unexpected difference\ngot: 0x%x\nwant: 0x%x", got, tc.want)
}
})
}
}