diff --git a/src/connection/handshake/dirsync.rs b/src/connection/handshake/dirsync.rs index 902e4bd..721ab9c 100644 --- a/src/connection/handshake/dirsync.rs +++ b/src/connection/handshake/dirsync.rs @@ -11,15 +11,16 @@ use super::{Error, HandshakeData}; use crate::{ auth, - connection::ID, + connection::{ProtocolVersion, ID}, enc::{ asym::{ExchangePubKey, KeyExchange, KeyID}, - sym::{CipherKind, Secret}, + sym::{CipherKind, HeadLen, Secret, TagLen}, }, }; use ::arrayref::array_mut_ref; use ::std::{collections::VecDeque, num::NonZeroU64, vec::Vec}; +use trust_dns_client::rr::rdata::key::Protocol; type Nonce = [u8; 16]; @@ -42,10 +43,15 @@ impl DirSync { } /// Serialize into raw bytes /// NOTE: assumes that there is exactly asa much buffer as needed - pub fn serialize(&self, out: &mut [u8]) { + pub fn serialize( + &self, + head_len: HeadLen, + tag_len: TagLen, + out: &mut [u8], + ) { match self { - DirSync::Req(req) => req.serialize(out), - DirSync::Resp(resp) => resp.serialize(out), + DirSync::Req(req) => req.serialize(head_len, tag_len, out), + DirSync::Resp(resp) => resp.serialize(head_len, tag_len, out), } } } @@ -67,9 +73,21 @@ pub struct Req { } impl Req { - /// Set the cleartext data after it was parsed - pub fn set_data(&mut self, data: ReqData) { - self.data = ReqInner::Data(data); + /// return the offset of the encrypted data + /// NOTE: starts from the beginning of the fenrir packet + pub fn encrypted_offset(&self) -> usize { + ProtocolVersion::len() + + KeyID::len() + + KeyExchange::len() + + CipherKind::len() + + self.exchange_key.len() + } + /// return the total length of the cleartext data + pub fn encrypted_length(&self) -> usize { + match &self.data { + ReqInner::ClearText(data) => data.len(), + _ => 0, + } } /// actual length of the directory synchronized request pub fn len(&self) -> usize { @@ -81,7 +99,12 @@ impl Req { } /// Serialize into raw bytes /// NOTE: assumes that there is exactly as much buffer as needed - pub fn serialize(&self, out: &mut [u8]) { + pub fn serialize( + &self, + head_len: HeadLen, + tag_len: TagLen, + out: &mut [u8], + ) { //assert!(out.len() > , ": not enough buffer to serialize"); todo!() } @@ -108,10 +131,7 @@ impl super::HandshakeParsing for Req { Ok(exchange_key) => exchange_key, Err(e) => return Err(e.into()), }; - let mut vec = VecDeque::with_capacity(raw.len() - (4 + len)); - vec.extend(raw[(4 + len)..].iter().copied()); - let _ = vec.make_contiguous(); - let data = ReqInner::CipherText(vec); + let data = ReqInner::CipherText(raw.len() - (4 + len)); Ok(HandshakeData::DirSync(DirSync::Req(Self { key_id, exchange, @@ -125,40 +145,35 @@ impl super::HandshakeParsing for Req { /// Quick way to avoid mixing cipher and clear text #[derive(Debug, Clone)] pub enum ReqInner { - /// Client data, still in ciphertext - CipherText(VecDeque), - /// Client data, decrypted but unprocessed - ClearText(VecDeque), - /// Parsed client data - Data(ReqData), + /// Data is still encrytped, we only keep the length + CipherText(usize), + /// Client data, decrypted and parsed + ClearText(ReqData), } impl ReqInner { /// The length of the data pub fn len(&self) -> usize { match self { - ReqInner::CipherText(c) => c.len(), - ReqInner::ClearText(c) => c.len(), - ReqInner::Data(d) => d.len(), + ReqInner::CipherText(len) => *len, + ReqInner::ClearText(data) => data.len(), } } - /// Get the ciptertext, or panic - pub fn ciphertext<'a>(&'a mut self) -> &'a mut VecDeque { - match self { - ReqInner::CipherText(data) => data, - _ => panic!(), - } - } - /// switch from ciphertext to cleartext - pub fn mark_as_cleartext(&mut self) { - let mut newdata: VecDeque; - match self { - ReqInner::CipherText(data) => { - newdata = VecDeque::new(); - ::core::mem::swap(&mut newdata, data); + /// parse the cleartext + pub fn deserialize_as_cleartext(&mut self, raw: &[u8]) { + let clear = match self { + ReqInner::CipherText(len) => { + assert!( + *len == raw.len(), + "DirSync::ReqInner::CipherText length mismatch" + ); + match ReqData::deserialize(raw) { + Ok(clear) => clear, + Err(_) => return, + } } _ => return, - } - *self = ReqInner::ClearText(newdata); + }; + *self = ReqInner::ClearText(clear); } } @@ -246,12 +261,7 @@ impl ReqData { pub const MIN_PKT_LEN: usize = 16 + KeyID::len() + ID::len() + AuthInfo::MIN_PKT_LEN; /// Parse the cleartext raw data - pub fn deserialize(raw: &ReqInner) -> Result { - let raw = match raw { - // raw is VecDeque, assume everything is on the first slice - ReqInner::ClearText(raw) => raw.as_slices().0, - _ => return Err(Error::Parsing), - }; + pub fn deserialize(raw: &[u8]) -> Result { if raw.len() < Self::MIN_PKT_LEN { return Err(Error::NotEnoughData); } @@ -285,21 +295,19 @@ impl ReqData { #[derive(Debug, Clone)] pub enum RespInner { /// Server data, still in ciphertext - CipherText(VecDeque), - /// Server data, decrypted but unprocessed - ClearText(VecDeque), - /// Parsed server data - Data(RespData), + CipherText(usize), + /// Parsed, cleartext server data + ClearText(RespData), } impl RespInner { /// The length of the data pub fn len(&self) -> usize { match self { - RespInner::CipherText(c) => c.len(), - RespInner::ClearText(c) => c.len(), - RespInner::Data(d) => RespData::len(), + RespInner::CipherText(len) => *len, + RespInner::ClearText(d) => RespData::len(), } } + /* /// Get the ciptertext, or panic pub fn ciphertext<'a>(&'a mut self) -> &'a mut VecDeque { match self { @@ -307,6 +315,25 @@ impl RespInner { _ => panic!(), } } + */ + /// parse the cleartext + pub fn deserialize_as_cleartext(&mut self, raw: &[u8]) { + let clear = match self { + RespInner::CipherText(len) => { + assert!( + *len == raw.len(), + "DirSync::RespInner::CipherText length mismatch" + ); + match RespData::deserialize(raw) { + Ok(clear) => clear, + Err(_) => return, + } + } + _ => return, + }; + *self = RespInner::ClearText(clear); + } + /* /// switch from ciphertext to cleartext pub fn mark_as_cleartext(&mut self) { let mut newdata: VecDeque; @@ -319,6 +346,7 @@ impl RespInner { } *self = RespInner::ClearText(newdata); } + */ /// serialize, but only if ciphertext pub fn serialize(&self, out: &mut [u8]) { todo!() @@ -344,31 +372,48 @@ impl super::HandshakeParsing for Resp { KeyID(u16::from_le_bytes(raw[0..1].try_into().unwrap())); Ok(HandshakeData::DirSync(DirSync::Resp(Self { client_key_id, - data: RespInner::CipherText(raw[KeyID::len()..].to_vec().into()), + data: RespInner::CipherText(raw[KeyID::len()..].len()), }))) } } impl Resp { + /// return the offset of the encrypted data + /// NOTE: starts from the beginning of the fenrir packet + pub fn encrypted_offset(&self) -> usize { + ProtocolVersion::len() + KeyID::len() + } + /// return the total length of the cleartext data + pub fn encrypted_length(&self) -> usize { + match &self.data { + RespInner::ClearText(_data) => RespData::len(), + _ => 0, + } + } /// Total length of the response handshake pub fn len(&self) -> usize { KeyID::len() + self.data.len() } /// Serialize into raw bytes /// NOTE: assumes that there is exactly as much buffer as needed - /// NOTE: assumes that the data is encrypted - pub fn serialize(&self, out: &mut [u8]) { + /// NOTE: assumes that the data is *ClearText* + pub fn serialize( + &self, + head_len: HeadLen, + tag_len: TagLen, + out: &mut [u8], + ) { assert!( out.len() == KeyID::len() + self.data.len(), "DirSync Resp: not enough buffer to serialize" ); self.client_key_id.serialize(array_mut_ref![out, 0, 2]); - let end_data = 2 + self.data.len(); - self.data.serialize(&mut out[2..end_data]); + let end_data = (2 + self.data.len()) - tag_len.0; + self.data.serialize(&mut out[(2 + head_len.0)..end_data]); } /// Set the cleartext data after it was parsed pub fn set_data(&mut self, data: RespData) { - self.data = RespInner::Data(data); + self.data = RespInner::ClearText(data); } } @@ -409,7 +454,7 @@ impl RespData { out[start..end].copy_from_slice(self.service_key.as_ref()); } /// Parse the cleartext raw data - pub fn deserialize(raw: &RespInner) -> Result { + pub fn deserialize(raw: &[u8]) -> Result { todo!(); } } diff --git a/src/connection/handshake/mod.rs b/src/connection/handshake/mod.rs index 6e91c28..74d3be1 100644 --- a/src/connection/handshake/mod.rs +++ b/src/connection/handshake/mod.rs @@ -4,7 +4,10 @@ pub mod dirsync; use ::num_traits::FromPrimitive; -use crate::connection::{self, ProtocolVersion}; +use crate::{ + connection::{self, ProtocolVersion}, + enc::sym::{HeadLen, TagLen}, +}; /// Handshake errors #[derive(::thiserror::Error, Debug, Copy, Clone)] @@ -53,9 +56,14 @@ impl HandshakeData { } /// Serialize into raw bytes /// NOTE: assumes that there is exactly asa much buffer as needed - pub fn serialize(&self, out: &mut [u8]) { + pub fn serialize( + &self, + head_len: HeadLen, + tag_len: TagLen, + out: &mut [u8], + ) { match self { - HandshakeData::DirSync(d) => d.serialize(out), + HandshakeData::DirSync(d) => d.serialize(head_len, tag_len, out), } } } @@ -126,10 +134,15 @@ impl Handshake { } /// serialize the handshake into bytes /// NOTE: assumes that there is exactly as much buffer as needed - pub fn serialize(&self, out: &mut [u8]) { + pub fn serialize( + &self, + head_len: HeadLen, + tag_len: TagLen, + out: &mut [u8], + ) { assert!(out.len() > 1, "Handshake: not enough buffer to serialize"); self.fenrir_version.serialize(&mut out[0]); - self.data.serialize(&mut out[1..]); + self.data.serialize(head_len, tag_len, &mut out[1..]); } pub(crate) fn work(&self, keys: &[HandshakeServer]) -> Result<(), Error> { diff --git a/src/connection/packet.rs b/src/connection/packet.rs index b59e02a..9e5eed3 100644 --- a/src/connection/packet.rs +++ b/src/connection/packet.rs @@ -1,6 +1,8 @@ // //! Raw packet handling, encryption, decryption, parsing +use crate::enc::sym::{HeadLen, TagLen}; + /// Fenrir Connection id /// 0 is special as it represents the handshake /// Connection IDs are to be considered u64 little endian @@ -100,10 +102,15 @@ impl PacketData { } /// serialize data into bytes /// NOTE: assumes that there is exactly asa much buffer as needed - pub fn serialize(&self, out: &mut [u8]) { + pub fn serialize( + &self, + head_len: HeadLen, + tag_len: TagLen, + out: &mut [u8], + ) { assert!(self.len() == out.len(), "PacketData: wrong buffer length"); match self { - PacketData::Handshake(h) => h.serialize(out), + PacketData::Handshake(h) => h.serialize(head_len, tag_len, out), } } } @@ -124,12 +131,18 @@ impl Packet { } /// serialize packet into buffer /// NOTE: assumes that there is exactly asa much buffer as needed - pub fn serialize(&self, out: &mut [u8]) { + pub fn serialize( + &self, + head_len: HeadLen, + tag_len: TagLen, + out: &mut [u8], + ) { assert!( out.len() > ConnectionID::len(), "Packet: not enough buffer to serialize" ); self.id.serialize(&mut out[0..ConnectionID::len()]); - self.data.serialize(&mut out[ConnectionID::len()..]); + self.data + .serialize(head_len, tag_len, &mut out[ConnectionID::len()..]); } } diff --git a/src/enc/sym.rs b/src/enc/sym.rs index 366f665..e8db1d6 100644 --- a/src/enc/sym.rs +++ b/src/enc/sym.rs @@ -59,9 +59,9 @@ impl CipherKind { 1 } /// required length of the nonce - pub fn nonce_len(&self) -> usize { + pub fn nonce_len(&self) -> HeadLen { // TODO: how the hell do I take this from ::chacha20poly1305? - Nonce::len() + HeadLen(Nonce::len()) } /// required length of the key pub fn key_len(&self) -> usize { @@ -69,9 +69,9 @@ impl CipherKind { XChaCha20Poly1305::key_size() } /// Length of the authentication tag - pub fn tag_len(&self) -> usize { + pub fn tag_len(&self) -> TagLen { // TODO: how the hell do I take this from ::chacha20poly1305? - ::ring::aead::CHACHA20_POLY1305.tag_len() + TagLen(::ring::aead::CHACHA20_POLY1305.tag_len()) } } @@ -90,6 +90,16 @@ pub enum CipherDirection { Send, } +/// strong typedef for header length +/// aka: nonce length in the encrypted data) +#[derive(Debug, Copy, Clone)] +pub struct HeadLen(pub usize); +/// strong typedef for the Tag length +/// aka: cryptographic authentication tag length at the end +/// of the encrypted data +#[derive(Debug, Copy, Clone)] +pub struct TagLen(pub usize); + /// actual ciphers enum Cipher { /// Cipher XChaha20_Poly1305 @@ -105,31 +115,33 @@ impl Cipher { } } } - fn nonce_len(&self) -> usize { + fn nonce_len(&self) -> HeadLen { match self { Cipher::XChaCha20Poly1305(_) => { // TODO: how the hell do I take this from ::chacha20poly1305? - ::ring::aead::CHACHA20_POLY1305.nonce_len() + HeadLen(::ring::aead::CHACHA20_POLY1305.nonce_len()) } } } - fn tag_len(&self) -> usize { + fn tag_len(&self) -> TagLen { match self { Cipher::XChaCha20Poly1305(_) => { // TODO: how the hell do I take this from ::chacha20poly1305? - ::ring::aead::CHACHA20_POLY1305.tag_len() + TagLen(::ring::aead::CHACHA20_POLY1305.tag_len()) } } } - fn decrypt(&self, aad: AAD, data: &mut VecDeque) -> Result<(), Error> { + fn decrypt<'a>( + &self, + aad: AAD, + raw_data: &'a mut [u8], + ) -> Result<&'a [u8], Error> { match self { Cipher::XChaCha20Poly1305(cipher) => { use ::chacha20poly1305::{ aead::generic_array::GenericArray, AeadInPlace, }; - let final_len: usize; - { - let raw_data = data.as_mut_slices().0; + let final_len: usize = { // FIXME: check min data length let (nonce_bytes, data_and_tag) = raw_data.split_at_mut(13); let (data_notag, tag_bytes) = data_and_tag.split_at_mut( @@ -147,11 +159,11 @@ impl Cipher { if maybe.is_err() { return Err(Error::Decrypt); } - final_len = data_notag.len(); - } - data.drain(..Nonce::len()); - data.truncate(final_len); - Ok(()) + data_notag.len() + }; + //data.drain(..Nonce::len()); + //data.truncate(final_len); + Ok(&raw_data[Nonce::len()..Nonce::len() + final_len]) } } } @@ -159,7 +171,7 @@ impl Cipher { match self { Cipher::XChaCha20Poly1305(cipher) => { let cipher = CipherKind::XChaCha20Poly1305; - cipher.nonce_len() + cipher.tag_len() + cipher.nonce_len().0 + cipher.tag_len().0 } } } @@ -167,28 +179,30 @@ impl Cipher { &self, nonce: &Nonce, aad: AAD, - data: &mut Data, + data: &mut [u8], ) -> Result<(), Error> { - // No need to check for minimum buffer size since `Data` assures we - // already went through that + // FIXME: check minimum buffer size match self { Cipher::XChaCha20Poly1305(cipher) => { use ::chacha20poly1305::{ aead::generic_array::GenericArray, AeadInPlace, }; + let tag_len: usize = ::ring::aead::CHACHA20_POLY1305.tag_len(); + let data_len_notag = data.len() - tag_len; // write nonce - data.get_slice_full()[..Nonce::len()] - .copy_from_slice(nonce.as_bytes()); + data[..Nonce::len()].copy_from_slice(nonce.as_bytes()); // encrypt data match cipher.cipher.encrypt_in_place_detached( nonce.as_bytes().into(), aad.0, - data.get_slice(), + &mut data[Nonce::len()..data_len_notag], ) { Ok(tag) => { - // add tag - data.get_tag_slice().copy_from_slice(tag.as_slice()); + data[data_len_notag..] + // add tag + //data.get_tag_slice() + .copy_from_slice(tag.as_slice()); Ok(()) } Err(_) => Err(Error::Encrypt), @@ -216,7 +230,7 @@ impl CipherRecv { Self(Cipher::new(kind, secret)) } /// Get the length of the nonce for this cipher - pub fn nonce_len(&self) -> usize { + pub fn nonce_len(&self) -> HeadLen { self.0.nonce_len() } /// Decrypt a paket. Nonce and Tag are taken from the packet, @@ -224,8 +238,8 @@ impl CipherRecv { pub fn decrypt<'a>( &self, aad: AAD, - data: &mut VecDeque, - ) -> Result<(), Error> { + data: &'a mut [u8], + ) -> Result<&'a [u8], Error> { self.0.decrypt(aad, data) } } @@ -289,12 +303,12 @@ impl CipherSend { pub fn make_data(&self, length: usize) -> Data { Data { data: Vec::with_capacity(length + self.cipher.overhead()), - skip_start: self.cipher.nonce_len(), - skip_end: self.cipher.tag_len(), + skip_start: self.cipher.nonce_len().0, + skip_end: self.cipher.tag_len().0, } } /// Encrypt the given data - pub fn encrypt(&self, aad: AAD, data: &mut Data) -> Result<(), Error> { + pub fn encrypt(&self, aad: AAD, data: &mut [u8]) -> Result<(), Error> { let old_nonce = self.nonce.advance(); self.cipher.encrypt(&old_nonce, aad, data)?; Ok(()) diff --git a/src/lib.rs b/src/lib.rs index b6965e8..0c75750 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -28,7 +28,7 @@ use ::tokio::{ use crate::enc::{ asym, hkdf::HkdfSha3, - sym::{CipherKind, CipherRecv, CipherSend}, + sym::{CipherKind, CipherRecv, CipherSend, HeadLen, TagLen}, }; pub use config::Config; use connection::{ @@ -67,13 +67,26 @@ pub struct AuthNeededInfo { pub cipher: CipherKind, } +/// Client information needed to fully establish the conenction +#[derive(Debug)] +pub struct ClientConnectInfo { + /// Parsed handshake + pub handshake: Handshake, + /// hkdf generated from the handshake + pub hkdf: HkdfSha3, + /// cipher to be used in both directions + pub cipher_recv: CipherRecv, +} + /// Intermediate actions to be taken while parsing the handshake -#[derive(Debug, Clone)] +#[derive(Debug)] pub enum HandshakeAction { /// Parsing finished, all ok, nothing to do None, /// Packet parsed, now go perform authentication AuthNeeded(AuthNeededInfo), + /// the client can fully establish a connection with this info + ClientConnect(ClientConnectInfo), } // No async here struct FenrirInner { @@ -95,6 +108,7 @@ impl FenrirInner { fn recv_handshake( &self, mut handshake: Handshake, + handshake_raw: &mut [u8], ) -> Result { use connection::handshake::{ dirsync::{self, DirSync}, @@ -159,13 +173,17 @@ impl FenrirInner { let cipher_recv = CipherRecv::new(req.cipher, secret_recv); use crate::enc::sym::AAD; let aad = AAD(&mut []); // no aad for now - match cipher_recv.decrypt(aad, &mut req.data.ciphertext()) { - Ok(()) => req.data.mark_as_cleartext(), + match cipher_recv.decrypt( + aad, + &mut handshake_raw[req.encrypted_offset()..], + ) { + Ok(cleartext) => { + req.data.deserialize_as_cleartext(cleartext) + } Err(e) => { return Err(handshake::Error::Key(e).into()); } } - req.set_data(dirsync::ReqData::deserialize(&req.data)?); let cipher = req.cipher; @@ -200,17 +218,26 @@ impl FenrirInner { let cipher_recv = CipherRecv::new(hshake.cipher, secret_recv); use crate::enc::sym::AAD; - let aad = AAD(&mut []); // no aad for now - match cipher_recv.decrypt(aad, &mut resp.data.ciphertext()) - { - Ok(()) => resp.data.mark_as_cleartext(), + // no aad for now + let aad = AAD(&mut []); + let mut raw_data = &mut handshake_raw[resp + .encrypted_offset() + ..(resp.encrypted_offset() + resp.encrypted_length())]; + match cipher_recv.decrypt(aad, &mut raw_data) { + Ok(cleartext) => { + resp.data.deserialize_as_cleartext(&cleartext) + } Err(e) => { return Err(handshake::Error::Key(e).into()); } } - resp.set_data(dirsync::RespData::deserialize(&resp.data)?); - - todo!(); + return Ok(HandshakeAction::ClientConnect( + ClientConnectInfo { + handshake, + hkdf: hshake.hkdf, + cipher_recv, + }, + )); } }, } @@ -615,7 +642,7 @@ impl Fenrir { const MIN_PACKET_BYTES: usize = 8; /// Read and do stuff with the raw udp packet - async fn recv(&self, udp: RawUdp) { + async fn recv(&self, mut udp: RawUdp) { if udp.data.len() < Self::MIN_PACKET_BYTES { return; } @@ -630,13 +657,15 @@ impl Fenrir { return; } }; - let action = match self._inner.recv_handshake(handshake) { - Ok(action) => action, - Err(err) => { - ::tracing::debug!("Handshake recv error {}", err); - return; - } - }; + let action = + match self._inner.recv_handshake(handshake, &mut udp.data[8..]) + { + Ok(action) => action, + Err(err) => { + ::tracing::debug!("Handshake recv error {}", err); + return; + } + }; match action { HandshakeAction::AuthNeeded(authinfo) => { let tk_check = match self.token_check.load_full() { @@ -657,10 +686,10 @@ impl Fenrir { DirSync::Req(req) => { use dirsync::ReqInner; let req_data = match req.data { - ReqInner::Data(req_data) => req_data, + ReqInner::ClearText(req_data) => req_data, _ => { ::tracing::error!( - "token_check: expected Data" + "token_check: expected ClearText" ); return; } @@ -697,6 +726,8 @@ impl Fenrir { connection::ID::new_rand(&self.rand); let srv_secret = enc::sym::Secret::new_rand(&self.rand); + let head_len = req.cipher.nonce_len(); + let tag_len = req.cipher.tag_len(); let raw_conn = Connection::new( authinfo.hkdf, @@ -711,9 +742,6 @@ impl Fenrir { lock.reserve_first(raw_conn) }; - // TODO: move all the next bits into - // dirsync::Req::respond(...) - let resp_data = dirsync::RespData { client_nonce: req_data.nonce, id: auth_conn.id, @@ -721,25 +749,18 @@ impl Fenrir { service_key: srv_secret, }; use crate::enc::sym::AAD; - let aad = AAD(&mut []); // no aad for now - let mut data = auth_conn - .cipher_send - .make_data(dirsync::RespData::len()); + // no aad for now + let aad = AAD(&mut []); - if let Err(e) = auth_conn - .cipher_send - .encrypt(aad, &mut data) - { - ::tracing::error!("can't encrypt: {:?}", e); - return; - } use dirsync::RespInner; let resp = dirsync::Resp { client_key_id: req_data.client_key_id, - data: RespInner::CipherText( - data.get_raw().into(), - ), + data: RespInner::ClearText(resp_data), }; + let offset_to_encrypt = resp.encrypted_offset(); + let encrypt_until = offset_to_encrypt + + resp.encrypted_length() + + tag_len.0; let resp_handshake = Handshake::new( HandshakeData::DirSync(DirSync::Resp(resp)), ); @@ -750,7 +771,20 @@ impl Fenrir { }; let mut raw_out = Vec::::with_capacity(packet.len()); - packet.serialize(&mut raw_out); + packet.serialize( + head_len, + tag_len, + &mut raw_out, + ); + + if let Err(e) = auth_conn.cipher_send.encrypt( + aad, + &mut raw_out + [offset_to_encrypt..encrypt_until], + ) { + ::tracing::error!("can't encrypt: {:?}", e); + return; + } self.send_packet(raw_out, udp.src, udp.dst) .await; }