use crate::common_state::Protocol;
use crate::crypto;
use crate::crypto::cipher::{AeadKey, Iv};
use crate::enums::{CipherSuite, ProtocolVersion, SignatureAlgorithm, SignatureScheme};
#[cfg(feature = "tls12")]
use crate::tls12::Tls12CipherSuite;
use crate::tls13::Tls13CipherSuite;
#[cfg(feature = "tls12")]
use crate::versions::TLS12;
use crate::versions::{SupportedProtocolVersion, TLS13};
use alloc::vec::Vec;
use core::fmt;
pub struct CipherSuiteCommon {
pub suite: CipherSuite,
pub hash_provider: &'static dyn crypto::hash::Hash,
pub confidentiality_limit: u64,
pub integrity_limit: u64,
}
#[derive(Clone, Copy, PartialEq)]
pub enum SupportedCipherSuite {
#[cfg(feature = "tls12")]
Tls12(&'static Tls12CipherSuite),
Tls13(&'static Tls13CipherSuite),
}
impl fmt::Debug for SupportedCipherSuite {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.suite().fmt(f)
}
}
impl SupportedCipherSuite {
pub fn suite(&self) -> CipherSuite {
self.common().suite
}
pub(crate) fn hash_provider(&self) -> &'static dyn crypto::hash::Hash {
self.common().hash_provider
}
pub(crate) fn common(&self) -> &CipherSuiteCommon {
match self {
#[cfg(feature = "tls12")]
Self::Tls12(inner) => &inner.common,
Self::Tls13(inner) => &inner.common,
}
}
pub fn tls13(&self) -> Option<&'static Tls13CipherSuite> {
match self {
#[cfg(feature = "tls12")]
Self::Tls12(_) => None,
Self::Tls13(inner) => Some(inner),
}
}
pub fn version(&self) -> &'static SupportedProtocolVersion {
match self {
#[cfg(feature = "tls12")]
Self::Tls12(_) => &TLS12,
Self::Tls13(_) => &TLS13,
}
}
pub fn usable_for_signature_algorithm(&self, _sig_alg: SignatureAlgorithm) -> bool {
match self {
Self::Tls13(_) => true, #[cfg(feature = "tls12")]
Self::Tls12(inner) => inner
.sign
.iter()
.any(|scheme| scheme.sign() == _sig_alg),
}
}
pub(crate) fn usable_for_protocol(&self, proto: Protocol) -> bool {
match proto {
Protocol::Tcp => true,
Protocol::Quic => self
.tls13()
.and_then(|cs| cs.quic)
.is_some(),
}
}
}
pub(crate) fn choose_ciphersuite_preferring_client(
client_suites: &[CipherSuite],
server_suites: &[SupportedCipherSuite],
) -> Option<SupportedCipherSuite> {
for client_suite in client_suites {
if let Some(selected) = server_suites
.iter()
.find(|x| *client_suite == x.suite())
{
return Some(*selected);
}
}
None
}
pub(crate) fn choose_ciphersuite_preferring_server(
client_suites: &[CipherSuite],
server_suites: &[SupportedCipherSuite],
) -> Option<SupportedCipherSuite> {
if let Some(selected) = server_suites
.iter()
.find(|x| client_suites.contains(&x.suite()))
{
return Some(*selected);
}
None
}
pub(crate) fn reduce_given_sigalg(
all: &[SupportedCipherSuite],
sigalg: SignatureAlgorithm,
) -> Vec<SupportedCipherSuite> {
all.iter()
.filter(|&&suite| suite.usable_for_signature_algorithm(sigalg))
.copied()
.collect()
}
pub(crate) fn reduce_given_version_and_protocol(
all: &[SupportedCipherSuite],
version: ProtocolVersion,
proto: Protocol,
) -> Vec<SupportedCipherSuite> {
all.iter()
.filter(|&&suite| suite.version().version == version && suite.usable_for_protocol(proto))
.copied()
.collect()
}
pub(crate) fn compatible_sigscheme_for_suites(
sigscheme: SignatureScheme,
common_suites: &[SupportedCipherSuite],
) -> bool {
let sigalg = sigscheme.sign();
common_suites
.iter()
.any(|&suite| suite.usable_for_signature_algorithm(sigalg))
}
pub struct ExtractedSecrets {
pub tx: (u64, ConnectionTrafficSecrets),
pub rx: (u64, ConnectionTrafficSecrets),
}
pub(crate) struct PartiallyExtractedSecrets {
pub(crate) tx: ConnectionTrafficSecrets,
pub(crate) rx: ConnectionTrafficSecrets,
}
#[non_exhaustive]
pub enum ConnectionTrafficSecrets {
Aes128Gcm {
key: AeadKey,
iv: Iv,
},
Aes256Gcm {
key: AeadKey,
iv: Iv,
},
Chacha20Poly1305 {
key: AeadKey,
iv: Iv,
},
}
#[cfg(all(test, feature = "ring"))]
#[cfg(all(test, any(feature = "ring", feature = "aws_lc_rs")))]
mod tests {
use super::*;
use crate::enums::CipherSuite;
use crate::test_provider::tls13::*;
#[test]
fn test_client_pref() {
let client = vec![
CipherSuite::TLS13_AES_128_GCM_SHA256,
CipherSuite::TLS13_AES_256_GCM_SHA384,
];
let server = vec![TLS13_AES_256_GCM_SHA384, TLS13_AES_128_GCM_SHA256];
let chosen = choose_ciphersuite_preferring_client(&client, &server);
assert!(chosen.is_some());
assert_eq!(chosen.unwrap(), TLS13_AES_128_GCM_SHA256);
}
#[test]
fn test_server_pref() {
let client = vec![
CipherSuite::TLS13_AES_128_GCM_SHA256,
CipherSuite::TLS13_AES_256_GCM_SHA384,
];
let server = vec![TLS13_AES_256_GCM_SHA384, TLS13_AES_128_GCM_SHA256];
let chosen = choose_ciphersuite_preferring_server(&client, &server);
assert!(chosen.is_some());
assert_eq!(chosen.unwrap(), TLS13_AES_256_GCM_SHA384);
}
#[test]
fn test_pref_fails() {
assert!(choose_ciphersuite_preferring_client(
&[CipherSuite::TLS_NULL_WITH_NULL_NULL],
crypto::ring::ALL_CIPHER_SUITES
)
.is_none());
assert!(choose_ciphersuite_preferring_server(
&[CipherSuite::TLS_NULL_WITH_NULL_NULL],
crypto::ring::ALL_CIPHER_SUITES
)
.is_none());
}
#[test]
fn test_scs_is_debug() {
println!("{:?}", crypto::ring::ALL_CIPHER_SUITES);
}
#[test]
fn test_can_resume_to() {
assert!(TLS13_AES_128_GCM_SHA256
.tls13()
.unwrap()
.can_resume_from(TLS13_CHACHA20_POLY1305_SHA256_INTERNAL)
.is_some());
assert!(TLS13_AES_256_GCM_SHA384
.tls13()
.unwrap()
.can_resume_from(TLS13_CHACHA20_POLY1305_SHA256_INTERNAL)
.is_none());
}
}