use alloc::boxed::Box;
use alloc::vec::Vec;
use core::ops::{Deref, DerefMut};
use core::{fmt, mem};
use pki_types::{DnsName, FipsStatus, ServerName};
use crate::TlsInputBuffer;
use crate::client::{ClientConfig, ClientSide};
pub use crate::common_state::Side;
use crate::common_state::{CommonState, ConnectionOutputs, Protocol};
use crate::conn::{ConnectionCore, KeyingMaterialExporter, MessageIter, SideData, StateMachine};
use crate::crypto::cipher::{AeadKey, Iv, Payload};
use crate::crypto::tls13::{Hkdf, HkdfExpander, OkmBlock};
use crate::enums::ApplicationProtocol;
use crate::error::{ApiMisuse, Error};
use crate::msgs::{
ClientExtensionsInput, Message, MessagePayload, ServerExtensionsInput, TransportParameters,
};
use crate::server::{ChooseConfig, ClientHello, ServerConfig, ServerSide, ServerState};
use crate::suites::SupportedCipherSuite;
use crate::sync::Arc;
use crate::tls13::Tls13CipherSuite;
use crate::tls13::key_schedule::{
hkdf_expand_label, hkdf_expand_label_aead_key, hkdf_expand_label_block,
};
pub trait Connection: fmt::Debug + Deref<Target = ConnectionOutputs> {
fn quic_transport_parameters(&self) -> Option<&[u8]>;
fn zero_rtt_keys(&self) -> Option<DirectionalKeys>;
fn read_hs(&mut self, input: &mut dyn TlsInputBuffer) -> Result<(), Error>;
fn events(&mut self) -> impl Iterator<Item = QuicEvent>;
fn is_handshaking(&self) -> bool;
}
pub struct ClientConnection {
inner: ConnectionCommon<ClientSide>,
}
impl ClientConnection {
pub fn new(
config: Arc<ClientConfig>,
quic_version: Version,
name: ServerName<'static>,
params: Vec<u8>,
) -> Result<Self, Error> {
let alpn_protocols = config.alpn_protocols.clone();
Self::new_with_alpn(config, quic_version, name, params, alpn_protocols)
}
pub fn new_with_alpn(
config: Arc<ClientConfig>,
version: Version,
name: ServerName<'static>,
params: Vec<u8>,
alpn_protocols: Vec<ApplicationProtocol<'static>>,
) -> Result<Self, Error> {
let suites = &config.provider().tls13_cipher_suites;
if suites.is_empty() {
return Err(ApiMisuse::QuicRequiresTls13Support.into());
}
if !suites
.iter()
.any(|scs| scs.quic.is_some())
{
return Err(ApiMisuse::NoQuicCompatibleCipherSuites.into());
}
let exts = ClientExtensionsInput {
transport_parameters: Some(match version {
Version::V1 | Version::V2 => TransportParameters::Quic(Payload::new(params)),
}),
..ClientExtensionsInput::from_alpn(alpn_protocols)
};
let mut quic = Quic {
version,
..Quic::default()
};
let inner = ConnectionCore::for_client(
config,
name,
exts,
Some(&mut quic),
Protocol::Quic(version),
)?;
Ok(Self {
inner: ConnectionCommon::new(inner, quic),
})
}
pub fn fips(&self) -> FipsStatus {
self.inner.fips
}
pub fn is_early_data_accepted(&self) -> bool {
self.inner.core.is_early_data_accepted()
}
pub fn tls13_tickets_received(&self) -> u32 {
self.inner
.core
.common
.recv
.tls13_tickets_received
}
pub fn exporter(&mut self) -> Result<KeyingMaterialExporter, Error> {
self.inner.core.exporter()
}
}
impl Connection for ClientConnection {
fn quic_transport_parameters(&self) -> Option<&[u8]> {
self.inner.quic_transport_parameters()
}
fn zero_rtt_keys(&self) -> Option<DirectionalKeys> {
self.inner.zero_rtt_keys()
}
fn read_hs(&mut self, input: &mut dyn TlsInputBuffer) -> Result<(), Error> {
self.inner.read_hs(input)
}
fn events(&mut self) -> impl Iterator<Item = QuicEvent> {
self.inner.events()
}
fn is_handshaking(&self) -> bool {
self.inner.is_handshaking()
}
}
impl Deref for ClientConnection {
type Target = ConnectionOutputs;
fn deref(&self) -> &Self::Target {
&self.inner
}
}
impl fmt::Debug for ClientConnection {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("quic::ClientConnection")
.finish_non_exhaustive()
}
}
pub struct ServerConnection {
inner: ConnectionCommon<ServerSide>,
}
impl ServerConnection {
pub fn new(
config: Arc<ServerConfig>,
version: Version,
params: Vec<u8>,
) -> Result<Self, Error> {
check_server_config(&config)?;
let exts = ServerExtensionsInput {
transport_parameters: Some(match version {
Version::V1 | Version::V2 => TransportParameters::Quic(Payload::new(params)),
}),
};
let core = ConnectionCore::for_server(config, exts, Protocol::Quic(version))?;
let inner = ConnectionCommon::new(
core,
Quic {
version,
..Quic::default()
},
);
Ok(Self { inner })
}
pub fn fips(&self) -> FipsStatus {
self.inner.fips
}
pub fn server_name(&self) -> Option<&DnsName<'_>> {
self.inner.core.side.server_name()
}
pub fn set_resumption_data(&mut self, resumption_data: &[u8]) -> Result<(), Error> {
assert!(resumption_data.len() < 2usize.pow(15));
match &mut self.inner.core.state {
Ok(st) => st.set_resumption_data(resumption_data),
Err(e) => Err(e.clone()),
}
}
pub fn received_resumption_data(&self) -> Option<&[u8]> {
self.inner
.core
.side
.received_resumption_data()
}
pub fn exporter(&mut self) -> Result<KeyingMaterialExporter, Error> {
self.inner.core.exporter()
}
}
impl Connection for ServerConnection {
fn quic_transport_parameters(&self) -> Option<&[u8]> {
self.inner.quic_transport_parameters()
}
fn zero_rtt_keys(&self) -> Option<DirectionalKeys> {
self.inner.zero_rtt_keys()
}
fn read_hs(&mut self, input: &mut dyn TlsInputBuffer) -> Result<(), Error> {
self.inner.read_hs(input)
}
fn events(&mut self) -> impl Iterator<Item = QuicEvent> {
self.inner.events()
}
fn is_handshaking(&self) -> bool {
self.inner.is_handshaking()
}
}
impl Deref for ServerConnection {
type Target = ConnectionOutputs;
fn deref(&self) -> &Self::Target {
&self.inner
}
}
impl fmt::Debug for ServerConnection {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("quic::ServerConnection")
.finish_non_exhaustive()
}
}
#[non_exhaustive]
#[derive(Debug)]
pub enum ServerHandshake {
NeedsInput(NeedsInput),
Accepted(Accepted),
Complete(ServerConnection),
}
impl ServerHandshake {
pub fn start(version: Version) -> NeedsInput {
NeedsInput {
inner: ConnectionCommon::new(
ConnectionCore::for_acceptor(Protocol::Quic(version)),
Quic {
version,
..Quic::default()
},
),
}
}
}
impl TryFrom<ConnectionCommon<ServerSide>> for ServerHandshake {
type Error = Error;
fn try_from(mut inner: ConnectionCommon<ServerSide>) -> Result<Self, Error> {
const MISUSED: Error = Error::Unreachable("forgot to restore state");
Ok(match mem::replace(&mut inner.core.state, Err(MISUSED))? {
ServerState::ChooseConfig(choose_config) => Self::Accepted(Accepted {
inner,
choose_config,
}),
state if state.is_traffic() => {
inner.core.state = Ok(state);
Self::Complete(ServerConnection { inner })
}
state => {
inner.core.state = Ok(state);
Self::NeedsInput(NeedsInput { inner })
}
})
}
}
pub struct NeedsInput {
inner: ConnectionCommon<ServerSide>,
}
impl NeedsInput {
pub fn process(
mut self,
input: &mut dyn TlsInputBuffer,
output: &mut Vec<QuicEvent>,
) -> Result<ServerHandshake, Error> {
self.inner.read_hs(input)?;
output.extend(self.inner.events());
ServerHandshake::try_from(self.inner)
}
}
impl fmt::Debug for NeedsInput {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("quic::NeedsInput")
.finish_non_exhaustive()
}
}
pub struct Accepted {
inner: ConnectionCommon<ServerSide>,
choose_config: Box<ChooseConfig>,
}
impl Accepted {
pub fn client_hello(&self) -> ClientHello<'_> {
self.choose_config.client_hello()
}
pub fn choose_config(
mut self,
config: Arc<ServerConfig>,
params: Vec<u8>,
output: &mut Vec<QuicEvent>,
) -> Result<ServerHandshake, Error> {
check_server_config(&config)?;
self.inner.core.accepted(
self.choose_config,
ServerExtensionsInput {
transport_parameters: Some(match self.inner.quic.version {
Version::V1 | Version::V2 => TransportParameters::Quic(Payload::new(params)),
}),
},
Some(&mut self.inner.quic),
config,
)?;
output.extend(self.inner.events());
ServerHandshake::try_from(self.inner)
}
}
impl fmt::Debug for Accepted {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("quic::Accepted")
.finish_non_exhaustive()
}
}
fn check_server_config(config: &ServerConfig) -> Result<(), Error> {
let suites = &config.provider.tls13_cipher_suites;
if suites.is_empty() {
return Err(ApiMisuse::QuicRequiresTls13Support.into());
}
if !suites
.iter()
.any(|scs| scs.quic.is_some())
{
return Err(ApiMisuse::NoQuicCompatibleCipherSuites.into());
}
if config.max_early_data_size != 0 && config.max_early_data_size != 0xffff_ffff {
return Err(ApiMisuse::QuicRestrictsMaxEarlyDataSize.into());
}
Ok(())
}
#[expect(clippy::large_enum_variant)]
#[derive(Debug)]
#[non_exhaustive]
pub enum QuicEvent {
Message(Vec<u8>),
KeyChange(KeyChange),
}
struct ConnectionCommon<Side: SideData> {
core: ConnectionCore<Side>,
quic: Quic,
}
impl<Side: SideData> ConnectionCommon<Side> {
fn new(core: ConnectionCore<Side>, quic: Quic) -> Self {
Self { core, quic }
}
fn quic_transport_parameters(&self) -> Option<&[u8]> {
self.quic
.params
.as_ref()
.map(|v| v.as_ref())
}
fn zero_rtt_keys(&self) -> Option<DirectionalKeys> {
let suite = self
.core
.common
.negotiated_cipher_suite()
.and_then(|suite| match suite {
SupportedCipherSuite::Tls13(suite) => Some(suite),
_ => None,
})?;
Some(DirectionalKeys::new(
suite,
suite.quic?,
self.quic.early_secret.as_ref()?,
self.quic.version,
))
}
fn read_hs(&mut self, input: &mut dyn TlsInputBuffer) -> Result<(), Error> {
self.core
.common
.recv
.deframer
.input_quic(input.slice_mut())?;
let mut iter = MessageIter::new(input, Some(&mut self.quic), &mut self.core);
let result = match iter.next() {
Some(Ok(_)) | None => Ok(()),
Some(Err(e)) => Err(e),
};
input.discard(
self.core
.common
.recv
.deframer
.take_discard(),
);
result
}
fn events(&mut self) -> impl Iterator<Item = QuicEvent> {
self.quic.events()
}
}
impl<Side: SideData> Deref for ConnectionCommon<Side> {
type Target = CommonState;
fn deref(&self) -> &Self::Target {
&self.core.common
}
}
impl<Side: SideData> DerefMut for ConnectionCommon<Side> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.core.common
}
}
#[derive(Default)]
pub(crate) struct Quic {
pub(crate) version: Version,
pub(crate) params: Option<Vec<u8>>,
pub(crate) events: Vec<QuicEvent>,
pub(crate) early_secret: Option<OkmBlock>,
}
impl Quic {
pub(crate) fn send_msg(&mut self, m: Message<'_>, _must_encrypt: bool) {
if let MessagePayload::Alert(_) = m.payload {
return;
}
debug_assert!(
matches!(
m.payload,
MessagePayload::Handshake { .. } | MessagePayload::HandshakeFlight(_)
),
"QUIC uses TLS for the cryptographic handshake only"
);
let mut bytes = Vec::new();
m.payload.encode(&mut bytes);
self.events
.push(QuicEvent::Message(bytes));
}
pub(crate) fn events(&mut self) -> impl Iterator<Item = QuicEvent> {
mem::take(&mut self.events).into_iter()
}
}
impl QuicOutput for Quic {
fn transport_parameters(&mut self, params: Vec<u8>) {
self.params = Some(params);
}
fn early_secret(&mut self, secret: Option<OkmBlock>) {
self.early_secret = secret;
}
fn handshake_secrets(
&mut self,
client_secret: OkmBlock,
server_secret: OkmBlock,
suite: &'static Tls13CipherSuite,
quic: &'static dyn Algorithm,
side: Side,
) {
self.events
.push(QuicEvent::KeyChange(KeyChange::Handshake {
keys: Keys::new(&Secrets::new(
client_secret,
server_secret,
suite,
quic,
side,
self.version,
)),
}));
}
fn traffic_secrets(
&mut self,
client_secret: OkmBlock,
server_secret: OkmBlock,
suite: &'static Tls13CipherSuite,
quic: &'static dyn Algorithm,
side: Side,
) {
let mut secrets = Secrets::new(
client_secret,
server_secret,
suite,
quic,
side,
self.version,
);
let keys = Keys::new(&secrets);
secrets.update();
self.events
.push(QuicEvent::KeyChange(KeyChange::OneRtt {
keys,
next: secrets,
}));
}
fn send_msg(&mut self, m: Message<'_>, must_encrypt: bool) {
self.send_msg(m, must_encrypt);
}
}
pub(crate) trait QuicOutput {
fn transport_parameters(&mut self, params: Vec<u8>);
fn early_secret(&mut self, secret: Option<OkmBlock>);
fn handshake_secrets(
&mut self,
client_secret: OkmBlock,
server_secret: OkmBlock,
suite: &'static Tls13CipherSuite,
quic: &'static dyn Algorithm,
side: Side,
);
fn traffic_secrets(
&mut self,
client_secret: OkmBlock,
server_secret: OkmBlock,
suite: &'static Tls13CipherSuite,
quic: &'static dyn Algorithm,
side: Side,
);
fn send_msg(&mut self, m: Message<'_>, must_encrypt: bool);
}
#[derive(Clone)]
pub struct Secrets {
pub(crate) client: OkmBlock,
pub(crate) server: OkmBlock,
suite: &'static Tls13CipherSuite,
quic: &'static dyn Algorithm,
side: Side,
version: Version,
}
impl Secrets {
pub(crate) fn new(
client: OkmBlock,
server: OkmBlock,
suite: &'static Tls13CipherSuite,
quic: &'static dyn Algorithm,
side: Side,
version: Version,
) -> Self {
Self {
client,
server,
suite,
quic,
side,
version,
}
}
pub fn next_packet_keys(&mut self) -> PacketKeySet {
let keys = PacketKeySet::new(self);
self.update();
keys
}
pub(crate) fn update(&mut self) {
self.client = hkdf_expand_label_block(
self.suite
.hkdf_provider
.expander_for_okm(&self.client)
.as_ref(),
self.version.key_update_label(),
&[],
);
self.server = hkdf_expand_label_block(
self.suite
.hkdf_provider
.expander_for_okm(&self.server)
.as_ref(),
self.version.key_update_label(),
&[],
);
}
fn local_remote(&self) -> (&OkmBlock, &OkmBlock) {
match self.side {
Side::Client => (&self.client, &self.server),
Side::Server => (&self.server, &self.client),
}
}
}
#[expect(clippy::exhaustive_structs)]
pub struct DirectionalKeys {
pub header: Box<dyn HeaderProtectionKey>,
pub packet: Box<dyn PacketKey>,
}
impl DirectionalKeys {
pub(crate) fn new(
suite: &'static Tls13CipherSuite,
quic: &'static dyn Algorithm,
secret: &OkmBlock,
version: Version,
) -> Self {
let builder = KeyBuilder::new(secret, version, quic, suite.hkdf_provider);
Self {
header: builder.header_protection_key(),
packet: builder.packet_key(),
}
}
}
const TAG_LEN: usize = 16;
pub struct Tag([u8; TAG_LEN]);
impl From<&[u8]> for Tag {
fn from(value: &[u8]) -> Self {
let mut array = [0u8; TAG_LEN];
array.copy_from_slice(value);
Self(array)
}
}
impl AsRef<[u8]> for Tag {
fn as_ref(&self) -> &[u8] {
&self.0
}
}
pub trait Algorithm: Send + Sync {
fn packet_key(&self, key: AeadKey, iv: Iv) -> Box<dyn PacketKey>;
fn header_protection_key(&self, key: AeadKey) -> Box<dyn HeaderProtectionKey>;
fn aead_key_len(&self) -> usize;
fn fips(&self) -> FipsStatus {
FipsStatus::Unvalidated
}
}
pub trait HeaderProtectionKey: Send + Sync {
fn encrypt_in_place(
&self,
sample: &[u8],
first: &mut u8,
packet_number: &mut [u8],
) -> Result<(), Error>;
fn decrypt_in_place(
&self,
sample: &[u8],
first: &mut u8,
packet_number: &mut [u8],
) -> Result<(), Error>;
fn sample_len(&self) -> usize;
}
pub trait PacketKey: Send + Sync {
fn encrypt_in_place(
&self,
packet_number: u64,
header: &[u8],
payload: &mut [u8],
path_id: Option<u32>,
) -> Result<Tag, Error>;
fn decrypt_in_place<'a>(
&self,
packet_number: u64,
header: &[u8],
payload: &'a mut [u8],
path_id: Option<u32>,
) -> Result<&'a [u8], Error>;
fn tag_len(&self) -> usize;
fn confidentiality_limit(&self) -> u64;
fn integrity_limit(&self) -> u64;
}
#[expect(clippy::exhaustive_structs)]
pub struct PacketKeySet {
pub local: Box<dyn PacketKey>,
pub remote: Box<dyn PacketKey>,
}
impl PacketKeySet {
fn new(secrets: &Secrets) -> Self {
let (local, remote) = secrets.local_remote();
let (version, alg, hkdf) = (secrets.version, secrets.quic, secrets.suite.hkdf_provider);
Self {
local: KeyBuilder::new(local, version, alg, hkdf).packet_key(),
remote: KeyBuilder::new(remote, version, alg, hkdf).packet_key(),
}
}
}
pub struct KeyBuilder<'a> {
expander: Box<dyn HkdfExpander>,
version: Version,
alg: &'a dyn Algorithm,
}
impl<'a> KeyBuilder<'a> {
pub fn new(
secret: &OkmBlock,
version: Version,
alg: &'a dyn Algorithm,
hkdf: &'a dyn Hkdf,
) -> Self {
Self {
expander: hkdf.expander_for_okm(secret),
version,
alg,
}
}
pub fn packet_key(&self) -> Box<dyn PacketKey> {
let aead_key_len = self.alg.aead_key_len();
let packet_key = hkdf_expand_label_aead_key(
self.expander.as_ref(),
aead_key_len,
self.version.packet_key_label(),
&[],
);
let packet_iv =
hkdf_expand_label(self.expander.as_ref(), self.version.packet_iv_label(), &[]);
self.alg
.packet_key(packet_key, packet_iv)
}
pub fn header_protection_key(&self) -> Box<dyn HeaderProtectionKey> {
let header_key = hkdf_expand_label_aead_key(
self.expander.as_ref(),
self.alg.aead_key_len(),
self.version.header_key_label(),
&[],
);
self.alg
.header_protection_key(header_key)
}
}
#[non_exhaustive]
#[derive(Clone, Copy)]
pub struct Suite {
pub suite: &'static Tls13CipherSuite,
pub quic: &'static dyn Algorithm,
}
impl Suite {
pub fn keys(&self, client_dst_connection_id: &[u8], side: Side, version: Version) -> Keys {
Keys::initial(
version,
self.suite,
self.quic,
client_dst_connection_id,
side,
)
}
}
#[expect(clippy::exhaustive_structs)]
pub struct Keys {
pub local: DirectionalKeys,
pub remote: DirectionalKeys,
}
impl Keys {
pub fn initial(
version: Version,
suite: &'static Tls13CipherSuite,
quic: &'static dyn Algorithm,
client_dst_connection_id: &[u8],
side: Side,
) -> Self {
const CLIENT_LABEL: &[u8] = b"client in";
const SERVER_LABEL: &[u8] = b"server in";
let salt = version.initial_salt();
let hs_secret = suite
.hkdf_provider
.extract_from_secret(Some(salt), client_dst_connection_id);
let secrets = Secrets {
client: hkdf_expand_label_block(hs_secret.as_ref(), CLIENT_LABEL, &[]),
server: hkdf_expand_label_block(hs_secret.as_ref(), SERVER_LABEL, &[]),
suite,
quic,
side,
version,
};
Self::new(&secrets)
}
fn new(secrets: &Secrets) -> Self {
let (local, remote) = secrets.local_remote();
Self {
local: DirectionalKeys::new(secrets.suite, secrets.quic, local, secrets.version),
remote: DirectionalKeys::new(secrets.suite, secrets.quic, remote, secrets.version),
}
}
}
#[expect(clippy::exhaustive_enums)]
pub enum KeyChange {
Handshake {
keys: Keys,
},
OneRtt {
keys: Keys,
next: Secrets,
},
}
impl fmt::Debug for KeyChange {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Handshake { .. } => f
.debug_struct("Handshake")
.finish_non_exhaustive(),
Self::OneRtt { .. } => f
.debug_struct("OneRtt")
.finish_non_exhaustive(),
}
}
}
#[non_exhaustive]
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
pub enum Version {
#[default]
V1,
V2,
}
impl Version {
fn initial_salt(self) -> &'static [u8; 20] {
match self {
Self::V1 => &[
0x38, 0x76, 0x2c, 0xf7, 0xf5, 0x59, 0x34, 0xb3, 0x4d, 0x17, 0x9a, 0xe6, 0xa4, 0xc8,
0x0c, 0xad, 0xcc, 0xbb, 0x7f, 0x0a,
],
Self::V2 => &[
0x0d, 0xed, 0xe3, 0xde, 0xf7, 0x00, 0xa6, 0xdb, 0x81, 0x93, 0x81, 0xbe, 0x6e, 0x26,
0x9d, 0xcb, 0xf9, 0xbd, 0x2e, 0xd9,
],
}
}
pub(crate) fn packet_key_label(&self) -> &'static [u8] {
match self {
Self::V1 => b"quic key",
Self::V2 => b"quicv2 key",
}
}
pub(crate) fn packet_iv_label(&self) -> &'static [u8] {
match self {
Self::V1 => b"quic iv",
Self::V2 => b"quicv2 iv",
}
}
pub(crate) fn header_key_label(&self) -> &'static [u8] {
match self {
Self::V1 => b"quic hp",
Self::V2 => b"quicv2 hp",
}
}
fn key_update_label(&self) -> &'static [u8] {
match self {
Self::V1 => b"quic ku",
Self::V2 => b"quicv2 ku",
}
}
}
#[cfg(all(test, any(target_arch = "aarch64", target_arch = "x86_64")))]
mod tests {
use super::*;
use crate::crypto::TLS13_TEST_SUITE;
use crate::crypto::tls13::OkmBlock;
use crate::quic::{HeaderProtectionKey, Secrets, Side, Version};
#[test]
fn key_update_test_vector() {
fn equal_okm(x: &OkmBlock, y: &OkmBlock) -> bool {
x.as_ref() == y.as_ref()
}
let mut secrets = Secrets {
client: OkmBlock::new(
&[
0xb8, 0x76, 0x77, 0x08, 0xf8, 0x77, 0x23, 0x58, 0xa6, 0xea, 0x9f, 0xc4, 0x3e,
0x4a, 0xdd, 0x2c, 0x96, 0x1b, 0x3f, 0x52, 0x87, 0xa6, 0xd1, 0x46, 0x7e, 0xe0,
0xae, 0xab, 0x33, 0x72, 0x4d, 0xbf,
][..],
),
server: OkmBlock::new(
&[
0x42, 0xdc, 0x97, 0x21, 0x40, 0xe0, 0xf2, 0xe3, 0x98, 0x45, 0xb7, 0x67, 0x61,
0x34, 0x39, 0xdc, 0x67, 0x58, 0xca, 0x43, 0x25, 0x9b, 0x87, 0x85, 0x06, 0x82,
0x4e, 0xb1, 0xe4, 0x38, 0xd8, 0x55,
][..],
),
suite: TLS13_TEST_SUITE,
quic: &FakeAlgorithm,
side: Side::Client,
version: Version::V1,
};
secrets.update();
assert!(equal_okm(
&secrets.client,
&OkmBlock::new(
&[
0x42, 0xca, 0xc8, 0xc9, 0x1c, 0xd5, 0xeb, 0x40, 0x68, 0x2e, 0x43, 0x2e, 0xdf,
0x2d, 0x2b, 0xe9, 0xf4, 0x1a, 0x52, 0xca, 0x6b, 0x22, 0xd8, 0xe6, 0xcd, 0xb1,
0xe8, 0xac, 0xa9, 0x6, 0x1f, 0xce
][..]
)
));
assert!(equal_okm(
&secrets.server,
&OkmBlock::new(
&[
0xeb, 0x7f, 0x5e, 0x2a, 0x12, 0x3f, 0x40, 0x7d, 0xb4, 0x99, 0xe3, 0x61, 0xca,
0xe5, 0x90, 0xd4, 0xd9, 0x92, 0xe1, 0x4b, 0x7a, 0xce, 0x3, 0xc2, 0x44, 0xe0,
0x42, 0x21, 0x15, 0xb6, 0xd3, 0x8a
][..]
)
));
}
struct FakeAlgorithm;
impl Algorithm for FakeAlgorithm {
fn packet_key(&self, _key: AeadKey, _iv: Iv) -> Box<dyn PacketKey> {
unimplemented!()
}
fn header_protection_key(&self, _key: AeadKey) -> Box<dyn HeaderProtectionKey> {
unimplemented!()
}
fn aead_key_len(&self) -> usize {
16
}
}
#[test]
fn auto_traits() {
fn assert_auto<T: Send + Sync>() {}
assert_auto::<Box<dyn PacketKey>>();
assert_auto::<Box<dyn HeaderProtectionKey>>();
}
}