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