proposal: crypto/tls: supported certificate types API #32426
Presently there is no simple way to determine whether a client/connection will support a particular TLS certificate type from within a
The logic within crypto/tls is complex, subtle and hard to reason with from without crypto/tls. With the addition of Ed25519 in CL 177698, this is now even harder to get right as there are now three distinct certificate types that may be supported: RSA, ECDSA and Ed25519.
What I'd like to propose is some addition to
An incomplete implementation was added to acme/autocert in CL 114501. This is documented as differing from crypto/tls (see commit message), and could cause handshake failures if there isn't a mutually supported ECDSA cipher suite. I've also tried implementing this myself using just the information in
It's impossible to correctly implement this safely without support from crypto/tls because it's dependant on the configured/default cipher suites (which aren't always easily accessible) and internal crypto/tls implementation details.
This is useful for servers that might want to support several certificate types–to allow for older clients to successful connect–like acme/autocert does. For example the server may wish to prioritise Ed25519 over ECDSA and use RSA as a fallback, or more simply prefer ECDSA over RSA.
: To successfully negotiate an ECDSA certificate, you need a valid point format (uncompressed), a mutually supported curve, a mutually supported ECDSA cipher suite or support for TLS 1.3, and a supported ECDSA signature scheme. I believe Ed25519 support is the same except for needing the Ed25519 signature scheme.
There's also the presently unsupported
I feel like the most future-proof and reusable API here would be
I like this because the logic is getting more complex over time, I heard requests that would be fulfilled by this multiple times, and even the crypto/tls internals would benefit from this API.
This refactors a lot of the certificate support logic to make it cleaner and reusable where possible. These changes will make the following CLs much simpler. In particular, the heavily overloaded pickSignatureAlgorithm is gone. That function used to cover both signing and verifying side, would work both for pre-signature_algorithms TLS 1.0/1.1 and TLS 1.2, and returned sigalg, type and hash. Now, TLS 1.0/1.1 and 1.2 are differentiated at the caller, as they have effectively completely different logic. TLS 1.0/1.1 simply use legacyTypeAndHashFromPublicKey as they employ a fixed hash function and signature algorithm for each public key type. TLS 1.2 is instead routed through selectSignatureScheme (on the signing side) or isSupportedSignatureAlgorithm (on the verifying side) and typeAndHashFromSignatureScheme, like TLS 1.3. On the signing side, signatureSchemesForCertificate was already version aware (for PKCS#1 v1.5 vs PSS support), so selectSignatureScheme just had to learn the Section 188.8.131.52.1 defaults for a missing signature_algorithms to replace pickSignatureAlgorithm. On the verifying side, pickSignatureAlgorithm was also checking the public key type, while isSupportedSignatureAlgorithm + typeAndHashFromSignatureScheme are not, but that check was redundant with the one in verifyHandshakeSignature. There should be no major change in behavior so far. A few minor changes came from the refactor: we now correctly require signature_algorithms in TLS 1.3 when using a certificate; we won't use Ed25519 in TLS 1.2 if the client didn't send signature_algorithms; and we don't send ec_points_format in the ServerHello (a compatibility measure) if we are not doing ECDHE anyway because there are no mutually supported curves. The tests also got simpler because they test simpler functions. The caller logic switching between TLS 1.0/1.1 and 1.2 is tested by the transcript tests. Updates #32426 Change-Id: Ice9dcaea78d204718f661f8d60efdb408ba41577 Reviewed-on: https://go-review.googlesource.com/c/go/+/205061 Reviewed-by: Katie Hockman <email@example.com>