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]
|
[dependencies]
|
||||||
# please keep these in alphabetical order
|
# 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 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" }
|
base85 = { git = "https://gitlab.com/darkwyrm/base85", rev = "d98efbfd171dd9ba48e30a5c88f94db92fc7b3c6" }
|
||||||
futures = { version = "^0.3" }
|
futures = { version = "^0.3" }
|
||||||
|
@ -33,6 +34,7 @@ libc = { version = "^0.2" }
|
||||||
num-traits = { version = "^0.2" }
|
num-traits = { version = "^0.2" }
|
||||||
num-derive = { version = "^0.3" }
|
num-derive = { version = "^0.3" }
|
||||||
ring = { version = "^0.16" }
|
ring = { version = "^0.16" }
|
||||||
|
bincode = { version = "^1.3" }
|
||||||
strum = { version = "^0.24" }
|
strum = { version = "^0.24" }
|
||||||
strum_macros = { version = "^0.24" }
|
strum_macros = { version = "^0.24" }
|
||||||
thiserror = { version = "^1.0" }
|
thiserror = { version = "^1.0" }
|
||||||
|
@ -43,6 +45,7 @@ tracing = { version = "^0.1" }
|
||||||
trust-dns-resolver = { version = "^0.22", features = [ "dnssec-ring" ] }
|
trust-dns-resolver = { version = "^0.22", features = [ "dnssec-ring" ] }
|
||||||
trust-dns-client = { version = "^0.22", features = [ "dnssec" ] }
|
trust-dns-client = { version = "^0.22", features = [ "dnssec" ] }
|
||||||
trust-dns-proto = { version = "^0.22" }
|
trust-dns-proto = { version = "^0.22" }
|
||||||
|
x25519-dalek = { version = "^1.2", features = [ "serde" ] }
|
||||||
|
|
||||||
[profile.dev]
|
[profile.dev]
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,10 @@
|
||||||
//! change the DNSSEC public/private keys
|
//! change the DNSSEC public/private keys
|
||||||
|
|
||||||
use super::{Error, HandshakeData};
|
use super::{Error, HandshakeData};
|
||||||
use crate::connection::{KeyID, UnparsedExchangePubKey, ID};
|
use crate::{
|
||||||
|
connection::ID,
|
||||||
|
enc::asym::{ExchangePubKey, KeyID},
|
||||||
|
};
|
||||||
use ::std::vec::Vec;
|
use ::std::vec::Vec;
|
||||||
|
|
||||||
/// Parsed handshake
|
/// Parsed handshake
|
||||||
|
@ -25,11 +28,11 @@ pub enum DirSync {
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Req {
|
pub struct Req {
|
||||||
/// Id of the server key used for the key exchange
|
/// 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
|
/// Client ephemeral public key used for key exchanges
|
||||||
exchange_key: UnparsedExchangePubKey,
|
pub exchange_key: ExchangePubKey,
|
||||||
/// encrypted data
|
/// encrypted data
|
||||||
enc: Vec<u8>,
|
pub enc: Vec<u8>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl super::HandshakeParsing for Req {
|
impl super::HandshakeParsing for Req {
|
||||||
|
@ -40,10 +43,9 @@ impl super::HandshakeParsing for Req {
|
||||||
}
|
}
|
||||||
let key_id: KeyID =
|
let key_id: KeyID =
|
||||||
KeyID(u16::from_le_bytes(raw[0..1].try_into().unwrap()));
|
KeyID(u16::from_le_bytes(raw[0..1].try_into().unwrap()));
|
||||||
let (exchange_key, len) =
|
let (exchange_key, len) = match ExchangePubKey::from_slice(&raw[2..]) {
|
||||||
match UnparsedExchangePubKey::from_raw(&raw[2..]) {
|
|
||||||
Ok(exchange_key) => exchange_key,
|
Ok(exchange_key) => exchange_key,
|
||||||
Err(e) => return Err(e),
|
Err(e) => return Err(e.into()),
|
||||||
};
|
};
|
||||||
let enc = raw[(2 + len)..].to_vec();
|
let enc = raw[(2 + len)..].to_vec();
|
||||||
Ok(HandshakeData::DirSync(DirSync::Req(Self {
|
Ok(HandshakeData::DirSync(DirSync::Req(Self {
|
||||||
|
@ -58,13 +60,13 @@ impl super::HandshakeParsing for Req {
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
pub struct ReqData {
|
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
|
||||||
nonce: [u8; 16],
|
pub nonce: [u8; 16],
|
||||||
/// Client key id so the client can use and rotate keys
|
/// Client key id so the client can use and rotate keys
|
||||||
client_key_id: KeyID,
|
pub client_key_id: KeyID,
|
||||||
/// Authentication token
|
/// Authentication token
|
||||||
token: [u8; 32],
|
pub token: [u8; 32],
|
||||||
/// Receiving connection id for the client
|
/// Receiving connection id for the client
|
||||||
id: ID,
|
pub id: ID,
|
||||||
// TODO: service info
|
// TODO: service info
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -72,9 +74,9 @@ pub struct ReqData {
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Resp {
|
pub struct Resp {
|
||||||
/// Tells the client with which key the exchange was done
|
/// Tells the client with which key the exchange was done
|
||||||
client_key_id: KeyID,
|
pub client_key_id: KeyID,
|
||||||
/// encrypted data
|
/// encrypted data
|
||||||
enc: Vec<u8>,
|
pub enc: Vec<u8>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl super::HandshakeParsing for Resp {
|
impl super::HandshakeParsing for Resp {
|
||||||
|
|
|
@ -4,7 +4,7 @@ pub mod dirsync;
|
||||||
|
|
||||||
use ::num_traits::FromPrimitive;
|
use ::num_traits::FromPrimitive;
|
||||||
|
|
||||||
use crate::connection::ProtocolVersion;
|
use crate::connection::{self, ProtocolVersion};
|
||||||
|
|
||||||
/// Handshake errors
|
/// Handshake errors
|
||||||
#[derive(::thiserror::Error, Debug, Copy, Clone)]
|
#[derive(::thiserror::Error, Debug, Copy, Clone)]
|
||||||
|
@ -13,11 +13,22 @@ pub enum Error {
|
||||||
/// TODO: more detailed parsing errors
|
/// TODO: more detailed parsing errors
|
||||||
#[error("not an handshake packet")]
|
#[error("not an handshake packet")]
|
||||||
Parsing,
|
Parsing,
|
||||||
|
/// No such Key ID
|
||||||
|
#[error("unknown key id")]
|
||||||
|
UnknownKeyID,
|
||||||
|
/// Key error
|
||||||
|
#[error("key: {0:?}")]
|
||||||
|
Key(#[from] crate::enc::Error),
|
||||||
/// Not enough data
|
/// Not enough data
|
||||||
#[error("not enough data")]
|
#[error("not enough data")]
|
||||||
NotEnoughData,
|
NotEnoughData,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) struct HandshakeKey {
|
||||||
|
pub id: crate::enc::asym::KeyID,
|
||||||
|
pub key: crate::enc::asym::PrivKey,
|
||||||
|
}
|
||||||
|
|
||||||
/// Parsed handshake
|
/// Parsed handshake
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum HandshakeData {
|
pub enum HandshakeData {
|
||||||
|
@ -49,9 +60,9 @@ pub enum Kind {
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Handshake {
|
pub struct Handshake {
|
||||||
/// Fenrir Protocol version
|
/// Fenrir Protocol version
|
||||||
fenrir_version: ProtocolVersion,
|
pub fenrir_version: ProtocolVersion,
|
||||||
/// enum for the parsed data
|
/// enum for the parsed data
|
||||||
data: HandshakeData,
|
pub data: HandshakeData,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Handshake {
|
impl Handshake {
|
||||||
|
@ -69,11 +80,16 @@ impl Handshake {
|
||||||
Some(handshake_kind) => handshake_kind,
|
Some(handshake_kind) => handshake_kind,
|
||||||
None => return Err(Error::Parsing),
|
None => return Err(Error::Parsing),
|
||||||
};
|
};
|
||||||
let parsed = match handshake_kind {
|
let data = match handshake_kind {
|
||||||
Kind::DirSyncReq => dirsync::Req::parse(&raw[2..])?,
|
Kind::DirSyncReq => dirsync::Req::parse(&raw[2..])?,
|
||||||
Kind::DirSyncResp => dirsync::Resp::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!()
|
todo!()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,10 +10,6 @@ pub use handshake::Handshake;
|
||||||
pub use packet::ConnectionID as ID;
|
pub use packet::ConnectionID as ID;
|
||||||
pub use packet::Packet;
|
pub use packet::Packet;
|
||||||
|
|
||||||
/// Public key ID
|
|
||||||
#[derive(Debug, Copy, Clone)]
|
|
||||||
pub struct KeyID(u16);
|
|
||||||
|
|
||||||
/// Version of the fenrir protocol in use
|
/// Version of the fenrir protocol in use
|
||||||
#[derive(::num_derive::FromPrimitive, Debug, Copy, Clone)]
|
#[derive(::num_derive::FromPrimitive, Debug, Copy, Clone)]
|
||||||
#[repr(u8)]
|
#[repr(u8)]
|
||||||
|
@ -21,54 +17,3 @@ pub enum ProtocolVersion {
|
||||||
/// First Fenrir Protocol Version
|
/// First Fenrir Protocol Version
|
||||||
V0 = 0,
|
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
|
/// Public Key ID
|
||||||
#[derive(Debug, Copy, Clone)]
|
#[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;
|
type Error = ::std::io::Error;
|
||||||
fn try_from(raw: &str) -> Result<Self, Self::Error> {
|
fn try_from(raw: &str) -> Result<Self, Self::Error> {
|
||||||
if let Ok(id_u8) = raw.parse::<u8>() {
|
if let Ok(id_u8) = raw.parse::<u8>() {
|
||||||
return Ok(PublicKeyId(id_u8));
|
return Ok(PublicKeyID(id_u8));
|
||||||
}
|
}
|
||||||
return Err(::std::io::Error::new(
|
return Err(::std::io::Error::new(
|
||||||
::std::io::ErrorKind::InvalidData,
|
::std::io::ErrorKind::InvalidData,
|
||||||
|
@ -95,7 +95,7 @@ pub struct PublicKey {
|
||||||
/// type of public key
|
/// type of public key
|
||||||
pub kind: PublicKeyType,
|
pub kind: PublicKeyType,
|
||||||
/// id of public key
|
/// id of public key
|
||||||
pub id: PublicKeyId,
|
pub id: PublicKeyID,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PublicKey {
|
impl PublicKey {
|
||||||
|
@ -114,7 +114,7 @@ impl PublicKey {
|
||||||
}
|
}
|
||||||
|
|
||||||
let kind = PublicKeyType::from_u8(raw[0]).unwrap();
|
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() {
|
if raw.len() < 2 + kind.key_len() {
|
||||||
return Err(Error::NotEnoughData(2));
|
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:
|
/// Authentication server address information:
|
||||||
/// * ip
|
/// * ip
|
||||||
/// * udp port
|
/// * udp port
|
||||||
/// * priority
|
/// * priority
|
||||||
/// * weight within priority
|
/// * weight within priority
|
||||||
#[derive(Debug, Clone, Copy)]
|
/// * list of supported handshakes IDs
|
||||||
|
/// * list of public keys IDs
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
pub struct Address {
|
pub struct Address {
|
||||||
/// Ip address of server, v4 or v6
|
/// Ip address of server, v4 or v6
|
||||||
pub ip: IpAddr,
|
pub ip: IpAddr,
|
||||||
|
@ -246,14 +279,18 @@ pub struct Address {
|
||||||
pub priority: AddressPriority,
|
pub priority: AddressPriority,
|
||||||
/// 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
|
||||||
|
pub handshake_ids: Vec<HandshakeID>,
|
||||||
/// Public key IDs used by this address
|
/// Public key IDs used by this address
|
||||||
pub public_key_id: PublicKeyId,
|
pub public_key_ids: Vec<PublicKeyID>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Address {
|
impl Address {
|
||||||
fn raw_len(&self) -> usize {
|
fn raw_len(&self) -> usize {
|
||||||
let size = 4; // UDP port + Priority + Weight
|
// UDP port + Priority + Weight + pubkey_len + handshake_len
|
||||||
match self.ip {
|
let mut size = 6;
|
||||||
|
size = size + self.public_key_ids.len() + self.handshake_ids.len();
|
||||||
|
size + match self.ip {
|
||||||
IpAddr::V4(_) => size + 4,
|
IpAddr::V4(_) => size + 4,
|
||||||
IpAddr::V6(_) => size + 16,
|
IpAddr::V6(_) => size + 16,
|
||||||
}
|
}
|
||||||
|
@ -270,7 +307,6 @@ impl Address {
|
||||||
|
|
||||||
raw.push(bitfield);
|
raw.push(bitfield);
|
||||||
|
|
||||||
raw.push(self.public_key_id.0);
|
|
||||||
raw.extend_from_slice(
|
raw.extend_from_slice(
|
||||||
&(match self.port {
|
&(match self.port {
|
||||||
Some(port) => port.get().to_le_bytes(),
|
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 {
|
match self.ip {
|
||||||
IpAddr::V4(ip) => {
|
IpAddr::V4(ip) => {
|
||||||
let raw_ip = ip.octets();
|
let raw_ip = ip.octets();
|
||||||
|
@ -290,7 +331,7 @@ impl Address {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
fn decode_raw(raw: &[u8]) -> Result<(Self, usize), Error> {
|
fn decode_raw(raw: &[u8]) -> Result<(Self, usize), Error> {
|
||||||
if raw.len() < 8 {
|
if raw.len() < 10 {
|
||||||
return Err(Error::NotEnoughData(0));
|
return Err(Error::NotEnoughData(0));
|
||||||
}
|
}
|
||||||
let ip_type = raw[0] >> 6;
|
let ip_type = raw[0] >> 6;
|
||||||
|
@ -315,19 +356,60 @@ impl Address {
|
||||||
let priority = AddressPriority::from_u8(raw_priority).unwrap();
|
let priority = AddressPriority::from_u8(raw_priority).unwrap();
|
||||||
let weight = AddressWeight::from_u8(raw_weight).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 {
|
let port = if raw_port == 0 {
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
Some(NonZeroU16::new(raw_port).unwrap())
|
Some(NonZeroU16::new(raw_port).unwrap())
|
||||||
};
|
};
|
||||||
let ip = if is_ipv6 {
|
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)
|
IpAddr::from(raw_ip)
|
||||||
} else {
|
} 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)
|
IpAddr::from(raw_ip)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -337,7 +419,8 @@ impl Address {
|
||||||
port,
|
port,
|
||||||
priority,
|
priority,
|
||||||
weight,
|
weight,
|
||||||
public_key_id,
|
public_key_ids,
|
||||||
|
handshake_ids,
|
||||||
},
|
},
|
||||||
total_length,
|
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
|
//! 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 ::std::{net::SocketAddr, sync::Arc};
|
||||||
use ::tokio::{net::UdpSocket, task::JoinHandle};
|
use ::tokio::{net::UdpSocket, task::JoinHandle};
|
||||||
|
|
||||||
mod config;
|
|
||||||
pub use config::Config;
|
pub use config::Config;
|
||||||
pub mod connection;
|
use connection::handshake::{self, Handshake, HandshakeKey};
|
||||||
pub mod dnssec;
|
|
||||||
|
|
||||||
/// Main fenrir library errors
|
/// Main fenrir library errors
|
||||||
#[derive(::thiserror::Error, Debug)]
|
#[derive(::thiserror::Error, Debug)]
|
||||||
|
@ -35,7 +39,66 @@ pub enum Error {
|
||||||
Dnssec(#[from] dnssec::Error),
|
Dnssec(#[from] dnssec::Error),
|
||||||
/// Handshake errors
|
/// Handshake errors
|
||||||
#[error("Handshake: {0:?}")]
|
#[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
|
/// Instance of a fenrir endpoint
|
||||||
|
@ -49,6 +112,8 @@ pub struct Fenrir {
|
||||||
dnssec: Option<dnssec::Dnssec>,
|
dnssec: Option<dnssec::Dnssec>,
|
||||||
/// Broadcast channel to tell workers to stop working
|
/// Broadcast channel to tell workers to stop working
|
||||||
stop_working: ::tokio::sync::broadcast::Sender<bool>,
|
stop_working: ::tokio::sync::broadcast::Sender<bool>,
|
||||||
|
/// Private keys used in the handshake
|
||||||
|
_inner: Arc<FenrirInner>,
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: graceful vs immediate stop
|
// TODO: graceful vs immediate stop
|
||||||
|
@ -69,6 +134,9 @@ impl Fenrir {
|
||||||
sockets: Vec::with_capacity(listen_num),
|
sockets: Vec::with_capacity(listen_num),
|
||||||
dnssec: None,
|
dnssec: None,
|
||||||
stop_working: sender,
|
stop_working: sender,
|
||||||
|
_inner: Arc::new(FenrirInner {
|
||||||
|
keys: ArcSwapAny::new(Arc::new(Vec::new())),
|
||||||
|
}),
|
||||||
};
|
};
|
||||||
Ok(endpoint)
|
Ok(endpoint)
|
||||||
}
|
}
|
||||||
|
@ -162,6 +230,7 @@ impl Fenrir {
|
||||||
/// Run a dedicated loop to read packets on the listening socket
|
/// Run a dedicated loop to read packets on the listening socket
|
||||||
async fn listen_udp(
|
async fn listen_udp(
|
||||||
mut stop_working: ::tokio::sync::broadcast::Receiver<bool>,
|
mut stop_working: ::tokio::sync::broadcast::Receiver<bool>,
|
||||||
|
fenrir: Arc<FenrirInner>,
|
||||||
socket: Arc<UdpSocket>,
|
socket: Arc<UdpSocket>,
|
||||||
) -> ::std::io::Result<()> {
|
) -> ::std::io::Result<()> {
|
||||||
// jumbo frames are 9K max
|
// jumbo frames are 9K max
|
||||||
|
@ -175,7 +244,7 @@ impl Fenrir {
|
||||||
result?
|
result?
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
Self::recv(&buffer[0..bytes], sock_from).await;
|
Self::recv(fenrir.clone(), &buffer[0..bytes], sock_from).await;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -195,6 +264,7 @@ impl Fenrir {
|
||||||
let stop_working = self.stop_working.subscribe();
|
let stop_working = self.stop_working.subscribe();
|
||||||
let join = ::tokio::spawn(Self::listen_udp(
|
let join = ::tokio::spawn(Self::listen_udp(
|
||||||
stop_working,
|
stop_working,
|
||||||
|
self._inner.clone(),
|
||||||
s.clone(),
|
s.clone(),
|
||||||
));
|
));
|
||||||
self.sockets.push((s, join));
|
self.sockets.push((s, join));
|
||||||
|
@ -223,14 +293,29 @@ impl Fenrir {
|
||||||
|
|
||||||
const MIN_PACKET_BYTES: usize = 8;
|
const MIN_PACKET_BYTES: usize = 8;
|
||||||
/// Read and do stuff with the udp packet
|
/// 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 {
|
if buffer.len() < Self::MIN_PACKET_BYTES {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
use connection::ID;
|
use connection::ID;
|
||||||
let raw_id: [u8; 8] = buffer.try_into().expect("unreachable");
|
let raw_id: [u8; 8] = buffer.try_into().expect("unreachable");
|
||||||
if ID::from(raw_id).is_handshake() {
|
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
|
// copy packet, spawn
|
||||||
todo!();
|
todo!();
|
||||||
|
|
Loading…
Reference in New Issue