Open woodruffw opened 1 month ago
Related Issues and Documentation
(Emoji vote if this was helpful or unhelpful; more detailed feedback welcome in this discussion.)
Here is the related downstream bug we observed: https://github.com/sigstore/timestamp-authority/issues/846
CC @golang/security via https://dev.golang.org/owners
https://github.com/golang/go/issues/19890 looks like a pre-existing report of the invalid DER encoding with Marshal
, although I can't find a matching pre-existing for the overly permissive parsing in Unmarshal
.
I guess that the same issue also affects the (*cryptobyte.Builder).AddASN1GeneralizedTime
and (*cryptobyte.String).ReadASN1GeneralizedTime
in the x/crypto/cryptobyte
package.
I guess that the same issue also affects the
(*cryptobyte.Builder).AddASN1GeneralizedTime
and(*cryptobyte.String).ReadASN1GeneralizedTime
in thex/crypto/cryptobyte
package.
Yep, looks like it:
const generalizedTimeFormatStr = "20060102150405Z0700"
// AddASN1GeneralizedTime appends a DER-encoded ASN.1 GENERALIZEDTIME.
func (b *Builder) AddASN1GeneralizedTime(t time.Time) {
if t.Year() < 0 || t.Year() > 9999 {
b.err = fmt.Errorf("cryptobyte: cannot represent %v as a GeneralizedTime", t)
return
}
b.AddASN1(asn1.GeneralizedTime, func(c *Builder) {
c.AddBytes([]byte(t.Format(generalizedTimeFormatStr)))
})
}
Huh, I'm surprised this has survived for so long. We should probably fix it 🙃.
This CL (from the related issue above) looks like it would address the Marshal
side of things, although that leaves the Unmarshal
side: https://go-review.googlesource.com/c/go/+/146117
Go version
go version go1.23.2 darwin/arm64
Output of
go env
in your module/workspace:What did you do?
My colleague @darkamaul noticed an invalid
GeneralizedTime
encoding while tracking down some DER decoding failures in the responses produced by a Go implementation of an RFC 3161 Time Stamp Authority.What did you see happen?
We observed DER encodings of
GeneralizedTime
objects with explicit timezone offsets, e.g.:This is an invalid DER encoding of a
GeneralizedTime
, per the DER encoding rules defined in ITU-T X.690. In particular, DER requires that allGeneralizedTime
encodings be UTC time with theZ
designator per X.690 11.7.1:(Ref: https://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf, page 19)
After looking into it, we determined that the codebase was using
encoding/asn1
'sMarshal
implementation, in particular for marshallingtime.Time
objects intoGeneralizedTime
encodings.For example:
Permalink: https://github.com/digitorus/timestamp/blob/220c5c2851b7435eea999de3daa773601a7ca126/rfc3161_struct.go#L57
We then checked the underlying
Marshal
implementation and itsGeneralizedTime
helper (appendGeneralizedTime
), and confirmed that it emits a relative offset instead of normalizing to UTC when the origintime.Time
is not already UTC:Ref: https://cs.opensource.google/go/go/+/refs/tags/go1.23.2:src/encoding/asn1/marshal.go;l=405-448
Based on the blame, this offset encoding has been present since at least 2011 and possibly earlier.
What did you expect to see?
We expected
encoding/asn1
to produce only valid DER encodings, which in this case means producing aGeneralizedTime
with only aZ
timezone component, and no relative timezone offsets.To achieve this, we believe the
Marshal
implementation can be tweaked to callUTC()
before performing encoding, which would normalize thetime.Time
into UTC form. The special-casing around relative offsets could then be removed entirely, as all encoded times would be UTC.Similarly, we believe (but haven't concretely observed) that
encoding/asn1
'sUnmarshal
accepts invalid DER encodings ofGeneralizedTime
s, per its format string:Ref: https://cs.opensource.google/go/go/+/refs/tags/go1.23.2:src/encoding/asn1/asn1.go;l=368-383
If our understanding of
time.Parse
is correct, this will admit multiple invalid DER encodings:.999999999
will allow trailing zeroes, which are allowed in BER but not in DER;Z0700
allows bothZ
and relative timezone offsets, when onlyZ
is allowed in DER.