Skip to content

Expose unverified server certificate chains #5821

Description

@Mark-Simulacrum

Security issue notifications

If you discover a potential security issue in s2n we ask that you notify
AWS Security via our vulnerability reporting page. Please do not create a public github issue.

Problem:

Applications using s2n-tls currently cannot access the server's certificate chain when certificate validation fails. The existing s2n_connection_get_peer_cert_chain API requires validation to have succeeded (S2N_ERR_CERT_NOT_VALIDATED is returned otherwise), which prevents applications from inspecting the certificate chain for logging or diagnostics when a handshake fails due to an untrusted or invalid certificate. This makes it harder to debug certificate validation failures, which currently return an opaque ("Certificate is untrusted") error which is not useful for debugging.

This would complement the existing s2n_connection_get_client_cert_chain API, which already provides this functionality for client-sent certificates.

Need By Date:

Sooner is better.

Solution:

A description of the possible solution in terms of S2N architecture. Highlight and explain any potentially controversial design decisions taken.

  • Does this change what S2N sends over the wire? no
  • Does this change any public APIs? yes, new public API
  • Which versions of TLS will this impact? all versions

Possible problems to think through:

  • How can we avoid increasing memory usage for all connections? Perhaps the chain can be removed after a successful handshake (since it's then available in the get_peer_cert_chain method? Or maybe we need an opt-in mechanism on the connection to enable collecting this information?
  • What format is the cert chain in? Directly what is received on the wire? Are there any TLS version differences in that format?
  • Can we improve the error message instead of or in addition to exposing the raw cert chain?

Requirements / Acceptance Criteria:

What must a solution address in order to solve the problem? How do we know the solution is complete?

  • RFC links: n/a
  • Related Issues: n/a
  • Will the Usage Guide or other documentation need to be updated? just documentation on the new function
  • Testing: How will this change be tested?
    • Will this change trigger SAW changes? probably no
    • Should this change be fuzz tested? Hopefully, no net-new surface area for untrusted input. Applications would need to deal safely with the untrusted output from the new function, but this shouldn't be a meaningful shift from what they already need to cope with for the client cert chain.

This will need to get exposed in the Rust bindings, so that it can then be exposed in s2n-quic (cc aws/s2n-quic#3037).

Out of scope:

  • Safely parsing the returned chain is in customer hands.

Activity

  1. Mark-Simulacrum commented on Apr 28, 2026

    @Mark-Simulacrum
    ContributorAuthor

    After some offline discussion, extending this issue with more related API surface area. For all of these we will want the information exposed via s2n-quic events as part of the delivery (can be unstable):

    Local configuration

    1. [Client + server local certificate chain] Expose the configured local certificate chain from an s2n-tls Connection. This would introspect what was configured on the Config, and return the default certificate chain. The certificate chain should roughly correspond to s2n_connection_get_selected_cert. selected_cert looks likely to be sufficient for this but I don't see tests for the handshake failure case, so adding/confirming test coverage may be the only s2n-tls ask.
    2. [Client + server local trust store] Expose the configured trust store from an s2n-tls Connection. This could use the same 'serialization' as the cert_authorities extension (i.e., return distinguished names for each contained certificate). This should always be accessible, even if the particular connection didn't use the trust store.
    3. Active s2n-tls security policy on the Config associated with the connection.

    Remote information

    1. unverified_client_cert_chain exposed in s2n-quic
    2. unverified_server_cert_chain added (the original ask in this issue)
    3. server-sent certificate_authorities persisted out of the CertificateRequest as an s2n-quic event / s2n-tls accessor
    4. client-sent certificate_authorities in ClientHello (new feature) also accessible
  2. maddeleine commented on Apr 29, 2026

    @maddeleine
    Contributor

    Briefly looked into our cert retrieval behavior. The API for retrieving your own cert works in both successful and failed handshakes. The API to retrieve your peer's cert doesn't work in handshakes that have failed cert authentication (s2n_connection_get_peer_cert_chain.) However, we have another API s2n_connection_get_client_cert_chain that does work after failed cert validation. Weirdly though, in our rust bindings, we gated it by handshake complete, so it cannot return a cert if the handshake fails.
    Anyways, yes, there is no way of retrieving a cert if the cert validation fails in our Rust bindings. We're going to need a new API for this so probably just get_unverified_peer_cert_chain() and have it work for both server and client. I suspect we can just generalize what we did for s2n_connection_get_client_cert_chain since it already returns unverified certs.

    This issue is basically three different asks:

    1. Expose unverified cert chains (acceptance criteria is s2n-quic can retrieve unverified certs.)
    2. Expose local and peer certificate authorities(No idea what this is going to look like)
    3. Connection getter for security policy
  3. maddeleine commented on May 2, 2026

    @maddeleine
    Contributor
  4. eyupcanakman commented on Jun 1, 2026

    @eyupcanakman

    @maddeleine I'd like to take the unverified peer cert chain part (just ask 1) if it's still open, and leave the certificate authorities and security policy items for later. For the memory concern in the issue, do you want the chain always collected and dropped after a successful handshake, or put behind an opt-in? If you'd rather take this one yourself given the priority, that works too.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions