Skip to content

0.23.27 Introduces HelloRetryRequest When Connecting to Non-PQ FIPS TLS servers #2495

@RobMor

Description

@RobMor

Checklist

  • I've searched the issue tracker for similar bugs.

Describe the bug
After updating rustls to 0.23.27 which enables prefer-post-quantum by default, connections to FIPS TLS servers that don't support the post-quantum algorithms have to go through a HelloRetryRequest to negotiate down to FIPS non-PQ KeyShare algorithms like SECP256R1.

We still have to deal with a lot of servers that don't support post-quantum algorithms. It would be nice if the extra round trip wasn't required in the default configuration.

It might make more sense to use a post-quantum algorithm that has a FIPS compliant "fallback" like SECP256R1MLKEM768 when rustls is running in FIPS mode. That way in the handshake rustls will send both the PQ and non-PQ KeyShare options, instead of just the PQ option like it does now. That would prevent a round trip even when talking to non-PQ FIPS servers.

In the meantime it's easy enough to mitigate by just disabling prefer-post-quantum.

To Reproduce

  1. Use rustls with fips feature enabled to connect to a server that supports only secp256r1.
  2. Observe that with rustls 0.23.26 there's no HelloRetryRequest, while in 0.23.27 there is.
Example code

Cargo.toml:

[package]
name = "repro"
version = "0.1.0"
edition = "2021"

[dependencies]
axum = "0.8.4"
axum-server = { version = "0.7.2", features = ["tls-rustls"] }
rcgen = "0.13.2"
reqwest = { version = "0.12.20", features = ["rustls-tls"] }
rustls = { version = "0.23.27", features = ["fips"] }
tokio = { version = "1.45.1", features = ["full"] }
tracing = "0.1.41"
tracing-subscriber = "0.3.19"

client.rs:

#[tokio::main]
async fn main() {
    tracing_subscriber::fmt::init();

    rustls::crypto::default_fips_provider().install_default().unwrap();

    let client = reqwest::ClientBuilder::new()
        .use_rustls_tls()
        .danger_accept_invalid_certs(true)
        .build()
        .unwrap();

    let resp = client.get("https://127.0.0.1:8000")
        .send().await.unwrap()
        .text().await.unwrap();

    println!("{}", resp);
}

server.rs

#[tokio::main]
async fn main() {
    tracing_subscriber::fmt::init();

    let rcgen::CertifiedKey { cert, key_pair } = rcgen::generate_simple_self_signed(["localhost".to_string()]).unwrap();

    let mut crypto_provider = rustls::crypto::default_fips_provider();
    crypto_provider.kx_groups = vec![rustls::crypto::aws_lc_rs::kx_group::SECP256R1];
    crypto_provider.install_default().unwrap();

    let config = axum_server::tls_rustls::RustlsConfig::from_der(vec![cert.der().to_vec()], key_pair.serialized_der().to_vec()).await.unwrap();

    let app = axum::Router::new().route("/", axum::routing::get(|| async { "Hello, World!" }));

    axum_server::bind_rustls(std::net::SocketAddr::from(([127, 0, 0, 1], 8000)), config)
        .serve(app.into_make_service())
        .await
        .unwrap()
}

Running:

RUST_LOG=trace cargo run --bin client
Client output:
2025-06-15T06:45:48.540799Z DEBUG rustls::client::hs: No cached session for IpAddress(V4(Ipv4Addr([127, 0, 0, 1])))    
2025-06-15T06:45:48.542694Z DEBUG rustls::client::hs: Not resuming any session    
2025-06-15T06:45:48.542947Z TRACE rustls::client::hs: Sending ClientHello Message {
    version: TLSv1_0,
    payload: Handshake {
        parsed: HandshakeMessagePayload {
            typ: ClientHello,
            payload: ClientHello(
                ClientHelloPayload {
                    client_version: TLSv1_2,
                    random: cca9a4792c34535cbe8e2161805406230ab8873e83ee428a69cf47deb0630d57,
                    session_id: 53af4dd2f8df6913d3ca531b271e136f42bb95f52649566f895315061a26e516,
                    cipher_suites: [
                        TLS13_AES_256_GCM_SHA384,
                        TLS13_AES_128_GCM_SHA256,
                        TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
                        TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
                        TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
                        TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
                        TLS_EMPTY_RENEGOTIATION_INFO_SCSV,
                    ],
                    compression_methods: [
                        Null,
                    ],
                    extensions: [
                        EcPointFormats(
                            [
                                Uncompressed,
                            ],
                        ),
                        ExtendedMasterSecretRequest,
                        SignatureAlgorithms(
                            [
                                RSA_PKCS1_SHA1,
                                ECDSA_SHA1_Legacy,
                                RSA_PKCS1_SHA256,
                                ECDSA_NISTP256_SHA256,
                                RSA_PKCS1_SHA384,
                                ECDSA_NISTP384_SHA384,
                                RSA_PKCS1_SHA512,
                                ECDSA_NISTP521_SHA512,
                                RSA_PSS_SHA256,
                                RSA_PSS_SHA384,
                                RSA_PSS_SHA512,
                                ED25519,
                                ED448,
                            ],
                        ),
                        Protocols(
                            [
                                ProtocolName(
                                    6832,
                                ),
                                ProtocolName(
                                    687474702f312e31,
                                ),
                            ],
                        ),
                        SessionTicket(
                            Request,
                        ),
                        KeyShare(
                            [
                                KeyShareEntry {
                                    group: X25519MLKEM768,
                                    payload: 9797772a163597426453baa15ac109033bb9915bca01c6699cf26c34e7254ff9b867877e88ac08ccb1c1c09332691b022475051356720085ad94928eecd02f0b9c12536065c02754041c1d6524b1578bc7527773f2641dac30b5897a6e5df3c667d94df24b6e6fb630f97783114b14cc1231efb07224e37097199b31185216c685e7381ecdd93d21d730d82765f519a2f6105a78147e856a73d7e685c73909bc77a8c9995f7e810e1ccc0c841c8c618b9183543c98c85a93f091a7e52c631a1b547888c2f9961ec5a362808727806cffc04809e2ce88737219c1478b13426eb151aecb971da6306f86971f8a4c9b27c0d5464c7b16cd44c5a3ee60058004880492634fcac66968cfc9275f821940fbe26bdcf934a5a085489ab4018a6f835657211a95c5eaabc4d90368110be0a18b1f526e53bb5b6898cd28c3ccdc425a6260291b1991124c7ae35a5cd51098104a30efb97c932aafd7843e4329513503a7c2428d4db2568f10b8f096c74b24c2765b3801e61e186904f638a4e5f557a972520d4c1196a1513c909eb55861cf496889c811c47087f69339e30373fc486a73d0be7afb95f29b5e2395414b1a57bc91576315b3b7d35d294c5425f90ffeb731a0f3751d6a91d0c07ff997a160a1ce54858790a2858082589240518b21360056ad0c4cbe9b70a348e751aa920259793ce73033e275025bc6668b9cb820014ac4d7621d05ca9ee236f6820d323526e2c207da1619c282063e07c3ef36963eb7b4aba2c98653cb28ea173b6456696598416bc8812211d7e55f9dc57abf80957b37bf7dc059e398885afb35ca9782bb7212de54877bd6b1fb8b4873932a59cc45f6e0834be8c0e7fc75b75c4a4089b25ad2a746a5a081ab75f0944ff3489177bb9e8e1b524ce0bd7cba7e9f90bf0004341af637ea66c7e122ae8256146ab315fc2b9dcd9116db5207578bb109f09564f1388e063575048b0af087ba5795e5a07a969a8c3ee988fd02bf8029a10de7ca7962bce5974651a420206319725a488bf55bda5111ef5b75daa479bdcba36713bdfa733455114fec4889ae4b5a654c34366964a4853694289aae006c38ac6ab70192ddd46db263559d8a130eb76bf664836ca5464d6a02aca39f37a8838cd9767410b76537a39355cfcae6268bf125e78cac5774b079b75f644917a2e094c2e618be2c5dad3c7e561502b0475687a2781889ad23e442d946b37d09be4bd863ab09adb893415dc749c104592dba265e6a587b3b1f3a3a5ea3b2626e06566e02002c35cc135a7f17003fa387cd98aa08ebe901ef2672bc42b3e74a4e1e2592dfb3cc51bbb3fc0c031cfcb6e1f7a468b0b339c9485e07c3c5f878dcc56594014ce6cb04aa5a19da055574755b5c159eef6630a6129f38c7cd957882b1465d6b185c46d4a737e9005fc501917799d128b08cb8a3ca02b70049acddb917ded103e941527c1b6f8b620393a67b827b13a3b5bfd9ac2f319c116d7087ebe7821ce9ac64a46c73621c9f81b77a0b1410ca249bb54fd8437185f36176fa8b0dfa3c6b7971395144e2eb703eb9c3a1fbb98eb744492726f61107182c5e317aa6a0127aa5d5ae1ce60d09d22347bbb62d996145b87059eaaac1641837803e3740c3c45adbc3d5285c736f686efa72a6fb78857f2a23a8853730cef5de559f200b8829c031111878e18286312ad755f5ab47477d3c217e7b69,
                                },
                            ],
                        ),
                        CertificateStatusRequest(
                            Ocsp(
                                OcspCertificateStatusRequest {
                                    responder_ids: [],
                                    extensions: ,
                                },
                            ),
                        ),
                        NamedGroups(
                            [
                                X25519MLKEM768,
                                secp256r1,
                                secp384r1,
                            ],
                        ),
                        SupportedVersions(
                            SupportedProtocolVersions {
                                tls13: true,
                                tls12: true,
                            },
                        ),
                        PresharedKeyModes(
                            [
                                PSK_DHE_KE,
                            ],
                        ),
                    ],
                },
            ),
        },
        encoded: 010005850303cca9a4792c34535cbe8e2161805406230ab8873e83ee428a69cf47deb0630d572053af4dd2f8df6913d3ca531b271e136f42bb95f52649566f895315061a26e516000e13021301c02cc02bc030c02f00ff0100052e000b0002010000170000000d001c001a02010203040104030501050306010603080408050806080708080010000e000c02683208687474702f312e3100230000003304c604c411ec04c09797772a163597426453baa15ac109033bb9915bca01c6699cf26c34e7254ff9b867877e88ac08ccb1c1c09332691b022475051356720085ad94928eecd02f0b9c12536065c02754041c1d6524b1578bc7527773f2641dac30b5897a6e5df3c667d94df24b6e6fb630f97783114b14cc1231efb07224e37097199b31185216c685e7381ecdd93d21d730d82765f519a2f6105a78147e856a73d7e685c73909bc77a8c9995f7e810e1ccc0c841c8c618b9183543c98c85a93f091a7e52c631a1b547888c2f9961ec5a362808727806cffc04809e2ce88737219c1478b13426eb151aecb971da6306f86971f8a4c9b27c0d5464c7b16cd44c5a3ee60058004880492634fcac66968cfc9275f821940fbe26bdcf934a5a085489ab4018a6f835657211a95c5eaabc4d90368110be0a18b1f526e53bb5b6898cd28c3ccdc425a6260291b1991124c7ae35a5cd51098104a30efb97c932aafd7843e4329513503a7c2428d4db2568f10b8f096c74b24c2765b3801e61e186904f638a4e5f557a972520d4c1196a1513c909eb55861cf496889c811c47087f69339e30373fc486a73d0be7afb95f29b5e2395414b1a57bc91576315b3b7d35d294c5425f90ffeb731a0f3751d6a91d0c07ff997a160a1ce54858790a2858082589240518b21360056ad0c4cbe9b70a348e751aa920259793ce73033e275025bc6668b9cb820014ac4d7621d05ca9ee236f6820d323526e2c207da1619c282063e07c3ef36963eb7b4aba2c98653cb28ea173b6456696598416bc8812211d7e55f9dc57abf80957b37bf7dc059e398885afb35ca9782bb7212de54877bd6b1fb8b4873932a59cc45f6e0834be8c0e7fc75b75c4a4089b25ad2a746a5a081ab75f0944ff3489177bb9e8e1b524ce0bd7cba7e9f90bf0004341af637ea66c7e122ae8256146ab315fc2b9dcd9116db5207578bb109f09564f1388e063575048b0af087ba5795e5a07a969a8c3ee988fd02bf8029a10de7ca7962bce5974651a420206319725a488bf55bda5111ef5b75daa479bdcba36713bdfa733455114fec4889ae4b5a654c34366964a4853694289aae006c38ac6ab70192ddd46db263559d8a130eb76bf664836ca5464d6a02aca39f37a8838cd9767410b76537a39355cfcae6268bf125e78cac5774b079b75f644917a2e094c2e618be2c5dad3c7e561502b0475687a2781889ad23e442d946b37d09be4bd863ab09adb893415dc749c104592dba265e6a587b3b1f3a3a5ea3b2626e06566e02002c35cc135a7f17003fa387cd98aa08ebe901ef2672bc42b3e74a4e1e2592dfb3cc51bbb3fc0c031cfcb6e1f7a468b0b339c9485e07c3c5f878dcc56594014ce6cb04aa5a19da055574755b5c159eef6630a6129f38c7cd957882b1465d6b185c46d4a737e9005fc501917799d128b08cb8a3ca02b70049acddb917ded103e941527c1b6f8b620393a67b827b13a3b5bfd9ac2f319c116d7087ebe7821ce9ac64a46c73621c9f81b77a0b1410ca249bb54fd8437185f36176fa8b0dfa3c6b7971395144e2eb703eb9c3a1fbb98eb744492726f61107182c5e317aa6a0127aa5d5ae1ce60d09d22347bbb62d996145b87059eaaac1641837803e3740c3c45adbc3d5285c736f686efa72a6fb78857f2a23a8853730cef5de559f200b8829c031111878e18286312ad755f5ab47477d3c217e7b69000500050100000000000a0008000611ec00170018002b00050403040303002d00020101,
    },
}    
2025-06-15T06:45:48.544361Z TRACE rustls::client::hs: Got HRR HelloRetryRequest { legacy_version: TLSv1_2, session_id: 53af4dd2f8df6913d3ca531b271e136f42bb95f52649566f895315061a26e516, cipher_suite: TLS13_AES_256_GCM_SHA384, extensions: [KeyShare(secp256r1), SupportedVersions(TLSv1_3)] }    
2025-06-15T06:45:48.544875Z TRACE rustls::client::hs: Sending ClientHello Message {
    version: TLSv1_2,
    payload: Handshake {
        parsed: HandshakeMessagePayload {
            typ: ClientHello,
            payload: ClientHello(
                ClientHelloPayload {
                    client_version: TLSv1_2,
                    random: cca9a4792c34535cbe8e2161805406230ab8873e83ee428a69cf47deb0630d57,
                    session_id: 53af4dd2f8df6913d3ca531b271e136f42bb95f52649566f895315061a26e516,
                    cipher_suites: [
                        TLS13_AES_256_GCM_SHA384,
                        TLS13_AES_128_GCM_SHA256,
                        TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
                        TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
                        TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
                        TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
                        TLS_EMPTY_RENEGOTIATION_INFO_SCSV,
                    ],
                    compression_methods: [
                        Null,
                    ],
                    extensions: [
                        EcPointFormats(
                            [
                                Uncompressed,
                            ],
                        ),
                        ExtendedMasterSecretRequest,
                        SignatureAlgorithms(
                            [
                                RSA_PKCS1_SHA1,
                                ECDSA_SHA1_Legacy,
                                RSA_PKCS1_SHA256,
                                ECDSA_NISTP256_SHA256,
                                RSA_PKCS1_SHA384,
                                ECDSA_NISTP384_SHA384,
                                RSA_PKCS1_SHA512,
                                ECDSA_NISTP521_SHA512,
                                RSA_PSS_SHA256,
                                RSA_PSS_SHA384,
                                RSA_PSS_SHA512,
                                ED25519,
                                ED448,
                            ],
                        ),
                        Protocols(
                            [
                                ProtocolName(
                                    6832,
                                ),
                                ProtocolName(
                                    687474702f312e31,
                                ),
                            ],
                        ),
                        SessionTicket(
                            Request,
                        ),
                        KeyShare(
                            [
                                KeyShareEntry {
                                    group: secp256r1,
                                    payload: 048d90ee7fbb14b245b5d30e385d1a9ae1bed286a7c1d77d8ce1bf2353a6ef3dc80cb452ff0c0519b1f1ab31f1d571d64e43513344bdedb3888b021740f7aadadd,
                                },
                            ],
                        ),
                        CertificateStatusRequest(
                            Ocsp(
                                OcspCertificateStatusRequest {
                                    responder_ids: [],
                                    extensions: ,
                                },
                            ),
                        ),
                        NamedGroups(
                            [
                                X25519MLKEM768,
                                secp256r1,
                                secp384r1,
                            ],
                        ),
                        SupportedVersions(
                            SupportedProtocolVersions {
                                tls13: true,
                                tls12: true,
                            },
                        ),
                        PresharedKeyModes(
                            [
                                PSK_DHE_KE,
                            ],
                        ),
                    ],
                },
            ),
        },
        encoded: 010001060303cca9a4792c34535cbe8e2161805406230ab8873e83ee428a69cf47deb0630d572053af4dd2f8df6913d3ca531b271e136f42bb95f52649566f895315061a26e516000e13021301c02cc02bc030c02f00ff010000af000b0002010000170000000d001c001a02010203040104030501050306010603080408050806080708080010000e000c02683208687474702f312e310023000000330047004500170041048d90ee7fbb14b245b5d30e385d1a9ae1bed286a7c1d77d8ce1bf2353a6ef3dc80cb452ff0c0519b1f1ab31f1d571d64e43513344bdedb3888b021740f7aadadd000500050100000000000a0008000611ec00170018002b00050403040303002d00020101,
    },
}    
2025-06-15T06:45:48.545067Z TRACE rustls::conn: Dropping CCS    
2025-06-15T06:45:48.546164Z TRACE rustls::client::hs: We got ServerHello ServerHelloPayload {
    extensions: [
        KeyShare(
            KeyShareEntry {
                group: secp256r1,
                payload: 04404abd137d7f80e54ee43d3764e7f2ab9d544150f2b23af961e843faaf451406d28d184ce79d36dafcae31deff103c25e31e0d763978820112ef1144cb6b0208,
            },
        ),
        SupportedVersions(
            TLSv1_3,
        ),
    ],
    legacy_version: TLSv1_2,
    random: 71848961bd81ebafde9a6b72ef2e7bb703a41ffbf8d4ddea86e2ad615be65e88,
    session_id: 53af4dd2f8df6913d3ca531b271e136f42bb95f52649566f895315061a26e516,
    cipher_suite: TLS13_AES_256_GCM_SHA384,
    compression_method: Null,
}    
2025-06-15T06:45:48.546206Z DEBUG rustls::client::hs: Using ciphersuite TLS13_AES_256_GCM_SHA384    
2025-06-15T06:45:48.546224Z DEBUG rustls::client::tls13: Not resuming    
2025-06-15T06:45:48.546232Z TRACE rustls::client::client_conn: EarlyData rejected    
2025-06-15T06:45:48.546461Z DEBUG rustls::client::tls13: TLS1.3 encrypted extensions: [Protocols(SingleProtocolName(ProtocolName(6832)))]    
2025-06-15T06:45:48.546477Z DEBUG rustls::client::hs: ALPN protocol is Some(b"h2")    
2025-06-15T06:45:48.546541Z TRACE rustls::client::tls13: Server cert is CertificateChain([CertificateDer(0x3082015e30820104a003020102021443f6ff1d1133a7fba690faf7acf23be9062eb3ac300a06082a8648ce3d0403023021311f301d06035504030c16726367656e2073656c66207369676e656420636572743020170d3735303130313030303030305a180f34303936303130313030303030305a3021311f301d06035504030c16726367656e2073656c66207369676e656420636572743059301306072a8648ce3d020106082a8648ce3d03010703420004aa707876af817c015dd0a9e35a55f3eb69760ab0e271995bc5675db2f9256d850d0b6383c89b08a81a8e4b0f2f3cfe1cdc05784aa7c7b16ec3230f4546db65afa318301630140603551d11040d300b82096c6f63616c686f7374300a06082a8648ce3d040302034800304502202af2319af32606301358b8630c3ba3bffeaffc236b2977c477d8ced0d38a06a6022100974f9d16806b355796dc3975cd9f951be09f35bb886d45693110ab755d91d754)])
Applicable Version(s)
The issue was introduced in 0.23.27 with the enablement of the use of PQ algorithms by default.

Expected behavior

This is an expected behavior based on how rustls is configured right now.

Maybe this shouldn't be a bug report, but it seemed like the best template for starting a discussion on this.

Additional context

X25519 is not FIPS: #1992

So when X25519MLKEM768 is used in a handshake, the bare X25519 KeyShare cannot be used, like it would be for non-FIPS handshakes: https://github.com/rustls/rustls/blob/main/rustls/src/client/hs.rs#L328-L331

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions

      pFad - Phonifier reborn

      Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

      Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


      Alternative Proxies:

      Alternative Proxy

      pFad Proxy

      pFad v3 Proxy

      pFad v4 Proxy