Compare commits
No commits in common. "main" and "handshake_dir_sync" have entirely different histories.
main
...
handshake_
24
flake.lock
24
flake.lock
|
@ -5,11 +5,11 @@
|
||||||
"systems": "systems"
|
"systems": "systems"
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1685518550,
|
"lastModified": 1681202837,
|
||||||
"narHash": "sha256-o2d0KcvaXzTrPRIo0kOLV0/QXHhDQ5DTi+OxcjO8xqY=",
|
"narHash": "sha256-H+Rh19JDwRtpVPAWp64F+rlEtxUWBAQW28eAi3SRSzg=",
|
||||||
"owner": "numtide",
|
"owner": "numtide",
|
||||||
"repo": "flake-utils",
|
"repo": "flake-utils",
|
||||||
"rev": "a1720a10a6cfe8234c0e93907ffe81be440f4cef",
|
"rev": "cfacdce06f30d2b68473a46042957675eebb3401",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
@ -38,11 +38,11 @@
|
||||||
},
|
},
|
||||||
"nixpkgs": {
|
"nixpkgs": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1686921029,
|
"lastModified": 1684922889,
|
||||||
"narHash": "sha256-J1bX9plPCFhTSh6E3TWn9XSxggBh/zDD4xigyaIQBy8=",
|
"narHash": "sha256-l0WZAmln8959O7RdYUJ3gnAIM9OPKFLKHKGX4q+Blrk=",
|
||||||
"owner": "nixos",
|
"owner": "nixos",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "c7ff1b9b95620ce8728c0d7bd501c458e6da9e04",
|
"rev": "04aaf8511678a0d0f347fdf1e8072fe01e4a509e",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
@ -54,11 +54,11 @@
|
||||||
},
|
},
|
||||||
"nixpkgs-unstable": {
|
"nixpkgs-unstable": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1686960236,
|
"lastModified": 1684844536,
|
||||||
"narHash": "sha256-AYCC9rXNLpUWzD9hm+askOfpliLEC9kwAo7ITJc4HIw=",
|
"narHash": "sha256-M7HhXYVqAuNb25r/d3FOO0z4GxPqDIZp5UjHFbBgw0Q=",
|
||||||
"owner": "nixos",
|
"owner": "nixos",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "04af42f3b31dba0ef742d254456dc4c14eedac86",
|
"rev": "d30264c2691128adc261d7c9388033645f0e742b",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
@ -98,11 +98,11 @@
|
||||||
"nixpkgs": "nixpkgs_2"
|
"nixpkgs": "nixpkgs_2"
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1687055571,
|
"lastModified": 1684894917,
|
||||||
"narHash": "sha256-UvLoO6u5n9TzY80BpM4DaacxvyJl7u9mm9CA72d309g=",
|
"narHash": "sha256-kwKCfmliHIxKuIjnM95TRcQxM/4AAEIZ+4A9nDJ6cJs=",
|
||||||
"owner": "oxalica",
|
"owner": "oxalica",
|
||||||
"repo": "rust-overlay",
|
"repo": "rust-overlay",
|
||||||
"rev": "2de557c780dcb127128ae987fca9d6c2b0d7dc0f",
|
"rev": "9ea38d547100edcf0da19aaebbdffa2810585495",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
|
|
@ -2,10 +2,11 @@
|
||||||
//! Configuration to initialize the Fenrir networking library
|
//! Configuration to initialize the Fenrir networking library
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
connection::handshake,
|
connection::handshake::HandshakeID,
|
||||||
enc::{
|
enc::{
|
||||||
asym::{KeyExchangeKind, KeyID, PrivKey, PubKey},
|
asym::{KeyExchangeKind, KeyID, PrivKey, PubKey},
|
||||||
hkdf, sym,
|
hkdf::HkdfKind,
|
||||||
|
sym::CipherKind,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use ::std::{
|
use ::std::{
|
||||||
|
@ -43,13 +44,13 @@ pub struct Config {
|
||||||
/// List of DNS resolvers to use
|
/// List of DNS resolvers to use
|
||||||
pub resolvers: Vec<SocketAddr>,
|
pub resolvers: Vec<SocketAddr>,
|
||||||
/// Supported handshakes
|
/// Supported handshakes
|
||||||
pub handshakes: Vec<handshake::ID>,
|
pub handshakes: Vec<HandshakeID>,
|
||||||
/// Supported key exchanges
|
/// Supported key exchanges
|
||||||
pub key_exchanges: Vec<KeyExchangeKind>,
|
pub key_exchanges: Vec<KeyExchangeKind>,
|
||||||
/// Supported Hkdfs
|
/// Supported Hkdfs
|
||||||
pub hkdfs: Vec<hkdf::Kind>,
|
pub hkdfs: Vec<HkdfKind>,
|
||||||
/// Supported Ciphers
|
/// Supported Ciphers
|
||||||
pub ciphers: Vec<sym::Kind>,
|
pub ciphers: Vec<CipherKind>,
|
||||||
/// list of authentication servers
|
/// list of authentication servers
|
||||||
/// clients will have this empty
|
/// clients will have this empty
|
||||||
pub servers: Vec<AuthServer>,
|
pub servers: Vec<AuthServer>,
|
||||||
|
@ -72,10 +73,10 @@ impl Default for Config {
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
resolvers: Vec::new(),
|
resolvers: Vec::new(),
|
||||||
handshakes: [handshake::ID::DirectorySynchronized].to_vec(),
|
handshakes: [HandshakeID::DirectorySynchronized].to_vec(),
|
||||||
key_exchanges: [KeyExchangeKind::X25519DiffieHellman].to_vec(),
|
key_exchanges: [KeyExchangeKind::X25519DiffieHellman].to_vec(),
|
||||||
hkdfs: [hkdf::Kind::Sha3].to_vec(),
|
hkdfs: [HkdfKind::Sha3].to_vec(),
|
||||||
ciphers: [sym::Kind::XChaCha20Poly1305].to_vec(),
|
ciphers: [CipherKind::XChaCha20Poly1305].to_vec(),
|
||||||
servers: Vec::new(),
|
servers: Vec::new(),
|
||||||
server_keys: Vec::new(),
|
server_keys: Vec::new(),
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,22 +1,85 @@
|
||||||
//! Directory synchronized handshake, Request parsing
|
//! Directory synchronized handshake
|
||||||
|
//! 1-RTT connection
|
||||||
|
//!
|
||||||
|
//! The simplest, fastest handshake supported by Fenrir
|
||||||
|
//! Downside: It does not offer protection from DDos,
|
||||||
|
//! no perfect forward secrecy
|
||||||
|
//!
|
||||||
|
//! To grant a form of perfect forward secrecy, the server should periodically
|
||||||
|
//! change the DNSSEC public/private keys
|
||||||
|
|
||||||
|
use super::{Error, HandshakeData};
|
||||||
use crate::{
|
use crate::{
|
||||||
auth,
|
auth,
|
||||||
connection::{
|
connection::{ProtocolVersion, ID},
|
||||||
handshake::{
|
|
||||||
self,
|
|
||||||
dirsync::{DirSync, Nonce},
|
|
||||||
Error,
|
|
||||||
},
|
|
||||||
ProtocolVersion, ID,
|
|
||||||
},
|
|
||||||
enc::{
|
enc::{
|
||||||
asym::{ExchangePubKey, KeyExchangeKind, KeyID},
|
asym::{ExchangePubKey, KeyExchangeKind, KeyID},
|
||||||
hkdf,
|
hkdf::HkdfKind,
|
||||||
sym::{self, NonceLen, TagLen},
|
sym::{CipherKind, HeadLen, TagLen},
|
||||||
|
Random, Secret,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// TODO: merge with crate::enc::sym::Nonce
|
||||||
|
/// random nonce
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||||
|
pub struct Nonce(pub(crate) [u8; 16]);
|
||||||
|
|
||||||
|
impl Nonce {
|
||||||
|
/// Create a new random Nonce
|
||||||
|
pub fn new(rnd: &Random) -> Self {
|
||||||
|
use ::core::mem::MaybeUninit;
|
||||||
|
let mut out: MaybeUninit<[u8; 16]>;
|
||||||
|
#[allow(unsafe_code)]
|
||||||
|
unsafe {
|
||||||
|
out = MaybeUninit::uninit();
|
||||||
|
let _ = rnd.fill(out.assume_init_mut());
|
||||||
|
Self(out.assume_init())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// Length of the serialized Nonce
|
||||||
|
pub const fn len() -> usize {
|
||||||
|
16
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl From<&[u8; 16]> for Nonce {
|
||||||
|
fn from(raw: &[u8; 16]) -> Self {
|
||||||
|
Self(raw.clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Parsed handshake
|
||||||
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
|
pub enum DirSync {
|
||||||
|
/// Directory synchronized handshake: client request
|
||||||
|
Req(Req),
|
||||||
|
/// Directory synchronized handshake: server response
|
||||||
|
Resp(Resp),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DirSync {
|
||||||
|
/// actual length of the dirsync handshake data
|
||||||
|
pub fn len(&self, head_len: HeadLen, tag_len: TagLen) -> usize {
|
||||||
|
match self {
|
||||||
|
DirSync::Req(req) => req.len(),
|
||||||
|
DirSync::Resp(resp) => resp.len(head_len, tag_len),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// Serialize into raw bytes
|
||||||
|
/// NOTE: assumes that there is exactly asa much buffer as needed
|
||||||
|
pub fn serialize(
|
||||||
|
&self,
|
||||||
|
head_len: HeadLen,
|
||||||
|
tag_len: TagLen,
|
||||||
|
out: &mut [u8],
|
||||||
|
) {
|
||||||
|
match self {
|
||||||
|
DirSync::Req(req) => req.serialize(head_len, tag_len, out),
|
||||||
|
DirSync::Resp(resp) => resp.serialize(head_len, tag_len, out),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Client request of a directory synchronized handshake
|
/// Client request of a directory synchronized handshake
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub struct Req {
|
pub struct Req {
|
||||||
|
@ -25,13 +88,13 @@ pub struct Req {
|
||||||
/// Selected key exchange
|
/// Selected key exchange
|
||||||
pub exchange: KeyExchangeKind,
|
pub exchange: KeyExchangeKind,
|
||||||
/// Selected hkdf
|
/// Selected hkdf
|
||||||
pub hkdf: hkdf::Kind,
|
pub hkdf: HkdfKind,
|
||||||
/// Selected cipher
|
/// Selected cipher
|
||||||
pub cipher: sym::Kind,
|
pub cipher: CipherKind,
|
||||||
/// Client ephemeral public key used for key exchanges
|
/// Client ephemeral public key used for key exchanges
|
||||||
pub exchange_key: ExchangePubKey,
|
pub exchange_key: ExchangePubKey,
|
||||||
/// encrypted data
|
/// encrypted data
|
||||||
pub data: State,
|
pub data: ReqInner,
|
||||||
// SECURITY: TODO: Add padding to min: 1200 bytes
|
// SECURITY: TODO: Add padding to min: 1200 bytes
|
||||||
// to avoid amplification attaks
|
// to avoid amplification attaks
|
||||||
// also: 1200 < 1280 to allow better vpn compatibility
|
// also: 1200 < 1280 to allow better vpn compatibility
|
||||||
|
@ -42,30 +105,30 @@ impl Req {
|
||||||
/// NOTE: starts from the beginning of the fenrir packet
|
/// NOTE: starts from the beginning of the fenrir packet
|
||||||
pub fn encrypted_offset(&self) -> usize {
|
pub fn encrypted_offset(&self) -> usize {
|
||||||
ProtocolVersion::len()
|
ProtocolVersion::len()
|
||||||
+ handshake::ID::len()
|
+ crate::handshake::HandshakeID::len()
|
||||||
+ KeyID::len()
|
+ KeyID::len()
|
||||||
+ KeyExchangeKind::len()
|
+ KeyExchangeKind::len()
|
||||||
+ hkdf::Kind::len()
|
+ HkdfKind::len()
|
||||||
+ sym::Kind::len()
|
+ CipherKind::len()
|
||||||
+ self.exchange_key.kind().pub_len()
|
+ self.exchange_key.kind().pub_len()
|
||||||
}
|
}
|
||||||
/// return the total length of the cleartext data
|
/// return the total length of the cleartext data
|
||||||
pub fn encrypted_length(
|
pub fn encrypted_length(
|
||||||
&self,
|
&self,
|
||||||
head_len: NonceLen,
|
head_len: HeadLen,
|
||||||
tag_len: TagLen,
|
tag_len: TagLen,
|
||||||
) -> usize {
|
) -> usize {
|
||||||
match &self.data {
|
match &self.data {
|
||||||
State::ClearText(data) => data.len() + head_len.0 + tag_len.0,
|
ReqInner::ClearText(data) => data.len() + head_len.0 + tag_len.0,
|
||||||
State::CipherText(length) => *length,
|
ReqInner::CipherText(length) => *length,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// actual length of the directory synchronized request
|
/// actual length of the directory synchronized request
|
||||||
pub fn len(&self) -> usize {
|
pub fn len(&self) -> usize {
|
||||||
KeyID::len()
|
KeyID::len()
|
||||||
+ KeyExchangeKind::len()
|
+ KeyExchangeKind::len()
|
||||||
+ hkdf::Kind::len()
|
+ HkdfKind::len()
|
||||||
+ sym::Kind::len()
|
+ CipherKind::len()
|
||||||
+ self.exchange_key.kind().pub_len()
|
+ self.exchange_key.kind().pub_len()
|
||||||
+ self.cipher.nonce_len().0
|
+ self.cipher.nonce_len().0
|
||||||
+ self.data.len()
|
+ self.data.len()
|
||||||
|
@ -75,7 +138,7 @@ impl Req {
|
||||||
/// 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(
|
pub fn serialize(
|
||||||
&self,
|
&self,
|
||||||
head_len: NonceLen,
|
head_len: HeadLen,
|
||||||
tag_len: TagLen,
|
tag_len: TagLen,
|
||||||
out: &mut [u8],
|
out: &mut [u8],
|
||||||
) {
|
) {
|
||||||
|
@ -87,7 +150,7 @@ impl Req {
|
||||||
let written_next = 5 + key_len;
|
let written_next = 5 + key_len;
|
||||||
self.exchange_key.serialize_into(&mut out[5..written_next]);
|
self.exchange_key.serialize_into(&mut out[5..written_next]);
|
||||||
let written = written_next;
|
let written = written_next;
|
||||||
if let State::ClearText(data) = &self.data {
|
if let ReqInner::ClearText(data) = &self.data {
|
||||||
let from = written + head_len.0;
|
let from = written + head_len.0;
|
||||||
let to = out.len() - tag_len.0;
|
let to = out.len() - tag_len.0;
|
||||||
data.serialize(&mut out[from..to]);
|
data.serialize(&mut out[from..to]);
|
||||||
|
@ -97,8 +160,8 @@ impl Req {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl handshake::Parsing for Req {
|
impl super::HandshakeParsing for Req {
|
||||||
fn deserialize(raw: &[u8]) -> Result<handshake::Data, Error> {
|
fn deserialize(raw: &[u8]) -> Result<HandshakeData, Error> {
|
||||||
const MIN_PKT_LEN: usize = 10;
|
const MIN_PKT_LEN: usize = 10;
|
||||||
if raw.len() < MIN_PKT_LEN {
|
if raw.len() < MIN_PKT_LEN {
|
||||||
return Err(Error::NotEnoughData);
|
return Err(Error::NotEnoughData);
|
||||||
|
@ -110,25 +173,25 @@ impl handshake::Parsing for Req {
|
||||||
Some(exchange) => exchange,
|
Some(exchange) => exchange,
|
||||||
None => return Err(Error::Parsing),
|
None => return Err(Error::Parsing),
|
||||||
};
|
};
|
||||||
let hkdf: hkdf::Kind = match hkdf::Kind::from_u8(raw[3]) {
|
let hkdf: HkdfKind = match HkdfKind::from_u8(raw[3]) {
|
||||||
Some(exchange) => exchange,
|
Some(exchange) => exchange,
|
||||||
None => return Err(Error::Parsing),
|
None => return Err(Error::Parsing),
|
||||||
};
|
};
|
||||||
let cipher: sym::Kind = match sym::Kind::from_u8(raw[4]) {
|
let cipher: CipherKind = match CipherKind::from_u8(raw[4]) {
|
||||||
Some(cipher) => cipher,
|
Some(cipher) => cipher,
|
||||||
None => return Err(Error::Parsing),
|
None => return Err(Error::Parsing),
|
||||||
};
|
};
|
||||||
const CURR_SIZE: usize = KeyID::len()
|
const CURR_SIZE: usize = KeyID::len()
|
||||||
+ KeyExchangeKind::len()
|
+ KeyExchangeKind::len()
|
||||||
+ hkdf::Kind::len()
|
+ HkdfKind::len()
|
||||||
+ sym::Kind::len();
|
+ CipherKind::len();
|
||||||
let (exchange_key, len) =
|
let (exchange_key, len) =
|
||||||
match ExchangePubKey::deserialize(&raw[CURR_SIZE..]) {
|
match ExchangePubKey::deserialize(&raw[CURR_SIZE..]) {
|
||||||
Ok(exchange_key) => exchange_key,
|
Ok(exchange_key) => exchange_key,
|
||||||
Err(e) => return Err(e.into()),
|
Err(e) => return Err(e.into()),
|
||||||
};
|
};
|
||||||
let data = State::CipherText(raw.len() - (CURR_SIZE + len));
|
let data = ReqInner::CipherText(raw.len() - (CURR_SIZE + len));
|
||||||
Ok(handshake::Data::DirSync(DirSync::Req(Self {
|
Ok(HandshakeData::DirSync(DirSync::Req(Self {
|
||||||
key_id,
|
key_id,
|
||||||
exchange,
|
exchange,
|
||||||
hkdf,
|
hkdf,
|
||||||
|
@ -141,18 +204,18 @@ impl handshake::Parsing for Req {
|
||||||
|
|
||||||
/// Quick way to avoid mixing cipher and clear text
|
/// Quick way to avoid mixing cipher and clear text
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub enum State {
|
pub enum ReqInner {
|
||||||
/// Data is still encrytped, we only keep the length
|
/// Data is still encrytped, we only keep the length
|
||||||
CipherText(usize),
|
CipherText(usize),
|
||||||
/// Client data, decrypted and parsed
|
/// Client data, decrypted and parsed
|
||||||
ClearText(Data),
|
ClearText(ReqData),
|
||||||
}
|
}
|
||||||
impl State {
|
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 {
|
||||||
State::CipherText(len) => *len,
|
ReqInner::CipherText(len) => *len,
|
||||||
State::ClearText(data) => data.len(),
|
ReqInner::ClearText(data) => data.len(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// parse the cleartext
|
/// parse the cleartext
|
||||||
|
@ -161,19 +224,19 @@ impl State {
|
||||||
raw: &[u8],
|
raw: &[u8],
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
let clear = match self {
|
let clear = match self {
|
||||||
State::CipherText(len) => {
|
ReqInner::CipherText(len) => {
|
||||||
assert!(
|
assert!(
|
||||||
*len > raw.len(),
|
*len > raw.len(),
|
||||||
"DirSync::State::CipherText length mismatch"
|
"DirSync::ReqInner::CipherText length mismatch"
|
||||||
);
|
);
|
||||||
match Data::deserialize(raw) {
|
match ReqData::deserialize(raw) {
|
||||||
Ok(clear) => clear,
|
Ok(clear) => clear,
|
||||||
Err(e) => return Err(e),
|
Err(e) => return Err(e),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => return Err(Error::Parsing),
|
_ => return Err(Error::Parsing),
|
||||||
};
|
};
|
||||||
*self = State::ClearText(clear);
|
*self = ReqInner::ClearText(clear);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -258,7 +321,7 @@ impl AuthInfo {
|
||||||
|
|
||||||
/// Decrypted request data
|
/// Decrypted request data
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub struct Data {
|
pub struct ReqData {
|
||||||
/// Random nonce, the client can use this to track multiple key exchanges
|
/// Random nonce, the client can use this to track multiple key exchanges
|
||||||
pub nonce: Nonce,
|
pub nonce: Nonce,
|
||||||
/// Client key id so the client can use and rotate keys
|
/// Client key id so the client can use and rotate keys
|
||||||
|
@ -268,7 +331,7 @@ pub struct Data {
|
||||||
/// Authentication data
|
/// Authentication data
|
||||||
pub auth: AuthInfo,
|
pub auth: AuthInfo,
|
||||||
}
|
}
|
||||||
impl Data {
|
impl ReqData {
|
||||||
/// actual length of the request data
|
/// actual length of the request data
|
||||||
pub fn len(&self) -> usize {
|
pub fn len(&self) -> usize {
|
||||||
Nonce::len() + KeyID::len() + ID::len() + self.auth.len()
|
Nonce::len() + KeyID::len() + ID::len() + self.auth.len()
|
||||||
|
@ -320,3 +383,179 @@ impl Data {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Quick way to avoid mixing cipher and clear text
|
||||||
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
|
pub enum RespInner {
|
||||||
|
/// Server data, still in ciphertext
|
||||||
|
CipherText(usize),
|
||||||
|
/// Parsed, cleartext server data
|
||||||
|
ClearText(RespData),
|
||||||
|
}
|
||||||
|
impl RespInner {
|
||||||
|
/// The length of the data
|
||||||
|
pub fn len(&self) -> usize {
|
||||||
|
match self {
|
||||||
|
RespInner::CipherText(len) => *len,
|
||||||
|
RespInner::ClearText(_) => RespData::len(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// parse the cleartext
|
||||||
|
pub fn deserialize_as_cleartext(
|
||||||
|
&mut self,
|
||||||
|
raw: &[u8],
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
let clear = match self {
|
||||||
|
RespInner::CipherText(len) => {
|
||||||
|
assert!(
|
||||||
|
*len > raw.len(),
|
||||||
|
"DirSync::RespInner::CipherText length mismatch"
|
||||||
|
);
|
||||||
|
match RespData::deserialize(raw) {
|
||||||
|
Ok(clear) => clear,
|
||||||
|
Err(e) => return Err(e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => return Err(Error::Parsing),
|
||||||
|
};
|
||||||
|
*self = RespInner::ClearText(clear);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
/// Serialize the still cleartext data
|
||||||
|
pub fn serialize(&self, out: &mut [u8]) {
|
||||||
|
if let RespInner::ClearText(clear) = &self {
|
||||||
|
clear.serialize(out);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Server response in a directory synchronized handshake
|
||||||
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
|
pub struct Resp {
|
||||||
|
/// Tells the client with which key the exchange was done
|
||||||
|
pub client_key_id: KeyID,
|
||||||
|
/// actual response data, might be encrypted
|
||||||
|
pub data: RespInner,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl super::HandshakeParsing for Resp {
|
||||||
|
fn deserialize(raw: &[u8]) -> Result<HandshakeData, Error> {
|
||||||
|
const MIN_PKT_LEN: usize = 68;
|
||||||
|
if raw.len() < MIN_PKT_LEN {
|
||||||
|
return Err(Error::NotEnoughData);
|
||||||
|
}
|
||||||
|
let client_key_id: KeyID =
|
||||||
|
KeyID(u16::from_le_bytes(raw[0..KeyID::len()].try_into().unwrap()));
|
||||||
|
Ok(HandshakeData::DirSync(DirSync::Resp(Self {
|
||||||
|
client_key_id,
|
||||||
|
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()
|
||||||
|
+ crate::connection::handshake::HandshakeID::len()
|
||||||
|
+ KeyID::len()
|
||||||
|
}
|
||||||
|
/// return the total length of the cleartext data
|
||||||
|
pub fn encrypted_length(
|
||||||
|
&self,
|
||||||
|
head_len: HeadLen,
|
||||||
|
tag_len: TagLen,
|
||||||
|
) -> usize {
|
||||||
|
match &self.data {
|
||||||
|
RespInner::ClearText(_data) => {
|
||||||
|
RespData::len() + head_len.0 + tag_len.0
|
||||||
|
}
|
||||||
|
RespInner::CipherText(len) => *len,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// Total length of the response handshake
|
||||||
|
pub fn len(&self, head_len: HeadLen, tag_len: TagLen) -> usize {
|
||||||
|
KeyID::len() + head_len.0 + self.data.len() + tag_len.0
|
||||||
|
}
|
||||||
|
/// Serialize into raw bytes
|
||||||
|
/// NOTE: assumes that there is exactly as much buffer as needed
|
||||||
|
pub fn serialize(
|
||||||
|
&self,
|
||||||
|
head_len: HeadLen,
|
||||||
|
_tag_len: TagLen,
|
||||||
|
out: &mut [u8],
|
||||||
|
) {
|
||||||
|
out[0..KeyID::len()]
|
||||||
|
.copy_from_slice(&self.client_key_id.0.to_le_bytes());
|
||||||
|
let start_data = KeyID::len() + head_len.0;
|
||||||
|
let end_data = start_data + self.data.len();
|
||||||
|
self.data.serialize(&mut out[start_data..end_data]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Decrypted response data
|
||||||
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
|
pub struct RespData {
|
||||||
|
/// Client nonce, copied from the request
|
||||||
|
pub client_nonce: Nonce,
|
||||||
|
/// Server Connection ID
|
||||||
|
pub id: ID,
|
||||||
|
/// Service Connection ID
|
||||||
|
pub service_connection_id: ID,
|
||||||
|
/// Service encryption key
|
||||||
|
pub service_key: Secret,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RespData {
|
||||||
|
/// Return the expected length for buffer allocation
|
||||||
|
pub fn len() -> usize {
|
||||||
|
Nonce::len() + ID::len() + ID::len() + Secret::len()
|
||||||
|
}
|
||||||
|
/// Serialize the data into a buffer
|
||||||
|
/// NOTE: assumes that there is exactly asa much buffer as needed
|
||||||
|
pub fn serialize(&self, out: &mut [u8]) {
|
||||||
|
let mut start = 0;
|
||||||
|
let mut end = Nonce::len();
|
||||||
|
out[start..end].copy_from_slice(&self.client_nonce.0);
|
||||||
|
start = end;
|
||||||
|
end = end + ID::len();
|
||||||
|
self.id.serialize(&mut out[start..end]);
|
||||||
|
start = end;
|
||||||
|
end = end + ID::len();
|
||||||
|
self.service_connection_id.serialize(&mut out[start..end]);
|
||||||
|
start = end;
|
||||||
|
end = end + Secret::len();
|
||||||
|
out[start..end].copy_from_slice(self.service_key.as_ref());
|
||||||
|
}
|
||||||
|
/// Parse the cleartext raw data
|
||||||
|
pub fn deserialize(raw: &[u8]) -> Result<Self, Error> {
|
||||||
|
let raw_sized: &[u8; 16] = raw[..Nonce::len()].try_into().unwrap();
|
||||||
|
let client_nonce: Nonce = raw_sized.into();
|
||||||
|
let end = Nonce::len() + ID::len();
|
||||||
|
let id: ID =
|
||||||
|
u64::from_le_bytes(raw[Nonce::len()..end].try_into().unwrap())
|
||||||
|
.into();
|
||||||
|
if id.is_handshake() {
|
||||||
|
return Err(Error::Parsing);
|
||||||
|
}
|
||||||
|
let parsed = end;
|
||||||
|
let end = parsed + ID::len();
|
||||||
|
let service_connection_id: ID =
|
||||||
|
u64::from_le_bytes(raw[parsed..end].try_into().unwrap()).into();
|
||||||
|
if service_connection_id.is_handshake() {
|
||||||
|
return Err(Error::Parsing);
|
||||||
|
}
|
||||||
|
let parsed = end;
|
||||||
|
let end = parsed + Secret::len();
|
||||||
|
let raw_secret: &[u8; 32] = raw[parsed..end].try_into().unwrap();
|
||||||
|
let service_key = raw_secret.into();
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
client_nonce,
|
||||||
|
id,
|
||||||
|
service_connection_id,
|
||||||
|
service_key,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,77 +0,0 @@
|
||||||
//! Directory synchronized handshake
|
|
||||||
//! 1-RTT connection
|
|
||||||
//!
|
|
||||||
//! The simplest, fastest handshake supported by Fenrir
|
|
||||||
//! Downside: It does not offer protection from DDos,
|
|
||||||
//! no perfect forward secrecy
|
|
||||||
//!
|
|
||||||
//! To grant a form of perfect forward secrecy, the server should periodically
|
|
||||||
//! change the DNSSEC public/private keys
|
|
||||||
|
|
||||||
use crate::enc::{
|
|
||||||
sym::{NonceLen, TagLen},
|
|
||||||
Random,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub mod req;
|
|
||||||
pub mod resp;
|
|
||||||
|
|
||||||
// TODO: merge with crate::enc::sym::Nonce
|
|
||||||
/// random nonce
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
|
||||||
pub struct Nonce(pub(crate) [u8; 16]);
|
|
||||||
|
|
||||||
impl Nonce {
|
|
||||||
/// Create a new random Nonce
|
|
||||||
pub fn new(rnd: &Random) -> Self {
|
|
||||||
use ::core::mem::MaybeUninit;
|
|
||||||
let mut out: MaybeUninit<[u8; 16]>;
|
|
||||||
#[allow(unsafe_code)]
|
|
||||||
unsafe {
|
|
||||||
out = MaybeUninit::uninit();
|
|
||||||
let _ = rnd.fill(out.assume_init_mut());
|
|
||||||
Self(out.assume_init())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/// Length of the serialized Nonce
|
|
||||||
pub const fn len() -> usize {
|
|
||||||
16
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl From<&[u8; 16]> for Nonce {
|
|
||||||
fn from(raw: &[u8; 16]) -> Self {
|
|
||||||
Self(raw.clone())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Parsed handshake
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
|
||||||
pub enum DirSync {
|
|
||||||
/// Directory synchronized handshake: client request
|
|
||||||
Req(req::Req),
|
|
||||||
/// Directory synchronized handshake: server response
|
|
||||||
Resp(resp::Resp),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl DirSync {
|
|
||||||
/// actual length of the dirsync handshake data
|
|
||||||
pub fn len(&self, head_len: NonceLen, tag_len: TagLen) -> usize {
|
|
||||||
match self {
|
|
||||||
DirSync::Req(req) => req.len(),
|
|
||||||
DirSync::Resp(resp) => resp.len(head_len, tag_len),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/// Serialize into raw bytes
|
|
||||||
/// NOTE: assumes that there is exactly asa much buffer as needed
|
|
||||||
pub fn serialize(
|
|
||||||
&self,
|
|
||||||
head_len: NonceLen,
|
|
||||||
tag_len: TagLen,
|
|
||||||
out: &mut [u8],
|
|
||||||
) {
|
|
||||||
match self {
|
|
||||||
DirSync::Req(req) => req.serialize(head_len, tag_len, out),
|
|
||||||
DirSync::Resp(resp) => resp.serialize(head_len, tag_len, out),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,189 +0,0 @@
|
||||||
//! Directory synchronized handshake, Response parsing
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
connection::{
|
|
||||||
handshake::{
|
|
||||||
self,
|
|
||||||
dirsync::{DirSync, Nonce},
|
|
||||||
Error,
|
|
||||||
},
|
|
||||||
ProtocolVersion, ID,
|
|
||||||
},
|
|
||||||
enc::{
|
|
||||||
asym::KeyID,
|
|
||||||
sym::{NonceLen, TagLen},
|
|
||||||
Secret,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Server response in a directory synchronized handshake
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
|
||||||
pub struct Resp {
|
|
||||||
/// Tells the client with which key the exchange was done
|
|
||||||
pub client_key_id: KeyID,
|
|
||||||
/// actual response data, might be encrypted
|
|
||||||
pub data: State,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl handshake::Parsing for Resp {
|
|
||||||
fn deserialize(raw: &[u8]) -> Result<handshake::Data, Error> {
|
|
||||||
const MIN_PKT_LEN: usize = 68;
|
|
||||||
if raw.len() < MIN_PKT_LEN {
|
|
||||||
return Err(Error::NotEnoughData);
|
|
||||||
}
|
|
||||||
let client_key_id: KeyID =
|
|
||||||
KeyID(u16::from_le_bytes(raw[0..KeyID::len()].try_into().unwrap()));
|
|
||||||
Ok(handshake::Data::DirSync(DirSync::Resp(Self {
|
|
||||||
client_key_id,
|
|
||||||
data: State::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() + handshake::ID::len() + KeyID::len()
|
|
||||||
}
|
|
||||||
/// return the total length of the cleartext data
|
|
||||||
pub fn encrypted_length(
|
|
||||||
&self,
|
|
||||||
head_len: NonceLen,
|
|
||||||
tag_len: TagLen,
|
|
||||||
) -> usize {
|
|
||||||
match &self.data {
|
|
||||||
State::ClearText(_data) => Data::len() + head_len.0 + tag_len.0,
|
|
||||||
State::CipherText(len) => *len,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/// Total length of the response handshake
|
|
||||||
pub fn len(&self, head_len: NonceLen, tag_len: TagLen) -> usize {
|
|
||||||
KeyID::len() + head_len.0 + self.data.len() + tag_len.0
|
|
||||||
}
|
|
||||||
/// Serialize into raw bytes
|
|
||||||
/// NOTE: assumes that there is exactly as much buffer as needed
|
|
||||||
pub fn serialize(
|
|
||||||
&self,
|
|
||||||
head_len: NonceLen,
|
|
||||||
_tag_len: TagLen,
|
|
||||||
out: &mut [u8],
|
|
||||||
) {
|
|
||||||
out[0..KeyID::len()]
|
|
||||||
.copy_from_slice(&self.client_key_id.0.to_le_bytes());
|
|
||||||
let start_data = KeyID::len() + head_len.0;
|
|
||||||
let end_data = start_data + self.data.len();
|
|
||||||
self.data.serialize(&mut out[start_data..end_data]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Quick way to avoid mixing cipher and clear text
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
|
||||||
pub enum State {
|
|
||||||
/// Server data, still in ciphertext
|
|
||||||
CipherText(usize),
|
|
||||||
/// Parsed, cleartext server data
|
|
||||||
ClearText(Data),
|
|
||||||
}
|
|
||||||
impl State {
|
|
||||||
/// The length of the data
|
|
||||||
pub fn len(&self) -> usize {
|
|
||||||
match self {
|
|
||||||
State::CipherText(len) => *len,
|
|
||||||
State::ClearText(_) => Data::len(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/// parse the cleartext
|
|
||||||
pub fn deserialize_as_cleartext(
|
|
||||||
&mut self,
|
|
||||||
raw: &[u8],
|
|
||||||
) -> Result<(), Error> {
|
|
||||||
let clear = match self {
|
|
||||||
State::CipherText(len) => {
|
|
||||||
assert!(
|
|
||||||
*len > raw.len(),
|
|
||||||
"DirSync::State::CipherText length mismatch"
|
|
||||||
);
|
|
||||||
match Data::deserialize(raw) {
|
|
||||||
Ok(clear) => clear,
|
|
||||||
Err(e) => return Err(e),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => return Err(Error::Parsing),
|
|
||||||
};
|
|
||||||
*self = State::ClearText(clear);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
/// Serialize the still cleartext data
|
|
||||||
pub fn serialize(&self, out: &mut [u8]) {
|
|
||||||
if let State::ClearText(clear) = &self {
|
|
||||||
clear.serialize(out);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Decrypted response data
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
|
||||||
pub struct Data {
|
|
||||||
/// Client nonce, copied from the request
|
|
||||||
pub client_nonce: Nonce,
|
|
||||||
/// Server Connection ID
|
|
||||||
pub id: ID,
|
|
||||||
/// Service Connection ID
|
|
||||||
pub service_connection_id: ID,
|
|
||||||
/// Service encryption key
|
|
||||||
pub service_key: Secret,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Data {
|
|
||||||
/// Return the expected length for buffer allocation
|
|
||||||
pub fn len() -> usize {
|
|
||||||
Nonce::len() + ID::len() + ID::len() + Secret::len()
|
|
||||||
}
|
|
||||||
/// Serialize the data into a buffer
|
|
||||||
/// NOTE: assumes that there is exactly asa much buffer as needed
|
|
||||||
pub fn serialize(&self, out: &mut [u8]) {
|
|
||||||
let mut start = 0;
|
|
||||||
let mut end = Nonce::len();
|
|
||||||
out[start..end].copy_from_slice(&self.client_nonce.0);
|
|
||||||
start = end;
|
|
||||||
end = end + ID::len();
|
|
||||||
self.id.serialize(&mut out[start..end]);
|
|
||||||
start = end;
|
|
||||||
end = end + ID::len();
|
|
||||||
self.service_connection_id.serialize(&mut out[start..end]);
|
|
||||||
start = end;
|
|
||||||
end = end + Secret::len();
|
|
||||||
out[start..end].copy_from_slice(self.service_key.as_ref());
|
|
||||||
}
|
|
||||||
/// Parse the cleartext raw data
|
|
||||||
pub fn deserialize(raw: &[u8]) -> Result<Self, Error> {
|
|
||||||
let raw_sized: &[u8; 16] = raw[..Nonce::len()].try_into().unwrap();
|
|
||||||
let client_nonce: Nonce = raw_sized.into();
|
|
||||||
let end = Nonce::len() + ID::len();
|
|
||||||
let id: ID =
|
|
||||||
u64::from_le_bytes(raw[Nonce::len()..end].try_into().unwrap())
|
|
||||||
.into();
|
|
||||||
if id.is_handshake() {
|
|
||||||
return Err(Error::Parsing);
|
|
||||||
}
|
|
||||||
let parsed = end;
|
|
||||||
let end = parsed + ID::len();
|
|
||||||
let service_connection_id: ID =
|
|
||||||
u64::from_le_bytes(raw[parsed..end].try_into().unwrap()).into();
|
|
||||||
if service_connection_id.is_handshake() {
|
|
||||||
return Err(Error::Parsing);
|
|
||||||
}
|
|
||||||
let parsed = end;
|
|
||||||
let end = parsed + Secret::len();
|
|
||||||
let raw_secret: &[u8; 32] = raw[parsed..end].try_into().unwrap();
|
|
||||||
let service_key = raw_secret.into();
|
|
||||||
|
|
||||||
Ok(Self {
|
|
||||||
client_nonce,
|
|
||||||
id,
|
|
||||||
service_connection_id,
|
|
||||||
service_key,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -4,11 +4,10 @@ pub mod dirsync;
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests;
|
mod tests;
|
||||||
pub(crate) mod tracker;
|
pub(crate) mod tracker;
|
||||||
pub(crate) use tracker::{Action, Tracker};
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
connection::ProtocolVersion,
|
connection::ProtocolVersion,
|
||||||
enc::sym::{NonceLen, TagLen},
|
enc::sym::{HeadLen, TagLen},
|
||||||
};
|
};
|
||||||
use ::num_traits::FromPrimitive;
|
use ::num_traits::FromPrimitive;
|
||||||
|
|
||||||
|
@ -57,7 +56,7 @@ pub enum Error {
|
||||||
::strum_macros::IntoStaticStr,
|
::strum_macros::IntoStaticStr,
|
||||||
)]
|
)]
|
||||||
#[repr(u8)]
|
#[repr(u8)]
|
||||||
pub enum ID {
|
pub enum HandshakeID {
|
||||||
/// 1-RTT Directory synchronized handshake. Fast, no forward secrecy
|
/// 1-RTT Directory synchronized handshake. Fast, no forward secrecy
|
||||||
#[strum(serialize = "directory_synchronized")]
|
#[strum(serialize = "directory_synchronized")]
|
||||||
DirectorySynchronized = 0,
|
DirectorySynchronized = 0,
|
||||||
|
@ -68,7 +67,7 @@ pub enum ID {
|
||||||
#[strum(serialize = "stateless")]
|
#[strum(serialize = "stateless")]
|
||||||
Stateless,
|
Stateless,
|
||||||
}
|
}
|
||||||
impl ID {
|
impl HandshakeID {
|
||||||
/// The length of the serialized field
|
/// The length of the serialized field
|
||||||
pub const fn len() -> usize {
|
pub const fn len() -> usize {
|
||||||
1
|
1
|
||||||
|
@ -76,28 +75,28 @@ impl ID {
|
||||||
}
|
}
|
||||||
/// Parsed handshake
|
/// Parsed handshake
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub enum Data {
|
pub enum HandshakeData {
|
||||||
/// Directory synchronized handhsake
|
/// Directory synchronized handhsake
|
||||||
DirSync(dirsync::DirSync),
|
DirSync(dirsync::DirSync),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Data {
|
impl HandshakeData {
|
||||||
/// actual length of the handshake data
|
/// actual length of the handshake data
|
||||||
pub fn len(&self, head_len: NonceLen, tag_len: TagLen) -> usize {
|
pub fn len(&self, head_len: HeadLen, tag_len: TagLen) -> usize {
|
||||||
match self {
|
match self {
|
||||||
Data::DirSync(d) => d.len(head_len, tag_len),
|
HandshakeData::DirSync(d) => d.len(head_len, tag_len),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// 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(
|
pub fn serialize(
|
||||||
&self,
|
&self,
|
||||||
head_len: NonceLen,
|
head_len: HeadLen,
|
||||||
tag_len: TagLen,
|
tag_len: TagLen,
|
||||||
out: &mut [u8],
|
out: &mut [u8],
|
||||||
) {
|
) {
|
||||||
match self {
|
match self {
|
||||||
Data::DirSync(d) => d.serialize(head_len, tag_len, out),
|
HandshakeData::DirSync(d) => d.serialize(head_len, tag_len, out),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -134,19 +133,19 @@ pub struct Handshake {
|
||||||
/// Fenrir Protocol version
|
/// Fenrir Protocol version
|
||||||
pub fenrir_version: ProtocolVersion,
|
pub fenrir_version: ProtocolVersion,
|
||||||
/// enum for the parsed data
|
/// enum for the parsed data
|
||||||
pub data: Data,
|
pub data: HandshakeData,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Handshake {
|
impl Handshake {
|
||||||
/// Build new handshake from the data
|
/// Build new handshake from the data
|
||||||
pub fn new(data: Data) -> Self {
|
pub fn new(data: HandshakeData) -> Self {
|
||||||
Handshake {
|
Handshake {
|
||||||
fenrir_version: ProtocolVersion::V0,
|
fenrir_version: ProtocolVersion::V0,
|
||||||
data,
|
data,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// return the total length of the handshake
|
/// return the total length of the handshake
|
||||||
pub fn len(&self, head_len: NonceLen, tag_len: TagLen) -> usize {
|
pub fn len(&self, head_len: HeadLen, tag_len: TagLen) -> usize {
|
||||||
ProtocolVersion::len()
|
ProtocolVersion::len()
|
||||||
+ HandshakeKind::len()
|
+ HandshakeKind::len()
|
||||||
+ self.data.len(head_len, tag_len)
|
+ self.data.len(head_len, tag_len)
|
||||||
|
@ -166,11 +165,9 @@ impl Handshake {
|
||||||
None => return Err(Error::Parsing),
|
None => return Err(Error::Parsing),
|
||||||
};
|
};
|
||||||
let data = match handshake_kind {
|
let data = match handshake_kind {
|
||||||
HandshakeKind::DirSyncReq => {
|
HandshakeKind::DirSyncReq => dirsync::Req::deserialize(&raw[2..])?,
|
||||||
dirsync::req::Req::deserialize(&raw[2..])?
|
|
||||||
}
|
|
||||||
HandshakeKind::DirSyncResp => {
|
HandshakeKind::DirSyncResp => {
|
||||||
dirsync::resp::Resp::deserialize(&raw[2..])?
|
dirsync::Resp::deserialize(&raw[2..])?
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
|
@ -182,13 +179,13 @@ impl Handshake {
|
||||||
/// 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(
|
pub fn serialize(
|
||||||
&self,
|
&self,
|
||||||
head_len: NonceLen,
|
head_len: HeadLen,
|
||||||
tag_len: TagLen,
|
tag_len: TagLen,
|
||||||
out: &mut [u8],
|
out: &mut [u8],
|
||||||
) {
|
) {
|
||||||
out[0] = self.fenrir_version as u8;
|
out[0] = self.fenrir_version as u8;
|
||||||
out[1] = match &self.data {
|
out[1] = match &self.data {
|
||||||
Data::DirSync(d) => match d {
|
HandshakeData::DirSync(d) => match d {
|
||||||
dirsync::DirSync::Req(_) => HandshakeKind::DirSyncReq,
|
dirsync::DirSync::Req(_) => HandshakeKind::DirSyncReq,
|
||||||
dirsync::DirSync::Resp(_) => HandshakeKind::DirSyncResp,
|
dirsync::DirSync::Resp(_) => HandshakeKind::DirSyncResp,
|
||||||
},
|
},
|
||||||
|
@ -197,6 +194,6 @@ impl Handshake {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
trait Parsing {
|
trait HandshakeParsing {
|
||||||
fn deserialize(raw: &[u8]) -> Result<Data, Error>;
|
fn deserialize(raw: &[u8]) -> Result<HandshakeData, Error>;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,16 +1,13 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
auth,
|
auth,
|
||||||
connection::{
|
connection::{handshake::*, ID},
|
||||||
handshake::{self, dirsync, Handshake},
|
|
||||||
ID,
|
|
||||||
},
|
|
||||||
enc::{self, asym::KeyID},
|
enc::{self, asym::KeyID},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_handshake_dirsync_req() {
|
fn test_handshake_dirsync_req() {
|
||||||
let rand = enc::Random::new();
|
let rand = enc::Random::new();
|
||||||
let cipher = enc::sym::Kind::XChaCha20Poly1305;
|
let cipher = enc::sym::CipherKind::XChaCha20Poly1305;
|
||||||
|
|
||||||
let (_, exchange_key) =
|
let (_, exchange_key) =
|
||||||
match enc::asym::KeyExchangeKind::X25519DiffieHellman.new_keypair(&rand)
|
match enc::asym::KeyExchangeKind::X25519DiffieHellman.new_keypair(&rand)
|
||||||
|
@ -22,11 +19,11 @@ fn test_handshake_dirsync_req() {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let data = dirsync::req::State::ClearText(dirsync::req::Data {
|
let data = dirsync::ReqInner::ClearText(dirsync::ReqData {
|
||||||
nonce: dirsync::Nonce::new(&rand),
|
nonce: dirsync::Nonce::new(&rand),
|
||||||
client_key_id: KeyID(2424),
|
client_key_id: KeyID(2424),
|
||||||
id: ID::ID(::core::num::NonZeroU64::new(424242).unwrap()),
|
id: ID::ID(::core::num::NonZeroU64::new(424242).unwrap()),
|
||||||
auth: dirsync::req::AuthInfo {
|
auth: dirsync::AuthInfo {
|
||||||
user: auth::UserID::new(&rand),
|
user: auth::UserID::new(&rand),
|
||||||
token: auth::Token::new_anonymous(&rand),
|
token: auth::Token::new_anonymous(&rand),
|
||||||
service_id: auth::SERVICEID_AUTH,
|
service_id: auth::SERVICEID_AUTH,
|
||||||
|
@ -34,16 +31,16 @@ fn test_handshake_dirsync_req() {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
let h_req = Handshake::new(handshake::Data::DirSync(
|
let h_req = Handshake::new(HandshakeData::DirSync(dirsync::DirSync::Req(
|
||||||
dirsync::DirSync::Req(dirsync::req::Req {
|
dirsync::Req {
|
||||||
key_id: KeyID(4224),
|
key_id: KeyID(4224),
|
||||||
exchange: enc::asym::KeyExchangeKind::X25519DiffieHellman,
|
exchange: enc::asym::KeyExchangeKind::X25519DiffieHellman,
|
||||||
hkdf: enc::hkdf::Kind::Sha3,
|
hkdf: enc::hkdf::HkdfKind::Sha3,
|
||||||
cipher: enc::sym::Kind::XChaCha20Poly1305,
|
cipher: enc::sym::CipherKind::XChaCha20Poly1305,
|
||||||
exchange_key,
|
exchange_key,
|
||||||
data,
|
data,
|
||||||
}),
|
},
|
||||||
));
|
)));
|
||||||
|
|
||||||
let mut bytes = Vec::<u8>::with_capacity(
|
let mut bytes = Vec::<u8>::with_capacity(
|
||||||
h_req.len(cipher.nonce_len(), cipher.tag_len()),
|
h_req.len(cipher.nonce_len(), cipher.tag_len()),
|
||||||
|
@ -58,7 +55,7 @@ fn test_handshake_dirsync_req() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
if let handshake::Data::DirSync(dirsync::DirSync::Req(r_a)) =
|
if let HandshakeData::DirSync(dirsync::DirSync::Req(r_a)) =
|
||||||
&mut deserialized.data
|
&mut deserialized.data
|
||||||
{
|
{
|
||||||
let enc_start = r_a.encrypted_offset() + cipher.nonce_len().0;
|
let enc_start = r_a.encrypted_offset() + cipher.nonce_len().0;
|
||||||
|
@ -77,11 +74,11 @@ fn test_handshake_dirsync_req() {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_handshake_dirsync_reqsp() {
|
fn test_handshake_dirsync_reqsp() {
|
||||||
let rand = enc::Random::new();
|
let rand = enc::Random::new();
|
||||||
let cipher = enc::sym::Kind::XChaCha20Poly1305;
|
let cipher = enc::sym::CipherKind::XChaCha20Poly1305;
|
||||||
|
|
||||||
let service_key = enc::Secret::new_rand(&rand);
|
let service_key = enc::Secret::new_rand(&rand);
|
||||||
|
|
||||||
let data = dirsync::resp::State::ClearText(dirsync::resp::Data {
|
let data = dirsync::RespInner::ClearText(dirsync::RespData {
|
||||||
client_nonce: dirsync::Nonce::new(&rand),
|
client_nonce: dirsync::Nonce::new(&rand),
|
||||||
id: ID::ID(::core::num::NonZeroU64::new(424242).unwrap()),
|
id: ID::ID(::core::num::NonZeroU64::new(424242).unwrap()),
|
||||||
service_connection_id: ID::ID(
|
service_connection_id: ID::ID(
|
||||||
|
@ -90,8 +87,8 @@ fn test_handshake_dirsync_reqsp() {
|
||||||
service_key,
|
service_key,
|
||||||
});
|
});
|
||||||
|
|
||||||
let h_resp = Handshake::new(handshake::Data::DirSync(
|
let h_resp = Handshake::new(HandshakeData::DirSync(
|
||||||
dirsync::DirSync::Resp(dirsync::resp::Resp {
|
dirsync::DirSync::Resp(dirsync::Resp {
|
||||||
client_key_id: KeyID(4444),
|
client_key_id: KeyID(4444),
|
||||||
data,
|
data,
|
||||||
}),
|
}),
|
||||||
|
@ -110,7 +107,7 @@ fn test_handshake_dirsync_reqsp() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
if let handshake::Data::DirSync(dirsync::DirSync::Resp(r_a)) =
|
if let HandshakeData::DirSync(dirsync::DirSync::Resp(r_a)) =
|
||||||
&mut deserialized.data
|
&mut deserialized.data
|
||||||
{
|
{
|
||||||
let enc_start = r_a.encrypted_offset() + cipher.nonce_len().0;
|
let enc_start = r_a.encrypted_offset() + cipher.nonce_len().0;
|
||||||
|
|
|
@ -3,21 +3,22 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
auth::{Domain, ServiceID},
|
auth::{Domain, ServiceID},
|
||||||
connection::{
|
connection::{
|
||||||
|
self,
|
||||||
handshake::{self, Error, Handshake},
|
handshake::{self, Error, Handshake},
|
||||||
Conn, IDRecv, IDSend,
|
Connection, IDRecv, IDSend,
|
||||||
},
|
},
|
||||||
enc::{
|
enc::{
|
||||||
self,
|
self,
|
||||||
asym::{self, KeyID, PrivKey, PubKey},
|
asym::{self, KeyID, PrivKey, PubKey},
|
||||||
hkdf::{self, Hkdf},
|
hkdf::{Hkdf, HkdfKind},
|
||||||
sym::{self, CipherRecv},
|
sym::{CipherKind, CipherRecv},
|
||||||
},
|
},
|
||||||
inner::ThreadTracker,
|
inner::ThreadTracker,
|
||||||
};
|
};
|
||||||
|
|
||||||
use ::tokio::sync::oneshot;
|
use ::tokio::sync::oneshot;
|
||||||
|
|
||||||
pub(crate) struct Server {
|
pub(crate) struct HandshakeServer {
|
||||||
pub id: KeyID,
|
pub id: KeyID,
|
||||||
pub key: PrivKey,
|
pub key: PrivKey,
|
||||||
pub domains: Vec<Domain>,
|
pub domains: Vec<Domain>,
|
||||||
|
@ -25,10 +26,10 @@ pub(crate) struct Server {
|
||||||
|
|
||||||
pub(crate) type ConnectAnswer = Result<(KeyID, IDSend), crate::Error>;
|
pub(crate) type ConnectAnswer = Result<(KeyID, IDSend), crate::Error>;
|
||||||
|
|
||||||
pub(crate) struct Client {
|
pub(crate) struct HandshakeClient {
|
||||||
pub service_id: ServiceID,
|
pub service_id: ServiceID,
|
||||||
pub service_conn_id: IDRecv,
|
pub service_conn_id: IDRecv,
|
||||||
pub connection: Conn,
|
pub connection: Connection,
|
||||||
pub timeout: Option<::tokio::task::JoinHandle<()>>,
|
pub timeout: Option<::tokio::task::JoinHandle<()>>,
|
||||||
pub answer: oneshot::Sender<ConnectAnswer>,
|
pub answer: oneshot::Sender<ConnectAnswer>,
|
||||||
pub srv_key_id: KeyID,
|
pub srv_key_id: KeyID,
|
||||||
|
@ -36,13 +37,13 @@ pub(crate) struct Client {
|
||||||
|
|
||||||
/// Tracks the keys used by the client and the handshake
|
/// Tracks the keys used by the client and the handshake
|
||||||
/// they are associated with
|
/// they are associated with
|
||||||
pub(crate) struct ClientList {
|
pub(crate) struct HandshakeClientList {
|
||||||
used: Vec<::bitmaps::Bitmap<1024>>, // index = KeyID
|
used: Vec<::bitmaps::Bitmap<1024>>, // index = KeyID
|
||||||
keys: Vec<Option<(PrivKey, PubKey)>>,
|
keys: Vec<Option<(PrivKey, PubKey)>>,
|
||||||
list: Vec<Option<Client>>,
|
list: Vec<Option<HandshakeClient>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ClientList {
|
impl HandshakeClientList {
|
||||||
pub(crate) fn new() -> Self {
|
pub(crate) fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
used: [::bitmaps::Bitmap::<1024>::new()].to_vec(),
|
used: [::bitmaps::Bitmap::<1024>::new()].to_vec(),
|
||||||
|
@ -50,13 +51,13 @@ impl ClientList {
|
||||||
list: Vec::with_capacity(16),
|
list: Vec::with_capacity(16),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub(crate) fn get(&self, id: KeyID) -> Option<&Client> {
|
pub(crate) fn get(&self, id: KeyID) -> Option<&HandshakeClient> {
|
||||||
if id.0 as usize >= self.list.len() {
|
if id.0 as usize >= self.list.len() {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
self.list[id.0 as usize].as_ref()
|
self.list[id.0 as usize].as_ref()
|
||||||
}
|
}
|
||||||
pub(crate) fn remove(&mut self, id: KeyID) -> Option<Client> {
|
pub(crate) fn remove(&mut self, id: KeyID) -> Option<HandshakeClient> {
|
||||||
if id.0 as usize >= self.list.len() {
|
if id.0 as usize >= self.list.len() {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
@ -78,10 +79,11 @@ impl ClientList {
|
||||||
pub_key: PubKey,
|
pub_key: PubKey,
|
||||||
service_id: ServiceID,
|
service_id: ServiceID,
|
||||||
service_conn_id: IDRecv,
|
service_conn_id: IDRecv,
|
||||||
connection: Conn,
|
connection: Connection,
|
||||||
answer: oneshot::Sender<ConnectAnswer>,
|
answer: oneshot::Sender<ConnectAnswer>,
|
||||||
srv_key_id: KeyID,
|
srv_key_id: KeyID,
|
||||||
) -> Result<(KeyID, &mut Client), oneshot::Sender<ConnectAnswer>> {
|
) -> Result<(KeyID, &mut HandshakeClient), oneshot::Sender<ConnectAnswer>>
|
||||||
|
{
|
||||||
let maybe_free_key_idx =
|
let maybe_free_key_idx =
|
||||||
self.used.iter().enumerate().find_map(|(idx, bmap)| {
|
self.used.iter().enumerate().find_map(|(idx, bmap)| {
|
||||||
match bmap.first_false_index() {
|
match bmap.first_false_index() {
|
||||||
|
@ -110,7 +112,7 @@ impl ClientList {
|
||||||
self.list.push(None);
|
self.list.push(None);
|
||||||
}
|
}
|
||||||
self.keys[free_key_idx] = Some((priv_key, pub_key));
|
self.keys[free_key_idx] = Some((priv_key, pub_key));
|
||||||
self.list[free_key_idx] = Some(Client {
|
self.list[free_key_idx] = Some(HandshakeClient {
|
||||||
service_id,
|
service_id,
|
||||||
service_conn_id,
|
service_conn_id,
|
||||||
connection,
|
connection,
|
||||||
|
@ -142,8 +144,8 @@ pub(crate) struct ClientConnectInfo {
|
||||||
pub service_connection_id: IDRecv,
|
pub service_connection_id: IDRecv,
|
||||||
/// Parsed handshake packet
|
/// Parsed handshake packet
|
||||||
pub handshake: Handshake,
|
pub handshake: Handshake,
|
||||||
/// Conn
|
/// Connection
|
||||||
pub connection: Conn,
|
pub connection: Connection,
|
||||||
/// where to wake up the waiting client
|
/// where to wake up the waiting client
|
||||||
pub answer: oneshot::Sender<ConnectAnswer>,
|
pub answer: oneshot::Sender<ConnectAnswer>,
|
||||||
/// server public key id that we used on the handshake
|
/// server public key id that we used on the handshake
|
||||||
|
@ -151,7 +153,7 @@ pub(crate) struct ClientConnectInfo {
|
||||||
}
|
}
|
||||||
/// Intermediate actions to be taken while parsing the handshake
|
/// Intermediate actions to be taken while parsing the handshake
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub(crate) enum Action {
|
pub(crate) enum HandshakeAction {
|
||||||
/// Parsing finished, all ok, nothing to do
|
/// Parsing finished, all ok, nothing to do
|
||||||
Nothing,
|
Nothing,
|
||||||
/// Packet parsed, now go perform authentication
|
/// Packet parsed, now go perform authentication
|
||||||
|
@ -165,20 +167,20 @@ pub(crate) enum Action {
|
||||||
/// Each of them will handle a subset of all handshakes.
|
/// Each of them will handle a subset of all handshakes.
|
||||||
/// Each handshake is routed to a different tracker by checking
|
/// Each handshake is routed to a different tracker by checking
|
||||||
/// core = (udp_src_sender_port % total_threads) - 1
|
/// core = (udp_src_sender_port % total_threads) - 1
|
||||||
pub(crate) struct Tracker {
|
pub(crate) struct HandshakeTracker {
|
||||||
thread_id: ThreadTracker,
|
thread_id: ThreadTracker,
|
||||||
key_exchanges: Vec<asym::KeyExchangeKind>,
|
key_exchanges: Vec<asym::KeyExchangeKind>,
|
||||||
ciphers: Vec<sym::Kind>,
|
ciphers: Vec<CipherKind>,
|
||||||
/// ephemeral keys used server side in key exchange
|
/// ephemeral keys used server side in key exchange
|
||||||
keys_srv: Vec<Server>,
|
keys_srv: Vec<HandshakeServer>,
|
||||||
/// ephemeral keys used client side in key exchange
|
/// ephemeral keys used client side in key exchange
|
||||||
hshake_cli: ClientList,
|
hshake_cli: HandshakeClientList,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Tracker {
|
impl HandshakeTracker {
|
||||||
pub(crate) fn new(
|
pub(crate) fn new(
|
||||||
thread_id: ThreadTracker,
|
thread_id: ThreadTracker,
|
||||||
ciphers: Vec<sym::Kind>,
|
ciphers: Vec<CipherKind>,
|
||||||
key_exchanges: Vec<asym::KeyExchangeKind>,
|
key_exchanges: Vec<asym::KeyExchangeKind>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
@ -186,7 +188,7 @@ impl Tracker {
|
||||||
ciphers,
|
ciphers,
|
||||||
key_exchanges,
|
key_exchanges,
|
||||||
keys_srv: Vec::new(),
|
keys_srv: Vec::new(),
|
||||||
hshake_cli: ClientList::new(),
|
hshake_cli: HandshakeClientList::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub(crate) fn add_server_key(
|
pub(crate) fn add_server_key(
|
||||||
|
@ -197,7 +199,7 @@ impl Tracker {
|
||||||
if self.keys_srv.iter().find(|&k| k.id == id).is_some() {
|
if self.keys_srv.iter().find(|&k| k.id == id).is_some() {
|
||||||
return Err(());
|
return Err(());
|
||||||
}
|
}
|
||||||
self.keys_srv.push(Server {
|
self.keys_srv.push(HandshakeServer {
|
||||||
id,
|
id,
|
||||||
key,
|
key,
|
||||||
domains: Vec::new(),
|
domains: Vec::new(),
|
||||||
|
@ -231,10 +233,11 @@ impl Tracker {
|
||||||
pub_key: PubKey,
|
pub_key: PubKey,
|
||||||
service_id: ServiceID,
|
service_id: ServiceID,
|
||||||
service_conn_id: IDRecv,
|
service_conn_id: IDRecv,
|
||||||
connection: Conn,
|
connection: Connection,
|
||||||
answer: oneshot::Sender<ConnectAnswer>,
|
answer: oneshot::Sender<ConnectAnswer>,
|
||||||
srv_key_id: KeyID,
|
srv_key_id: KeyID,
|
||||||
) -> Result<(KeyID, &mut Client), oneshot::Sender<ConnectAnswer>> {
|
) -> Result<(KeyID, &mut HandshakeClient), oneshot::Sender<ConnectAnswer>>
|
||||||
|
{
|
||||||
self.hshake_cli.add(
|
self.hshake_cli.add(
|
||||||
priv_key,
|
priv_key,
|
||||||
pub_key,
|
pub_key,
|
||||||
|
@ -245,7 +248,10 @@ impl Tracker {
|
||||||
srv_key_id,
|
srv_key_id,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
pub(crate) fn remove_client(&mut self, key_id: KeyID) -> Option<Client> {
|
pub(crate) fn remove_client(
|
||||||
|
&mut self,
|
||||||
|
key_id: KeyID,
|
||||||
|
) -> Option<HandshakeClient> {
|
||||||
self.hshake_cli.remove(key_id)
|
self.hshake_cli.remove(key_id)
|
||||||
}
|
}
|
||||||
pub(crate) fn timeout_client(
|
pub(crate) fn timeout_client(
|
||||||
|
@ -263,10 +269,10 @@ impl Tracker {
|
||||||
&mut self,
|
&mut self,
|
||||||
mut handshake: Handshake,
|
mut handshake: Handshake,
|
||||||
handshake_raw: &mut [u8],
|
handshake_raw: &mut [u8],
|
||||||
) -> Result<Action, Error> {
|
) -> Result<HandshakeAction, Error> {
|
||||||
use handshake::dirsync::DirSync;
|
use connection::handshake::{dirsync::DirSync, HandshakeData};
|
||||||
match handshake.data {
|
match handshake.data {
|
||||||
handshake::Data::DirSync(ref mut ds) => match ds {
|
HandshakeData::DirSync(ref mut ds) => match ds {
|
||||||
DirSync::Req(ref mut req) => {
|
DirSync::Req(ref mut req) => {
|
||||||
if !self.key_exchanges.contains(&req.exchange) {
|
if !self.key_exchanges.contains(&req.exchange) {
|
||||||
return Err(enc::Error::UnsupportedKeyExchange.into());
|
return Err(enc::Error::UnsupportedKeyExchange.into());
|
||||||
|
@ -304,8 +310,7 @@ impl Tracker {
|
||||||
Ok(shared_key) => shared_key,
|
Ok(shared_key) => shared_key,
|
||||||
Err(e) => return Err(handshake::Error::Key(e).into()),
|
Err(e) => return Err(handshake::Error::Key(e).into()),
|
||||||
};
|
};
|
||||||
let hkdf =
|
let hkdf = Hkdf::new(HkdfKind::Sha3, b"fenrir", shared_key);
|
||||||
Hkdf::new(hkdf::Kind::Sha3, b"fenrir", shared_key);
|
|
||||||
let secret_recv = hkdf.get_secret(b"to_server");
|
let secret_recv = hkdf.get_secret(b"to_server");
|
||||||
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;
|
||||||
|
@ -329,7 +334,7 @@ impl Tracker {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return Ok(Action::AuthNeeded(AuthNeededInfo {
|
return Ok(HandshakeAction::AuthNeeded(AuthNeededInfo {
|
||||||
handshake,
|
handshake,
|
||||||
hkdf,
|
hkdf,
|
||||||
}));
|
}));
|
||||||
|
@ -369,14 +374,16 @@ impl Tracker {
|
||||||
if let Some(timeout) = hshake.timeout {
|
if let Some(timeout) = hshake.timeout {
|
||||||
timeout.abort();
|
timeout.abort();
|
||||||
}
|
}
|
||||||
return Ok(Action::ClientConnect(ClientConnectInfo {
|
return Ok(HandshakeAction::ClientConnect(
|
||||||
|
ClientConnectInfo {
|
||||||
service_id: hshake.service_id,
|
service_id: hshake.service_id,
|
||||||
service_connection_id: hshake.service_conn_id,
|
service_connection_id: hshake.service_conn_id,
|
||||||
handshake,
|
handshake,
|
||||||
connection: hshake.connection,
|
connection: hshake.connection,
|
||||||
answer: hshake.answer,
|
answer: hshake.answer,
|
||||||
srv_key_id: hshake.srv_key_id,
|
srv_key_id: hshake.srv_key_id,
|
||||||
}));
|
},
|
||||||
|
));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,109 +3,30 @@
|
||||||
pub mod handshake;
|
pub mod handshake;
|
||||||
pub mod packet;
|
pub mod packet;
|
||||||
pub mod socket;
|
pub mod socket;
|
||||||
pub mod stream;
|
|
||||||
|
|
||||||
use ::std::{rc::Rc, vec::Vec};
|
use ::std::{rc::Rc, vec::Vec};
|
||||||
|
|
||||||
pub use crate::connection::{handshake::Handshake, packet::Packet};
|
pub use crate::connection::{
|
||||||
|
handshake::Handshake,
|
||||||
|
packet::{ConnectionID as ID, Packet, PacketData},
|
||||||
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
dnssec,
|
dnssec,
|
||||||
enc::{
|
enc::{
|
||||||
asym::PubKey,
|
asym::PubKey,
|
||||||
hkdf::Hkdf,
|
hkdf::Hkdf,
|
||||||
sym::{self, CipherRecv, CipherSend},
|
sym::{CipherKind, CipherRecv, CipherSend},
|
||||||
Random,
|
Random,
|
||||||
},
|
},
|
||||||
inner::ThreadTracker,
|
inner::ThreadTracker,
|
||||||
};
|
};
|
||||||
use ::std::rc;
|
|
||||||
|
|
||||||
/// Fenrir Connection ID
|
|
||||||
///
|
|
||||||
/// 0 is special as it represents the handshake
|
|
||||||
/// Connection IDs are to be considered u64 little endian
|
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
|
|
||||||
pub enum ID {
|
|
||||||
/// Connection id 0 represent the handshake
|
|
||||||
Handshake,
|
|
||||||
/// Non-zero id can represent any connection
|
|
||||||
ID(::core::num::NonZeroU64),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ID {
|
|
||||||
/// Set the conenction id to handshake
|
|
||||||
pub fn new_handshake() -> Self {
|
|
||||||
Self::Handshake
|
|
||||||
}
|
|
||||||
/// New id from u64. PLZ NON ZERO
|
|
||||||
pub(crate) fn new_u64(raw: u64) -> Self {
|
|
||||||
#[allow(unsafe_code)]
|
|
||||||
unsafe {
|
|
||||||
ID::ID(::core::num::NonZeroU64::new_unchecked(raw))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub(crate) fn as_u64(&self) -> u64 {
|
|
||||||
match self {
|
|
||||||
ID::Handshake => 0,
|
|
||||||
ID::ID(id) => id.get(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/// New random service ID
|
|
||||||
pub fn new_rand(rand: &Random) -> Self {
|
|
||||||
let mut raw = [0; 8];
|
|
||||||
let mut num = 0;
|
|
||||||
while num == 0 {
|
|
||||||
rand.fill(&mut raw);
|
|
||||||
num = u64::from_le_bytes(raw);
|
|
||||||
}
|
|
||||||
#[allow(unsafe_code)]
|
|
||||||
unsafe {
|
|
||||||
ID::ID(::core::num::NonZeroU64::new_unchecked(num))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/// Quick check to know if this is an handshake
|
|
||||||
pub fn is_handshake(&self) -> bool {
|
|
||||||
*self == ID::Handshake
|
|
||||||
}
|
|
||||||
/// length if the connection ID in bytes
|
|
||||||
pub const fn len() -> usize {
|
|
||||||
8
|
|
||||||
}
|
|
||||||
/// write the ID to a buffer
|
|
||||||
pub fn serialize(&self, out: &mut [u8]) {
|
|
||||||
match self {
|
|
||||||
ID::Handshake => out[..8].copy_from_slice(&[0; 8]),
|
|
||||||
ID::ID(id) => out[..8].copy_from_slice(&id.get().to_le_bytes()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<u64> for ID {
|
|
||||||
fn from(raw: u64) -> Self {
|
|
||||||
if raw == 0 {
|
|
||||||
ID::Handshake
|
|
||||||
} else {
|
|
||||||
#[allow(unsafe_code)]
|
|
||||||
unsafe {
|
|
||||||
ID::ID(::core::num::NonZeroU64::new_unchecked(raw))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<[u8; 8]> for ID {
|
|
||||||
fn from(raw: [u8; 8]) -> Self {
|
|
||||||
let raw_u64 = u64::from_le_bytes(raw);
|
|
||||||
raw_u64.into()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// strong typedef for receiving connection id
|
/// strong typedef for receiving connection id
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
|
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||||
pub struct IDRecv(pub ID);
|
pub struct IDRecv(pub ID);
|
||||||
/// strong typedef for sending connection id
|
/// strong typedef for sending connection id
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
|
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||||
pub struct IDSend(pub ID);
|
pub struct IDSend(pub ID);
|
||||||
|
|
||||||
/// Version of the fenrir protocol in use
|
/// Version of the fenrir protocol in use
|
||||||
|
@ -126,16 +47,12 @@ impl ProtocolVersion {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The connection, as seen from a user of libFenrir
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct Connection(rc::Weak<Conn>);
|
|
||||||
|
|
||||||
/// A single connection and its data
|
/// A single connection and its data
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub(crate) struct Conn {
|
pub struct Connection {
|
||||||
/// Receiving Conn ID
|
/// Receiving Connection ID
|
||||||
pub id_recv: IDRecv,
|
pub id_recv: IDRecv,
|
||||||
/// Sending Conn ID
|
/// Sending Connection ID
|
||||||
pub id_send: IDSend,
|
pub id_send: IDSend,
|
||||||
/// The main hkdf used for all secrets in this connection
|
/// The main hkdf used for all secrets in this connection
|
||||||
pub hkdf: Hkdf,
|
pub hkdf: Hkdf,
|
||||||
|
@ -145,12 +62,9 @@ pub(crate) struct Conn {
|
||||||
pub cipher_send: CipherSend,
|
pub cipher_send: CipherSend,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Role: track the connection direction
|
/// Role: used to set the correct secrets
|
||||||
///
|
/// * Server: Connection is Incoming
|
||||||
/// The Role is used to select the correct secrets, and track the direction
|
/// * Client: Connection is Outgoing
|
||||||
/// of the connection
|
|
||||||
/// * Server: Conn is Incoming
|
|
||||||
/// * Client: Conn is Outgoing
|
|
||||||
#[derive(Debug, Copy, Clone)]
|
#[derive(Debug, Copy, Clone)]
|
||||||
#[repr(u8)]
|
#[repr(u8)]
|
||||||
pub enum Role {
|
pub enum Role {
|
||||||
|
@ -160,10 +74,10 @@ pub enum Role {
|
||||||
Client,
|
Client,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Conn {
|
impl Connection {
|
||||||
pub(crate) fn new(
|
pub(crate) fn new(
|
||||||
hkdf: Hkdf,
|
hkdf: Hkdf,
|
||||||
cipher: sym::Kind,
|
cipher: CipherKind,
|
||||||
role: Role,
|
role: Role,
|
||||||
rand: &Random,
|
rand: &Random,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
|
@ -188,9 +102,11 @@ impl Conn {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PERF: Arc<RwLock<ConnList>> loks a bit too much, need to find
|
||||||
|
// faster ways to do this
|
||||||
pub(crate) struct ConnList {
|
pub(crate) struct ConnList {
|
||||||
thread_id: ThreadTracker,
|
thread_id: ThreadTracker,
|
||||||
connections: Vec<Option<Rc<Conn>>>,
|
connections: Vec<Option<Rc<Connection>>>,
|
||||||
/// Bitmap to track which connection ids are used or free
|
/// Bitmap to track which connection ids are used or free
|
||||||
ids_used: Vec<::bitmaps::Bitmap<1024>>,
|
ids_used: Vec<::bitmaps::Bitmap<1024>>,
|
||||||
}
|
}
|
||||||
|
@ -261,7 +177,7 @@ impl ConnList {
|
||||||
new_id
|
new_id
|
||||||
}
|
}
|
||||||
/// NOTE: does NOT check if the connection has been previously reserved!
|
/// NOTE: does NOT check if the connection has been previously reserved!
|
||||||
pub(crate) fn track(&mut self, conn: Rc<Conn>) -> Result<(), ()> {
|
pub(crate) fn track(&mut self, conn: Rc<Connection>) -> Result<(), ()> {
|
||||||
let conn_id = match conn.id_recv {
|
let conn_id = match conn.id_recv {
|
||||||
IDRecv(ID::Handshake) => {
|
IDRecv(ID::Handshake) => {
|
||||||
return Err(());
|
return Err(());
|
||||||
|
|
|
@ -1,44 +1,125 @@
|
||||||
//
|
//
|
||||||
//! Raw packet handling, encryption, decryption, parsing
|
//! Raw packet handling, encryption, decryption, parsing
|
||||||
|
|
||||||
use crate::{
|
use crate::enc::{
|
||||||
connection,
|
sym::{HeadLen, TagLen},
|
||||||
enc::sym::{NonceLen, TagLen},
|
Random,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// Fenrir Connection id
|
||||||
|
/// 0 is special as it represents the handshake
|
||||||
|
/// Connection IDs are to be considered u64 little endian
|
||||||
|
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||||
|
pub enum ConnectionID {
|
||||||
|
/// Connection id 0 represent the handshake
|
||||||
|
Handshake,
|
||||||
|
/// Non-zero id can represent any connection
|
||||||
|
ID(::core::num::NonZeroU64),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ConnectionID {
|
||||||
|
/// Set the conenction id to handshake
|
||||||
|
pub fn new_handshake() -> Self {
|
||||||
|
Self::Handshake
|
||||||
|
}
|
||||||
|
/// New id from u64. PLZ NON ZERO
|
||||||
|
pub(crate) fn new_u64(raw: u64) -> Self {
|
||||||
|
#[allow(unsafe_code)]
|
||||||
|
unsafe {
|
||||||
|
ConnectionID::ID(::core::num::NonZeroU64::new_unchecked(raw))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub(crate) fn as_u64(&self) -> u64 {
|
||||||
|
match self {
|
||||||
|
ConnectionID::Handshake => 0,
|
||||||
|
ConnectionID::ID(id) => id.get(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// New random service ID
|
||||||
|
pub fn new_rand(rand: &Random) -> Self {
|
||||||
|
let mut raw = [0; 8];
|
||||||
|
let mut num = 0;
|
||||||
|
while num == 0 {
|
||||||
|
rand.fill(&mut raw);
|
||||||
|
num = u64::from_le_bytes(raw);
|
||||||
|
}
|
||||||
|
#[allow(unsafe_code)]
|
||||||
|
unsafe {
|
||||||
|
ConnectionID::ID(::core::num::NonZeroU64::new_unchecked(num))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// Quick check to know if this is an handshake
|
||||||
|
pub fn is_handshake(&self) -> bool {
|
||||||
|
*self == ConnectionID::Handshake
|
||||||
|
}
|
||||||
|
/// length if the connection ID in bytes
|
||||||
|
pub const fn len() -> usize {
|
||||||
|
8
|
||||||
|
}
|
||||||
|
/// write the ID to a buffer
|
||||||
|
pub fn serialize(&self, out: &mut [u8]) {
|
||||||
|
match self {
|
||||||
|
ConnectionID::Handshake => out[..8].copy_from_slice(&[0; 8]),
|
||||||
|
ConnectionID::ID(id) => {
|
||||||
|
out[..8].copy_from_slice(&id.get().to_le_bytes())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<u64> for ConnectionID {
|
||||||
|
fn from(raw: u64) -> Self {
|
||||||
|
if raw == 0 {
|
||||||
|
ConnectionID::Handshake
|
||||||
|
} else {
|
||||||
|
#[allow(unsafe_code)]
|
||||||
|
unsafe {
|
||||||
|
ConnectionID::ID(::core::num::NonZeroU64::new_unchecked(raw))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<[u8; 8]> for ConnectionID {
|
||||||
|
fn from(raw: [u8; 8]) -> Self {
|
||||||
|
let raw_u64 = u64::from_le_bytes(raw);
|
||||||
|
raw_u64.into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Enumerate the possible data in a fenrir packet
|
/// Enumerate the possible data in a fenrir packet
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum Data {
|
pub enum PacketData {
|
||||||
/// A parsed handshake packet
|
/// A parsed handshake packet
|
||||||
Handshake(super::Handshake),
|
Handshake(super::Handshake),
|
||||||
/// Raw packet. we only have the connection ID and packet length
|
/// Raw packet. we only have the connection ID and packet length
|
||||||
Raw(usize),
|
Raw(usize),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Data {
|
impl PacketData {
|
||||||
/// total length of the data in bytes
|
/// total length of the data in bytes
|
||||||
pub fn len(&self, head_len: NonceLen, tag_len: TagLen) -> usize {
|
pub fn len(&self, head_len: HeadLen, tag_len: TagLen) -> usize {
|
||||||
match self {
|
match self {
|
||||||
Data::Handshake(h) => h.len(head_len, tag_len),
|
PacketData::Handshake(h) => h.len(head_len, tag_len),
|
||||||
Data::Raw(len) => *len,
|
PacketData::Raw(len) => *len,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// 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(
|
pub fn serialize(
|
||||||
&self,
|
&self,
|
||||||
head_len: NonceLen,
|
head_len: HeadLen,
|
||||||
tag_len: TagLen,
|
tag_len: TagLen,
|
||||||
out: &mut [u8],
|
out: &mut [u8],
|
||||||
) {
|
) {
|
||||||
assert!(
|
assert!(
|
||||||
self.len(head_len, tag_len) == out.len(),
|
self.len(head_len, tag_len) == out.len(),
|
||||||
"Data: wrong buffer length"
|
"PacketData: wrong buffer length"
|
||||||
);
|
);
|
||||||
match self {
|
match self {
|
||||||
Data::Handshake(h) => h.serialize(head_len, tag_len, out),
|
PacketData::Handshake(h) => h.serialize(head_len, tag_len, out),
|
||||||
Data::Raw(_) => {
|
PacketData::Raw(_) => {
|
||||||
::tracing::error!("Tried to serialize a raw Data!");
|
::tracing::error!("Tried to serialize a raw PacketData!");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -50,9 +131,9 @@ const MIN_PACKET_BYTES: usize = 16;
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Packet {
|
pub struct Packet {
|
||||||
/// Id of the Fenrir connection.
|
/// Id of the Fenrir connection.
|
||||||
pub id: connection::ID,
|
pub id: ConnectionID,
|
||||||
/// actual data inside the packet
|
/// actual data inside the packet
|
||||||
pub data: Data,
|
pub data: PacketData,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Packet {
|
impl Packet {
|
||||||
|
@ -65,30 +146,27 @@ impl Packet {
|
||||||
let raw_id: [u8; 8] = (raw[..8]).try_into().expect("unreachable");
|
let raw_id: [u8; 8] = (raw[..8]).try_into().expect("unreachable");
|
||||||
Ok(Packet {
|
Ok(Packet {
|
||||||
id: raw_id.into(),
|
id: raw_id.into(),
|
||||||
data: Data::Raw(raw.len()),
|
data: PacketData::Raw(raw.len()),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
/// get the total length of the packet
|
/// get the total length of the packet
|
||||||
pub fn len(&self, head_len: NonceLen, tag_len: TagLen) -> usize {
|
pub fn len(&self, head_len: HeadLen, tag_len: TagLen) -> usize {
|
||||||
connection::ID::len() + self.data.len(head_len, tag_len)
|
ConnectionID::len() + self.data.len(head_len, tag_len)
|
||||||
}
|
}
|
||||||
/// 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(
|
pub fn serialize(
|
||||||
&self,
|
&self,
|
||||||
head_len: NonceLen,
|
head_len: HeadLen,
|
||||||
tag_len: TagLen,
|
tag_len: TagLen,
|
||||||
out: &mut [u8],
|
out: &mut [u8],
|
||||||
) {
|
) {
|
||||||
assert!(
|
assert!(
|
||||||
out.len() > connection::ID::len(),
|
out.len() > ConnectionID::len(),
|
||||||
"Packet: not enough buffer to serialize"
|
"Packet: not enough buffer to serialize"
|
||||||
);
|
);
|
||||||
self.id.serialize(&mut out[0..connection::ID::len()]);
|
self.id.serialize(&mut out[0..ConnectionID::len()]);
|
||||||
self.data.serialize(
|
self.data
|
||||||
head_len,
|
.serialize(head_len, tag_len, &mut out[ConnectionID::len()..]);
|
||||||
tag_len,
|
|
||||||
&mut out[connection::ID::len()..],
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +0,0 @@
|
||||||
//! Errors while parsing streams
|
|
||||||
|
|
||||||
|
|
||||||
/// Crypto errors
|
|
||||||
#[derive(::thiserror::Error, Debug, Copy, Clone)]
|
|
||||||
pub enum Error {
|
|
||||||
/// Error while parsing key material
|
|
||||||
#[error("Not enough data for stream chunk: {0}")]
|
|
||||||
NotEnoughData(usize),
|
|
||||||
}
|
|
|
@ -1,183 +0,0 @@
|
||||||
//! Here we implement the multiplexing stream feature of Fenrir
|
|
||||||
//!
|
|
||||||
//! For now we will only have the TCP-like, reliable, in-order delivery
|
|
||||||
|
|
||||||
mod errors;
|
|
||||||
mod rob;
|
|
||||||
pub use errors::Error;
|
|
||||||
|
|
||||||
use crate::{connection::stream::rob::ReliableOrderedBytestream, enc::Random};
|
|
||||||
|
|
||||||
/// Kind of stream. any combination of:
|
|
||||||
/// reliable/unreliable ordered/unordered, bytestream/datagram
|
|
||||||
#[derive(Debug, Copy, Clone)]
|
|
||||||
#[repr(u8)]
|
|
||||||
pub enum Kind {
|
|
||||||
/// ROB: Reliable, Ordered, Bytestream
|
|
||||||
/// AKA: TCP-like
|
|
||||||
ROB = 0,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Id of the stream
|
|
||||||
#[derive(Debug, Copy, Clone)]
|
|
||||||
pub struct ID(pub u16);
|
|
||||||
|
|
||||||
impl ID {
|
|
||||||
/// Length of the serialized field
|
|
||||||
pub const fn len() -> usize {
|
|
||||||
2
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// length of the chunk
|
|
||||||
#[derive(Debug, Copy, Clone)]
|
|
||||||
pub struct ChunkLen(pub u16);
|
|
||||||
|
|
||||||
impl ChunkLen {
|
|
||||||
/// Length of the serialized field
|
|
||||||
pub const fn len() -> usize {
|
|
||||||
2
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sequence number to rebuild the stream correctly
|
|
||||||
#[derive(Debug, Copy, Clone)]
|
|
||||||
pub struct Sequence(pub ::core::num::Wrapping<u32>);
|
|
||||||
|
|
||||||
impl Sequence {
|
|
||||||
const SEQ_NOFLAG: u32 = 0x3FFFFFFF;
|
|
||||||
/// return a new sequence number, starting at random
|
|
||||||
pub fn new(rand: &Random) -> Self {
|
|
||||||
let seq: u32 = 0;
|
|
||||||
rand.fill(&mut seq.to_le_bytes());
|
|
||||||
Self(::core::num::Wrapping(seq & Self::SEQ_NOFLAG))
|
|
||||||
}
|
|
||||||
/// Length of the serialized field
|
|
||||||
pub const fn len() -> usize {
|
|
||||||
4
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Chunk of data representing a stream
|
|
||||||
/// Every chunk is as follows:
|
|
||||||
/// | id (2 bytes) | length (2 bytes) |
|
|
||||||
/// | flag_start (1 BIT) | flag_end (1 BIT) | sequence (30 bits) |
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct Chunk<'a> {
|
|
||||||
/// Id of the stream this chunk is part of
|
|
||||||
pub id: ID,
|
|
||||||
/// Is this the beginning of a message?
|
|
||||||
pub flag_start: bool,
|
|
||||||
/// Is this the end of a message?
|
|
||||||
pub flag_end: bool,
|
|
||||||
/// Sequence number to reconstruct the Stream
|
|
||||||
pub sequence: Sequence,
|
|
||||||
data: &'a [u8],
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> Chunk<'a> {
|
|
||||||
const FLAGS_EXCLUDED_BITMASK: u8 = 0x3F;
|
|
||||||
const FLAG_START_BITMASK: u8 = 0x80;
|
|
||||||
const FLAG_END_BITMASK: u8 = 0x40;
|
|
||||||
/// Returns the total length of the chunk, including headers
|
|
||||||
pub fn len(&self) -> usize {
|
|
||||||
ID::len() + ChunkLen::len() + Sequence::len() + self.data.len()
|
|
||||||
}
|
|
||||||
/// deserialize a chunk of a stream
|
|
||||||
pub fn deserialize(raw: &'a [u8]) -> Result<Self, Error> {
|
|
||||||
if raw.len() <= ID::len() + ChunkLen::len() + Sequence::len() {
|
|
||||||
return Err(Error::NotEnoughData(0));
|
|
||||||
}
|
|
||||||
|
|
||||||
let id = ID(u16::from_le_bytes(raw[0..ID::len()].try_into().unwrap()));
|
|
||||||
|
|
||||||
let mut bytes_next = ID::len() + ChunkLen::len();
|
|
||||||
let length = ChunkLen(u16::from_le_bytes(
|
|
||||||
raw[ID::len()..bytes_next].try_into().unwrap(),
|
|
||||||
));
|
|
||||||
if ID::len() + ChunkLen::len() + Sequence::len() + length.0 as usize
|
|
||||||
> raw.len()
|
|
||||||
{
|
|
||||||
return Err(Error::NotEnoughData(4));
|
|
||||||
}
|
|
||||||
|
|
||||||
let flag_start = (raw[bytes_next] & Self::FLAG_START_BITMASK) != 0;
|
|
||||||
let flag_end = (raw[bytes_next] & Self::FLAG_END_BITMASK) != 0;
|
|
||||||
|
|
||||||
let bytes = bytes_next + 1;
|
|
||||||
bytes_next = bytes + Sequence::len();
|
|
||||||
let mut sequence_bytes: [u8; Sequence::len()] =
|
|
||||||
raw[bytes..bytes_next].try_into().unwrap();
|
|
||||||
sequence_bytes[0] = sequence_bytes[0] & Self::FLAGS_EXCLUDED_BITMASK;
|
|
||||||
let sequence =
|
|
||||||
Sequence(::core::num::Wrapping(u32::from_le_bytes(sequence_bytes)));
|
|
||||||
|
|
||||||
Ok(Self {
|
|
||||||
id,
|
|
||||||
flag_start,
|
|
||||||
flag_end,
|
|
||||||
sequence,
|
|
||||||
data: &raw[bytes_next..(bytes_next + length.0 as usize)],
|
|
||||||
})
|
|
||||||
}
|
|
||||||
/// serialize a chunk of a stream
|
|
||||||
pub fn serialize(&self, raw_out: &mut [u8]) {
|
|
||||||
raw_out[0..ID::len()].copy_from_slice(&self.id.0.to_le_bytes());
|
|
||||||
let mut bytes_next = ID::len() + ChunkLen::len();
|
|
||||||
raw_out[ID::len()..bytes_next]
|
|
||||||
.copy_from_slice(&(self.data.len() as u16).to_le_bytes());
|
|
||||||
let bytes = bytes_next;
|
|
||||||
bytes_next = bytes_next + Sequence::len();
|
|
||||||
raw_out[bytes..bytes_next]
|
|
||||||
.copy_from_slice(&self.sequence.0 .0.to_le_bytes());
|
|
||||||
let mut flag_byte = raw_out[bytes] & Self::FLAGS_EXCLUDED_BITMASK;
|
|
||||||
if self.flag_start {
|
|
||||||
flag_byte = flag_byte | Self::FLAG_START_BITMASK;
|
|
||||||
}
|
|
||||||
if self.flag_end {
|
|
||||||
flag_byte = flag_byte | Self::FLAG_END_BITMASK;
|
|
||||||
}
|
|
||||||
raw_out[bytes] = flag_byte;
|
|
||||||
let bytes = bytes_next;
|
|
||||||
bytes_next = bytes_next + self.data.len();
|
|
||||||
raw_out[bytes..bytes_next].copy_from_slice(&self.data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Kind of stream. any combination of:
|
|
||||||
/// reliable/unreliable ordered/unordered, bytestream/datagram
|
|
||||||
/// differences from Kind:
|
|
||||||
/// * not public
|
|
||||||
/// * has actual data
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub(crate) enum Tracker {
|
|
||||||
/// ROB: Reliable, Ordered, Bytestream
|
|
||||||
/// AKA: TCP-like
|
|
||||||
ROB(ReliableOrderedBytestream),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Tracker {
|
|
||||||
pub(crate) fn new(kind: Kind, rand: &Random) -> Self {
|
|
||||||
match kind {
|
|
||||||
Kind::ROB => Tracker::ROB(ReliableOrderedBytestream::new(rand)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Actual stream-tracking structure
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub(crate) struct Stream {
|
|
||||||
id: ID,
|
|
||||||
data: Tracker,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Stream {
|
|
||||||
pub(crate) fn new(kind: Kind, rand: &Random) -> Self {
|
|
||||||
let id: u16 = 0;
|
|
||||||
rand.fill(&mut id.to_le_bytes());
|
|
||||||
Self {
|
|
||||||
id: ID(id),
|
|
||||||
data: Tracker::new(kind, rand),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,29 +0,0 @@
|
||||||
//! Implementation of the Reliable, Ordered, Bytestream transmission model
|
|
||||||
//! AKA: TCP-like
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
connection::stream::{Chunk, Error, Sequence},
|
|
||||||
enc::Random,
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Reliable, Ordered, Bytestream stream tracker
|
|
||||||
/// AKA: TCP-like
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub(crate) struct ReliableOrderedBytestream {
|
|
||||||
window_start: Sequence,
|
|
||||||
window_len: usize,
|
|
||||||
data: Vec<u8>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ReliableOrderedBytestream {
|
|
||||||
pub(crate) fn new(rand: &Random) -> Self {
|
|
||||||
Self {
|
|
||||||
window_start: Sequence::new(rand),
|
|
||||||
window_len: 1048576, // 1MB. should be enough for anybody. (lol)
|
|
||||||
data: Vec::new(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub(crate) fn recv(&mut self, chunk: Chunk) -> Result<(), Error> {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -43,11 +43,12 @@
|
||||||
//! ]
|
//! ]
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
connection::handshake,
|
connection::handshake::HandshakeID,
|
||||||
enc::{
|
enc::{
|
||||||
self,
|
self,
|
||||||
asym::{KeyExchangeKind, KeyID, PubKey},
|
asym::{KeyExchangeKind, KeyID, PubKey},
|
||||||
hkdf, sym,
|
hkdf::HkdfKind,
|
||||||
|
sym::CipherKind,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use ::core::num::NonZeroU16;
|
use ::core::num::NonZeroU16;
|
||||||
|
@ -179,7 +180,7 @@ pub struct Address {
|
||||||
/// Weight of this address in the priority group
|
/// Weight of this address in the priority group
|
||||||
pub weight: AddressWeight,
|
pub weight: AddressWeight,
|
||||||
/// List of supported handshakes
|
/// List of supported handshakes
|
||||||
pub handshake_ids: Vec<handshake::ID>,
|
pub handshake_ids: Vec<HandshakeID>,
|
||||||
/// Public key IDs used by this address
|
/// Public key IDs used by this address
|
||||||
pub public_key_idx: Vec<PubKeyIdx>,
|
pub public_key_idx: Vec<PubKeyIdx>,
|
||||||
}
|
}
|
||||||
|
@ -330,7 +331,7 @@ impl Address {
|
||||||
for raw_handshake_id in
|
for raw_handshake_id in
|
||||||
raw[bytes_parsed..(bytes_parsed + num_handshake_ids)].iter()
|
raw[bytes_parsed..(bytes_parsed + num_handshake_ids)].iter()
|
||||||
{
|
{
|
||||||
match handshake::ID::from_u8(*raw_handshake_id) {
|
match HandshakeID::from_u8(*raw_handshake_id) {
|
||||||
Some(h_id) => handshake_ids.push(h_id),
|
Some(h_id) => handshake_ids.push(h_id),
|
||||||
None => {
|
None => {
|
||||||
::tracing::warn!(
|
::tracing::warn!(
|
||||||
|
@ -391,9 +392,9 @@ pub struct Record {
|
||||||
/// List of supported key exchanges
|
/// List of supported key exchanges
|
||||||
pub key_exchanges: Vec<KeyExchangeKind>,
|
pub key_exchanges: Vec<KeyExchangeKind>,
|
||||||
/// List of supported key exchanges
|
/// List of supported key exchanges
|
||||||
pub hkdfs: Vec<hkdf::Kind>,
|
pub hkdfs: Vec<HkdfKind>,
|
||||||
/// List of supported ciphers
|
/// List of supported ciphers
|
||||||
pub ciphers: Vec<sym::Kind>,
|
pub ciphers: Vec<CipherKind>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Record {
|
impl Record {
|
||||||
|
@ -596,7 +597,7 @@ impl Record {
|
||||||
num_key_exchanges = num_key_exchanges - 1;
|
num_key_exchanges = num_key_exchanges - 1;
|
||||||
}
|
}
|
||||||
while num_hkdfs > 0 {
|
while num_hkdfs > 0 {
|
||||||
let hkdf = match hkdf::Kind::from_u8(raw[bytes_parsed]) {
|
let hkdf = match HkdfKind::from_u8(raw[bytes_parsed]) {
|
||||||
Some(hkdf) => hkdf,
|
Some(hkdf) => hkdf,
|
||||||
None => {
|
None => {
|
||||||
// continue parsing. This could be a new hkdf type
|
// continue parsing. This could be a new hkdf type
|
||||||
|
@ -614,7 +615,7 @@ impl Record {
|
||||||
num_hkdfs = num_hkdfs - 1;
|
num_hkdfs = num_hkdfs - 1;
|
||||||
}
|
}
|
||||||
while num_ciphers > 0 {
|
while num_ciphers > 0 {
|
||||||
let cipher = match sym::Kind::from_u8(raw[bytes_parsed]) {
|
let cipher = match CipherKind::from_u8(raw[bytes_parsed]) {
|
||||||
Some(cipher) => cipher,
|
Some(cipher) => cipher,
|
||||||
None => {
|
None => {
|
||||||
// continue parsing. This could be a new cipher type
|
// continue parsing. This could be a new cipher type
|
||||||
|
|
|
@ -12,7 +12,7 @@ fn test_dnssec_serialization() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
use crate::{connection::handshake, enc};
|
use crate::{connection::handshake::HandshakeID, enc};
|
||||||
|
|
||||||
let record = Record {
|
let record = Record {
|
||||||
public_keys: [(
|
public_keys: [(
|
||||||
|
@ -25,14 +25,14 @@ fn test_dnssec_serialization() {
|
||||||
port: Some(::core::num::NonZeroU16::new(31337).unwrap()),
|
port: Some(::core::num::NonZeroU16::new(31337).unwrap()),
|
||||||
priority: record::AddressPriority::P1,
|
priority: record::AddressPriority::P1,
|
||||||
weight: record::AddressWeight::W1,
|
weight: record::AddressWeight::W1,
|
||||||
handshake_ids: [handshake::ID::DirectorySynchronized].to_vec(),
|
handshake_ids: [HandshakeID::DirectorySynchronized].to_vec(),
|
||||||
public_key_idx: [record::PubKeyIdx(0)].to_vec(),
|
public_key_idx: [record::PubKeyIdx(0)].to_vec(),
|
||||||
}]
|
}]
|
||||||
.to_vec(),
|
.to_vec(),
|
||||||
key_exchanges: [enc::asym::KeyExchangeKind::X25519DiffieHellman]
|
key_exchanges: [enc::asym::KeyExchangeKind::X25519DiffieHellman]
|
||||||
.to_vec(),
|
.to_vec(),
|
||||||
hkdfs: [enc::hkdf::Kind::Sha3].to_vec(),
|
hkdfs: [enc::hkdf::HkdfKind::Sha3].to_vec(),
|
||||||
ciphers: [enc::sym::Kind::XChaCha20Poly1305].to_vec(),
|
ciphers: [enc::sym::CipherKind::XChaCha20Poly1305].to_vec(),
|
||||||
};
|
};
|
||||||
let encoded = match record.encode() {
|
let encoded = match record.encode() {
|
||||||
Ok(encoded) => encoded,
|
Ok(encoded) => encoded,
|
||||||
|
|
|
@ -45,7 +45,7 @@ impl ::std::fmt::Display for KeyID {
|
||||||
|
|
||||||
/// Capabilities of each key
|
/// Capabilities of each key
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
pub enum Capabilities {
|
pub enum KeyCapabilities {
|
||||||
/// signing *only*
|
/// signing *only*
|
||||||
Sign,
|
Sign,
|
||||||
/// encrypt *only*
|
/// encrypt *only*
|
||||||
|
@ -61,13 +61,13 @@ pub enum Capabilities {
|
||||||
/// All: sign, encrypt, Key Exchange
|
/// All: sign, encrypt, Key Exchange
|
||||||
SignEncryptExchage,
|
SignEncryptExchage,
|
||||||
}
|
}
|
||||||
impl Capabilities {
|
impl KeyCapabilities {
|
||||||
/// Check if this key supports eky exchage
|
/// Check if this key supports eky exchage
|
||||||
pub fn has_exchange(&self) -> bool {
|
pub fn has_exchange(&self) -> bool {
|
||||||
match self {
|
match self {
|
||||||
Capabilities::Exchange
|
KeyCapabilities::Exchange
|
||||||
| Capabilities::SignExchange
|
| KeyCapabilities::SignExchange
|
||||||
| Capabilities::SignEncryptExchage => true,
|
| KeyCapabilities::SignEncryptExchage => true,
|
||||||
_ => false,
|
_ => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -85,7 +85,7 @@ impl Capabilities {
|
||||||
)]
|
)]
|
||||||
#[non_exhaustive]
|
#[non_exhaustive]
|
||||||
#[repr(u8)]
|
#[repr(u8)]
|
||||||
pub enum Kind {
|
pub enum KeyKind {
|
||||||
/// Ed25519 Public key (sign only)
|
/// Ed25519 Public key (sign only)
|
||||||
#[strum(serialize = "ed25519")]
|
#[strum(serialize = "ed25519")]
|
||||||
Ed25519 = 0,
|
Ed25519 = 0,
|
||||||
|
@ -93,25 +93,25 @@ pub enum Kind {
|
||||||
#[strum(serialize = "x25519")]
|
#[strum(serialize = "x25519")]
|
||||||
X25519,
|
X25519,
|
||||||
}
|
}
|
||||||
impl Kind {
|
impl KeyKind {
|
||||||
/// Length of the serialized field
|
/// Length of the serialized field
|
||||||
pub const fn len() -> usize {
|
pub const fn len() -> usize {
|
||||||
1
|
1
|
||||||
}
|
}
|
||||||
/// return the expected length of the public key
|
/// return the expected length of the public key
|
||||||
pub fn pub_len(&self) -> usize {
|
pub fn pub_len(&self) -> usize {
|
||||||
Kind::len()
|
KeyKind::len()
|
||||||
+ match self {
|
+ match self {
|
||||||
// FIXME: 99% wrong size
|
// FIXME: 99% wrong size
|
||||||
Kind::Ed25519 => ::ring::signature::ED25519_PUBLIC_KEY_LEN,
|
KeyKind::Ed25519 => ::ring::signature::ED25519_PUBLIC_KEY_LEN,
|
||||||
Kind::X25519 => 32,
|
KeyKind::X25519 => 32,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// Get the capabilities of this key type
|
/// Get the capabilities of this key type
|
||||||
pub fn capabilities(&self) -> Capabilities {
|
pub fn capabilities(&self) -> KeyCapabilities {
|
||||||
match self {
|
match self {
|
||||||
Kind::Ed25519 => Capabilities::Sign,
|
KeyKind::Ed25519 => KeyCapabilities::Sign,
|
||||||
Kind::X25519 => Capabilities::Exchange,
|
KeyKind::X25519 => KeyCapabilities::Exchange,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// Returns the key exchanges supported by this key
|
/// Returns the key exchanges supported by this key
|
||||||
|
@ -120,8 +120,8 @@ impl Kind {
|
||||||
const X25519_KEY_EXCHANGES: [KeyExchangeKind; 1] =
|
const X25519_KEY_EXCHANGES: [KeyExchangeKind; 1] =
|
||||||
[KeyExchangeKind::X25519DiffieHellman];
|
[KeyExchangeKind::X25519DiffieHellman];
|
||||||
match self {
|
match self {
|
||||||
Kind::Ed25519 => &EMPTY,
|
KeyKind::Ed25519 => &EMPTY,
|
||||||
Kind::X25519 => &X25519_KEY_EXCHANGES,
|
KeyKind::X25519 => &X25519_KEY_EXCHANGES,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// generate new keypair
|
/// generate new keypair
|
||||||
|
@ -193,21 +193,21 @@ impl PubKey {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// return the kind of public key
|
/// return the kind of public key
|
||||||
pub fn kind(&self) -> Kind {
|
pub fn kind(&self) -> KeyKind {
|
||||||
match self {
|
match self {
|
||||||
// FIXME: lie, we don't fully support this
|
// FIXME: lie, we don't fully support this
|
||||||
PubKey::Signing => Kind::Ed25519,
|
PubKey::Signing => KeyKind::Ed25519,
|
||||||
PubKey::Exchange(ex) => ex.kind(),
|
PubKey::Exchange(ex) => ex.kind(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// generate new keypair
|
/// generate new keypair
|
||||||
fn new_keypair(
|
fn new_keypair(
|
||||||
kind: Kind,
|
kind: KeyKind,
|
||||||
rnd: &Random,
|
rnd: &Random,
|
||||||
) -> Result<(PrivKey, PubKey), Error> {
|
) -> Result<(PrivKey, PubKey), Error> {
|
||||||
match kind {
|
match kind {
|
||||||
Kind::Ed25519 => todo!(),
|
KeyKind::Ed25519 => todo!(),
|
||||||
Kind::X25519 => {
|
KeyKind::X25519 => {
|
||||||
let (priv_key, pub_key) =
|
let (priv_key, pub_key) =
|
||||||
KeyExchangeKind::X25519DiffieHellman.new_keypair(rnd)?;
|
KeyExchangeKind::X25519DiffieHellman.new_keypair(rnd)?;
|
||||||
Ok((PrivKey::Exchange(priv_key), PubKey::Exchange(pub_key)))
|
Ok((PrivKey::Exchange(priv_key), PubKey::Exchange(pub_key)))
|
||||||
|
@ -231,7 +231,7 @@ impl PubKey {
|
||||||
if raw.len() < 1 {
|
if raw.len() < 1 {
|
||||||
return Err(Error::NotEnoughData(0));
|
return Err(Error::NotEnoughData(0));
|
||||||
}
|
}
|
||||||
let kind: Kind = match Kind::from_u8(raw[0]) {
|
let kind: KeyKind = match KeyKind::from_u8(raw[0]) {
|
||||||
Some(kind) => kind,
|
Some(kind) => kind,
|
||||||
None => return Err(Error::UnsupportedKey(1)),
|
None => return Err(Error::UnsupportedKey(1)),
|
||||||
};
|
};
|
||||||
|
@ -239,11 +239,11 @@ impl PubKey {
|
||||||
return Err(Error::NotEnoughData(1));
|
return Err(Error::NotEnoughData(1));
|
||||||
}
|
}
|
||||||
match kind {
|
match kind {
|
||||||
Kind::Ed25519 => {
|
KeyKind::Ed25519 => {
|
||||||
::tracing::error!("ed25519 keys are not yet supported");
|
::tracing::error!("ed25519 keys are not yet supported");
|
||||||
return Err(Error::Parsing);
|
return Err(Error::Parsing);
|
||||||
}
|
}
|
||||||
Kind::X25519 => {
|
KeyKind::X25519 => {
|
||||||
let pub_key: ::x25519_dalek::PublicKey =
|
let pub_key: ::x25519_dalek::PublicKey =
|
||||||
//match ::bincode::deserialize(&raw[1..(1 + kind.pub_len())])
|
//match ::bincode::deserialize(&raw[1..(1 + kind.pub_len())])
|
||||||
match ::bincode::deserialize(&raw[1..])
|
match ::bincode::deserialize(&raw[1..])
|
||||||
|
@ -284,7 +284,7 @@ impl PrivKey {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// return the kind of public key
|
/// return the kind of public key
|
||||||
pub fn kind(&self) -> Kind {
|
pub fn kind(&self) -> KeyKind {
|
||||||
match self {
|
match self {
|
||||||
PrivKey::Signing => todo!(),
|
PrivKey::Signing => todo!(),
|
||||||
PrivKey::Exchange(ex) => ex.kind(),
|
PrivKey::Exchange(ex) => ex.kind(),
|
||||||
|
@ -322,13 +322,13 @@ impl ExchangePrivKey {
|
||||||
/// Get the serialized key length
|
/// Get the serialized key length
|
||||||
pub fn len(&self) -> usize {
|
pub fn len(&self) -> usize {
|
||||||
match self {
|
match self {
|
||||||
ExchangePrivKey::X25519(_) => Kind::X25519.pub_len(),
|
ExchangePrivKey::X25519(_) => KeyKind::X25519.pub_len(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// Get the kind of key
|
/// Get the kind of key
|
||||||
pub fn kind(&self) -> Kind {
|
pub fn kind(&self) -> KeyKind {
|
||||||
match self {
|
match self {
|
||||||
ExchangePrivKey::X25519(_) => Kind::X25519,
|
ExchangePrivKey::X25519(_) => KeyKind::X25519,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// Run the key exchange between two keys of the same kind
|
/// Run the key exchange between two keys of the same kind
|
||||||
|
@ -372,13 +372,13 @@ impl ExchangePubKey {
|
||||||
/// Get the serialized key length
|
/// Get the serialized key length
|
||||||
pub fn len(&self) -> usize {
|
pub fn len(&self) -> usize {
|
||||||
match self {
|
match self {
|
||||||
ExchangePubKey::X25519(_) => Kind::X25519.pub_len(),
|
ExchangePubKey::X25519(_) => KeyKind::X25519.pub_len(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// Get the kind of key
|
/// Get the kind of key
|
||||||
pub fn kind(&self) -> Kind {
|
pub fn kind(&self) -> KeyKind {
|
||||||
match self {
|
match self {
|
||||||
ExchangePubKey::X25519(_) => Kind::X25519,
|
ExchangePubKey::X25519(_) => KeyKind::X25519,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// serialize the key into the buffer
|
/// serialize the key into the buffer
|
||||||
|
@ -396,13 +396,13 @@ impl ExchangePubKey {
|
||||||
/// The riesult is "unparsed" since we don't verify
|
/// The riesult is "unparsed" since we don't verify
|
||||||
/// the actual key
|
/// the actual key
|
||||||
pub fn deserialize(raw: &[u8]) -> Result<(Self, usize), Error> {
|
pub fn deserialize(raw: &[u8]) -> Result<(Self, usize), Error> {
|
||||||
match Kind::from_u8(raw[0]) {
|
match KeyKind::from_u8(raw[0]) {
|
||||||
Some(kind) => match kind {
|
Some(kind) => match kind {
|
||||||
Kind::Ed25519 => {
|
KeyKind::Ed25519 => {
|
||||||
::tracing::error!("ed25519 keys are not yet supported");
|
::tracing::error!("ed25519 keys are not yet supported");
|
||||||
return Err(Error::Parsing);
|
return Err(Error::Parsing);
|
||||||
}
|
}
|
||||||
Kind::X25519 => {
|
KeyKind::X25519 => {
|
||||||
let pub_key: ::x25519_dalek::PublicKey =
|
let pub_key: ::x25519_dalek::PublicKey =
|
||||||
match ::bincode::deserialize(
|
match ::bincode::deserialize(
|
||||||
&raw[1..(1 + kind.pub_len())],
|
&raw[1..(1 + kind.pub_len())],
|
||||||
|
|
|
@ -18,12 +18,12 @@ use crate::{config::Config, enc::Secret};
|
||||||
)]
|
)]
|
||||||
#[non_exhaustive]
|
#[non_exhaustive]
|
||||||
#[repr(u8)]
|
#[repr(u8)]
|
||||||
pub enum Kind {
|
pub enum HkdfKind {
|
||||||
/// Sha3
|
/// Sha3
|
||||||
#[strum(serialize = "sha3")]
|
#[strum(serialize = "sha3")]
|
||||||
Sha3 = 0,
|
Sha3 = 0,
|
||||||
}
|
}
|
||||||
impl Kind {
|
impl HkdfKind {
|
||||||
/// Length of the serialized type
|
/// Length of the serialized type
|
||||||
pub const fn len() -> usize {
|
pub const fn len() -> usize {
|
||||||
1
|
1
|
||||||
|
@ -34,7 +34,7 @@ impl Kind {
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub enum Hkdf {
|
pub enum Hkdf {
|
||||||
/// Sha3 based
|
/// Sha3 based
|
||||||
Sha3(Sha3),
|
Sha3(HkdfSha3),
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fake debug implementation to avoid leaking secrets
|
// Fake debug implementation to avoid leaking secrets
|
||||||
|
@ -49,9 +49,9 @@ impl ::core::fmt::Debug for Hkdf {
|
||||||
|
|
||||||
impl Hkdf {
|
impl Hkdf {
|
||||||
/// New Hkdf
|
/// New Hkdf
|
||||||
pub fn new(kind: Kind, salt: &[u8], key: Secret) -> Self {
|
pub fn new(kind: HkdfKind, salt: &[u8], key: Secret) -> Self {
|
||||||
match kind {
|
match kind {
|
||||||
Kind::Sha3 => Self::Sha3(Sha3::new(salt, key)),
|
HkdfKind::Sha3 => Self::Sha3(HkdfSha3::new(salt, key)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// Get a secret generated from the key and a given context
|
/// Get a secret generated from the key and a given context
|
||||||
|
@ -61,9 +61,9 @@ impl Hkdf {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// get the kind of this Hkdf
|
/// get the kind of this Hkdf
|
||||||
pub fn kind(&self) -> Kind {
|
pub fn kind(&self) -> HkdfKind {
|
||||||
match self {
|
match self {
|
||||||
Hkdf::Sha3(_) => Kind::Sha3,
|
Hkdf::Sha3(_) => HkdfKind::Sha3,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -106,11 +106,11 @@ impl Clone for HkdfInner {
|
||||||
|
|
||||||
/// Sha3 based HKDF
|
/// Sha3 based HKDF
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Sha3 {
|
pub struct HkdfSha3 {
|
||||||
inner: HkdfInner,
|
inner: HkdfInner,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Sha3 {
|
impl HkdfSha3 {
|
||||||
/// Instantiate a new HKDF with Sha3-256
|
/// Instantiate a new HKDF with Sha3-256
|
||||||
pub(crate) fn new(salt: &[u8], key: Secret) -> Self {
|
pub(crate) fn new(salt: &[u8], key: Secret) -> Self {
|
||||||
let hkdf = ::hkdf::Hkdf::<Sha3_256>::new(Some(salt), key.as_ref());
|
let hkdf = ::hkdf::Hkdf::<Sha3_256>::new(Some(salt), key.as_ref());
|
||||||
|
@ -132,7 +132,7 @@ impl Sha3 {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fake debug implementation to avoid leaking secrets
|
// Fake debug implementation to avoid leaking secrets
|
||||||
impl ::core::fmt::Debug for Sha3 {
|
impl ::core::fmt::Debug for HkdfSha3 {
|
||||||
fn fmt(
|
fn fmt(
|
||||||
&self,
|
&self,
|
||||||
f: &mut core::fmt::Formatter<'_>,
|
f: &mut core::fmt::Formatter<'_>,
|
||||||
|
@ -146,8 +146,8 @@ impl ::core::fmt::Debug for Sha3 {
|
||||||
/// Give priority to our list
|
/// Give priority to our list
|
||||||
pub fn server_select_hkdf(
|
pub fn server_select_hkdf(
|
||||||
cfg: &Config,
|
cfg: &Config,
|
||||||
client_supported: &Vec<Kind>,
|
client_supported: &Vec<HkdfKind>,
|
||||||
) -> Option<Kind> {
|
) -> Option<HkdfKind> {
|
||||||
cfg.hkdfs
|
cfg.hkdfs
|
||||||
.iter()
|
.iter()
|
||||||
.find(|h| client_supported.contains(h))
|
.find(|h| client_supported.contains(h))
|
||||||
|
@ -159,8 +159,8 @@ pub fn server_select_hkdf(
|
||||||
/// this is used only in the directory synchronized handshake
|
/// this is used only in the directory synchronized handshake
|
||||||
pub fn client_select_hkdf(
|
pub fn client_select_hkdf(
|
||||||
cfg: &Config,
|
cfg: &Config,
|
||||||
server_supported: &Vec<Kind>,
|
server_supported: &Vec<HkdfKind>,
|
||||||
) -> Option<Kind> {
|
) -> Option<HkdfKind> {
|
||||||
server_supported
|
server_supported
|
||||||
.iter()
|
.iter()
|
||||||
.find(|h| cfg.hkdfs.contains(h))
|
.find(|h| cfg.hkdfs.contains(h))
|
||||||
|
|
107
src/enc/sym.rs
107
src/enc/sym.rs
|
@ -17,20 +17,20 @@ use crate::{
|
||||||
::strum_macros::IntoStaticStr,
|
::strum_macros::IntoStaticStr,
|
||||||
)]
|
)]
|
||||||
#[repr(u8)]
|
#[repr(u8)]
|
||||||
pub enum Kind {
|
pub enum CipherKind {
|
||||||
/// XChaCha20_Poly1305
|
/// XChaCha20_Poly1305
|
||||||
#[strum(serialize = "xchacha20poly1305")]
|
#[strum(serialize = "xchacha20poly1305")]
|
||||||
XChaCha20Poly1305 = 0,
|
XChaCha20Poly1305 = 0,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Kind {
|
impl CipherKind {
|
||||||
/// length of the serialized id for the cipher kind field
|
/// length of the serialized id for the cipher kind field
|
||||||
pub const fn len() -> usize {
|
pub const fn len() -> usize {
|
||||||
1
|
1
|
||||||
}
|
}
|
||||||
/// required length of the nonce
|
/// required length of the nonce
|
||||||
pub fn nonce_len(&self) -> NonceLen {
|
pub fn nonce_len(&self) -> HeadLen {
|
||||||
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 {
|
||||||
|
@ -48,10 +48,21 @@ impl Kind {
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct AAD<'a>(pub &'a [u8]);
|
pub struct AAD<'a>(pub &'a [u8]);
|
||||||
|
|
||||||
|
/// Cipher direction, to make sure we don't reuse the same cipher
|
||||||
|
/// for both decrypting and encrypting
|
||||||
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
#[repr(u8)]
|
||||||
|
pub enum CipherDirection {
|
||||||
|
/// Receive, to decrypt only
|
||||||
|
Recv = 0,
|
||||||
|
/// Send, to encrypt only
|
||||||
|
Send,
|
||||||
|
}
|
||||||
|
|
||||||
/// strong typedef for header length
|
/// strong typedef for header length
|
||||||
/// aka: nonce length in the encrypted data)
|
/// aka: nonce length in the encrypted data)
|
||||||
#[derive(Debug, Copy, Clone)]
|
#[derive(Debug, Copy, Clone)]
|
||||||
pub struct NonceLen(pub usize);
|
pub struct HeadLen(pub usize);
|
||||||
/// strong typedef for the Tag length
|
/// strong typedef for the Tag length
|
||||||
/// aka: cryptographic authentication tag length at the end
|
/// aka: cryptographic authentication tag length at the end
|
||||||
/// of the encrypted data
|
/// of the encrypted data
|
||||||
|
@ -66,21 +77,21 @@ enum Cipher {
|
||||||
|
|
||||||
impl Cipher {
|
impl Cipher {
|
||||||
/// Build a new Cipher
|
/// Build a new Cipher
|
||||||
fn new(kind: Kind, secret: Secret) -> Self {
|
fn new(kind: CipherKind, secret: Secret) -> Self {
|
||||||
match kind {
|
match kind {
|
||||||
Kind::XChaCha20Poly1305 => {
|
CipherKind::XChaCha20Poly1305 => {
|
||||||
Self::XChaCha20Poly1305(XChaCha20Poly1305::new(secret))
|
Self::XChaCha20Poly1305(XChaCha20Poly1305::new(secret))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn kind(&self) -> Kind {
|
pub fn kind(&self) -> CipherKind {
|
||||||
match self {
|
match self {
|
||||||
Cipher::XChaCha20Poly1305(_) => Kind::XChaCha20Poly1305,
|
Cipher::XChaCha20Poly1305(_) => CipherKind::XChaCha20Poly1305,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn nonce_len(&self) -> NonceLen {
|
fn nonce_len(&self) -> HeadLen {
|
||||||
match self {
|
match self {
|
||||||
Cipher::XChaCha20Poly1305(_) => Nonce::len(),
|
Cipher::XChaCha20Poly1305(_) => HeadLen(Nonce::len()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn tag_len(&self) -> TagLen {
|
fn tag_len(&self) -> TagLen {
|
||||||
|
@ -106,7 +117,7 @@ impl Cipher {
|
||||||
return Err(Error::NotEnoughData(raw_data.len()));
|
return Err(Error::NotEnoughData(raw_data.len()));
|
||||||
}
|
}
|
||||||
let (nonce_bytes, data_and_tag) =
|
let (nonce_bytes, data_and_tag) =
|
||||||
raw_data.split_at_mut(Nonce::len().0);
|
raw_data.split_at_mut(Nonce::len());
|
||||||
let (data_notag, tag_bytes) = data_and_tag.split_at_mut(
|
let (data_notag, tag_bytes) = data_and_tag.split_at_mut(
|
||||||
data_and_tag.len()
|
data_and_tag.len()
|
||||||
- ::ring::aead::CHACHA20_POLY1305.tag_len(),
|
- ::ring::aead::CHACHA20_POLY1305.tag_len(),
|
||||||
|
@ -126,20 +137,20 @@ impl Cipher {
|
||||||
};
|
};
|
||||||
//data.drain(..Nonce::len());
|
//data.drain(..Nonce::len());
|
||||||
//data.truncate(final_len);
|
//data.truncate(final_len);
|
||||||
Ok(&raw_data[Nonce::len().0..Nonce::len().0 + final_len])
|
Ok(&raw_data[Nonce::len()..Nonce::len() + final_len])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn overhead(&self) -> usize {
|
fn overhead(&self) -> usize {
|
||||||
match self {
|
match self {
|
||||||
Cipher::XChaCha20Poly1305(_) => {
|
Cipher::XChaCha20Poly1305(_) => {
|
||||||
let cipher = Kind::XChaCha20Poly1305;
|
let cipher = CipherKind::XChaCha20Poly1305;
|
||||||
cipher.nonce_len().0 + cipher.tag_len().0
|
cipher.nonce_len().0 + cipher.tag_len().0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn encrypt(
|
fn encrypt(
|
||||||
&mut self,
|
&self,
|
||||||
nonce: &Nonce,
|
nonce: &Nonce,
|
||||||
aad: AAD,
|
aad: AAD,
|
||||||
data: &mut [u8],
|
data: &mut [u8],
|
||||||
|
@ -151,13 +162,13 @@ impl Cipher {
|
||||||
let tag_len: usize = ::ring::aead::CHACHA20_POLY1305.tag_len();
|
let tag_len: usize = ::ring::aead::CHACHA20_POLY1305.tag_len();
|
||||||
let data_len_notag = data.len() - tag_len;
|
let data_len_notag = data.len() - tag_len;
|
||||||
// write nonce
|
// write nonce
|
||||||
data[..Nonce::len().0].copy_from_slice(nonce.as_bytes());
|
data[..Nonce::len()].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,
|
||||||
&mut data[Nonce::len().0..data_len_notag],
|
&mut data[Nonce::len()..data_len_notag],
|
||||||
) {
|
) {
|
||||||
Ok(tag) => {
|
Ok(tag) => {
|
||||||
data[data_len_notag..].copy_from_slice(tag.as_slice());
|
data[data_len_notag..].copy_from_slice(tag.as_slice());
|
||||||
|
@ -183,11 +194,11 @@ impl ::core::fmt::Debug for CipherRecv {
|
||||||
|
|
||||||
impl CipherRecv {
|
impl CipherRecv {
|
||||||
/// Build a new Cipher
|
/// Build a new Cipher
|
||||||
pub fn new(kind: Kind, secret: Secret) -> Self {
|
pub fn new(kind: CipherKind, secret: Secret) -> Self {
|
||||||
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) -> NonceLen {
|
pub fn nonce_len(&self) -> HeadLen {
|
||||||
self.0.nonce_len()
|
self.0.nonce_len()
|
||||||
}
|
}
|
||||||
/// Get the length of the nonce for this cipher
|
/// Get the length of the nonce for this cipher
|
||||||
|
@ -204,14 +215,14 @@ impl CipherRecv {
|
||||||
self.0.decrypt(aad, data)
|
self.0.decrypt(aad, data)
|
||||||
}
|
}
|
||||||
/// return the underlying cipher id
|
/// return the underlying cipher id
|
||||||
pub fn kind(&self) -> Kind {
|
pub fn kind(&self) -> CipherKind {
|
||||||
self.0.kind()
|
self.0.kind()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Send only cipher
|
/// Send only cipher
|
||||||
pub struct CipherSend {
|
pub struct CipherSend {
|
||||||
nonce: Nonce,
|
nonce: NonceSync,
|
||||||
cipher: Cipher,
|
cipher: Cipher,
|
||||||
}
|
}
|
||||||
impl ::core::fmt::Debug for CipherSend {
|
impl ::core::fmt::Debug for CipherSend {
|
||||||
|
@ -225,20 +236,20 @@ impl ::core::fmt::Debug for CipherSend {
|
||||||
|
|
||||||
impl CipherSend {
|
impl CipherSend {
|
||||||
/// Build a new Cipher
|
/// Build a new Cipher
|
||||||
pub fn new(kind: Kind, secret: Secret, rand: &Random) -> Self {
|
pub fn new(kind: CipherKind, secret: Secret, rand: &Random) -> Self {
|
||||||
Self {
|
Self {
|
||||||
nonce: Nonce::new(rand),
|
nonce: NonceSync::new(rand),
|
||||||
cipher: Cipher::new(kind, secret),
|
cipher: Cipher::new(kind, secret),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// Encrypt the given data
|
/// Encrypt the given data
|
||||||
pub fn encrypt(&mut self, aad: AAD, data: &mut [u8]) -> 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(())
|
||||||
}
|
}
|
||||||
/// return the underlying cipher id
|
/// return the underlying cipher id
|
||||||
pub fn kind(&self) -> Kind {
|
pub fn kind(&self) -> CipherKind {
|
||||||
self.cipher.kind()
|
self.cipher.kind()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -274,7 +285,7 @@ struct NonceNum {
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
pub union Nonce {
|
pub union Nonce {
|
||||||
num: NonceNum,
|
num: NonceNum,
|
||||||
raw: [u8; Self::len().0],
|
raw: [u8; Self::len()],
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ::core::fmt::Debug for Nonce {
|
impl ::core::fmt::Debug for Nonce {
|
||||||
|
@ -292,17 +303,17 @@ impl ::core::fmt::Debug for Nonce {
|
||||||
impl Nonce {
|
impl Nonce {
|
||||||
/// Generate a new random Nonce
|
/// Generate a new random Nonce
|
||||||
pub fn new(rand: &Random) -> Self {
|
pub fn new(rand: &Random) -> Self {
|
||||||
let mut raw = [0; Self::len().0];
|
let mut raw = [0; Self::len()];
|
||||||
rand.fill(&mut raw);
|
rand.fill(&mut raw);
|
||||||
Self { raw }
|
Self { raw }
|
||||||
}
|
}
|
||||||
/// Length of this nonce in bytes
|
/// Length of this nonce in bytes
|
||||||
pub const fn len() -> NonceLen {
|
pub const fn len() -> usize {
|
||||||
// FIXME: was:12. xchacha20poly1305 requires 24.
|
// FIXME: was:12. xchacha20poly1305 requires 24.
|
||||||
// but we should change keys much earlier than that, and our
|
// but we should change keys much earlier than that, and our
|
||||||
// nonces are not random, but sequential.
|
// nonces are not random, but sequential.
|
||||||
// we should change keys every 2^30 bytes to be sure (stream max window)
|
// we should change keys every 2^30 bytes to be sure (stream max window)
|
||||||
return NonceLen(24);
|
return 24;
|
||||||
}
|
}
|
||||||
/// Get reference to the nonce bytes
|
/// Get reference to the nonce bytes
|
||||||
pub fn as_bytes(&self) -> &[u8] {
|
pub fn as_bytes(&self) -> &[u8] {
|
||||||
|
@ -312,12 +323,11 @@ impl Nonce {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// Create Nonce from array
|
/// Create Nonce from array
|
||||||
pub fn from_slice(raw: [u8; Self::len().0]) -> Self {
|
pub fn from_slice(raw: [u8; Self::len()]) -> Self {
|
||||||
Self { raw }
|
Self { raw }
|
||||||
}
|
}
|
||||||
/// Go to the next nonce
|
/// Go to the next nonce
|
||||||
pub fn advance(&mut self) -> Self {
|
pub fn advance(&mut self) {
|
||||||
let old_nonce = self.clone();
|
|
||||||
#[allow(unsafe_code)]
|
#[allow(unsafe_code)]
|
||||||
unsafe {
|
unsafe {
|
||||||
let old_low = self.num.low;
|
let old_low = self.num.low;
|
||||||
|
@ -326,17 +336,40 @@ impl Nonce {
|
||||||
self.num.high = self.num.high;
|
self.num.high = self.num.high;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
old_nonce
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Synchronize the mutex acess with a nonce for multithread safety
|
||||||
|
// TODO: remove mutex, not needed anymore
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct NonceSync {
|
||||||
|
nonce: ::std::sync::Mutex<Nonce>,
|
||||||
|
}
|
||||||
|
impl NonceSync {
|
||||||
|
/// Create a new thread safe nonce
|
||||||
|
pub fn new(rand: &Random) -> Self {
|
||||||
|
Self {
|
||||||
|
nonce: ::std::sync::Mutex::new(Nonce::new(rand)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// Advance the nonce and return the *old* value
|
||||||
|
pub fn advance(&self) -> Nonce {
|
||||||
|
let old_nonce: Nonce;
|
||||||
|
{
|
||||||
|
let mut nonce = self.nonce.lock().unwrap();
|
||||||
|
old_nonce = *nonce;
|
||||||
|
nonce.advance();
|
||||||
|
}
|
||||||
|
old_nonce
|
||||||
|
}
|
||||||
|
}
|
||||||
/// Select the best cipher from our supported list
|
/// Select the best cipher from our supported list
|
||||||
/// and the other endpoint supported list.
|
/// and the other endpoint supported list.
|
||||||
/// Give priority to our list
|
/// Give priority to our list
|
||||||
pub fn server_select_cipher(
|
pub fn server_select_cipher(
|
||||||
cfg: &Config,
|
cfg: &Config,
|
||||||
client_supported: &Vec<Kind>,
|
client_supported: &Vec<CipherKind>,
|
||||||
) -> Option<Kind> {
|
) -> Option<CipherKind> {
|
||||||
cfg.ciphers
|
cfg.ciphers
|
||||||
.iter()
|
.iter()
|
||||||
.find(|c| client_supported.contains(c))
|
.find(|c| client_supported.contains(c))
|
||||||
|
@ -348,8 +381,8 @@ pub fn server_select_cipher(
|
||||||
/// This is used only in the Directory synchronized handshake
|
/// This is used only in the Directory synchronized handshake
|
||||||
pub fn client_select_cipher(
|
pub fn client_select_cipher(
|
||||||
cfg: &Config,
|
cfg: &Config,
|
||||||
server_supported: &Vec<Kind>,
|
server_supported: &Vec<CipherKind>,
|
||||||
) -> Option<Kind> {
|
) -> Option<CipherKind> {
|
||||||
server_supported
|
server_supported
|
||||||
.iter()
|
.iter()
|
||||||
.find(|c| cfg.ciphers.contains(c))
|
.find(|c| cfg.ciphers.contains(c))
|
||||||
|
|
|
@ -1,26 +1,24 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
connection::{
|
auth,
|
||||||
handshake::{self, *},
|
connection::{handshake::*, ID},
|
||||||
ID,
|
|
||||||
},
|
|
||||||
enc::{self, asym::KeyID},
|
enc::{self, asym::KeyID},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_simple_encrypt_decrypt() {
|
fn test_simple_encrypt_decrypt() {
|
||||||
let rand = enc::Random::new();
|
let rand = enc::Random::new();
|
||||||
let cipher = enc::sym::Kind::XChaCha20Poly1305;
|
let cipher = enc::sym::CipherKind::XChaCha20Poly1305;
|
||||||
let secret = enc::Secret::new_rand(&rand);
|
let secret = enc::Secret::new_rand(&rand);
|
||||||
let secret2 = secret.clone();
|
let secret2 = secret.clone();
|
||||||
|
|
||||||
let mut cipher_send = enc::sym::CipherSend::new(cipher, secret, &rand);
|
let cipher_send = enc::sym::CipherSend::new(cipher, secret, &rand);
|
||||||
let cipher_recv = enc::sym::CipherRecv::new(cipher, secret2);
|
let cipher_recv = enc::sym::CipherRecv::new(cipher, secret2);
|
||||||
|
|
||||||
let mut data = Vec::new();
|
let mut data = Vec::new();
|
||||||
let tot_len = cipher_recv.nonce_len().0 + 1234 + cipher_recv.tag_len().0;
|
let tot_len = cipher_recv.nonce_len().0 + 1234 + cipher_recv.tag_len().0;
|
||||||
data.resize(tot_len, 0);
|
data.resize(tot_len, 0);
|
||||||
rand.fill(&mut data);
|
rand.fill(&mut data);
|
||||||
data[..enc::sym::Nonce::len().0].copy_from_slice(&[0; 24]);
|
data[..enc::sym::Nonce::len()].copy_from_slice(&[0; 24]);
|
||||||
let last = data.len() - cipher_recv.tag_len().0;
|
let last = data.len() - cipher_recv.tag_len().0;
|
||||||
data[last..].copy_from_slice(&[0; 16]);
|
data[last..].copy_from_slice(&[0; 16]);
|
||||||
let orig = data.clone();
|
let orig = data.clone();
|
||||||
|
@ -33,7 +31,7 @@ fn test_simple_encrypt_decrypt() {
|
||||||
if cipher_recv.decrypt(aad2, &mut data).is_err() {
|
if cipher_recv.decrypt(aad2, &mut data).is_err() {
|
||||||
assert!(false, "Decrypt failed");
|
assert!(false, "Decrypt failed");
|
||||||
}
|
}
|
||||||
data[..enc::sym::Nonce::len().0].copy_from_slice(&[0; 24]);
|
data[..enc::sym::Nonce::len()].copy_from_slice(&[0; 24]);
|
||||||
let last = data.len() - cipher_recv.tag_len().0;
|
let last = data.len() - cipher_recv.tag_len().0;
|
||||||
data[last..].copy_from_slice(&[0; 16]);
|
data[last..].copy_from_slice(&[0; 16]);
|
||||||
assert!(orig == data, "DIFFERENT!\n{:?}\n{:?}\n", orig, data);
|
assert!(orig == data, "DIFFERENT!\n{:?}\n{:?}\n", orig, data);
|
||||||
|
@ -42,18 +40,18 @@ fn test_simple_encrypt_decrypt() {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_encrypt_decrypt() {
|
fn test_encrypt_decrypt() {
|
||||||
let rand = enc::Random::new();
|
let rand = enc::Random::new();
|
||||||
let cipher = enc::sym::Kind::XChaCha20Poly1305;
|
let cipher = enc::sym::CipherKind::XChaCha20Poly1305;
|
||||||
let secret = enc::Secret::new_rand(&rand);
|
let secret = enc::Secret::new_rand(&rand);
|
||||||
let secret2 = secret.clone();
|
let secret2 = secret.clone();
|
||||||
|
|
||||||
let mut cipher_send = enc::sym::CipherSend::new(cipher, secret, &rand);
|
let cipher_send = enc::sym::CipherSend::new(cipher, secret, &rand);
|
||||||
let cipher_recv = enc::sym::CipherRecv::new(cipher, secret2);
|
let cipher_recv = enc::sym::CipherRecv::new(cipher, secret2);
|
||||||
let nonce_len = cipher_recv.nonce_len();
|
let nonce_len = cipher_recv.nonce_len();
|
||||||
let tag_len = cipher_recv.tag_len();
|
let tag_len = cipher_recv.tag_len();
|
||||||
|
|
||||||
let service_key = enc::Secret::new_rand(&rand);
|
let service_key = enc::Secret::new_rand(&rand);
|
||||||
|
|
||||||
let data = dirsync::resp::State::ClearText(dirsync::resp::Data {
|
let data = dirsync::RespInner::ClearText(dirsync::RespData {
|
||||||
client_nonce: dirsync::Nonce::new(&rand),
|
client_nonce: dirsync::Nonce::new(&rand),
|
||||||
id: ID::ID(::core::num::NonZeroU64::new(424242).unwrap()),
|
id: ID::ID(::core::num::NonZeroU64::new(424242).unwrap()),
|
||||||
service_connection_id: ID::ID(
|
service_connection_id: ID::ID(
|
||||||
|
@ -62,7 +60,7 @@ fn test_encrypt_decrypt() {
|
||||||
service_key,
|
service_key,
|
||||||
});
|
});
|
||||||
|
|
||||||
let resp = dirsync::resp::Resp {
|
let resp = dirsync::Resp {
|
||||||
client_key_id: KeyID(4444),
|
client_key_id: KeyID(4444),
|
||||||
data,
|
data,
|
||||||
};
|
};
|
||||||
|
@ -70,7 +68,7 @@ fn test_encrypt_decrypt() {
|
||||||
let encrypt_to = encrypt_from + resp.encrypted_length(nonce_len, tag_len);
|
let encrypt_to = encrypt_from + resp.encrypted_length(nonce_len, tag_len);
|
||||||
|
|
||||||
let h_resp =
|
let h_resp =
|
||||||
Handshake::new(handshake::Data::DirSync(dirsync::DirSync::Resp(resp)));
|
Handshake::new(HandshakeData::DirSync(dirsync::DirSync::Resp(resp)));
|
||||||
|
|
||||||
let mut bytes = Vec::<u8>::with_capacity(
|
let mut bytes = Vec::<u8>::with_capacity(
|
||||||
h_resp.len(cipher.nonce_len(), cipher.tag_len()),
|
h_resp.len(cipher.nonce_len(), cipher.tag_len()),
|
||||||
|
@ -119,7 +117,7 @@ fn test_encrypt_decrypt() {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
// reparse
|
// reparse
|
||||||
if let handshake::Data::DirSync(dirsync::DirSync::Resp(r_a)) =
|
if let HandshakeData::DirSync(dirsync::DirSync::Resp(r_a)) =
|
||||||
&mut deserialized.data
|
&mut deserialized.data
|
||||||
{
|
{
|
||||||
let enc_start = r_a.encrypted_offset() + cipher.nonce_len().0;
|
let enc_start = r_a.encrypted_offset() + cipher.nonce_len().0;
|
||||||
|
|
|
@ -7,16 +7,16 @@ use crate::{
|
||||||
handshake::{
|
handshake::{
|
||||||
self,
|
self,
|
||||||
dirsync::{self, DirSync},
|
dirsync::{self, DirSync},
|
||||||
Handshake,
|
tracker::{HandshakeAction, HandshakeTracker},
|
||||||
|
Handshake, HandshakeData,
|
||||||
},
|
},
|
||||||
packet::{self, Packet},
|
|
||||||
socket::{UdpClient, UdpServer},
|
socket::{UdpClient, UdpServer},
|
||||||
Conn, ConnList, IDSend,
|
ConnList, Connection, IDSend, Packet,
|
||||||
},
|
},
|
||||||
dnssec,
|
dnssec,
|
||||||
enc::{
|
enc::{
|
||||||
asym::{self, KeyID, PrivKey, PubKey},
|
asym::{self, KeyID, PrivKey, PubKey},
|
||||||
hkdf::{self, Hkdf},
|
hkdf::{self, Hkdf, HkdfKind},
|
||||||
sym, Random, Secret,
|
sym, Random, Secret,
|
||||||
},
|
},
|
||||||
inner::ThreadTracker,
|
inner::ThreadTracker,
|
||||||
|
@ -68,7 +68,7 @@ pub struct Worker {
|
||||||
queue_timeouts_send: mpsc::UnboundedSender<Work>,
|
queue_timeouts_send: mpsc::UnboundedSender<Work>,
|
||||||
thread_channels: Vec<::async_channel::Sender<Work>>,
|
thread_channels: Vec<::async_channel::Sender<Work>>,
|
||||||
connections: ConnList,
|
connections: ConnList,
|
||||||
handshakes: handshake::Tracker,
|
handshakes: HandshakeTracker,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(unsafe_code)]
|
#[allow(unsafe_code)]
|
||||||
|
@ -85,7 +85,7 @@ impl Worker {
|
||||||
) -> ::std::io::Result<Self> {
|
) -> ::std::io::Result<Self> {
|
||||||
let (queue_timeouts_send, queue_timeouts_recv) =
|
let (queue_timeouts_send, queue_timeouts_recv) =
|
||||||
mpsc::unbounded_channel();
|
mpsc::unbounded_channel();
|
||||||
let mut handshakes = handshake::Tracker::new(
|
let mut handshakes = HandshakeTracker::new(
|
||||||
thread_id,
|
thread_id,
|
||||||
cfg.ciphers.clone(),
|
cfg.ciphers.clone(),
|
||||||
cfg.key_exchanges.clone(),
|
cfg.key_exchanges.clone(),
|
||||||
|
@ -125,7 +125,6 @@ impl Worker {
|
||||||
handshakes,
|
handshakes,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Continuously loop and process work as needed
|
/// Continuously loop and process work as needed
|
||||||
pub async fn work_loop(&mut self) {
|
pub async fn work_loop(&mut self) {
|
||||||
'mainloop: loop {
|
'mainloop: loop {
|
||||||
|
@ -293,7 +292,7 @@ impl Worker {
|
||||||
// are PubKey::Exchange
|
// are PubKey::Exchange
|
||||||
unreachable!()
|
unreachable!()
|
||||||
}
|
}
|
||||||
let mut conn = Conn::new(
|
let mut conn = Connection::new(
|
||||||
hkdf,
|
hkdf,
|
||||||
cipher_selected,
|
cipher_selected,
|
||||||
connection::Role::Client,
|
connection::Role::Client,
|
||||||
|
@ -326,39 +325,39 @@ impl Worker {
|
||||||
};
|
};
|
||||||
|
|
||||||
// build request
|
// build request
|
||||||
let auth_info = dirsync::req::AuthInfo {
|
let auth_info = dirsync::AuthInfo {
|
||||||
user: UserID::new_anonymous(),
|
user: UserID::new_anonymous(),
|
||||||
token: Token::new_anonymous(&self.rand),
|
token: Token::new_anonymous(&self.rand),
|
||||||
service_id: conn_info.service_id,
|
service_id: conn_info.service_id,
|
||||||
domain: conn_info.domain,
|
domain: conn_info.domain,
|
||||||
};
|
};
|
||||||
let req_data = dirsync::req::Data {
|
let req_data = dirsync::ReqData {
|
||||||
nonce: dirsync::Nonce::new(&self.rand),
|
nonce: dirsync::Nonce::new(&self.rand),
|
||||||
client_key_id,
|
client_key_id,
|
||||||
id: auth_recv_id.0, //FIXME: is zero
|
id: auth_recv_id.0, //FIXME: is zero
|
||||||
auth: auth_info,
|
auth: auth_info,
|
||||||
};
|
};
|
||||||
let req = dirsync::req::Req {
|
let req = dirsync::Req {
|
||||||
key_id: key.0,
|
key_id: key.0,
|
||||||
exchange,
|
exchange,
|
||||||
hkdf: hkdf_selected,
|
hkdf: hkdf_selected,
|
||||||
cipher: cipher_selected,
|
cipher: cipher_selected,
|
||||||
exchange_key: pub_key,
|
exchange_key: pub_key,
|
||||||
data: dirsync::req::State::ClearText(req_data),
|
data: dirsync::ReqInner::ClearText(req_data),
|
||||||
};
|
};
|
||||||
let encrypt_start =
|
let encrypt_start = ID::len() + req.encrypted_offset();
|
||||||
connection::ID::len() + req.encrypted_offset();
|
|
||||||
let encrypt_end = encrypt_start
|
let encrypt_end = encrypt_start
|
||||||
+ req.encrypted_length(
|
+ req.encrypted_length(
|
||||||
cipher_selected.nonce_len(),
|
cipher_selected.nonce_len(),
|
||||||
cipher_selected.tag_len(),
|
cipher_selected.tag_len(),
|
||||||
);
|
);
|
||||||
let h_req = Handshake::new(handshake::Data::DirSync(
|
let h_req = Handshake::new(HandshakeData::DirSync(
|
||||||
DirSync::Req(req),
|
DirSync::Req(req),
|
||||||
));
|
));
|
||||||
|
use connection::{PacketData, ID};
|
||||||
let packet = Packet {
|
let packet = Packet {
|
||||||
id: connection::ID::Handshake,
|
id: ID::Handshake,
|
||||||
data: packet::Data::Handshake(h_req),
|
data: PacketData::Handshake(h_req),
|
||||||
};
|
};
|
||||||
|
|
||||||
let tot_len = packet.len(
|
let tot_len = packet.len(
|
||||||
|
@ -449,9 +448,9 @@ impl Worker {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
match action {
|
match action {
|
||||||
handshake::Action::AuthNeeded(authinfo) => {
|
HandshakeAction::AuthNeeded(authinfo) => {
|
||||||
let req;
|
let req;
|
||||||
if let handshake::Data::DirSync(DirSync::Req(r)) =
|
if let HandshakeData::DirSync(DirSync::Req(r)) =
|
||||||
authinfo.handshake.data
|
authinfo.handshake.data
|
||||||
{
|
{
|
||||||
req = r;
|
req = r;
|
||||||
|
@ -459,8 +458,9 @@ impl Worker {
|
||||||
::tracing::error!("AuthInfo on non DS::Req");
|
::tracing::error!("AuthInfo on non DS::Req");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
use dirsync::ReqInner;
|
||||||
let req_data = match req.data {
|
let req_data = match req.data {
|
||||||
dirsync::req::State::ClearText(req_data) => req_data,
|
ReqInner::ClearText(req_data) => req_data,
|
||||||
_ => {
|
_ => {
|
||||||
::tracing::error!("AuthNeeded: expected ClearText");
|
::tracing::error!("AuthNeeded: expected ClearText");
|
||||||
assert!(false, "AuthNeeded: unreachable");
|
assert!(false, "AuthNeeded: unreachable");
|
||||||
|
@ -510,12 +510,12 @@ impl Worker {
|
||||||
// Client has correctly authenticated
|
// Client has correctly authenticated
|
||||||
// TODO: contact the service, get the key and
|
// TODO: contact the service, get the key and
|
||||||
// connection ID
|
// connection ID
|
||||||
let srv_conn_id = connection::ID::new_rand(&self.rand);
|
let srv_conn_id = ID::new_rand(&self.rand);
|
||||||
let srv_secret = Secret::new_rand(&self.rand);
|
let srv_secret = Secret::new_rand(&self.rand);
|
||||||
let head_len = req.cipher.nonce_len();
|
let head_len = req.cipher.nonce_len();
|
||||||
let tag_len = req.cipher.tag_len();
|
let tag_len = req.cipher.tag_len();
|
||||||
|
|
||||||
let mut auth_conn = Conn::new(
|
let mut auth_conn = Connection::new(
|
||||||
authinfo.hkdf,
|
authinfo.hkdf,
|
||||||
req.cipher,
|
req.cipher,
|
||||||
connection::Role::Server,
|
connection::Role::Server,
|
||||||
|
@ -526,7 +526,7 @@ impl Worker {
|
||||||
let auth_id_recv = self.connections.reserve_first();
|
let auth_id_recv = self.connections.reserve_first();
|
||||||
auth_conn.id_recv = auth_id_recv;
|
auth_conn.id_recv = auth_id_recv;
|
||||||
|
|
||||||
let resp_data = dirsync::resp::Data {
|
let resp_data = dirsync::RespData {
|
||||||
client_nonce: req_data.nonce,
|
client_nonce: req_data.nonce,
|
||||||
id: auth_conn.id_recv.0,
|
id: auth_conn.id_recv.0,
|
||||||
service_connection_id: srv_conn_id,
|
service_connection_id: srv_conn_id,
|
||||||
|
@ -536,20 +536,21 @@ impl Worker {
|
||||||
// no aad for now
|
// no aad for now
|
||||||
let aad = AAD(&mut []);
|
let aad = AAD(&mut []);
|
||||||
|
|
||||||
let resp = dirsync::resp::Resp {
|
use dirsync::RespInner;
|
||||||
|
let resp = dirsync::Resp {
|
||||||
client_key_id: req_data.client_key_id,
|
client_key_id: req_data.client_key_id,
|
||||||
data: dirsync::resp::State::ClearText(resp_data),
|
data: RespInner::ClearText(resp_data),
|
||||||
};
|
};
|
||||||
let encrypt_from =
|
let encrypt_from = ID::len() + resp.encrypted_offset();
|
||||||
connection::ID::len() + resp.encrypted_offset();
|
|
||||||
let encrypt_until =
|
let encrypt_until =
|
||||||
encrypt_from + resp.encrypted_length(head_len, tag_len);
|
encrypt_from + resp.encrypted_length(head_len, tag_len);
|
||||||
let resp_handshake = Handshake::new(
|
let resp_handshake = Handshake::new(
|
||||||
handshake::Data::DirSync(DirSync::Resp(resp)),
|
HandshakeData::DirSync(DirSync::Resp(resp)),
|
||||||
);
|
);
|
||||||
|
use connection::{PacketData, ID};
|
||||||
let packet = Packet {
|
let packet = Packet {
|
||||||
id: connection::ID::new_handshake(),
|
id: ID::new_handshake(),
|
||||||
data: packet::Data::Handshake(resp_handshake),
|
data: PacketData::Handshake(resp_handshake),
|
||||||
};
|
};
|
||||||
let tot_len = packet.len(head_len, tag_len);
|
let tot_len = packet.len(head_len, tag_len);
|
||||||
let mut raw_out = Vec::<u8>::with_capacity(tot_len);
|
let mut raw_out = Vec::<u8>::with_capacity(tot_len);
|
||||||
|
@ -565,9 +566,9 @@ impl Worker {
|
||||||
}
|
}
|
||||||
self.send_packet(raw_out, udp.src, udp.dst).await;
|
self.send_packet(raw_out, udp.src, udp.dst).await;
|
||||||
}
|
}
|
||||||
handshake::Action::ClientConnect(cci) => {
|
HandshakeAction::ClientConnect(cci) => {
|
||||||
let ds_resp;
|
let ds_resp;
|
||||||
if let handshake::Data::DirSync(DirSync::Resp(resp)) =
|
if let HandshakeData::DirSync(DirSync::Resp(resp)) =
|
||||||
cci.handshake.data
|
cci.handshake.data
|
||||||
{
|
{
|
||||||
ds_resp = resp;
|
ds_resp = resp;
|
||||||
|
@ -577,8 +578,7 @@ impl Worker {
|
||||||
}
|
}
|
||||||
// track connection
|
// track connection
|
||||||
let resp_data;
|
let resp_data;
|
||||||
if let dirsync::resp::State::ClearText(r_data) =
|
if let dirsync::RespInner::ClearText(r_data) = ds_resp.data
|
||||||
ds_resp.data
|
|
||||||
{
|
{
|
||||||
resp_data = r_data;
|
resp_data = r_data;
|
||||||
} else {
|
} else {
|
||||||
|
@ -607,11 +607,11 @@ impl Worker {
|
||||||
//FIXME: the Secret should be XORed with the client
|
//FIXME: the Secret should be XORed with the client
|
||||||
// stored secret (if any)
|
// stored secret (if any)
|
||||||
let hkdf = Hkdf::new(
|
let hkdf = Hkdf::new(
|
||||||
hkdf::Kind::Sha3,
|
HkdfKind::Sha3,
|
||||||
cci.service_id.as_bytes(),
|
cci.service_id.as_bytes(),
|
||||||
resp_data.service_key,
|
resp_data.service_key,
|
||||||
);
|
);
|
||||||
let mut service_connection = Conn::new(
|
let mut service_connection = Connection::new(
|
||||||
hkdf,
|
hkdf,
|
||||||
cipher,
|
cipher,
|
||||||
connection::Role::Client,
|
connection::Role::Client,
|
||||||
|
@ -626,7 +626,7 @@ impl Worker {
|
||||||
let _ =
|
let _ =
|
||||||
cci.answer.send(Ok((cci.srv_key_id, auth_srv_conn)));
|
cci.answer.send(Ok((cci.srv_key_id, auth_srv_conn)));
|
||||||
}
|
}
|
||||||
handshake::Action::Nothing => {}
|
HandshakeAction::Nothing => {}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,7 +39,6 @@ use crate::{
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
pub use config::Config;
|
pub use config::Config;
|
||||||
pub use connection::Connection;
|
|
||||||
|
|
||||||
/// Main fenrir library errors
|
/// Main fenrir library errors
|
||||||
#[derive(::thiserror::Error, Debug)]
|
#[derive(::thiserror::Error, Debug)]
|
||||||
|
@ -333,7 +332,7 @@ impl Fenrir {
|
||||||
let data: Vec<u8> = buffer[..bytes].to_vec();
|
let data: Vec<u8> = buffer[..bytes].to_vec();
|
||||||
|
|
||||||
// we very likely have multiple threads, pinned to different cpus.
|
// we very likely have multiple threads, pinned to different cpus.
|
||||||
// use the connection::ID to send the same connection
|
// use the ConnectionID to send the same connection
|
||||||
// to the same thread.
|
// to the same thread.
|
||||||
// Handshakes have connection ID 0, so we use the sender's UDP port
|
// Handshakes have connection ID 0, so we use the sender's UDP port
|
||||||
|
|
||||||
|
@ -342,12 +341,13 @@ impl Fenrir {
|
||||||
Err(_) => continue, // packet way too short, ignore.
|
Err(_) => continue, // packet way too short, ignore.
|
||||||
};
|
};
|
||||||
let thread_idx: usize = {
|
let thread_idx: usize = {
|
||||||
|
use connection::packet::ConnectionID;
|
||||||
match packet.id {
|
match packet.id {
|
||||||
connection::ID::Handshake => {
|
ConnectionID::Handshake => {
|
||||||
let send_port = sock_sender.0.port() as u64;
|
let send_port = sock_sender.0.port() as u64;
|
||||||
(send_port % queues_num) as usize
|
(send_port % queues_num) as usize
|
||||||
}
|
}
|
||||||
connection::ID::ID(id) => (id.get() % queues_num) as usize,
|
ConnectionID::ID(id) => (id.get() % queues_num) as usize,
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let _ = work_queues[thread_idx]
|
let _ = work_queues[thread_idx]
|
||||||
|
|
11
src/tests.rs
11
src/tests.rs
|
@ -62,7 +62,10 @@ async fn test_connection_dirsync() {
|
||||||
rt.block_on(local_thread);
|
rt.block_on(local_thread);
|
||||||
});
|
});
|
||||||
|
|
||||||
use crate::dnssec::{record, Record};
|
use crate::{
|
||||||
|
connection::handshake::HandshakeID,
|
||||||
|
dnssec::{record, Record},
|
||||||
|
};
|
||||||
|
|
||||||
let port: u16 = server.addresses()[0].port();
|
let port: u16 = server.addresses()[0].port();
|
||||||
|
|
||||||
|
@ -73,14 +76,14 @@ async fn test_connection_dirsync() {
|
||||||
port: Some(::core::num::NonZeroU16::new(port).unwrap()),
|
port: Some(::core::num::NonZeroU16::new(port).unwrap()),
|
||||||
priority: record::AddressPriority::P1,
|
priority: record::AddressPriority::P1,
|
||||||
weight: record::AddressWeight::W1,
|
weight: record::AddressWeight::W1,
|
||||||
handshake_ids: [handshake::ID::DirectorySynchronized].to_vec(),
|
handshake_ids: [HandshakeID::DirectorySynchronized].to_vec(),
|
||||||
public_key_idx: [record::PubKeyIdx(0)].to_vec(),
|
public_key_idx: [record::PubKeyIdx(0)].to_vec(),
|
||||||
}]
|
}]
|
||||||
.to_vec(),
|
.to_vec(),
|
||||||
key_exchanges: [enc::asym::KeyExchangeKind::X25519DiffieHellman]
|
key_exchanges: [enc::asym::KeyExchangeKind::X25519DiffieHellman]
|
||||||
.to_vec(),
|
.to_vec(),
|
||||||
hkdfs: [enc::hkdf::Kind::Sha3].to_vec(),
|
hkdfs: [enc::hkdf::HkdfKind::Sha3].to_vec(),
|
||||||
ciphers: [enc::sym::Kind::XChaCha20Poly1305].to_vec(),
|
ciphers: [enc::sym::CipherKind::XChaCha20Poly1305].to_vec(),
|
||||||
};
|
};
|
||||||
|
|
||||||
::tokio::time::sleep(::std::time::Duration::from_millis(500)).await;
|
::tokio::time::sleep(::std::time::Duration::from_millis(500)).await;
|
||||||
|
|
Loading…
Reference in New Issue