crypto/tls: populate Leaf in X509KeyPair

Fixes #67065

Change-Id: I189e194de8aa94523eb64e1dd294a70cb81cbdf6
Reviewed-on: https://go-review.googlesource.com/c/go/+/585856
Auto-Submit: Roland Shoemaker <roland@golang.org>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Filippo Valsorda <filippo@golang.org>
Reviewed-by: Damien Neil <dneil@google.com>
This commit is contained in:
Roland Shoemaker 2024-05-15 13:46:38 -07:00 committed by Gopher Robot
parent 375031d8dc
commit 56ec5d96bc
5 changed files with 92 additions and 7 deletions

View File

@ -201,6 +201,13 @@ Go 1.23 changed the default TLS cipher suites used by clients and servers when
not explicitly configured, removing 3DES cipher suites. The default can be reverted not explicitly configured, removing 3DES cipher suites. The default can be reverted
using the [`tls3des` setting](/pkg/crypto/tls/#Config.CipherSuites). using the [`tls3des` setting](/pkg/crypto/tls/#Config.CipherSuites).
Go 1.23 changed the behavior of [`tls.X509KeyPair`](/pkg/crypto/tls#X509KeyPair)
and [`tls.LoadX509KeyPair`](/pkg/crypto/tls#LoadX509KeyPair) to populate the
Leaf field of the returned [`tls.Certificate`](/pkg/crypto/tls#Certificate).
This behavior is controlled by the `x509keypairleaf` setting. For Go 1.23, it
defaults to `x509keypairleaf=1`. Previous versions default to
`x509keypairleaf=0`.
### Go 1.22 ### Go 1.22
Go 1.22 adds a configurable limit to control the maximum acceptable RSA key size Go 1.22 adds a configurable limit to control the maximum acceptable RSA key size

View File

@ -22,6 +22,7 @@ import (
"encoding/pem" "encoding/pem"
"errors" "errors"
"fmt" "fmt"
"internal/godebug"
"net" "net"
"os" "os"
"strings" "strings"
@ -222,11 +223,14 @@ func (d *Dialer) DialContext(ctx context.Context, network, addr string) (net.Con
return c, nil return c, nil
} }
// LoadX509KeyPair reads and parses a public/private key pair from a pair // LoadX509KeyPair reads and parses a public/private key pair from a pair of
// of files. The files must contain PEM encoded data. The certificate file // files. The files must contain PEM encoded data. The certificate file may
// may contain intermediate certificates following the leaf certificate to // contain intermediate certificates following the leaf certificate to form a
// form a certificate chain. On successful return, Certificate.Leaf will // certificate chain. On successful return, Certificate.Leaf will be populated.
// be nil because the parsed form of the certificate is not retained. //
// Before Go 1.23 Certificate.Leaf was left nil, and the parsed certificate was
// discarded. This behavior can be re-enabled by setting "x509keypairleaf=0"
// in the GODEBUG environment variable.
func LoadX509KeyPair(certFile, keyFile string) (Certificate, error) { func LoadX509KeyPair(certFile, keyFile string) (Certificate, error) {
certPEMBlock, err := os.ReadFile(certFile) certPEMBlock, err := os.ReadFile(certFile)
if err != nil { if err != nil {
@ -239,9 +243,14 @@ func LoadX509KeyPair(certFile, keyFile string) (Certificate, error) {
return X509KeyPair(certPEMBlock, keyPEMBlock) return X509KeyPair(certPEMBlock, keyPEMBlock)
} }
var x509keypairleaf = godebug.New("x509keypairleaf")
// X509KeyPair parses a public/private key pair from a pair of // X509KeyPair parses a public/private key pair from a pair of
// PEM encoded data. On successful return, Certificate.Leaf will be nil because // PEM encoded data. On successful return, Certificate.Leaf will be populated.
// the parsed form of the certificate is not retained. //
// Before Go 1.23 Certificate.Leaf was left nil, and the parsed certificate was
// discarded. This behavior can be re-enabled by setting "x509keypairleaf=0"
// in the GODEBUG environment variable.
func X509KeyPair(certPEMBlock, keyPEMBlock []byte) (Certificate, error) { func X509KeyPair(certPEMBlock, keyPEMBlock []byte) (Certificate, error) {
fail := func(err error) (Certificate, error) { return Certificate{}, err } fail := func(err error) (Certificate, error) { return Certificate{}, err }
@ -296,6 +305,12 @@ func X509KeyPair(certPEMBlock, keyPEMBlock []byte) (Certificate, error) {
return fail(err) return fail(err)
} }
if x509keypairleaf.Value() != "0" {
cert.Leaf = x509Cert
} else {
x509keypairleaf.IncNonDefault()
}
cert.PrivateKey, err = parsePrivateKey(keyDERBlock.Bytes) cert.PrivateKey, err = parsePrivateKey(keyDERBlock.Bytes)
if err != nil { if err != nil {
return fail(err) return fail(err)

View File

@ -8,13 +8,19 @@ import (
"bytes" "bytes"
"context" "context"
"crypto" "crypto"
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/x509" "crypto/x509"
"crypto/x509/pkix"
"encoding/json" "encoding/json"
"encoding/pem"
"errors" "errors"
"fmt" "fmt"
"internal/testenv" "internal/testenv"
"io" "io"
"math" "math"
"math/big"
"net" "net"
"os" "os"
"reflect" "reflect"
@ -1945,3 +1951,54 @@ func TestHandshakeKyber(t *testing.T) {
}) })
} }
} }
func TestX509KeyPairPopulateCertificate(t *testing.T) {
key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
if err != nil {
t.Fatal(err)
}
keyDER, err := x509.MarshalPKCS8PrivateKey(key)
if err != nil {
t.Fatal(err)
}
keyPEM := pem.EncodeToMemory(&pem.Block{Type: "PRIVATE KEY", Bytes: keyDER})
tmpl := &x509.Certificate{
SerialNumber: big.NewInt(1),
Subject: pkix.Name{CommonName: "test"},
}
certDER, err := x509.CreateCertificate(rand.Reader, tmpl, tmpl, key.Public(), key)
if err != nil {
t.Fatal(err)
}
certPEM := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: certDER})
t.Run("x509keypairleaf=0", func(t *testing.T) {
t.Setenv("GODEBUG", "x509keypairleaf=0")
cert, err := X509KeyPair(certPEM, keyPEM)
if err != nil {
t.Fatal(err)
}
if cert.Leaf != nil {
t.Fatal("Leaf should not be populated")
}
})
t.Run("x509keypairleaf=1", func(t *testing.T) {
t.Setenv("GODEBUG", "x509keypairleaf=1")
cert, err := X509KeyPair(certPEM, keyPEM)
if err != nil {
t.Fatal(err)
}
if cert.Leaf == nil {
t.Fatal("Leaf should be populated")
}
})
t.Run("GODEBUG unset", func(t *testing.T) {
cert, err := X509KeyPair(certPEM, keyPEM)
if err != nil {
t.Fatal(err)
}
if cert.Leaf == nil {
t.Fatal("Leaf should be populated")
}
})
}

View File

@ -54,6 +54,7 @@ var All = []Info{
{Name: "tlsunsafeekm", Package: "crypto/tls", Changed: 22, Old: "1"}, {Name: "tlsunsafeekm", Package: "crypto/tls", Changed: 22, Old: "1"},
{Name: "winreadlinkvolume", Package: "os", Changed: 22, Old: "0"}, {Name: "winreadlinkvolume", Package: "os", Changed: 22, Old: "0"},
{Name: "winsymlink", Package: "os", Changed: 22, Old: "0"}, {Name: "winsymlink", Package: "os", Changed: 22, Old: "0"},
{Name: "x509keypairleaf", Package: "crypto/tls", Changed: 23, Old: "0"},
{Name: "x509negativeserial", Package: "crypto/x509", Changed: 23, Old: "1"}, {Name: "x509negativeserial", Package: "crypto/x509", Changed: 23, Old: "1"},
{Name: "x509seriallength", Package: "crypto/x509", Changed: 23, Old: "1"}, {Name: "x509seriallength", Package: "crypto/x509", Changed: 23, Old: "1"},
{Name: "x509sha1", Package: "crypto/x509"}, {Name: "x509sha1", Package: "crypto/x509"},

View File

@ -326,6 +326,11 @@ Below is the full list of supported metrics, ordered lexicographically.
The number of non-default behaviors executed by the os package The number of non-default behaviors executed by the os package
due to a non-default GODEBUG=winsymlink=... setting. due to a non-default GODEBUG=winsymlink=... setting.
/godebug/non-default-behavior/x509keypairleaf:events
The number of non-default behaviors executed by the crypto/tls
package due to a non-default GODEBUG=x509keypairleaf=...
setting.
/godebug/non-default-behavior/x509negativeserial:events /godebug/non-default-behavior/x509negativeserial:events
The number of non-default behaviors executed by the crypto/x509 The number of non-default behaviors executed by the crypto/x509
package due to a non-default GODEBUG=x509negativeserial=... package due to a non-default GODEBUG=x509negativeserial=...