mirror of https://github.com/ctz/rustls
client: add support for sending unsupported cipher suites
This commit is contained in:
parent
8c75101878
commit
2682eb7446
|
@ -187,6 +187,7 @@ impl ConfigBuilder<ClientConfig, WantsClientCert> {
|
|||
#[cfg(feature = "secret_extraction")]
|
||||
enable_secret_extraction: false,
|
||||
enable_early_data: false,
|
||||
unsupported_suites: Vec::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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),
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue