use alloc::boxed::Box;
use core::cmp::min;
use crate::crypto::cipher::{InboundOpaqueMessage, MessageDecrypter, MessageEncrypter};
use crate::error::Error;
use crate::log::trace;
use crate::msgs::message::{InboundPlainMessage, OutboundOpaqueMessage, OutboundPlainMessage};
#[derive(PartialEq)]
enum DirectionState {
Invalid,
Prepared,
Active,
}
pub(crate) struct RecordLayer {
message_encrypter: Box<dyn MessageEncrypter>,
message_decrypter: Box<dyn MessageDecrypter>,
write_seq_max: u64,
write_seq: u64,
read_seq: u64,
has_decrypted: bool,
encrypt_state: DirectionState,
decrypt_state: DirectionState,
trial_decryption_len: Option<usize>,
}
impl RecordLayer {
pub(crate) fn new() -> Self {
Self {
message_encrypter: <dyn MessageEncrypter>::invalid(),
message_decrypter: <dyn MessageDecrypter>::invalid(),
write_seq_max: 0,
write_seq: 0,
read_seq: 0,
has_decrypted: false,
encrypt_state: DirectionState::Invalid,
decrypt_state: DirectionState::Invalid,
trial_decryption_len: None,
}
}
pub(crate) fn decrypt_incoming<'a>(
&mut self,
encr: InboundOpaqueMessage<'a>,
) -> Result<Option<Decrypted<'a>>, Error> {
if self.decrypt_state != DirectionState::Active {
return Ok(Some(Decrypted {
want_close_before_decrypt: false,
plaintext: encr.into_plain_message(),
}));
}
let want_close_before_decrypt = self.read_seq == SEQ_SOFT_LIMIT;
let encrypted_len = encr.payload.len();
match self
.message_decrypter
.decrypt(encr, self.read_seq)
{
Ok(plaintext) => {
self.read_seq += 1;
if !self.has_decrypted {
self.has_decrypted = true;
}
Ok(Some(Decrypted {
want_close_before_decrypt,
plaintext,
}))
}
Err(Error::DecryptError) if self.doing_trial_decryption(encrypted_len) => {
trace!("Dropping undecryptable message after aborted early_data");
Ok(None)
}
Err(err) => Err(err),
}
}
pub(crate) fn encrypt_outgoing(
&mut self,
plain: OutboundPlainMessage<'_>,
) -> OutboundOpaqueMessage {
debug_assert!(self.encrypt_state == DirectionState::Active);
assert!(self.next_pre_encrypt_action() != PreEncryptAction::Refuse);
let seq = self.write_seq;
self.write_seq += 1;
self.message_encrypter
.encrypt(plain, seq)
.unwrap()
}
pub(crate) fn prepare_message_encrypter(
&mut self,
cipher: Box<dyn MessageEncrypter>,
max_messages: u64,
) {
self.message_encrypter = cipher;
self.write_seq = 0;
self.write_seq_max = min(SEQ_SOFT_LIMIT, max_messages);
self.encrypt_state = DirectionState::Prepared;
}
pub(crate) fn prepare_message_decrypter(&mut self, cipher: Box<dyn MessageDecrypter>) {
self.message_decrypter = cipher;
self.read_seq = 0;
self.decrypt_state = DirectionState::Prepared;
}
pub(crate) fn start_encrypting(&mut self) {
debug_assert!(self.encrypt_state == DirectionState::Prepared);
self.encrypt_state = DirectionState::Active;
}
pub(crate) fn start_decrypting(&mut self) {
debug_assert!(self.decrypt_state == DirectionState::Prepared);
self.decrypt_state = DirectionState::Active;
}
pub(crate) fn set_message_encrypter(
&mut self,
cipher: Box<dyn MessageEncrypter>,
max_messages: u64,
) {
self.prepare_message_encrypter(cipher, max_messages);
self.start_encrypting();
}
pub(crate) fn set_message_decrypter(&mut self, cipher: Box<dyn MessageDecrypter>) {
self.prepare_message_decrypter(cipher);
self.start_decrypting();
self.trial_decryption_len = None;
}
pub(crate) fn set_message_decrypter_with_trial_decryption(
&mut self,
cipher: Box<dyn MessageDecrypter>,
max_length: usize,
) {
self.prepare_message_decrypter(cipher);
self.start_decrypting();
self.trial_decryption_len = Some(max_length);
}
pub(crate) fn finish_trial_decryption(&mut self) {
self.trial_decryption_len = None;
}
pub(crate) fn next_pre_encrypt_action(&self) -> PreEncryptAction {
self.pre_encrypt_action(0)
}
pub(crate) fn pre_encrypt_action(&self, add: u64) -> PreEncryptAction {
match self.write_seq.saturating_add(add) {
SEQ_HARD_LIMIT.. => PreEncryptAction::Refuse,
v if v >= self.write_seq_max => PreEncryptAction::RefreshOrClose,
_ => PreEncryptAction::Nothing,
}
}
pub(crate) fn is_encrypting(&self) -> bool {
self.encrypt_state == DirectionState::Active
}
pub(crate) fn has_decrypted(&self) -> bool {
self.has_decrypted
}
pub(crate) fn write_seq(&self) -> u64 {
self.write_seq
}
pub(crate) fn read_seq(&self) -> u64 {
self.read_seq
}
pub(crate) fn encrypted_len(&self, payload_len: usize) -> usize {
self.message_encrypter
.encrypted_payload_len(payload_len)
}
fn doing_trial_decryption(&mut self, requested: usize) -> bool {
match self
.trial_decryption_len
.and_then(|value| value.checked_sub(requested))
{
Some(remaining) => {
self.trial_decryption_len = Some(remaining);
true
}
_ => false,
}
}
}
#[derive(Debug)]
pub(crate) struct Decrypted<'a> {
pub(crate) want_close_before_decrypt: bool,
pub(crate) plaintext: InboundPlainMessage<'a>,
}
#[derive(Debug, Eq, PartialEq)]
pub(crate) enum PreEncryptAction {
Nothing,
RefreshOrClose,
Refuse,
}
const SEQ_SOFT_LIMIT: u64 = 0xffff_ffff_ffff_0000u64;
const SEQ_HARD_LIMIT: u64 = 0xffff_ffff_ffff_fffeu64;
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn pre_encrypt_action_checks_limits() {
let action = |write_seq_max, write_seq, add| {
let mut record_layer = RecordLayer::new();
record_layer.write_seq_max = write_seq_max;
record_layer.write_seq = write_seq;
record_layer.pre_encrypt_action(add)
};
assert_eq!(action(10, 8, 1), PreEncryptAction::Nothing);
assert_eq!(action(10, 8, 2), PreEncryptAction::RefreshOrClose);
assert_eq!(action(10, 11, 0), PreEncryptAction::RefreshOrClose);
assert_eq!(
action(SEQ_SOFT_LIMIT, SEQ_HARD_LIMIT - 1, 0),
PreEncryptAction::RefreshOrClose
);
assert_eq!(
action(SEQ_SOFT_LIMIT, SEQ_HARD_LIMIT - 1, 1),
PreEncryptAction::Refuse
);
}
#[test]
fn test_has_decrypted() {
use crate::{ContentType, ProtocolVersion};
struct PassThroughDecrypter;
impl MessageDecrypter for PassThroughDecrypter {
fn decrypt<'a>(
&mut self,
m: InboundOpaqueMessage<'a>,
_: u64,
) -> Result<InboundPlainMessage<'a>, Error> {
Ok(m.into_plain_message())
}
}
let mut record_layer = RecordLayer::new();
assert!(matches!(
record_layer.decrypt_state,
DirectionState::Invalid
));
assert_eq!(record_layer.read_seq, 0);
assert!(!record_layer.has_decrypted());
record_layer.prepare_message_decrypter(Box::new(PassThroughDecrypter));
assert!(matches!(
record_layer.decrypt_state,
DirectionState::Prepared
));
assert_eq!(record_layer.read_seq, 0);
assert!(!record_layer.has_decrypted());
record_layer.start_decrypting();
assert!(matches!(record_layer.decrypt_state, DirectionState::Active));
assert_eq!(record_layer.read_seq, 0);
assert!(!record_layer.has_decrypted());
record_layer
.decrypt_incoming(InboundOpaqueMessage::new(
ContentType::Handshake,
ProtocolVersion::TLSv1_2,
&mut [0xC0, 0xFF, 0xEE],
))
.unwrap();
assert!(matches!(record_layer.decrypt_state, DirectionState::Active));
assert_eq!(record_layer.read_seq, 1);
assert!(record_layer.has_decrypted());
record_layer.set_message_decrypter(Box::new(PassThroughDecrypter));
assert!(matches!(record_layer.decrypt_state, DirectionState::Active));
assert_eq!(record_layer.read_seq, 0);
assert!(record_layer.has_decrypted());
}
}