chacha20poly1305 decryption
Signed-off-by: Luca Fulchir <luca.fulchir@runesauth.com>
This commit is contained in:
parent
a39767d32b
commit
1d5316c738
11
Cargo.toml
11
Cargo.toml
|
@ -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]
|
||||||
|
|
|
@ -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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
191
src/enc/sym.rs
191
src/enc/sym.rs
|
@ -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))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
10
src/lib.rs
10
src/lib.rs
|
@ -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) => {
|
||||||
|
|
Loading…
Reference in New Issue