use core::hash::Hasher;
use super::config::{ClientCredentialResolver, ClientSessionStore};
use super::{ClientSessionKey, CredentialRequest, Tls12Session, Tls13Session};
use crate::crypto::SelectedCredential;
use crate::crypto::kx::NamedGroup;
use crate::enums::CertificateType;
#[derive(Debug)]
pub(super) struct NoClientSessionStorage;
impl ClientSessionStore for NoClientSessionStorage {
fn set_kx_hint(&self, _: ClientSessionKey<'static>, _: NamedGroup) {}
fn kx_hint(&self, _: &ClientSessionKey<'_>) -> Option<NamedGroup> {
None
}
fn set_tls12_session(&self, _: ClientSessionKey<'static>, _: Tls12Session) {}
fn tls12_session(&self, _: &ClientSessionKey<'_>) -> Option<Tls12Session> {
None
}
fn remove_tls12_session(&self, _: &ClientSessionKey<'_>) {}
fn insert_tls13_ticket(&self, _: ClientSessionKey<'static>, _: Tls13Session) {}
fn take_tls13_ticket(&self, _: &ClientSessionKey<'_>) -> Option<Tls13Session> {
None
}
}
mod cache {
use alloc::collections::VecDeque;
use core::fmt;
use super::*;
use crate::client::Tls13Session;
use crate::crypto::kx::NamedGroup;
use crate::limited_cache;
use crate::lock::Mutex;
const MAX_TLS13_TICKETS_PER_SERVER: usize = 8;
struct ServerData {
kx_hint: Option<NamedGroup>,
tls12: Option<Tls12Session>,
tls13: VecDeque<Tls13Session>,
}
impl Default for ServerData {
fn default() -> Self {
Self {
kx_hint: None,
tls12: None,
tls13: VecDeque::with_capacity(MAX_TLS13_TICKETS_PER_SERVER),
}
}
}
pub struct ClientSessionMemoryCache {
servers: Mutex<limited_cache::LimitedCache<ClientSessionKey<'static>, ServerData>>,
}
impl ClientSessionMemoryCache {
pub fn new(size: usize) -> Self {
let max_servers = size.saturating_add(MAX_TLS13_TICKETS_PER_SERVER - 1)
/ MAX_TLS13_TICKETS_PER_SERVER;
Self {
servers: Mutex::new(limited_cache::LimitedCache::new(max_servers)),
}
}
}
impl ClientSessionStore for ClientSessionMemoryCache {
fn set_kx_hint(&self, key: ClientSessionKey<'static>, group: NamedGroup) {
self.servers
.lock()
.unwrap()
.get_or_insert_default_and_edit(key, |data| data.kx_hint = Some(group));
}
fn kx_hint(&self, key: &ClientSessionKey<'_>) -> Option<NamedGroup> {
self.servers
.lock()
.unwrap()
.get(key)
.and_then(|sd| sd.kx_hint)
}
fn set_tls12_session(&self, key: ClientSessionKey<'static>, value: Tls12Session) {
self.servers
.lock()
.unwrap()
.get_or_insert_default_and_edit(key, |data| data.tls12 = Some(value));
}
fn tls12_session(&self, key: &ClientSessionKey<'_>) -> Option<Tls12Session> {
self.servers
.lock()
.unwrap()
.get(key)
.and_then(|sd| sd.tls12.as_ref().cloned())
}
fn remove_tls12_session(&self, key: &ClientSessionKey<'static>) {
self.servers
.lock()
.unwrap()
.get_mut(key)
.and_then(|data| data.tls12.take());
}
fn insert_tls13_ticket(&self, key: ClientSessionKey<'static>, value: Tls13Session) {
self.servers
.lock()
.unwrap()
.get_or_insert_default_and_edit(key, |data| {
if data.tls13.len() == data.tls13.capacity() {
data.tls13.pop_front();
}
data.tls13.push_back(value);
});
}
fn take_tls13_ticket(&self, key: &ClientSessionKey<'static>) -> Option<Tls13Session> {
self.servers
.lock()
.unwrap()
.get_mut(key)
.and_then(|data| data.tls13.pop_back())
}
}
impl fmt::Debug for ClientSessionMemoryCache {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("ClientSessionMemoryCache")
.finish_non_exhaustive()
}
}
}
pub use cache::ClientSessionMemoryCache;
#[derive(Debug)]
pub(super) struct FailResolveClientCert {}
impl ClientCredentialResolver for FailResolveClientCert {
fn resolve(&self, _: &CredentialRequest<'_>) -> Option<SelectedCredential> {
None
}
fn supported_certificate_types(&self) -> &'static [CertificateType] {
&[]
}
fn hash_config(&self, _: &mut dyn Hasher) {}
}
#[cfg(test)]
mod tests {
use alloc::vec::Vec;
use core::time::Duration;
use pki_types::{CertificateDer, ServerName, UnixTime};
use super::NoClientSessionStorage;
use crate::client::{
ClientSessionKey, ClientSessionStore, Tls12Session, Tls13ClientSessionInput, Tls13Session,
};
use crate::crypto::kx::NamedGroup;
use crate::crypto::{
CertificateIdentity, CipherSuite, Identity, TEST_PROVIDER, tls12_suite, tls13_suite,
};
use crate::msgs::{
NewSessionTicketExtensions, NewSessionTicketPayloadTls13, SessionId, SizedPayload,
};
use crate::sync::Arc;
#[test]
fn test_noclientsessionstorage_does_nothing() {
let c = NoClientSessionStorage {};
let server_name = ServerName::try_from("example.com").unwrap();
let key = ClientSessionKey {
config_hash: Default::default(),
server_name,
};
let now = UnixTime::now();
c.set_kx_hint(key.clone(), NamedGroup::X25519);
assert_eq!(None, c.kx_hint(&key));
{
c.set_tls12_session(
key.clone(),
Tls12Session::new(
tls12_suite(CipherSuite(0xff12), &TEST_PROVIDER),
SessionId::empty(),
Arc::new(SizedPayload::empty()),
&[0u8; 48],
Identity::X509(CertificateIdentity {
end_entity: CertificateDer::from(&[][..]),
intermediates: Vec::new(),
}),
now,
Duration::ZERO,
true,
),
);
assert!(c.tls12_session(&key).is_none());
c.remove_tls12_session(&key);
}
c.insert_tls13_ticket(
key.clone(),
Tls13Session::new(
&NewSessionTicketPayloadTls13 {
lifetime: Duration::ZERO,
age_add: 0,
nonce: SizedPayload::empty(),
ticket: Arc::new(SizedPayload::empty()),
extensions: NewSessionTicketExtensions {
max_early_data_size: None,
},
},
Tls13ClientSessionInput {
suite: tls13_suite(CipherSuite(0xff13), &TEST_PROVIDER),
peer_identity: Identity::X509(CertificateIdentity {
end_entity: CertificateDer::from(&[][..]),
intermediates: Vec::new(),
}),
quic_params: None,
},
&[],
now,
),
);
assert!(c.take_tls13_ticket(&key).is_none());
}
}