chacha20poly1305 decryption

Signed-off-by: Luca Fulchir <luca.fulchir@runesauth.com>
This commit is contained in:
Luca Fulchir 2023-02-17 23:09:49 +01:00
parent a39767d32b
commit 1d5316c738
Signed by: luca.fulchir
GPG Key ID: 8F6440603D13A78E
4 changed files with 179 additions and 62 deletions

View File

@ -29,6 +29,7 @@ crate_type = [ "lib", "cdylib", "staticlib" ]
arc-swap = { version = "1.6" } 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" }
chacha20poly1305 = { version = "0.10" }
futures = { version = "0.3" } futures = { version = "0.3" }
hkdf = { version = "0.12" } hkdf = { version = "0.12" }
libc = { version = "0.2" } libc = { version = "0.2" }
@ -47,11 +48,11 @@ 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" ] } # don't use stable dalek. forces zeroize 1.3,
# NOTE: can not force newer versions of zeroize: # breaks our and chacha20poly1305
# * zeroize is a dependency of x25519-dalek # reason: zeroize is not pure rust,
# * zeroize is not *pure* rust, but uses `alloc` # so we can't have multiple versions of if
# * rust can have multiple versions as deps, but only if the are pure rust x25519-dalek = { version = "2.0.0-pre.1", features = [ "serde" ] }
zeroize = { version = "1" } zeroize = { version = "1" }
[profile.dev] [profile.dev]

View File

@ -8,9 +8,9 @@ use ::zeroize::Zeroize;
use crate::enc::sym::Secret; use crate::enc::sym::Secret;
// Hack & tricks: // Hack & tricks:
// HKDF are pretty important, but they don't zero out the data. // HKDF are pretty important, but this lib don't zero out the data.
// we can't user #[derive(Zeroing)] either. // we can't use #[derive(Zeroing)] either.
// So we craete a union with a Zeroing object, and drop manually both. // So we craete a union with a Zeroing object, and drop both manually.
#[derive(Zeroize)] #[derive(Zeroize)]
#[zeroize(drop)] #[zeroize(drop)]
@ -34,14 +34,29 @@ impl Drop for HkdfInner {
/// Sha3 based HKDF /// Sha3 based HKDF
#[allow(missing_debug_implementations)] #[allow(missing_debug_implementations)]
pub struct HkdfSha3 { pub struct HkdfSha3 {
_inner: Hkdf<Sha3_256>, inner: HkdfInner,
} }
impl HkdfSha3 { impl HkdfSha3 {
/// Instantiate a new HKDF with Sha3-256 /// Instantiate a new HKDF with Sha3-256
pub fn new(salt: Option<&[u8]>, key: Secret) -> Self { pub fn new(salt: &[u8], key: Secret) -> Self {
let hkdf = Hkdf::<Sha3_256>::new(Some(salt), key.as_ref());
#[allow(unsafe_code)]
unsafe {
Self { Self {
_inner: Hkdf::<Sha3_256>::new(salt, key.as_ref()), inner: HkdfInner {
hkdf: ::core::mem::ManuallyDrop::new(hkdf),
},
} }
} }
} }
/// Get a secret generated from the key and a given context
pub fn get_secret(&self, context: &[u8]) -> Secret {
let mut out: [u8; 32] = [0; 32];
#[allow(unsafe_code)]
unsafe {
self.inner.hkdf.expand(context, &mut out);
}
out.into()
}
}

View File

@ -17,6 +17,15 @@ impl Secret {
&self.secret &self.secret
} }
} }
impl From<[u8; 32]> for Secret {
fn from(shared_secret: [u8; 32]) -> Self {
Self {
secret: shared_secret,
}
}
}
impl From<::x25519_dalek::SharedSecret> for Secret { impl From<::x25519_dalek::SharedSecret> for Secret {
fn from(shared_secret: ::x25519_dalek::SharedSecret) -> Self { fn from(shared_secret: ::x25519_dalek::SharedSecret) -> Self {
Self { Self {
@ -29,68 +38,160 @@ impl From<::x25519_dalek::SharedSecret> for Secret {
#[derive(Debug, Copy, Clone, PartialEq, ::num_derive::FromPrimitive)] #[derive(Debug, Copy, Clone, PartialEq, ::num_derive::FromPrimitive)]
#[repr(u8)] #[repr(u8)]
pub enum CipherKind { pub enum CipherKind {
/// Chaha20_Poly1305 /// XChaCha20_Poly1305
Chacha20Poly1305 = 0, XChaCha20Poly1305 = 0,
} }
impl CipherKind { impl CipherKind {
/// required length of the nonce /// required length of the nonce
pub fn nonce_len(&self) -> usize { pub fn nonce_len(&self) -> usize {
// TODO: how the hell do I take this from ::chacha20poly1305?
::ring::aead::CHACHA20_POLY1305.nonce_len() ::ring::aead::CHACHA20_POLY1305.nonce_len()
} }
/// required length of the key /// required length of the key
pub fn key_len(&self) -> usize { pub fn key_len(&self) -> usize {
::ring::aead::CHACHA20_POLY1305.key_len() use ::chacha20poly1305::KeySizeUser;
::chacha20poly1305::XChaCha20Poly1305::key_size()
} }
/// Length of the authentication tag /// Length of the authentication tag
pub fn tag_len(&self) -> usize { pub fn tag_len(&self) -> usize {
// TODO: how the hell do I take this from ::chacha20poly1305?
::ring::aead::CHACHA20_POLY1305.tag_len() ::ring::aead::CHACHA20_POLY1305.tag_len()
} }
} }
/// actual ciphers /// Additional Authenticated Data
#[derive(Debug)] #[derive(Debug)]
pub enum Cipher { pub struct AAD<'a>(pub &'a mut [u8]);
/// Cipher Chaha20_Poly1305
Chacha20Poly1305(Chacha20Poly1305), /// 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,
}
/// actual ciphers
enum Cipher {
/// Cipher XChaha20_Poly1305
XChaCha20Poly1305(XChaCha20Poly1305),
} }
impl Cipher { impl Cipher {
/// Build a new Cipher /// Build a new Cipher
pub fn new(kind: CipherKind, secret: Secret) -> Self { fn new(kind: CipherKind, secret: Secret) -> Self {
match kind { match kind {
CipherKind::Chacha20Poly1305 => { CipherKind::XChaCha20Poly1305 => {
Self::Chacha20Poly1305(Chacha20Poly1305::new(secret)) Self::XChaCha20Poly1305(XChaCha20Poly1305::new(secret))
}
}
}
fn nonce_len(&self) -> usize {
match self {
Cipher::XChaCha20Poly1305(_) => {
// TODO: how the hell do I take this from ::chacha20poly1305?
::ring::aead::CHACHA20_POLY1305.nonce_len()
}
}
}
pub fn tag_len(&self) -> usize {
match self {
Cipher::XChaCha20Poly1305(_) => {
// TODO: how the hell do I take this from ::chacha20poly1305?
::ring::aead::CHACHA20_POLY1305.tag_len()
}
}
}
fn decrypt(
&self,
nonce: Nonce,
aad: AAD,
data: &mut [u8],
) -> Result<(), ()> {
match self {
Cipher::XChaCha20Poly1305(cipher) => {
use ::chacha20poly1305::{
aead::generic_array::GenericArray, AeadInPlace,
};
let (data_notag, tag_bytes) = data.split_at_mut(
data.len() + 1 - ::ring::aead::CHACHA20_POLY1305.tag_len(),
);
let tag = GenericArray::from_slice(tag_bytes);
let maybe = cipher.cipher.decrypt_in_place_detached(
nonce.as_bytes().into(),
aad.0,
data_notag,
tag,
);
if maybe.is_err() {
Err(())
} else {
Ok(())
}
} }
} }
} }
} }
/// Chacha20Poly1305 cipher /// Receive only cipher
#[derive(Debug)] #[allow(missing_debug_implementations)]
pub struct Chacha20Poly1305 { pub struct CipherRecv(Cipher);
cipher: ::ring::aead::OpeningKey<Nonce>,
impl CipherRecv {
/// Build a new Cipher
pub fn new(kind: CipherKind, secret: Secret) -> Self {
Self(Cipher::new(kind, secret))
}
/// Get the length of the nonce for this cipher
pub fn nonce_len(&self) -> usize {
self.0.nonce_len()
}
} }
impl Chacha20Poly1305 { /// Send only cipher
/// Initialize the cipher with the given nonce #[allow(missing_debug_implementations)]
pub fn with_nonce(key: Secret, nonce: Nonce) -> Self { pub struct CipherSend {
let unbound_key = ::ring::aead::UnboundKey::new( nonce: Nonce,
&::ring::aead::CHACHA20_POLY1305, cipher: Cipher,
key.as_ref(), }
)
.unwrap(); impl CipherSend {
use ::ring::aead::BoundKey; /// Build a new Cipher
pub fn new(kind: CipherKind, secret: Secret) -> Self {
Self { Self {
cipher: ::ring::aead::OpeningKey::<Nonce>::new(unbound_key, nonce), nonce: Nonce::new(),
cipher: Cipher::new(kind, secret),
} }
} }
/// Initialize a new ChachaPoly20 cipher with a random nonce /// Get the current nonce as &[u8]
pub fn new(key: Secret) -> Self { pub fn nonce_as_bytes(&self) -> &[u8] {
Self::with_nonce(key, Nonce::new()) self.nonce.as_bytes()
} }
} }
/// XChaCha20Poly1305 cipher
struct XChaCha20Poly1305 {
cipher: ::chacha20poly1305::XChaCha20Poly1305,
}
impl XChaCha20Poly1305 {
/// Initialize a new ChaChaPoly20 cipher with a random nonce
fn new(key: Secret) -> Self {
use ::chacha20poly1305::{KeyInit, XChaCha20Poly1305};
Self {
cipher: XChaCha20Poly1305::new(key.as_ref().into()),
}
}
}
//
// TODO: For efficiency "Nonce" should become a reference.
//
#[derive(Debug, Copy, Clone)] #[derive(Debug, Copy, Clone)]
#[repr(C)] #[repr(C)]
#[allow(missing_debug_implementations)] #[allow(missing_debug_implementations)]
@ -99,13 +200,11 @@ struct NonceNum {
low: u64, low: u64,
} }
/// Nonce with sequence for chach20_apoly1305 /// Nonce with sequence for chach20_apoly1305
#[derive(Copy, Clone)]
#[repr(C)] #[repr(C)]
pub union Nonce { pub union Nonce {
num: NonceNum, num: NonceNum,
// ring::aead::Nonce does not implement 'Copy' raw: [u8; 12],
// even though it's just [u8;12]
raw: ::core::mem::ManuallyDrop<::ring::aead::Nonce>,
easy_from: [u8; 12],
} }
impl ::core::fmt::Debug for Nonce { impl ::core::fmt::Debug for Nonce {
@ -113,7 +212,6 @@ impl ::core::fmt::Debug for Nonce {
&self, &self,
f: &mut core::fmt::Formatter<'_>, f: &mut core::fmt::Formatter<'_>,
) -> Result<(), ::std::fmt::Error> { ) -> Result<(), ::std::fmt::Error> {
// use the Debug from NonceNum
#[allow(unsafe_code)] #[allow(unsafe_code)]
unsafe { unsafe {
core::fmt::Debug::fmt(&self.num, f) core::fmt::Debug::fmt(&self.num, f)
@ -128,32 +226,28 @@ impl Nonce {
#[allow(unsafe_code)] #[allow(unsafe_code)]
unsafe { unsafe {
Self { Self {
// chosen by a fair dice roll
// ahh, who am I kidding...
num: NonceNum { high: 42, low: 69 }, num: NonceNum { high: 42, low: 69 },
} }
} }
} }
/// Get reference to the nonce bytes
pub fn as_bytes(&self) -> &[u8] {
#[allow(unsafe_code)]
unsafe {
&self.raw
}
}
/// Create Nonce from array /// Create Nonce from array
pub fn from_slice(raw: [u8; 12]) -> Self { pub fn from_slice(raw: [u8; 12]) -> Self {
#[allow(unsafe_code)] #[allow(unsafe_code)]
unsafe { unsafe {
Self { easy_from: raw } Self { raw }
} }
} }
} /// Go to the next nonce
pub fn advance(&mut self) {
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)] #[allow(unsafe_code)]
unsafe { unsafe {
let old_low = self.num.low; let old_low = self.num.low;
@ -161,7 +255,6 @@ impl ::ring::aead::NonceSequence for Nonce {
if self.num.low < old_low { if self.num.low < old_low {
self.num.high = self.num.high; self.num.high = self.num.high;
} }
Ok(::core::mem::ManuallyDrop::take(&mut self.raw))
} }
} }
} }

View File

@ -22,7 +22,11 @@ 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};
use crate::enc::{asym, sym::CipherKind}; use crate::enc::{
asym,
hkdf::HkdfSha3,
sym::{CipherKind, CipherRecv, CipherSend},
};
pub use config::Config; pub use config::Config;
use connection::handshake::{self, Handshake, HandshakeKey}; use connection::handshake::{self, Handshake, HandshakeKey};
@ -106,6 +110,10 @@ impl FenrirInner {
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 = HkdfSha3::new(b"fenrir", shared_key);
let secret_in = hkdf.get_secret(b"to_server");
let cipher_in = CipherRecv::new(req.cipher, secret_in);
todo!(); todo!();
} }
DirSync::Resp(resp) => { DirSync::Resp(resp) => {