use crate::aead_policy::{RecordType, Flags};
#[cfg(feature = "policy-l5")]
use crate::aead_policy::sc;
use crate::frame::Frame;
use crate::providers::aes_gcm_siv::Aes256GcmSivProvider;
use crate::aead_policy::AAD;
use crate::journal::{Journal, JournalEvent, NoopJournal};
use crate::error::{AnubisError, Result};
use std::collections::VecDeque;
pub struct SubchSend {
pub subchannel: u32,
pub next_seq: u64,
pub closed: bool,
capacity: usize,
queue: VecDeque<Vec<u8>>,
}
impl SubchSend {
pub fn new(subchannel: u32) -> Self { Self { subchannel, next_seq: 0, closed: false, capacity: 64, queue: VecDeque::new() } }
pub fn with_capacity(mut self, cap: usize) -> Self { self.capacity = cap; self }
pub fn seal(&mut self, key: &[u8;32], session_id: [u8;16], role: super::aead_policy::Role, rtype: RecordType, payload: &[u8], fin: bool) -> Vec<u8> {
assert!(!self.closed);
#[allow(unused_mut)]
let mut flags = if fin { Flags::FIN } else { Flags::empty() };
#[cfg(feature = "policy-l5")]
{
if crate::policy::l5_enforcement_enabled() {
if rtype == RecordType::Control && self.subchannel == sc::CONTROL && self.next_seq == 0 {
flags |= Flags::L5_POLICY;
}
}
}
let aad = AAD { version: 1, role, record_type: rtype, subchannel: self.subchannel, seq: self.next_seq, flags, session_id, capabilities_hash16: None };
let ct = Frame::seal(&Aes256GcmSivProvider, key, &aad, payload).expect("seal");
if fin { self.closed = true; }
self.next_seq += 1;
ct
}
pub fn enqueue_with_journal<J: Journal>(&mut self, key: &[u8;32], session_id: [u8;16], role: super::aead_policy::Role, rtype: RecordType, payload: &[u8], fin: bool, journal: &mut J) -> Result<()> {
if self.queue.len() >= self.capacity { journal.record(&JournalEvent::SubchQueueOverflow { subchannel: self.subchannel, capacity: self.capacity }); return Err(AnubisError::InvalidInput); }
let ct = self.seal(key, session_id, role, rtype, payload, fin);
self.queue.push_back(ct);
Ok(())
}
pub fn enqueue(&mut self, key: &[u8;32], session_id: [u8;16], role: super::aead_policy::Role, rtype: RecordType, payload: &[u8], fin: bool) -> Result<()> {
let mut j = NoopJournal; self.enqueue_with_journal(key, session_id, role, rtype, payload, fin, &mut j)
}
pub fn dequeue(&mut self) -> Option<Vec<u8>> { self.queue.pop_front() }
pub fn is_full(&self) -> bool { self.queue.len() >= self.capacity }
pub fn len(&self) -> usize { self.queue.len() }
}
pub struct SubchRecv {
pub subchannel: u32,
pub expected_seq: u64,
pub closed: bool,
}
impl SubchRecv {
pub fn new(subchannel: u32) -> Self { Self { subchannel, expected_seq: 0, closed: false } }
pub fn open_with_journal<J: Journal>(&mut self, key: &[u8;32], session_id: [u8;16], role: super::aead_policy::Role, rtype: RecordType, ct: &[u8], flags: Flags, seq: u64, journal: &mut J) -> Result<Vec<u8>> {
if self.closed { journal.record(&JournalEvent::SubchFinAfterFin { subchannel: self.subchannel }); return Err(AnubisError::InvalidInput); }
if seq != self.expected_seq {
if seq < self.expected_seq { journal.record(&JournalEvent::SubchDuplicate { subchannel: self.subchannel, expected: self.expected_seq, got: seq }); } else { journal.record(&JournalEvent::SubchOutOfOrder { subchannel: self.subchannel, expected: self.expected_seq, got: seq }); }
return Err(AnubisError::InvalidInput);
}
#[cfg(feature = "policy-l5")]
{
if crate::policy::l5_enforcement_enabled() {
if rtype == RecordType::Control && self.subchannel == sc::CONTROL && seq == 0 {
if !flags.contains(Flags::L5_POLICY) {
return Err(AnubisError::InvalidInput);
}
}
}
}
let aad = AAD { version: 1, role, record_type: rtype, subchannel: self.subchannel, seq, flags, session_id, capabilities_hash16: None };
let pt = Frame::open(&Aes256GcmSivProvider, key, &aad, ct)?;
self.expected_seq += 1;
if flags.contains(Flags::FIN) { self.closed = true; }
Ok(pt)
}
pub fn open(&mut self, key: &[u8;32], session_id: [u8;16], role: super::aead_policy::Role, rtype: RecordType, ct: &[u8], flags: Flags, seq: u64) -> Option<Vec<u8>> {
let mut j = NoopJournal;
self.open_with_journal(key, session_id, role, rtype, ct, flags, seq, &mut j).ok()
}
}