More work on handshake. obviously unfinished
Signed-off-by: Luca Fulchir <luca.fulchir@runesauth.com>
This commit is contained in:
parent
e3af78eaba
commit
238a0a5516
|
@ -26,6 +26,7 @@ crate_type = [ "lib", "cdylib", "staticlib" ]
|
|||
[dependencies]
|
||||
# please keep these in alphabetical order
|
||||
|
||||
arc-swap = { version = "^1.6" }
|
||||
# base85 repo has no tags, fix on a commit. v1.1.1 points to older, wrong version
|
||||
base85 = { git = "https://gitlab.com/darkwyrm/base85", rev = "d98efbfd171dd9ba48e30a5c88f94db92fc7b3c6" }
|
||||
futures = { version = "^0.3" }
|
||||
|
@ -33,6 +34,7 @@ libc = { version = "^0.2" }
|
|||
num-traits = { version = "^0.2" }
|
||||
num-derive = { version = "^0.3" }
|
||||
ring = { version = "^0.16" }
|
||||
bincode = { version = "^1.3" }
|
||||
strum = { version = "^0.24" }
|
||||
strum_macros = { version = "^0.24" }
|
||||
thiserror = { version = "^1.0" }
|
||||
|
@ -43,6 +45,7 @@ tracing = { version = "^0.1" }
|
|||
trust-dns-resolver = { version = "^0.22", features = [ "dnssec-ring" ] }
|
||||
trust-dns-client = { version = "^0.22", features = [ "dnssec" ] }
|
||||
trust-dns-proto = { version = "^0.22" }
|
||||
x25519-dalek = { version = "^1.2", features = [ "serde" ] }
|
||||
|
||||
[profile.dev]
|
||||
|
||||
|
|
|
@ -9,7 +9,10 @@
|
|||
//! change the DNSSEC public/private keys
|
||||
|
||||
use super::{Error, HandshakeData};
|
||||
use crate::connection::{KeyID, UnparsedExchangePubKey, ID};
|
||||
use crate::{
|
||||
connection::ID,
|
||||
enc::asym::{ExchangePubKey, KeyID},
|
||||
};
|
||||
use ::std::vec::Vec;
|
||||
|
||||
/// Parsed handshake
|
||||
|
@ -25,11 +28,11 @@ pub enum DirSync {
|
|||
#[derive(Debug, Clone)]
|
||||
pub struct Req {
|
||||
/// Id of the server key used for the key exchange
|
||||
key_id: KeyID,
|
||||
pub key_id: KeyID,
|
||||
/// Client ephemeral public key used for key exchanges
|
||||
exchange_key: UnparsedExchangePubKey,
|
||||
pub exchange_key: ExchangePubKey,
|
||||
/// encrypted data
|
||||
enc: Vec<u8>,
|
||||
pub enc: Vec<u8>,
|
||||
}
|
||||
|
||||
impl super::HandshakeParsing for Req {
|
||||
|
@ -40,10 +43,9 @@ impl super::HandshakeParsing for Req {
|
|||
}
|
||||
let key_id: KeyID =
|
||||
KeyID(u16::from_le_bytes(raw[0..1].try_into().unwrap()));
|
||||
let (exchange_key, len) =
|
||||
match UnparsedExchangePubKey::from_raw(&raw[2..]) {
|
||||
let (exchange_key, len) = match ExchangePubKey::from_slice(&raw[2..]) {
|
||||
Ok(exchange_key) => exchange_key,
|
||||
Err(e) => return Err(e),
|
||||
Err(e) => return Err(e.into()),
|
||||
};
|
||||
let enc = raw[(2 + len)..].to_vec();
|
||||
Ok(HandshakeData::DirSync(DirSync::Req(Self {
|
||||
|
@ -58,13 +60,13 @@ impl super::HandshakeParsing for Req {
|
|||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct ReqData {
|
||||
/// Random nonce, the client can use this to track multiple key exchanges
|
||||
nonce: [u8; 16],
|
||||
pub nonce: [u8; 16],
|
||||
/// Client key id so the client can use and rotate keys
|
||||
client_key_id: KeyID,
|
||||
pub client_key_id: KeyID,
|
||||
/// Authentication token
|
||||
token: [u8; 32],
|
||||
pub token: [u8; 32],
|
||||
/// Receiving connection id for the client
|
||||
id: ID,
|
||||
pub id: ID,
|
||||
// TODO: service info
|
||||
}
|
||||
|
||||
|
@ -72,9 +74,9 @@ pub struct ReqData {
|
|||
#[derive(Debug, Clone)]
|
||||
pub struct Resp {
|
||||
/// Tells the client with which key the exchange was done
|
||||
client_key_id: KeyID,
|
||||
pub client_key_id: KeyID,
|
||||
/// encrypted data
|
||||
enc: Vec<u8>,
|
||||
pub enc: Vec<u8>,
|
||||
}
|
||||
|
||||
impl super::HandshakeParsing for Resp {
|
||||
|
|
|
@ -4,7 +4,7 @@ pub mod dirsync;
|
|||
|
||||
use ::num_traits::FromPrimitive;
|
||||
|
||||
use crate::connection::ProtocolVersion;
|
||||
use crate::connection::{self, ProtocolVersion};
|
||||
|
||||
/// Handshake errors
|
||||
#[derive(::thiserror::Error, Debug, Copy, Clone)]
|
||||
|
@ -13,11 +13,22 @@ pub enum Error {
|
|||
/// TODO: more detailed parsing errors
|
||||
#[error("not an handshake packet")]
|
||||
Parsing,
|
||||
/// No such Key ID
|
||||
#[error("unknown key id")]
|
||||
UnknownKeyID,
|
||||
/// Key error
|
||||
#[error("key: {0:?}")]
|
||||
Key(#[from] crate::enc::Error),
|
||||
/// Not enough data
|
||||
#[error("not enough data")]
|
||||
NotEnoughData,
|
||||
}
|
||||
|
||||
pub(crate) struct HandshakeKey {
|
||||
pub id: crate::enc::asym::KeyID,
|
||||
pub key: crate::enc::asym::PrivKey,
|
||||
}
|
||||
|
||||
/// Parsed handshake
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum HandshakeData {
|
||||
|
@ -49,9 +60,9 @@ pub enum Kind {
|
|||
#[derive(Debug, Clone)]
|
||||
pub struct Handshake {
|
||||
/// Fenrir Protocol version
|
||||
fenrir_version: ProtocolVersion,
|
||||
pub fenrir_version: ProtocolVersion,
|
||||
/// enum for the parsed data
|
||||
data: HandshakeData,
|
||||
pub data: HandshakeData,
|
||||
}
|
||||
|
||||
impl Handshake {
|
||||
|
@ -69,11 +80,16 @@ impl Handshake {
|
|||
Some(handshake_kind) => handshake_kind,
|
||||
None => return Err(Error::Parsing),
|
||||
};
|
||||
let parsed = match handshake_kind {
|
||||
let data = match handshake_kind {
|
||||
Kind::DirSyncReq => dirsync::Req::parse(&raw[2..])?,
|
||||
Kind::DirSyncResp => dirsync::Resp::parse(&raw[2..])?,
|
||||
};
|
||||
|
||||
Ok(Self {
|
||||
fenrir_version,
|
||||
data,
|
||||
})
|
||||
}
|
||||
pub(crate) fn work(&self, keys: &[HandshakeKey]) -> Result<(), Error> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,10 +10,6 @@ pub use handshake::Handshake;
|
|||
pub use packet::ConnectionID as ID;
|
||||
pub use packet::Packet;
|
||||
|
||||
/// Public key ID
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct KeyID(u16);
|
||||
|
||||
/// Version of the fenrir protocol in use
|
||||
#[derive(::num_derive::FromPrimitive, Debug, Copy, Clone)]
|
||||
#[repr(u8)]
|
||||
|
@ -21,54 +17,3 @@ pub enum ProtocolVersion {
|
|||
/// First Fenrir Protocol Version
|
||||
V0 = 0,
|
||||
}
|
||||
|
||||
/// Kind of key in the key exchange
|
||||
#[derive(Debug, Copy, Clone, ::num_derive::FromPrimitive)]
|
||||
#[repr(u8)]
|
||||
pub enum ExchangePubKeyKind {
|
||||
/// X25519 Public key
|
||||
X25519 = 0,
|
||||
}
|
||||
|
||||
/// all Ephemeral Public key types
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum ExchangePubKey {
|
||||
/// X25519(Curve25519) used for key exchange
|
||||
X25519(Vec<u8>),
|
||||
}
|
||||
|
||||
/// Mark the Ephemeral key as yet unparsed, meaning it could be
|
||||
/// maliciously currupted data
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct UnparsedExchangePubKey(ExchangePubKey);
|
||||
|
||||
impl UnparsedExchangePubKey {
|
||||
/// Load public key used for key exchange from it raw bytes
|
||||
/// The riesult is "unparsed" since we don't verify
|
||||
/// the actual key
|
||||
pub fn from_raw(raw: &[u8]) -> Result<(Self, usize), handshake::Error> {
|
||||
// FIXME: get *real* minimum key size
|
||||
const MIN_KEY_SIZE: usize = 8;
|
||||
if raw.len() < 1 + MIN_KEY_SIZE {
|
||||
return Err(handshake::Error::NotEnoughData);
|
||||
}
|
||||
match ExchangePubKeyKind::from_u8(raw[0]) {
|
||||
Some(kind) => match kind {
|
||||
ExchangePubKeyKind::X25519 => {
|
||||
// FIXME: is this really the
|
||||
const len: usize =
|
||||
1 + ::ring::signature::ED25519_PUBLIC_KEY_LEN;
|
||||
Ok((
|
||||
UnparsedExchangePubKey(ExchangePubKey::X25519(
|
||||
raw[1..len].to_vec(),
|
||||
)),
|
||||
len,
|
||||
))
|
||||
}
|
||||
},
|
||||
None => {
|
||||
return Err(handshake::Error::Parsing);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -33,13 +33,13 @@ use ::std::{net::IpAddr, vec::Vec};
|
|||
|
||||
/// Public Key ID
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct PublicKeyId(u8);
|
||||
pub struct PublicKeyID(u8);
|
||||
|
||||
impl TryFrom<&str> for PublicKeyId {
|
||||
impl TryFrom<&str> for PublicKeyID {
|
||||
type Error = ::std::io::Error;
|
||||
fn try_from(raw: &str) -> Result<Self, Self::Error> {
|
||||
if let Ok(id_u8) = raw.parse::<u8>() {
|
||||
return Ok(PublicKeyId(id_u8));
|
||||
return Ok(PublicKeyID(id_u8));
|
||||
}
|
||||
return Err(::std::io::Error::new(
|
||||
::std::io::ErrorKind::InvalidData,
|
||||
|
@ -95,7 +95,7 @@ pub struct PublicKey {
|
|||
/// type of public key
|
||||
pub kind: PublicKeyType,
|
||||
/// id of public key
|
||||
pub id: PublicKeyId,
|
||||
pub id: PublicKeyID,
|
||||
}
|
||||
|
||||
impl PublicKey {
|
||||
|
@ -114,7 +114,7 @@ impl PublicKey {
|
|||
}
|
||||
|
||||
let kind = PublicKeyType::from_u8(raw[0]).unwrap();
|
||||
let id = PublicKeyId(raw[1]);
|
||||
let id = PublicKeyID(raw[1]);
|
||||
if raw.len() < 2 + kind.key_len() {
|
||||
return Err(Error::NotEnoughData(2));
|
||||
}
|
||||
|
@ -230,12 +230,45 @@ impl TryFrom<&str> for AddressWeight {
|
|||
}
|
||||
}
|
||||
|
||||
/// List of possible handshakes
|
||||
#[derive(::num_derive::FromPrimitive, Debug, Clone, Copy)]
|
||||
#[repr(u8)]
|
||||
pub enum HandshakeID {
|
||||
/// 1-RTT Directory synchronized handshake. Fast, no forward secrecy
|
||||
DirectorySynchronized = 0,
|
||||
/// 2-RTT Stateful exchange. Little DDos protection
|
||||
Stateful,
|
||||
/// 3-RTT stateless exchange. Forward secrecy and ddos protection
|
||||
Stateless,
|
||||
}
|
||||
|
||||
impl TryFrom<&str> for HandshakeID {
|
||||
type Error = ::std::io::Error;
|
||||
// TODO: from actual names, not only numeric
|
||||
fn try_from(raw: &str) -> Result<Self, Self::Error> {
|
||||
if let Ok(handshake_u8) = raw.parse::<u8>() {
|
||||
if handshake_u8 >= 1 {
|
||||
if let Some(handshake) = HandshakeID::from_u8(handshake_u8 - 1)
|
||||
{
|
||||
return Ok(handshake);
|
||||
}
|
||||
}
|
||||
}
|
||||
return Err(::std::io::Error::new(
|
||||
::std::io::ErrorKind::InvalidData,
|
||||
"Unknown handshake ID",
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
/// Authentication server address information:
|
||||
/// * ip
|
||||
/// * udp port
|
||||
/// * priority
|
||||
/// * weight within priority
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
/// * list of supported handshakes IDs
|
||||
/// * list of public keys IDs
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Address {
|
||||
/// Ip address of server, v4 or v6
|
||||
pub ip: IpAddr,
|
||||
|
@ -246,14 +279,18 @@ pub struct Address {
|
|||
pub priority: AddressPriority,
|
||||
/// Weight of this address in the priority group
|
||||
pub weight: AddressWeight,
|
||||
/// List of supported handshakes
|
||||
pub handshake_ids: Vec<HandshakeID>,
|
||||
/// Public key IDs used by this address
|
||||
pub public_key_id: PublicKeyId,
|
||||
pub public_key_ids: Vec<PublicKeyID>,
|
||||
}
|
||||
|
||||
impl Address {
|
||||
fn raw_len(&self) -> usize {
|
||||
let size = 4; // UDP port + Priority + Weight
|
||||
match self.ip {
|
||||
// UDP port + Priority + Weight + pubkey_len + handshake_len
|
||||
let mut size = 6;
|
||||
size = size + self.public_key_ids.len() + self.handshake_ids.len();
|
||||
size + match self.ip {
|
||||
IpAddr::V4(_) => size + 4,
|
||||
IpAddr::V6(_) => size + 16,
|
||||
}
|
||||
|
@ -270,7 +307,6 @@ impl Address {
|
|||
|
||||
raw.push(bitfield);
|
||||
|
||||
raw.push(self.public_key_id.0);
|
||||
raw.extend_from_slice(
|
||||
&(match self.port {
|
||||
Some(port) => port.get().to_le_bytes(),
|
||||
|
@ -278,6 +314,11 @@ impl Address {
|
|||
}),
|
||||
);
|
||||
|
||||
raw.push(self.public_key_ids.len() as u8);
|
||||
for id in self.public_key_ids.iter() {
|
||||
raw.push(id.0);
|
||||
}
|
||||
|
||||
match self.ip {
|
||||
IpAddr::V4(ip) => {
|
||||
let raw_ip = ip.octets();
|
||||
|
@ -290,7 +331,7 @@ impl Address {
|
|||
};
|
||||
}
|
||||
fn decode_raw(raw: &[u8]) -> Result<(Self, usize), Error> {
|
||||
if raw.len() < 8 {
|
||||
if raw.len() < 10 {
|
||||
return Err(Error::NotEnoughData(0));
|
||||
}
|
||||
let ip_type = raw[0] >> 6;
|
||||
|
@ -315,19 +356,60 @@ impl Address {
|
|||
let priority = AddressPriority::from_u8(raw_priority).unwrap();
|
||||
let weight = AddressWeight::from_u8(raw_weight).unwrap();
|
||||
|
||||
let public_key_id = PublicKeyId(raw[1]);
|
||||
let raw_port = u16::from_le_bytes([raw[1], raw[2]]);
|
||||
|
||||
// Add publi key ids
|
||||
let num_pubkey_ids = raw[3] as usize;
|
||||
if raw.len() < 3 + num_pubkey_ids {
|
||||
return Err(Error::NotEnoughData(3));
|
||||
}
|
||||
let mut public_key_ids = Vec::with_capacity(num_pubkey_ids);
|
||||
|
||||
for raw_pubkey_id in raw[4..num_pubkey_ids].iter() {
|
||||
public_key_ids.push(PublicKeyID(*raw_pubkey_id));
|
||||
}
|
||||
|
||||
// add handshake ids
|
||||
let next_ptr = 3 + num_pubkey_ids;
|
||||
let num_handshake_ids = raw[next_ptr] as usize;
|
||||
if raw.len() < next_ptr + num_handshake_ids {
|
||||
return Err(Error::NotEnoughData(next_ptr));
|
||||
}
|
||||
let mut handshake_ids = Vec::with_capacity(num_handshake_ids);
|
||||
for raw_handshake_id in
|
||||
raw[next_ptr..(next_ptr + num_pubkey_ids)].iter()
|
||||
{
|
||||
match HandshakeID::from_u8(*raw_handshake_id) {
|
||||
Some(h_id) => handshake_ids.push(h_id),
|
||||
None => {
|
||||
::tracing::warn!(
|
||||
"Unsupported handshake {}. Upgrade?",
|
||||
*raw_handshake_id
|
||||
);
|
||||
// ignore unsupported handshakes
|
||||
}
|
||||
}
|
||||
}
|
||||
let next_ptr = next_ptr + num_pubkey_ids;
|
||||
|
||||
let raw_port = u16::from_le_bytes([raw[2], raw[3]]);
|
||||
let port = if raw_port == 0 {
|
||||
None
|
||||
} else {
|
||||
Some(NonZeroU16::new(raw_port).unwrap())
|
||||
};
|
||||
let ip = if is_ipv6 {
|
||||
let raw_ip: [u8; 16] = raw[4..20].try_into().unwrap();
|
||||
let ip_end = next_ptr + 16;
|
||||
if raw.len() < ip_end {
|
||||
return Err(Error::NotEnoughData(next_ptr));
|
||||
}
|
||||
let raw_ip: [u8; 16] = raw[next_ptr..ip_end].try_into().unwrap();
|
||||
IpAddr::from(raw_ip)
|
||||
} else {
|
||||
let raw_ip: [u8; 4] = raw[4..8].try_into().unwrap();
|
||||
let ip_end = next_ptr + 4;
|
||||
if raw.len() < ip_end {
|
||||
return Err(Error::NotEnoughData(next_ptr));
|
||||
}
|
||||
let raw_ip: [u8; 4] = raw[next_ptr..ip_end].try_into().unwrap();
|
||||
IpAddr::from(raw_ip)
|
||||
};
|
||||
|
||||
|
@ -337,7 +419,8 @@ impl Address {
|
|||
port,
|
||||
priority,
|
||||
weight,
|
||||
public_key_id,
|
||||
public_key_ids,
|
||||
handshake_ids,
|
||||
},
|
||||
total_length,
|
||||
))
|
||||
|
|
|
@ -0,0 +1,92 @@
|
|||
//! Asymmetric key handling and wrappers
|
||||
|
||||
use ::num_traits::FromPrimitive;
|
||||
use ::std::vec::Vec;
|
||||
|
||||
use super::Error;
|
||||
|
||||
/// Public key ID
|
||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||
pub struct KeyID(pub u16);
|
||||
|
||||
/// Kind of key in the handshake
|
||||
#[derive(Clone)]
|
||||
#[allow(missing_debug_implementations)]
|
||||
pub enum PrivKey {
|
||||
/// Keys to be used only in key exchanges, not for signing
|
||||
Exchange(ExchangePrivKey),
|
||||
/// Keys to be used only for signing
|
||||
Signing,
|
||||
}
|
||||
|
||||
/// Ephemeral private keys
|
||||
#[derive(Clone)]
|
||||
#[allow(missing_debug_implementations)]
|
||||
pub enum ExchangePrivKey {
|
||||
/// X25519(Curve25519) used for key exchange
|
||||
X25519(::x25519_dalek::StaticSecret),
|
||||
}
|
||||
|
||||
impl ExchangePrivKey {
|
||||
/// Run the key exchange between two keys of the same kind
|
||||
pub fn key_exchange(
|
||||
&self,
|
||||
pub_key: ExchangePubKey,
|
||||
) -> Result<[u8; 32], Error> {
|
||||
todo!();
|
||||
}
|
||||
}
|
||||
|
||||
/// Kind of key in the key exchange
|
||||
#[derive(Debug, Copy, Clone, ::num_derive::FromPrimitive)]
|
||||
#[repr(u8)]
|
||||
pub enum ExchangePubKeyKind {
|
||||
/// X25519 Public key
|
||||
X25519 = 0,
|
||||
}
|
||||
impl ExchangePubKeyKind {
|
||||
fn len(&self) -> usize {
|
||||
match self {
|
||||
// FIXME: 99% wrong size
|
||||
ExchangePubKeyKind::X25519 => {
|
||||
::ring::signature::ED25519_PUBLIC_KEY_LEN
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// all Ephemeral Public keys
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub enum ExchangePubKey {
|
||||
/// X25519(Curve25519) used for key exchange
|
||||
X25519(::x25519_dalek::PublicKey),
|
||||
}
|
||||
|
||||
impl ExchangePubKey {
|
||||
/// Load public key used for key exchange from it raw bytes
|
||||
/// The riesult is "unparsed" since we don't verify
|
||||
/// the actual key
|
||||
pub fn from_slice(raw: &[u8]) -> Result<(Self, usize), Error> {
|
||||
// FIXME: get *real* minimum key size
|
||||
const MIN_KEY_SIZE: usize = 8;
|
||||
if raw.len() < 1 + MIN_KEY_SIZE {
|
||||
return Err(Error::NotEnoughData);
|
||||
}
|
||||
match ExchangePubKeyKind::from_u8(raw[0]) {
|
||||
Some(kind) => match kind {
|
||||
ExchangePubKeyKind::X25519 => {
|
||||
let pub_key: ::x25519_dalek::PublicKey =
|
||||
match ::bincode::deserialize(&raw[1..(1 + kind.len())])
|
||||
{
|
||||
Ok(pub_key) => pub_key,
|
||||
Err(_) => return Err(Error::Parsing),
|
||||
};
|
||||
Ok((ExchangePubKey::X25519(pub_key), kind.len()))
|
||||
}
|
||||
},
|
||||
None => {
|
||||
return Err(Error::Parsing);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
//! Common errors when handling cryptographic functions
|
||||
|
||||
/// Crypto errors
|
||||
#[derive(::thiserror::Error, Debug, Copy, Clone)]
|
||||
pub enum Error {
|
||||
/// Error while parsing key material
|
||||
#[error("can't parse key")]
|
||||
Parsing,
|
||||
/// Not enough data
|
||||
#[error("not enough data")]
|
||||
NotEnoughData,
|
||||
/// Wrong Key type found.
|
||||
/// You might have passed rsa keys where x25519 was expected
|
||||
#[error("wrong key type")]
|
||||
WrongKey,
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
//! Public or symmetric key wrappers, nonces and all that magic stuff
|
||||
|
||||
pub mod asym;
|
||||
mod errors;
|
||||
pub mod sym;
|
||||
|
||||
pub use errors::Error;
|
|
@ -0,0 +1,75 @@
|
|||
//! Symmetric cypher stuff
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
#[repr(C)]
|
||||
#[allow(missing_debug_implementations)]
|
||||
struct NonceNum {
|
||||
high: u32,
|
||||
low: u64,
|
||||
}
|
||||
/// Nonce with sequence for chach20_apoly1305
|
||||
#[repr(C)]
|
||||
pub union Nonce {
|
||||
num: NonceNum,
|
||||
raw: ::core::mem::ManuallyDrop<::ring::aead::Nonce>,
|
||||
easy_from: [u8; 12],
|
||||
}
|
||||
|
||||
impl ::core::fmt::Debug for Nonce {
|
||||
fn fmt(
|
||||
&self,
|
||||
f: &mut core::fmt::Formatter<'_>,
|
||||
) -> Result<(), ::std::fmt::Error> {
|
||||
// use the Debug from NonceNum
|
||||
#[allow(unsafe_code)]
|
||||
unsafe {
|
||||
core::fmt::Debug::fmt(&self.num, f)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Nonce {
|
||||
// FIXME: nonces should be random!
|
||||
/// Generate a new random Nonce
|
||||
pub fn new() -> Self {
|
||||
#[allow(unsafe_code)]
|
||||
unsafe {
|
||||
Self {
|
||||
num: NonceNum { high: 42, low: 69 },
|
||||
}
|
||||
}
|
||||
}
|
||||
/// Create Nonce from array
|
||||
pub fn from_slice(raw: [u8; 12]) -> Self {
|
||||
#[allow(unsafe_code)]
|
||||
unsafe {
|
||||
Self { easy_from: raw }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//impl Copy for Nonce {}
|
||||
impl Clone for Nonce {
|
||||
fn clone(&self) -> Self {
|
||||
#[allow(unsafe_code)]
|
||||
unsafe {
|
||||
Self { num: self.num }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ::ring::aead::NonceSequence for Nonce {
|
||||
fn advance(
|
||||
&mut self,
|
||||
) -> Result<::ring::aead::Nonce, ::ring::error::Unspecified> {
|
||||
#[allow(unsafe_code)]
|
||||
unsafe {
|
||||
let old_low = self.num.low;
|
||||
self.num.low = self.num.low + 1;
|
||||
if self.num.low < old_low {
|
||||
self.num.high = self.num.high;
|
||||
}
|
||||
Ok(::core::mem::ManuallyDrop::take(&mut self.raw))
|
||||
}
|
||||
}
|
||||
}
|
99
src/lib.rs
99
src/lib.rs
|
@ -13,13 +13,17 @@
|
|||
//!
|
||||
//! libFenrir is the official rust library implementing the Fenrir protocol
|
||||
|
||||
mod config;
|
||||
pub mod connection;
|
||||
pub mod dnssec;
|
||||
pub mod enc;
|
||||
|
||||
use ::arc_swap::{ArcSwap, ArcSwapAny};
|
||||
use ::std::{net::SocketAddr, sync::Arc};
|
||||
use ::tokio::{net::UdpSocket, task::JoinHandle};
|
||||
|
||||
mod config;
|
||||
pub use config::Config;
|
||||
pub mod connection;
|
||||
pub mod dnssec;
|
||||
use connection::handshake::{self, Handshake, HandshakeKey};
|
||||
|
||||
/// Main fenrir library errors
|
||||
#[derive(::thiserror::Error, Debug)]
|
||||
|
@ -35,7 +39,66 @@ pub enum Error {
|
|||
Dnssec(#[from] dnssec::Error),
|
||||
/// Handshake errors
|
||||
#[error("Handshake: {0:?}")]
|
||||
Handshake(#[from] connection::handshake::Error),
|
||||
Handshake(#[from] handshake::Error),
|
||||
}
|
||||
|
||||
struct FenrirInner {
|
||||
keys: ArcSwapAny<Arc<Vec<HandshakeKey>>>,
|
||||
}
|
||||
|
||||
impl FenrirInner {
|
||||
fn recv_handshake(&self, handshake: Handshake) -> Result<(), Error> {
|
||||
use connection::handshake::{dirsync::DirSync, HandshakeData};
|
||||
match handshake.data {
|
||||
HandshakeData::DirSync(ds) => match ds {
|
||||
DirSync::Req(req) => {
|
||||
let ephemeral_key = {
|
||||
// Keep this block short to avoid contention
|
||||
// on self.keys
|
||||
let keys = self.keys.load();
|
||||
if let Some(h_k) =
|
||||
keys.iter().find(|k| k.id == req.key_id)
|
||||
{
|
||||
use enc::asym::PrivKey;
|
||||
// Directory synchronized can only used keys
|
||||
// for key exchange, not signing keys
|
||||
if let PrivKey::Exchange(k) = &h_k.key {
|
||||
Some(k.clone())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
};
|
||||
if ephemeral_key.is_none() {
|
||||
::tracing::debug!("No such key id: {:?}", req.key_id);
|
||||
return Err(handshake::Error::UnknownKeyID.into());
|
||||
}
|
||||
let ephemeral_key = ephemeral_key.unwrap();
|
||||
let shared_key = match ephemeral_key
|
||||
.key_exchange(req.exchange_key)
|
||||
{
|
||||
Ok(shared_key) => shared_key,
|
||||
Err(e) => return Err(handshake::Error::Key(e).into()),
|
||||
};
|
||||
use crate::enc::sym::Nonce;
|
||||
use ::ring::aead::{self, BoundKey};
|
||||
let alg = aead::UnboundKey::new(
|
||||
&aead::CHACHA20_POLY1305,
|
||||
&shared_key,
|
||||
)
|
||||
.unwrap();
|
||||
let nonce = Nonce::new();
|
||||
let chacha = aead::OpeningKey::new(alg, nonce);
|
||||
todo!();
|
||||
}
|
||||
DirSync::Resp(resp) => {
|
||||
todo!();
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Instance of a fenrir endpoint
|
||||
|
@ -49,6 +112,8 @@ pub struct Fenrir {
|
|||
dnssec: Option<dnssec::Dnssec>,
|
||||
/// Broadcast channel to tell workers to stop working
|
||||
stop_working: ::tokio::sync::broadcast::Sender<bool>,
|
||||
/// Private keys used in the handshake
|
||||
_inner: Arc<FenrirInner>,
|
||||
}
|
||||
|
||||
// TODO: graceful vs immediate stop
|
||||
|
@ -69,6 +134,9 @@ impl Fenrir {
|
|||
sockets: Vec::with_capacity(listen_num),
|
||||
dnssec: None,
|
||||
stop_working: sender,
|
||||
_inner: Arc::new(FenrirInner {
|
||||
keys: ArcSwapAny::new(Arc::new(Vec::new())),
|
||||
}),
|
||||
};
|
||||
Ok(endpoint)
|
||||
}
|
||||
|
@ -162,6 +230,7 @@ impl Fenrir {
|
|||
/// Run a dedicated loop to read packets on the listening socket
|
||||
async fn listen_udp(
|
||||
mut stop_working: ::tokio::sync::broadcast::Receiver<bool>,
|
||||
fenrir: Arc<FenrirInner>,
|
||||
socket: Arc<UdpSocket>,
|
||||
) -> ::std::io::Result<()> {
|
||||
// jumbo frames are 9K max
|
||||
|
@ -175,7 +244,7 @@ impl Fenrir {
|
|||
result?
|
||||
}
|
||||
};
|
||||
Self::recv(&buffer[0..bytes], sock_from).await;
|
||||
Self::recv(fenrir.clone(), &buffer[0..bytes], sock_from).await;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
@ -195,6 +264,7 @@ impl Fenrir {
|
|||
let stop_working = self.stop_working.subscribe();
|
||||
let join = ::tokio::spawn(Self::listen_udp(
|
||||
stop_working,
|
||||
self._inner.clone(),
|
||||
s.clone(),
|
||||
));
|
||||
self.sockets.push((s, join));
|
||||
|
@ -223,14 +293,29 @@ impl Fenrir {
|
|||
|
||||
const MIN_PACKET_BYTES: usize = 8;
|
||||
/// Read and do stuff with the udp packet
|
||||
async fn recv(buffer: &[u8], _sock_from: SocketAddr) {
|
||||
async fn recv(
|
||||
fenrir: Arc<FenrirInner>,
|
||||
buffer: &[u8],
|
||||
_sock_from: SocketAddr,
|
||||
) {
|
||||
if buffer.len() < Self::MIN_PACKET_BYTES {
|
||||
return;
|
||||
}
|
||||
use connection::ID;
|
||||
let raw_id: [u8; 8] = buffer.try_into().expect("unreachable");
|
||||
if ID::from(raw_id).is_handshake() {
|
||||
todo!();
|
||||
use connection::handshake::Handshake;
|
||||
let handshake = match Handshake::parse(&buffer[8..]) {
|
||||
Ok(handshake) => handshake,
|
||||
Err(e) => {
|
||||
::tracing::warn!("Handshake parsing: {}", e);
|
||||
return;
|
||||
}
|
||||
};
|
||||
if let Err(err) = fenrir.recv_handshake(handshake) {
|
||||
::tracing::debug!("Handshake recv error {}", err);
|
||||
return;
|
||||
}
|
||||
}
|
||||
// copy packet, spawn
|
||||
todo!();
|
||||
|
|
Loading…
Reference in New Issue