Proposal Details

I recently found myself needing x/crypto/acme/autocert's func supportsECDSA but it's not exported.

Copy/pasting it works, but is slightly sad as it's kinda long and I worry about it getting out of sync.

It's possible to use it indirectly via ugly hacks like https://github.com/tailscale/scertec/pull/3 (using a fake acme/autocert.Cache that looks which keys are accessed) but it's a bit too ugly.

Proposal: export that code somewhere? method on ClientHelloInfo even?

/cc @rolandshoemaker

Comment From: rolandshoemaker

This seems like something that would likely be useful in general for people writing Config.GetCertificate functions.

We kind of have an inverse version of this already with ClientHelloInfo.SupportsCertificate, but that requires you already have a certificate to test against, which is obviously not the case in this case and the autocert case (hence why it had to be written in the first place).

I'd not be opposed to adding the following method in crypto/tls:

// SupportsECDSA returns true if the ClientHelloInfo indicates the the client supports both ECDSA based
// key exchanges and cipher suites.
func (*ClientHelloInfo) SupportsECDSA() bool

cc @FiloSottile

Comment From: aclements

This proposal has been added to the active column of the proposals project and will now be reviewed at the weekly proposal review meetings. — aclements for the proposal review group

Comment From: tmthrgd

You can achieve this with SupportsCertificate:


func isECDSACertificateSupported(hello *tls.ClientHelloInfo) bool {
    // We want to skip the ServerName check in SupportsCertificate as we're
    // only interested in what types of certificates the client supports.
    chi := *hello
    chi.ServerName = ""

    return chi.SupportsCertificate(ecdsaP256Certificate) == nil
}

var ecdsaP256Certificate = &tls.Certificate{
    PrivateKey: &opaqueSigner{&ecdsa.PublicKey{Curve: elliptic.P256()}},
}

type opaqueSigner struct{ pub crypto.PublicKey }

func (s *opaqueSigner) Public() crypto.PublicKey { return s.pub }

func (*opaqueSigner) Sign(rand io.Reader, digest []byte, opts crypto.SignerOpts) ([]byte, error) {
    panic("not implemented")
}

It's a slight hack, but I don't think it's too bad. You could also just use a self-signed certificate if you wanted.

Because support also depends on the type of ECDSA curve you choose (and Ed25519), a generic SupportsECDSA() bool isn't really specific enough. (See the original issue that created this API: https://github.com/golang/go/issues/32426).