}
// 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 {
// 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.
//
_ = 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)
+ }
+}
// 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) {