DirSync::Resp work

Signed-off-by: Luca Fulchir <luca.fulchir@runesauth.com>
This commit is contained in:
Luca Fulchir 2023-05-17 10:26:39 +02:00
parent 7a129dbe90
commit a5f18ac533
Signed by: luca.fulchir
GPG Key ID: 8F6440603D13A78E
5 changed files with 259 additions and 140 deletions

View File

@ -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<u8>),
/// Client data, decrypted but unprocessed
ClearText(VecDeque<u8>),
/// 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<u8> {
match self {
ReqInner::CipherText(data) => data,
_ => panic!(),
/// 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,
}
}
/// switch from ciphertext to cleartext
pub fn mark_as_cleartext(&mut self) {
let mut newdata: VecDeque<u8>;
match self {
ReqInner::CipherText(data) => {
newdata = VecDeque::new();
::core::mem::swap(&mut newdata, data);
}
_ => 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<Self, Error> {
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<Self, Error> {
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<u8>),
/// Server data, decrypted but unprocessed
ClearText(VecDeque<u8>),
/// 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<u8> {
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<u8>;
@ -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<Self, Error> {
pub fn deserialize(raw: &[u8]) -> Result<Self, Error> {
todo!();
}
}

View File

@ -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> {

View File

@ -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()..]);
}
}

View File

@ -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<u8>) -> 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) => {
data[data_len_notag..]
// add tag
data.get_tag_slice().copy_from_slice(tag.as_slice());
//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<u8>,
) -> 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(())

View File

@ -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<HandshakeAction, Error> {
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,7 +657,9 @@ impl Fenrir {
return;
}
};
let action = match self._inner.recv_handshake(handshake) {
let action =
match self._inner.recv_handshake(handshake, &mut udp.data[8..])
{
Ok(action) => action,
Err(err) => {
::tracing::debug!("Handshake recv error {}", err);
@ -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::<u8>::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;
}