client: add support for sending unsupported cipher suites

This commit is contained in:
Dirkjan Ochtman 2023-02-02 12:18:50 +01:00
parent 8c75101878
commit 2682eb7446
4 changed files with 45 additions and 22 deletions

View File

@ -187,6 +187,7 @@ impl ConfigBuilder<ClientConfig, WantsClientCert> {
#[cfg(feature = "secret_extraction")]
enable_secret_extraction: false,
enable_early_data: false,
unsupported_suites: Vec::new(),
}
}
}

View File

@ -1,12 +1,10 @@
use crate::builder::{ConfigBuilder, WantsCipherSuites};
use crate::conn::{CommonState, ConnectionCommon, Protocol, Side};
use crate::enums::{CipherSuite, ProtocolVersion, SignatureScheme};
use crate::error::Error;
use crate::error::{Error, PeerMisbehaved};
use crate::kx::SupportedKxGroup;
#[cfg(feature = "logging")]
use crate::log::trace;
#[cfg(feature = "quic")]
use crate::msgs::enums::AlertDescription;
use crate::msgs::enums::NamedGroup;
use crate::msgs::handshake::ClientExtension;
@ -195,6 +193,11 @@ pub struct ClientConfig {
///
/// The default is false.
pub enable_early_data: bool,
/// Send unsupported cipher suites to appease fingerprinting servers.
///
/// Note: if the server selects one of these suites, the connection will fail.
pub unsupported_suites: Vec<CipherSuite>,
}
impl fmt::Debug for ClientConfig {
@ -238,11 +241,31 @@ impl ClientConfig {
danger::DangerousClientConfig { cfg: self }
}
pub(super) fn find_cipher_suite(&self, suite: CipherSuite) -> Option<SupportedCipherSuite> {
self.cipher_suites
pub(super) fn find_cipher_suite(
&self,
suite: CipherSuite,
retried: bool,
common: &mut CommonState,
) -> Result<SupportedCipherSuite, Error> {
let supported = self
.cipher_suites
.iter()
.copied()
.find(|&scs| scs.suite() == suite)
.find(|&scs| scs.suite() == suite);
if let Some(supported) = supported {
Ok(supported)
} else if self.unsupported_suites.contains(&suite) {
Err(Error::UnsupportedSuiteSelected)
} else if retried {
Err(common
.illegal_param(PeerMisbehaved::IllegalHelloRetryRequestWithUnofferedCipherSuite))
} else {
common.send_fatal_alert(AlertDescription::HandshakeFailure);
Err(Error::PeerMisbehaved(
PeerMisbehaved::SelectedUnofferedCipherSuite,
))
}
}
}

View File

@ -339,6 +339,12 @@ fn emit_client_hello_for_retry(
.collect();
// We don't do renegotiation at all, in fact.
cipher_suites.push(CipherSuite::TLS_EMPTY_RENEGOTIATION_INFO_SCSV);
cipher_suites.extend(
config
.unsupported_suites
.iter()
.copied(),
);
let mut chp = HandshakeMessagePayload {
typ: HandshakeType::ClientHello,
@ -555,12 +561,7 @@ impl State<ClientConnectionData> for ExpectServerHello {
let suite = self
.config
.find_cipher_suite(server_hello.cipher_suite)
.ok_or_else(|| {
cx.common
.send_fatal_alert(AlertDescription::HandshakeFailure);
Error::PeerMisbehaved(PeerMisbehaved::SelectedUnofferedCipherSuite)
})?;
.find_cipher_suite(server_hello.cipher_suite, false, cx.common)?;
if version != suite.version().version {
return Err(cx
@ -715,18 +716,10 @@ impl ExpectServerHelloOrHelloRetryRequest {
}
// Or asks us to use a ciphersuite we didn't offer.
let maybe_cs = self
let cs = self
.next
.config
.find_cipher_suite(hrr.cipher_suite);
let cs = match maybe_cs {
Some(cs) => cs,
None => {
return Err(cx.common.illegal_param(
PeerMisbehaved::IllegalHelloRetryRequestWithUnofferedCipherSuite,
));
}
};
.find_cipher_suite(hrr.cipher_suite, true, cx.common)?;
// HRR selects the ciphersuite.
cx.common.suite = Some(cs);

View File

@ -92,6 +92,9 @@ pub enum Error {
/// The `max_fragment_size` value supplied in configuration was too small,
/// or too large.
BadMaxFragmentSize,
/// The peer selected a unsupported cipher suite that we configured.
UnsupportedSuiteSelected,
}
#[non_exhaustive]
@ -321,6 +324,9 @@ impl fmt::Display for Error {
Self::BadMaxFragmentSize => {
write!(f, "the supplied max_fragment_size was too small or large")
}
Self::UnsupportedSuiteSelected => {
write!(f, "configured unsupported cipher suite selected")
}
Self::General(ref err) => write!(f, "unexpected error: {}", err),
}
}