Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

crypto/x509: Certificate no longer encodable using encoding/gob in Go1.22 #65633

Closed
VictorLowther opened this issue Feb 9, 2024 · 13 comments
Closed
Labels
NeedsFix The path to resolution is known, but the work has not been done.
Milestone

Comments

@VictorLowther
Copy link

Go version

go 1.22.0 linux/amd64

Output of go env in your module/workspace:

GO111MODULE=''
GOARCH='amd64'
GOBIN=''
GOCACHE='/home/victor/.cache/go-build'
GOENV='/home/victor/.config/go/env'
GOEXE=''
GOEXPERIMENT=''
GOFLAGS=''
GOHOSTARCH='amd64'
GOHOSTOS='linux'
GOINSECURE=''
GOMODCACHE='/home/victor/go/pkg/mod'
GONOPROXY='gitlab.com/rackn'
GONOSUMDB='gitlab.com/rackn'
GOOS='linux'
GOPATH='/home/victor/go'
GOPRIVATE='gitlab.com/rackn'
GOPROXY='https://proxy.golang.org,direct'
GOROOT='/usr/lib/go'
GOSUMDB='sum.golang.org'
GOTMPDIR=''
GOTOOLCHAIN='auto'
GOTOOLDIR='/usr/lib/go/pkg/tool/linux_amd64'
GOVCS=''
GOVERSION='go1.22.0'
GCCGO='gccgo'
GOAMD64='v1'
AR='ar'
CC='gcc'
CXX='g++'
CGO_ENABLED='1'
GOMOD='/dev/null'
GOWORK=''
CGO_CFLAGS='-O2 -g'
CGO_CPPFLAGS=''
CGO_CXXFLAGS='-O2 -g'
CGO_FFLAGS='-O2 -g'
CGO_LDFLAGS='-O2 -g'
PKG_CONFIG='pkg-config'
GOGCCFLAGS='-fPIC -m64 -pthread -Wl,--no-gc-sections -fmessage-length=0 -ffile-prefix-map=/tmp/go-build2733416375=/tmp/go-build -gno-record-gcc-switches'

What did you do?

A project of mine directly embeds the *x509.Certificate struct in a larger struct, which is encoded and decoded using encoding/gob. The struct added fields that use a new x509.OID structure, which contains a single non-exported field.

Test program is here on the Go playground. Running it under Go 1.22 displays the error, and running it under Go 1.21 does not.

What did you see happen?

go run main.go
2024/02/09 08:42:03 Failed to gob encode enpty x.509 cert: gob: type x509.OID has no exported fields

What did you expect to see?

go run main.go
2024/02/09 08:41:47 Encoded empty x.509 cert

Adding MarshalBinary and UnmarshalBinary methods to *x509.OID allows encoding to succeed. Patch against 1.22.0:

diff --git a/src/crypto/x509/oid.go b/src/crypto/x509/oid.go
index 5359af624b..0e2b872539 100644
--- a/src/crypto/x509/oid.go
+++ b/src/crypto/x509/oid.go
@@ -24,6 +24,15 @@ type OID struct {
        der []byte
 }

+func (o *OID) MarshalBinary() ([]byte, error) {
+       return append([]byte{}, o.der...), nil
+}
+
+func (o *OID) UnmarshalBinary(b []byte) error {
+       o.der = append(o.der[:0], b...)
+       return nil
+}
+
 func newOIDFromDER(der []byte) (OID, bool) {
        if len(der) == 0 || der[len(der)-1]&0x80 != 0 {
                return OID{}, false
@mateusz834 mateusz834 added the NeedsInvestigation Someone must examine and confirm this is a valid issue and not a duplicate of an existing one. label Feb 9, 2024
@mateusz834
Copy link
Member

CC @rolandshoemaker

@odeke-em odeke-em changed the title crypto/x509: x509.Certificate no longer encodable using encoding/gob crypto/x509: Certificate no longer encodable using encoding/gob Feb 14, 2024
@odeke-em odeke-em changed the title crypto/x509: Certificate no longer encodable using encoding/gob crypto/x509: Certificate no longer encodable using encoding/gob in Go1.22 Feb 14, 2024
@Akaame
Copy link

Akaame commented Mar 4, 2024

Has there been any progress on this matter?

@VictorLowther
Copy link
Author

As far as I can tell, no. I didn'r see a fix go into 1.22.1

@rolandshoemaker
Copy link
Member

While I'm not strictly opposed to fixing this, we've never explicitly claimed that Certificate is compatible with encoding/gob, so I don't think we will consider this a breaking change, and I'm not particularly sure how much I want to encourage using encoding/gob here.

Is there a particular reason to use encoding/gob over the DER encoding of a certificate (which is available via Certificate.Raw), which is likely to be significantly smaller than a gob encoded struct (which will, itself, contain the raw DER encoding of the certificate, since it is a part of the struct)?

If its just because it's embedded in another struct, this problem can be worked around by defining your own type which wraps x509.Certificate (i.e. type gobCert *x509.Certificate) and implements MarshalBinary/UnmarshalBinary methods which encode it (i.e. by just returning the Raw field, and calling x509.ParseCertificate respectively). This may actually end up being more performant because of the specialized nature of the x509 parser. i.e. https://go.dev/play/p/exGyeSWuFAL.

@VictorLowther
Copy link
Author

The struct in question is embedded in the replicated state machine for a Raft cluster, making dealing with marshalling changes on the fly like that challenging -- changing how the struct is marshalled like that would involve manual intervention and downtime for our customers, whereas encoding/gob will transparently just work across changes like this as long as the OID field implements MarshalBinary/UnmarshalBinary with some possibly minor fixups to arrange for the Policies field to be filled out if it isn't already.

And while y'all never claimed that Certificate is compatible with encoding/gob, Hyrum's Law still applies and y'all made a change that broke encoding/gob that could have been avoided by making the OID a []byte directly instead of doing the single private struct field thing, and making it the only thing in an x509.Certificate that is not exported.

@rsc
Copy link
Contributor

rsc commented Mar 11, 2024

There are ways to work around this, yes, but if gob worked before on these types we should probably keep it working one way or another. We have run into similar issues in the past and in general treat those wire changes as subject to compatibility. Unfortunately, the fix seems to be to add new methods, which as a matter of policy we don't do in point releases. So the fix (new methods) would have to wait for Go 1.23. Unless we have evidence of widespread impact, that may be the best approach.

@tycho
Copy link

tycho commented Mar 11, 2024

How widespread is widespread enough? I found this issue because it was causing builds of Mattermost and its plugins to fail to work properly. To work around it I had to modify the build to avoid using the system-installed Go and download a Go 1.21 binary.

@rsc
Copy link
Contributor

rsc commented Mar 11, 2024

Filed #66249. Thanks for that extra impact information @tycho. That's more impact than I realized.

@rsc
Copy link
Contributor

rsc commented Mar 12, 2024

@gopherbot please backport go1.22

@gopherbot
Copy link

Backport issue(s) opened: #66273 (for 1.22).

Remember to create the cherry-pick CL(s) as soon as the patch is submitted to master, according to https://go.dev/wiki/MinorReleases.

@gopherbot
Copy link

Change https://go.dev/cl/571095 mentions this issue: encoding/gob: make x509.Certificate marshalable again

@rsc
Copy link
Contributor

rsc commented Mar 12, 2024

I spoke to @robpike about this yesterday. We think the simplest, least invasive change for Go 1.22.x would be to change gob to look for the x509.Certificate type and have it skip over the Policies field entirely. That seems like it would fix Mattermost and other affected users, and then we can proceed with new methods on the normal timeline and then remove the gob change. The pending CL 571095 does this.

@gopherbot
Copy link

Change https://go.dev/cl/571715 mentions this issue: [release-branch.go1.22] encoding/gob: make x509.Certificate marshalable again

@dmitshur dmitshur added NeedsFix The path to resolution is known, but the work has not been done. and removed NeedsInvestigation Someone must examine and confirm this is a valid issue and not a duplicate of an existing one. labels Mar 20, 2024
@dmitshur dmitshur added this to the Go1.23 milestone Mar 20, 2024
gopherbot pushed a commit that referenced this issue Mar 27, 2024
…le again

The OID type is not exported data like most of the other x509 structs.
Using it in x509.Certificate made Certificate not gob-compatible anymore,
which breaks real-world code. As a temporary fix, make gob ignore
that field, making it work as well as it did in Go 1.21.

For Go 1.23, we anticipate adding a proper fix and removing the gob
workaround. See #65633 and #66249 for more details.

For #66249.
For #65633.
Fixes #66273.

Change-Id: Idd1431d15063b3009e15d0565cd3120b9fa13f61
Reviewed-on: https://go-review.googlesource.com/c/go/+/571095
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Rob Pike <r@golang.org>
Reviewed-by: Roland Shoemaker <roland@golang.org>
Reviewed-on: https://go-review.googlesource.com/c/go/+/571715
Reviewed-by: David Chase <drchase@google.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
NeedsFix The path to resolution is known, but the work has not been done.
Projects
None yet
Development

No branches or pull requests

8 participants