#3214·rustls

Expose signature_algorithms_cert to ResolvesServerCert::resolve

Author: DannyBCarrJrCreated Aug 13, 2026Updated Sep 1, 2026

Checklist

  • I've searched the issue tracker for similar requests

Is your feature request related to a problem? Please describe. I have a TLS server that needs to hold two certificate chains during a post-quantum migration. One is classical, the other has certificates signed with ML-DSA. Both are for the same hostname, so which chain goes out has to be decided per connection from what the client advertises.

Clients will signal which signature algorithms they accept on certificates in signature_algorithms_cert. A rustls server cannot see it. ResolvesServerCert::resolve receives a ClientHello carrying eight fields (server_name, signature_schemes, alpn, server_cert_types, client_cert_types, cipher_suites, certificate_authorities, and named_groups), and signature_algorithms_cert is not among them, in v/0.23.43 or on dev HEAD. signature_schemes is signature_algorithms, which constrains the handshake signature, so it describes the leaf key rather than the signatures on the chain. From inside a resolver the two chains are indistinguishable on the axis that decides between them.

This is not a bug report. RFC 8446 section 4.4.2.2 makes the chain-signature constraint a SHOULD, and where a server cannot produce a conforming chain it says the server SHOULD send a chain of its choice anyway. rustls is conformant. I am just asking for the input to be available to resolvers that want to act on it.

Describe the solution you'd like A signature_algorithms_cert accessor on ClientHello, populated when the client sends the extension. I'm not asking rustls to change any default behavior, to enforce the contraints, or to send it as a client. I'm only asking to expose it so a custom ResolvesServerCert can use it.

One detail worth settling in the API rather than after it ships. RFC 8446 section 4.2.3 lets a client omit signature_algorithms_cert when it would duplicate signature_algorithms, so absence means "fall back to signature_schemes" and not "no constraint". Whatever shape the accessor takes, that distinction probably belongs in its doc comment.

Describe alternatives you've considered

  1. ResolvesServerCertUsingSni selects on hostname, and both chains are for the same hostname. SingleCertAndKey selects on nothing.
  2. A custom resolver keyed on signature_schemes. That is the leaf key type. I tested a chain with an EC leaf key under an ML-DSA-signed intermediate, which looks acceptable on signature_schemes and is exactly the chain the client excluded.
  3. Waiting for post-quantum support to land. It does not close this. With rustls-post-quantum 0.2.4 and aws-lc-rs-unstable the resolver reports signature_schemes = [ML_DSA_44] and serves the chain, and there is still no way to ask what the client accepts on certificates.
  4. Terminating TLS on OpenSSL or nginx instead. That works, since both honor the extension, and it means not using rustls for this.
  5. Having rustls enforce the constraint by default. I'm specifically not asking for this piece. It will change the behavior for existing users and sits further from the fallback clause than the current behavior does.

Additional context certificate_authorities is already on ClientHello, with a doc comment linking RFC 8446 section 4.2.4, and on dev HEAD ( 2cd5cc1, 0.24.0-dev.1, read 2026-08-10) there is a deliberate carve-out hiding it from the resolver on TLS 1.2. That extension exists to guide server certificate selection. signature_algorithms_cert is the other extension in that job, and it is the one a post-quantum migration turns on.

What I measured: one configured chain, an EC leaf key under an ML-DSA-44-signed intermediate, with a client advertising ecdsa_secp256r1_sha256 (0x0403) in both signature_algorithms and signature_algorithms_cert. rustls serves that chain. Reproduced on three builds of v/0.23.43, ring, default aws_lc_rs, and rustls-post-quantum 0.2.4 with aws-lc-rs-unstable, so it is not specific to one provider.

Other stacks read the same SHOULD differently. openssl s_server 3.5.5 and nginx 1.31.3 refuse the same configuration with handshake_failure, while Caddy and Envoy serve the chain as rustls does. My scripts, captured output, and per-cell versions are at https://github.com/DannyBCarrJr/pqc-chain-selection, archived at https://doi.org/10.5281/zenodo.21911032. The rustls column is runners/FINDINGS-rustls.md.

I'm more than willing to test any changes against that harness.