mirror of
https://github.com/golang/go
synced 2025-05-29 15:20:52 +00:00
encoding/asn1: make sure implicit fields roundtrip
Make sure Marshal and Unmarshal support the same field tags for implicit encoding choices. In particular this adds support for Unmarshalling implicitly tagged GeneralizedTime fields. Also add tests and update the docs. Fixes #72078 Change-Id: I21465ee4bcd73a7db0d0c36b2df53cabfc480185 Reviewed-on: https://go-review.googlesource.com/c/go/+/654275 LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Reviewed-by: Ian Lance Taylor <iant@google.com>
This commit is contained in:
src/encoding/asn1
@ -828,9 +828,18 @@ func parseField(v reflect.Value, bytes []byte, initOffset int, params fieldParam
|
||||
}
|
||||
|
||||
// Special case for time: UTCTime and GeneralizedTime both map to the
|
||||
// Go type time.Time.
|
||||
if universalTag == TagUTCTime && t.tag == TagGeneralizedTime && t.class == ClassUniversal {
|
||||
universalTag = TagGeneralizedTime
|
||||
// Go type time.Time. getUniversalType returns the tag for UTCTime when
|
||||
// it sees a time.Time, so if we see a different time type on the wire,
|
||||
// or the field is tagged with a different type, we change the universal
|
||||
// type to match.
|
||||
if universalTag == TagUTCTime {
|
||||
if t.class == ClassUniversal {
|
||||
if t.tag == TagGeneralizedTime {
|
||||
universalTag = t.tag
|
||||
}
|
||||
} else if params.timeType != 0 {
|
||||
universalTag = params.timeType
|
||||
}
|
||||
}
|
||||
|
||||
if params.set {
|
||||
@ -1103,6 +1112,13 @@ func setDefaultValue(v reflect.Value, params fieldParameters) (ok bool) {
|
||||
// numeric causes strings to be unmarshaled as ASN.1 NumericString values
|
||||
// utf8 causes strings to be unmarshaled as ASN.1 UTF8String values
|
||||
//
|
||||
// When decoding an ASN.1 value with an IMPLICIT tag into a time.Time field,
|
||||
// Unmarshal will default to a UTCTime, which doesn't support time zones or
|
||||
// fractional seconds. To force usage of GeneralizedTime, use the following
|
||||
// tag:
|
||||
//
|
||||
// generalized causes time.Times to be unmarshaled as ASN.1 GeneralizedTime values
|
||||
//
|
||||
// If the type of the first field of a structure is RawContent then the raw
|
||||
// ASN1 contents of the struct will be stored in it.
|
||||
//
|
||||
|
@ -1185,3 +1185,34 @@ func BenchmarkObjectIdentifierString(b *testing.B) {
|
||||
_ = oidPublicKeyRSA.String()
|
||||
}
|
||||
}
|
||||
|
||||
func TestImplicitTypeRoundtrip(t *testing.T) {
|
||||
type tagged struct {
|
||||
IA5 string `asn1:"tag:1,ia5"`
|
||||
Printable string `asn1:"tag:2,printable"`
|
||||
UTF8 string `asn1:"tag:3,utf8"`
|
||||
Numeric string `asn1:"tag:4,numeric"`
|
||||
UTC time.Time `asn1:"tag:5,utc"`
|
||||
Generalized time.Time `asn1:"tag:6,generalized"`
|
||||
}
|
||||
a := tagged{
|
||||
IA5: "ia5",
|
||||
Printable: "printable",
|
||||
UTF8: "utf8",
|
||||
Numeric: "123 456",
|
||||
UTC: time.Now().UTC().Truncate(time.Second),
|
||||
Generalized: time.Now().UTC().Truncate(time.Second),
|
||||
}
|
||||
enc, err := Marshal(a)
|
||||
if err != nil {
|
||||
t.Fatalf("Marshal failed: %s", err)
|
||||
}
|
||||
var b tagged
|
||||
if _, err := Unmarshal(enc, &b); err != nil {
|
||||
t.Fatalf("Unmarshal failed: %s", err)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(a, b) {
|
||||
t.Fatalf("Unexpected diff after roundtripping struct\na: %#v\nb: %#v", a, b)
|
||||
}
|
||||
}
|
||||
|
@ -725,6 +725,7 @@ func makeField(v reflect.Value, params fieldParameters) (e encoder, err error) {
|
||||
// omitempty: causes empty slices to be skipped
|
||||
// printable: causes strings to be marshaled as ASN.1, PrintableString values
|
||||
// utf8: causes strings to be marshaled as ASN.1, UTF8String values
|
||||
// numeric: causes strings to be marshaled as ASN.1, NumericString values
|
||||
// utc: causes time.Time to be marshaled as ASN.1, UTCTime values
|
||||
// generalized: causes time.Time to be marshaled as ASN.1, GeneralizedTime values
|
||||
func Marshal(val any) ([]byte, error) {
|
||||
|
Reference in New Issue
Block a user