mirror of https://github.com/ctz/rustls
client: use type + builder pattern for resumption config API
Originally developed in #1259. Co-authored-by: Daniel McCarney <daniel@binaryparadox.net> Co-authored-by: Jacob Hoffman-Andrews <github@hoffman-andrews.com>
This commit is contained in:
parent
2ac21fb684
commit
8a2a87b240
|
@ -429,7 +429,9 @@ fn make_config(args: &Args) -> Arc<rustls::ClientConfig> {
|
|||
config.key_log = Arc::new(rustls::KeyLogFile::new());
|
||||
|
||||
if args.flag_no_tickets {
|
||||
config.tls12_resumption = Some(rustls::client::Tls12Resumption::SessionIdOnly);
|
||||
config.resumption = config
|
||||
.resumption
|
||||
.tls12_resumption(rustls::client::Tls12Resumption::SessionIdOnly);
|
||||
}
|
||||
|
||||
if args.flag_no_sni {
|
||||
|
|
|
@ -11,7 +11,7 @@ use std::ops::DerefMut;
|
|||
use std::sync::Arc;
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
use rustls::client::{ClientSessionMemoryCache, NoClientSessionStorage};
|
||||
use rustls::client::Resumption;
|
||||
use rustls::server::{
|
||||
AllowAnyAuthenticatedClient, NoClientAuth, NoServerSessionStorage, ServerSessionMemoryCache,
|
||||
};
|
||||
|
@ -358,9 +358,9 @@ fn make_client_config(
|
|||
};
|
||||
|
||||
if resume != ResumptionParam::No {
|
||||
cfg.session_storage = ClientSessionMemoryCache::new(128);
|
||||
cfg.resumption = Resumption::in_memory_sessions(128);
|
||||
} else {
|
||||
cfg.session_storage = Arc::new(NoClientSessionStorage {});
|
||||
cfg.resumption = Resumption::disabled();
|
||||
}
|
||||
|
||||
cfg
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
// https://boringssl.googlesource.com/boringssl/+/master/ssl/test
|
||||
//
|
||||
|
||||
use rustls::client::{ClientConfig, ClientConnection};
|
||||
use rustls::client::{ClientConfig, ClientConnection, Resumption};
|
||||
use rustls::internal::msgs::codec::Codec;
|
||||
use rustls::internal::msgs::persist;
|
||||
use rustls::server::{ClientHello, ServerConfig, ServerConnection};
|
||||
|
@ -466,7 +466,7 @@ impl ClientCacheWithoutKxHints {
|
|||
fn new(delay: u32) -> Arc<ClientCacheWithoutKxHints> {
|
||||
Arc::new(ClientCacheWithoutKxHints {
|
||||
delay,
|
||||
storage: client::ClientSessionMemoryCache::new(32),
|
||||
storage: Arc::new(client::ClientSessionMemoryCache::new(32)),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -503,7 +503,7 @@ impl client::ClientSessionStore for ClientCacheWithoutKxHints {
|
|||
) {
|
||||
value.rewind_epoch(self.delay);
|
||||
self.storage
|
||||
.insert_tls13_ticket(server_name, value);
|
||||
.insert_tls13_ticket(server_name, value)
|
||||
}
|
||||
|
||||
fn take_tls13_ticket(
|
||||
|
@ -550,8 +550,7 @@ fn make_client_cfg(opts: &Options) -> Arc<ClientConfig> {
|
|||
});
|
||||
}
|
||||
|
||||
let persist = ClientCacheWithoutKxHints::new(opts.resumption_delay);
|
||||
cfg.session_storage = persist;
|
||||
cfg.resumption = Resumption::store(ClientCacheWithoutKxHints::new(opts.resumption_delay));
|
||||
cfg.enable_sni = opts.use_sni;
|
||||
cfg.max_fragment_size = opts.max_fragment;
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@ use crate::suites::SupportedCipherSuite;
|
|||
use crate::verify::{self, CertificateTransparencyPolicy};
|
||||
use crate::{anchors, key, versions};
|
||||
|
||||
use super::Tls12Resumption;
|
||||
use super::client_conn::Resumption;
|
||||
|
||||
use std::marker::PhantomData;
|
||||
use std::sync::Arc;
|
||||
|
@ -175,10 +175,9 @@ impl ConfigBuilder<ClientConfig, WantsClientCert> {
|
|||
cipher_suites: self.state.cipher_suites,
|
||||
kx_groups: self.state.kx_groups,
|
||||
alpn_protocols: Vec::new(),
|
||||
session_storage: handy::ClientSessionMemoryCache::new(256),
|
||||
resumption: Resumption::default(),
|
||||
max_fragment_size: None,
|
||||
client_auth_cert_resolver,
|
||||
tls12_resumption: Some(Tls12Resumption::SessionIdOrTickets),
|
||||
versions: self.state.versions,
|
||||
enable_sni: true,
|
||||
verifier: self.state.verifier,
|
||||
|
|
|
@ -17,6 +17,7 @@ use crate::versions;
|
|||
use crate::ExtractedSecrets;
|
||||
use crate::KeyLog;
|
||||
|
||||
use super::handy::{ClientSessionMemoryCache, NoClientSessionStorage};
|
||||
use super::hs;
|
||||
|
||||
use std::error::Error as StdError;
|
||||
|
@ -116,7 +117,8 @@ pub trait ResolvesClientCert: Send + Sync {
|
|||
/// # Defaults
|
||||
///
|
||||
/// * [`ClientConfig::max_fragment_size`]: the default is `None`: TLS packets are not fragmented to a specific size.
|
||||
/// * [`ClientConfig::session_storage`]: the default stores 256 sessions in memory.
|
||||
/// * [`ClientConfig::resumption`]: supports resumption with up to 256 server names, using session
|
||||
/// ids or tickets, with a max of eight tickets per server.
|
||||
/// * [`ClientConfig::alpn_protocols`]: the default is empty -- no ALPN protocol is negotiated.
|
||||
/// * [`ClientConfig::key_log`]: key material is not logged.
|
||||
#[derive(Clone)]
|
||||
|
@ -135,8 +137,8 @@ pub struct ClientConfig {
|
|||
/// If empty, no ALPN extension is sent.
|
||||
pub alpn_protocols: Vec<Vec<u8>>,
|
||||
|
||||
/// How we store session data or tickets.
|
||||
pub session_storage: Arc<dyn ClientSessionStore>,
|
||||
/// How and when the client can resume a previous session.
|
||||
pub resumption: Resumption,
|
||||
|
||||
/// The maximum size of TLS message we'll emit. If None, we don't limit TLS
|
||||
/// message lengths except to the 2**16 limit specified in the standard.
|
||||
|
@ -150,13 +152,6 @@ pub struct ClientConfig {
|
|||
/// How to decide what client auth certificate/keys to use.
|
||||
pub client_auth_cert_resolver: Arc<dyn ResolvesClientCert>,
|
||||
|
||||
/// Whether to support RFC5077 tickets. You must provide a working
|
||||
/// `session_storage` member for this to have any meaningful
|
||||
/// effect.
|
||||
///
|
||||
/// The default is true.
|
||||
pub tls12_resumption: Option<Tls12Resumption>,
|
||||
|
||||
/// Supported versions, in no particular order. The default
|
||||
/// is all supported versions.
|
||||
pub(super) versions: versions::EnabledVersions,
|
||||
|
@ -190,6 +185,8 @@ pub struct ClientConfig {
|
|||
/// What mechanisms to support for resuming a TLS 1.2 session.
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
pub enum Tls12Resumption {
|
||||
/// Disable 1.2 resumption.
|
||||
Disabled,
|
||||
/// Support 1.2 resumption using session ids only.
|
||||
SessionIdOnly,
|
||||
/// Support 1.2 resumption using session ids or RFC 5077 tickets.
|
||||
|
@ -205,8 +202,8 @@ impl fmt::Debug for ClientConfig {
|
|||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("ClientConfig")
|
||||
.field("alpn_protocols", &self.alpn_protocols)
|
||||
.field("resumption", &self.resumption)
|
||||
.field("max_fragment_size", &self.max_fragment_size)
|
||||
.field("tls12_resumption", &self.tls12_resumption)
|
||||
.field("enable_sni", &self.enable_sni)
|
||||
.field("enable_early_data", &self.enable_early_data)
|
||||
.finish_non_exhaustive()
|
||||
|
@ -250,6 +247,72 @@ impl ClientConfig {
|
|||
}
|
||||
}
|
||||
|
||||
/// Configuration for how/when a client is allowed to resume a previous session.
|
||||
#[derive(Clone)]
|
||||
pub struct Resumption {
|
||||
/// How we store session data or tickets. The default is to use an in-memory
|
||||
/// [ClientSessionMemoryCache].
|
||||
pub(super) store: Arc<dyn ClientSessionStore>,
|
||||
|
||||
/// What mechanism is used for resuming a TLS 1.2 session.
|
||||
pub(super) tls12_resumption: Tls12Resumption,
|
||||
}
|
||||
|
||||
impl Resumption {
|
||||
/// Create a new `Resumption` that stores data for the given number of sessions in memory.
|
||||
///
|
||||
/// This is the default `Resumption` choice, and enables resuming a TLS 1.2 session with
|
||||
/// a session id or RFC 5077 ticket.
|
||||
pub fn in_memory_sessions(num: usize) -> Self {
|
||||
Self {
|
||||
store: Arc::new(ClientSessionMemoryCache::new(num)),
|
||||
tls12_resumption: Tls12Resumption::SessionIdOrTickets,
|
||||
}
|
||||
}
|
||||
|
||||
/// Use a custom [`ClientSessionStore`] implementation to store sessions.
|
||||
///
|
||||
/// By default, enables resuming a TLS 1.2 session with a session id or RFC 5077 ticket.
|
||||
pub fn store(store: Arc<dyn ClientSessionStore>) -> Self {
|
||||
Self {
|
||||
store,
|
||||
tls12_resumption: Tls12Resumption::SessionIdOrTickets,
|
||||
}
|
||||
}
|
||||
|
||||
/// Disable all use of session resumption.
|
||||
pub fn disabled() -> Self {
|
||||
Self {
|
||||
store: Arc::new(NoClientSessionStorage),
|
||||
tls12_resumption: Tls12Resumption::Disabled,
|
||||
}
|
||||
}
|
||||
|
||||
/// Configure whether TLS 1.2 sessions may be resumed, and by what mechanism.
|
||||
///
|
||||
/// This is meaningless if you've disabled resumption entirely.
|
||||
pub fn tls12_resumption(mut self, tls12: Tls12Resumption) -> Self {
|
||||
self.tls12_resumption = tls12;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Resumption {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("Resumption")
|
||||
.field("tls12_resumption", &self.tls12_resumption)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Resumption {
|
||||
/// Create an in-memory session store resumption with up to 256 server names, allowing
|
||||
/// a TLS 1.2 session to resume with a session id or RFC 5077 ticket.
|
||||
fn default() -> Self {
|
||||
Self::in_memory_sessions(256)
|
||||
}
|
||||
}
|
||||
|
||||
/// Encodes ways a client can know the expected name of the server.
|
||||
///
|
||||
/// This currently covers knowing the DNS name of the server, but
|
||||
|
|
|
@ -12,7 +12,7 @@ use std::collections::VecDeque;
|
|||
use std::sync::{Arc, Mutex};
|
||||
|
||||
/// An implementer of `ClientSessionStore` which does nothing.
|
||||
pub struct NoClientSessionStorage {}
|
||||
pub(super) struct NoClientSessionStorage;
|
||||
|
||||
impl client::ClientSessionStore for NoClientSessionStorage {
|
||||
fn set_kx_hint(&self, _: &ServerName, _: NamedGroup) {}
|
||||
|
@ -71,12 +71,12 @@ pub struct ClientSessionMemoryCache {
|
|||
impl ClientSessionMemoryCache {
|
||||
/// Make a new ClientSessionMemoryCache. `size` is the
|
||||
/// maximum number of stored sessions.
|
||||
pub fn new(size: usize) -> Arc<Self> {
|
||||
pub fn new(size: usize) -> Self {
|
||||
let max_servers =
|
||||
size.saturating_add(MAX_TLS13_TICKETS_PER_SERVER - 1) / MAX_TLS13_TICKETS_PER_SERVER;
|
||||
Arc::new(Self {
|
||||
Self {
|
||||
servers: Mutex::new(limited_cache::LimitedCache::new(max_servers)),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -45,14 +45,16 @@ fn find_session(
|
|||
) -> Option<persist::Retrieved<ClientSessionValue>> {
|
||||
#[allow(clippy::let_and_return, clippy::unnecessary_lazy_evaluations)]
|
||||
let found = config
|
||||
.session_storage
|
||||
.resumption
|
||||
.store
|
||||
.take_tls13_ticket(server_name)
|
||||
.map(ClientSessionValue::Tls13)
|
||||
.or_else(|| {
|
||||
#[cfg(feature = "tls12")]
|
||||
{
|
||||
config
|
||||
.session_storage
|
||||
.resumption
|
||||
.store
|
||||
.tls12_session(server_name)
|
||||
.map(ClientSessionValue::Tls12)
|
||||
}
|
||||
|
@ -386,7 +388,7 @@ fn prepare_resumption<'a>(
|
|||
Some(resuming) if !resuming.ticket().is_empty() => resuming,
|
||||
_ => {
|
||||
if config.supports_version(ProtocolVersion::TLSv1_3)
|
||||
|| config.tls12_resumption == Some(Tls12Resumption::SessionIdOrTickets)
|
||||
|| config.resumption.tls12_resumption == Tls12Resumption::SessionIdOrTickets
|
||||
{
|
||||
// If we don't have a ticket, request one.
|
||||
exts.push(ClientExtension::SessionTicket(ClientSessionTicket::Request));
|
||||
|
@ -400,7 +402,7 @@ fn prepare_resumption<'a>(
|
|||
None => {
|
||||
// TLS 1.2; send the ticket if we have support this protocol version
|
||||
if config.supports_version(ProtocolVersion::TLSv1_2)
|
||||
&& config.tls12_resumption == Some(Tls12Resumption::SessionIdOrTickets)
|
||||
&& config.resumption.tls12_resumption == Tls12Resumption::SessionIdOrTickets
|
||||
{
|
||||
exts.push(ClientExtension::SessionTicket(ClientSessionTicket::Offer(
|
||||
Payload::new(resuming.ticket()),
|
||||
|
|
|
@ -1019,7 +1019,8 @@ impl ExpectFinished {
|
|||
);
|
||||
|
||||
self.config
|
||||
.session_storage
|
||||
.resumption
|
||||
.store
|
||||
.set_tls12_session(&self.server_name, session_value);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -140,7 +140,8 @@ pub(super) fn handle_server_hello(
|
|||
|
||||
// Remember what KX group the server liked for next time.
|
||||
config
|
||||
.session_storage
|
||||
.resumption
|
||||
.store
|
||||
.set_kx_hint(&server_name, their_key_share.group);
|
||||
|
||||
// If we change keying when a subsequent handshake message is being joined,
|
||||
|
@ -190,7 +191,8 @@ pub(super) fn initial_key_share(
|
|||
server_name: &ServerName,
|
||||
) -> Result<kx::KeyExchange, Error> {
|
||||
let group = config
|
||||
.session_storage
|
||||
.resumption
|
||||
.store
|
||||
.kx_hint(server_name)
|
||||
.and_then(|group| kx::KeyExchange::choose(group, &config.kx_groups))
|
||||
.unwrap_or_else(|| {
|
||||
|
@ -883,7 +885,8 @@ impl State<ClientConnectionData> for ExpectFinished {
|
|||
/* We're now sure this server supports TLS1.3. But if we run out of TLS1.3 tickets
|
||||
* when connecting to it again, we definitely don't want to attempt a TLS1.2 resumption. */
|
||||
st.config
|
||||
.session_storage
|
||||
.resumption
|
||||
.store
|
||||
.remove_tls12_session(&st.server_name);
|
||||
|
||||
/* Now move to our application traffic keys. */
|
||||
|
@ -892,7 +895,7 @@ impl State<ClientConnectionData> for ExpectFinished {
|
|||
cx.common.start_traffic();
|
||||
|
||||
let st = ExpectTraffic {
|
||||
session_storage: Arc::clone(&st.config.session_storage),
|
||||
session_storage: Arc::clone(&st.config.resumption.store),
|
||||
server_name: st.server_name,
|
||||
suite: st.suite,
|
||||
transcript: st.transcript,
|
||||
|
|
|
@ -409,9 +409,10 @@ pub mod client {
|
|||
pub use builder::{WantsClientCert, WantsTransparencyPolicyOrClientCert};
|
||||
pub use client_conn::{
|
||||
ClientConfig, ClientConnection, ClientConnectionData, ClientSessionStore,
|
||||
InvalidDnsNameError, ResolvesClientCert, ServerName, Tls12Resumption, WriteEarlyData,
|
||||
InvalidDnsNameError, ResolvesClientCert, Resumption, ServerName, Tls12Resumption,
|
||||
WriteEarlyData,
|
||||
};
|
||||
pub use handy::{ClientSessionMemoryCache, NoClientSessionStorage};
|
||||
pub use handy::ClientSessionMemoryCache;
|
||||
|
||||
#[cfg(feature = "dangerous_configuration")]
|
||||
pub use crate::verify::{
|
||||
|
|
|
@ -8,7 +8,7 @@ use std::sync::atomic::{AtomicUsize, Ordering};
|
|||
use std::sync::Arc;
|
||||
use std::sync::Mutex;
|
||||
|
||||
use rustls::client::ResolvesClientCert;
|
||||
use rustls::client::{ResolvesClientCert, Resumption};
|
||||
use rustls::internal::msgs::base::Payload;
|
||||
use rustls::internal::msgs::codec::Codec;
|
||||
use rustls::server::{AllowAnyAnonymousOrAuthenticatedClient, ClientHello, ResolvesServerCert};
|
||||
|
@ -2678,7 +2678,7 @@ struct ClientStorage {
|
|||
impl ClientStorage {
|
||||
fn new() -> Self {
|
||||
Self {
|
||||
storage: rustls::client::ClientSessionMemoryCache::new(1024),
|
||||
storage: Arc::new(rustls::client::ClientSessionMemoryCache::new(1024)),
|
||||
ops: Mutex::new(Vec::new()),
|
||||
}
|
||||
}
|
||||
|
@ -2908,7 +2908,7 @@ fn early_data_configs() -> (Arc<ClientConfig>, Arc<ServerConfig>) {
|
|||
let kt = KeyType::Rsa;
|
||||
let mut client_config = make_client_config(kt);
|
||||
client_config.enable_early_data = true;
|
||||
client_config.session_storage = Arc::new(ClientStorage::new());
|
||||
client_config.resumption = Resumption::store(Arc::new(ClientStorage::new()));
|
||||
|
||||
let mut server_config = make_server_config(kt);
|
||||
server_config.max_early_data_size = 1234;
|
||||
|
@ -3725,7 +3725,7 @@ fn test_client_sends_helloretryrequest() {
|
|||
);
|
||||
|
||||
let storage = Arc::new(ClientStorage::new());
|
||||
client_config.session_storage = storage.clone();
|
||||
client_config.resumption = Resumption::store(storage.clone());
|
||||
|
||||
// but server only accepts x25519, so a HRR is required
|
||||
let server_config =
|
||||
|
@ -3823,13 +3823,13 @@ fn test_client_attempts_to_use_unsupported_kx_group() {
|
|||
// into kx group cache.
|
||||
let mut client_config_1 =
|
||||
make_client_config_with_kx_groups(KeyType::Rsa, &[&rustls::kx_group::X25519]);
|
||||
client_config_1.session_storage = shared_storage.clone();
|
||||
client_config_1.resumption = Resumption::store(shared_storage.clone());
|
||||
|
||||
// second, client only supports secp-384 and so kx group cache
|
||||
// contains an unusable value.
|
||||
let mut client_config_2 =
|
||||
make_client_config_with_kx_groups(KeyType::Rsa, &[&rustls::kx_group::SECP384R1]);
|
||||
client_config_2.session_storage = shared_storage.clone();
|
||||
client_config_2.resumption = Resumption::store(shared_storage.clone());
|
||||
|
||||
let server_config = make_server_config(KeyType::Rsa);
|
||||
|
||||
|
@ -3869,7 +3869,7 @@ fn test_tls13_client_resumption_does_not_reuse_tickets() {
|
|||
let shared_storage = Arc::new(ClientStorage::new());
|
||||
|
||||
let mut client_config = make_client_config(KeyType::Rsa);
|
||||
client_config.session_storage = shared_storage.clone();
|
||||
client_config.resumption = Resumption::store(shared_storage.clone());
|
||||
let client_config = Arc::new(client_config);
|
||||
|
||||
let mut server_config = make_server_config(KeyType::Rsa);
|
||||
|
@ -4170,7 +4170,7 @@ fn test_client_rejects_illegal_tls13_ccs() {
|
|||
fn test_client_tls12_no_resume_after_server_downgrade() {
|
||||
let mut client_config = common::make_client_config(KeyType::Ed25519);
|
||||
let client_storage = Arc::new(ClientStorage::new());
|
||||
client_config.session_storage = client_storage.clone();
|
||||
client_config.resumption = Resumption::store(client_storage.clone());
|
||||
let client_config = Arc::new(client_config);
|
||||
|
||||
let server_config_1 = Arc::new(common::finish_server_config(
|
||||
|
|
Loading…
Reference in New Issue