2023-02-16 18:11:45 +00:00
|
|
|
//! Symmetric cypher stuff
|
|
|
|
|
2023-02-21 21:06:17 +00:00
|
|
|
use super::Error;
|
2023-05-30 08:52:54 +00:00
|
|
|
use crate::enc::Random;
|
2023-02-17 13:59:02 +00:00
|
|
|
use ::zeroize::Zeroize;
|
|
|
|
|
|
|
|
/// Secret, used for keys.
|
|
|
|
/// Grants that on drop() we will zero out memory
|
2023-02-22 20:10:00 +00:00
|
|
|
#[derive(Zeroize, Clone)]
|
2023-02-17 13:59:02 +00:00
|
|
|
#[zeroize(drop)]
|
2023-02-22 11:30:00 +00:00
|
|
|
pub struct Secret([u8; 32]);
|
2023-02-22 20:10:00 +00:00
|
|
|
// Fake debug implementation to avoid leaking secrets
|
|
|
|
impl ::core::fmt::Debug for Secret {
|
|
|
|
fn fmt(
|
|
|
|
&self,
|
|
|
|
f: &mut core::fmt::Formatter<'_>,
|
|
|
|
) -> Result<(), ::std::fmt::Error> {
|
|
|
|
::core::fmt::Debug::fmt("[hidden secret]", f)
|
|
|
|
}
|
|
|
|
}
|
2023-02-17 13:59:02 +00:00
|
|
|
|
|
|
|
impl Secret {
|
2023-02-22 11:30:00 +00:00
|
|
|
/// New randomly generated secret
|
2023-05-30 08:52:54 +00:00
|
|
|
pub fn new_rand(rand: &Random) -> Self {
|
2023-02-22 11:30:00 +00:00
|
|
|
let mut ret = Self([0; 32]);
|
|
|
|
rand.fill(&mut ret.0);
|
|
|
|
ret
|
|
|
|
}
|
2023-02-17 13:59:02 +00:00
|
|
|
/// return a reference to the secret
|
|
|
|
pub fn as_ref(&self) -> &[u8; 32] {
|
2023-02-22 11:30:00 +00:00
|
|
|
&self.0
|
2023-02-17 13:59:02 +00:00
|
|
|
}
|
|
|
|
}
|
2023-02-17 22:09:49 +00:00
|
|
|
|
|
|
|
impl From<[u8; 32]> for Secret {
|
|
|
|
fn from(shared_secret: [u8; 32]) -> Self {
|
2023-02-22 11:30:00 +00:00
|
|
|
Self(shared_secret)
|
2023-02-17 22:09:49 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-02-17 13:59:02 +00:00
|
|
|
impl From<::x25519_dalek::SharedSecret> for Secret {
|
|
|
|
fn from(shared_secret: ::x25519_dalek::SharedSecret) -> Self {
|
2023-02-22 11:30:00 +00:00
|
|
|
Self(shared_secret.to_bytes())
|
2023-02-17 13:59:02 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// List of possible Ciphers
|
|
|
|
#[derive(Debug, Copy, Clone, PartialEq, ::num_derive::FromPrimitive)]
|
|
|
|
#[repr(u8)]
|
|
|
|
pub enum CipherKind {
|
2023-02-17 22:09:49 +00:00
|
|
|
/// XChaCha20_Poly1305
|
|
|
|
XChaCha20Poly1305 = 0,
|
2023-02-17 13:59:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl CipherKind {
|
2023-02-25 14:36:14 +00:00
|
|
|
/// length of the serialized id for the cipher kind field
|
|
|
|
pub fn len() -> usize {
|
|
|
|
1
|
|
|
|
}
|
2023-02-17 13:59:02 +00:00
|
|
|
/// required length of the nonce
|
2023-05-17 08:26:39 +00:00
|
|
|
pub fn nonce_len(&self) -> HeadLen {
|
2023-02-17 22:09:49 +00:00
|
|
|
// TODO: how the hell do I take this from ::chacha20poly1305?
|
2023-05-17 08:26:39 +00:00
|
|
|
HeadLen(Nonce::len())
|
2023-02-17 13:59:02 +00:00
|
|
|
}
|
|
|
|
/// required length of the key
|
|
|
|
pub fn key_len(&self) -> usize {
|
2023-02-22 20:10:00 +00:00
|
|
|
use ::chacha20poly1305::{KeySizeUser, XChaCha20Poly1305};
|
|
|
|
XChaCha20Poly1305::key_size()
|
2023-02-17 13:59:02 +00:00
|
|
|
}
|
|
|
|
/// Length of the authentication tag
|
2023-05-17 08:26:39 +00:00
|
|
|
pub fn tag_len(&self) -> TagLen {
|
2023-02-17 22:09:49 +00:00
|
|
|
// TODO: how the hell do I take this from ::chacha20poly1305?
|
2023-05-17 08:26:39 +00:00
|
|
|
TagLen(::ring::aead::CHACHA20_POLY1305.tag_len())
|
2023-02-17 13:59:02 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-02-17 22:09:49 +00:00
|
|
|
/// Additional Authenticated Data
|
2023-02-17 13:59:02 +00:00
|
|
|
#[derive(Debug)]
|
2023-02-17 22:09:49 +00:00
|
|
|
pub struct AAD<'a>(pub &'a mut [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,
|
|
|
|
}
|
|
|
|
|
2023-05-17 08:26:39 +00:00
|
|
|
/// strong typedef for header length
|
|
|
|
/// aka: nonce length in the encrypted data)
|
|
|
|
#[derive(Debug, Copy, Clone)]
|
|
|
|
pub struct HeadLen(pub usize);
|
|
|
|
/// strong typedef for the Tag length
|
|
|
|
/// aka: cryptographic authentication tag length at the end
|
|
|
|
/// of the encrypted data
|
|
|
|
#[derive(Debug, Copy, Clone)]
|
|
|
|
pub struct TagLen(pub usize);
|
|
|
|
|
2023-02-17 22:09:49 +00:00
|
|
|
/// actual ciphers
|
|
|
|
enum Cipher {
|
|
|
|
/// Cipher XChaha20_Poly1305
|
|
|
|
XChaCha20Poly1305(XChaCha20Poly1305),
|
2023-02-17 13:59:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Cipher {
|
|
|
|
/// Build a new Cipher
|
2023-02-17 22:09:49 +00:00
|
|
|
fn new(kind: CipherKind, secret: Secret) -> Self {
|
2023-02-17 13:59:02 +00:00
|
|
|
match kind {
|
2023-02-17 22:09:49 +00:00
|
|
|
CipherKind::XChaCha20Poly1305 => {
|
|
|
|
Self::XChaCha20Poly1305(XChaCha20Poly1305::new(secret))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2023-05-26 13:02:21 +00:00
|
|
|
pub fn kind(&self) -> CipherKind {
|
|
|
|
match self {
|
|
|
|
Cipher::XChaCha20Poly1305(_) => CipherKind::XChaCha20Poly1305,
|
|
|
|
}
|
|
|
|
}
|
2023-05-17 08:26:39 +00:00
|
|
|
fn nonce_len(&self) -> HeadLen {
|
2023-02-17 22:09:49 +00:00
|
|
|
match self {
|
|
|
|
Cipher::XChaCha20Poly1305(_) => {
|
|
|
|
// TODO: how the hell do I take this from ::chacha20poly1305?
|
2023-05-17 08:26:39 +00:00
|
|
|
HeadLen(::ring::aead::CHACHA20_POLY1305.nonce_len())
|
2023-02-17 22:09:49 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2023-05-17 08:26:39 +00:00
|
|
|
fn tag_len(&self) -> TagLen {
|
2023-02-17 22:09:49 +00:00
|
|
|
match self {
|
|
|
|
Cipher::XChaCha20Poly1305(_) => {
|
|
|
|
// TODO: how the hell do I take this from ::chacha20poly1305?
|
2023-05-17 08:26:39 +00:00
|
|
|
TagLen(::ring::aead::CHACHA20_POLY1305.tag_len())
|
2023-02-17 22:09:49 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2023-05-17 08:26:39 +00:00
|
|
|
fn decrypt<'a>(
|
|
|
|
&self,
|
|
|
|
aad: AAD,
|
|
|
|
raw_data: &'a mut [u8],
|
|
|
|
) -> Result<&'a [u8], Error> {
|
2023-02-17 22:09:49 +00:00
|
|
|
match self {
|
|
|
|
Cipher::XChaCha20Poly1305(cipher) => {
|
|
|
|
use ::chacha20poly1305::{
|
|
|
|
aead::generic_array::GenericArray, AeadInPlace,
|
|
|
|
};
|
2023-05-17 08:26:39 +00:00
|
|
|
let final_len: usize = {
|
2023-02-21 21:06:17 +00:00
|
|
|
// FIXME: check min data length
|
|
|
|
let (nonce_bytes, data_and_tag) = raw_data.split_at_mut(13);
|
|
|
|
let (data_notag, tag_bytes) = data_and_tag.split_at_mut(
|
|
|
|
data_and_tag.len() + 1
|
|
|
|
- ::ring::aead::CHACHA20_POLY1305.tag_len(),
|
|
|
|
);
|
|
|
|
let nonce = GenericArray::from_slice(nonce_bytes);
|
|
|
|
let tag = GenericArray::from_slice(tag_bytes);
|
|
|
|
let maybe = cipher.cipher.decrypt_in_place_detached(
|
|
|
|
nonce.into(),
|
|
|
|
aad.0,
|
|
|
|
data_notag,
|
|
|
|
tag,
|
|
|
|
);
|
|
|
|
if maybe.is_err() {
|
|
|
|
return Err(Error::Decrypt);
|
|
|
|
}
|
2023-05-17 08:26:39 +00:00
|
|
|
data_notag.len()
|
|
|
|
};
|
|
|
|
//data.drain(..Nonce::len());
|
|
|
|
//data.truncate(final_len);
|
|
|
|
Ok(&raw_data[Nonce::len()..Nonce::len() + final_len])
|
2023-02-17 13:59:02 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2023-02-23 20:57:21 +00:00
|
|
|
fn overhead(&self) -> usize {
|
|
|
|
match self {
|
2023-05-27 08:57:15 +00:00
|
|
|
Cipher::XChaCha20Poly1305(_) => {
|
2023-02-23 20:57:21 +00:00
|
|
|
let cipher = CipherKind::XChaCha20Poly1305;
|
2023-05-17 08:26:39 +00:00
|
|
|
cipher.nonce_len().0 + cipher.tag_len().0
|
2023-02-23 20:57:21 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2023-02-22 20:10:00 +00:00
|
|
|
fn encrypt(
|
|
|
|
&self,
|
2023-02-23 20:57:21 +00:00
|
|
|
nonce: &Nonce,
|
2023-02-22 20:10:00 +00:00
|
|
|
aad: AAD,
|
2023-05-17 08:26:39 +00:00
|
|
|
data: &mut [u8],
|
2023-02-22 20:10:00 +00:00
|
|
|
) -> Result<(), Error> {
|
2023-05-26 13:02:21 +00:00
|
|
|
// FIXME: check minimum buffer size
|
2023-02-22 20:10:00 +00:00
|
|
|
match self {
|
|
|
|
Cipher::XChaCha20Poly1305(cipher) => {
|
2023-05-27 08:57:15 +00:00
|
|
|
use ::chacha20poly1305::AeadInPlace;
|
2023-05-17 08:26:39 +00:00
|
|
|
let tag_len: usize = ::ring::aead::CHACHA20_POLY1305.tag_len();
|
|
|
|
let data_len_notag = data.len() - tag_len;
|
2023-02-23 20:57:21 +00:00
|
|
|
// write nonce
|
2023-05-17 08:26:39 +00:00
|
|
|
data[..Nonce::len()].copy_from_slice(nonce.as_bytes());
|
2023-02-22 20:10:00 +00:00
|
|
|
|
|
|
|
// encrypt data
|
2023-02-23 20:57:21 +00:00
|
|
|
match cipher.cipher.encrypt_in_place_detached(
|
|
|
|
nonce.as_bytes().into(),
|
|
|
|
aad.0,
|
2023-05-17 08:26:39 +00:00
|
|
|
&mut data[Nonce::len()..data_len_notag],
|
2023-02-23 20:57:21 +00:00
|
|
|
) {
|
|
|
|
Ok(tag) => {
|
2023-05-17 08:26:39 +00:00
|
|
|
data[data_len_notag..]
|
|
|
|
// add tag
|
|
|
|
//data.get_tag_slice()
|
|
|
|
.copy_from_slice(tag.as_slice());
|
2023-02-23 20:57:21 +00:00
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
Err(_) => Err(Error::Encrypt),
|
2023-05-27 08:57:15 +00:00
|
|
|
}
|
2023-02-22 20:10:00 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2023-02-17 13:59:02 +00:00
|
|
|
}
|
|
|
|
|
2023-02-17 22:09:49 +00:00
|
|
|
/// Receive only cipher
|
|
|
|
pub struct CipherRecv(Cipher);
|
2023-02-26 09:44:21 +00:00
|
|
|
impl ::core::fmt::Debug for CipherRecv {
|
|
|
|
fn fmt(
|
|
|
|
&self,
|
|
|
|
f: &mut core::fmt::Formatter<'_>,
|
|
|
|
) -> Result<(), ::std::fmt::Error> {
|
|
|
|
::core::fmt::Debug::fmt("[hidden cipher recv]", f)
|
|
|
|
}
|
|
|
|
}
|
2023-02-17 22:09:49 +00:00
|
|
|
|
|
|
|
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
|
2023-05-17 08:26:39 +00:00
|
|
|
pub fn nonce_len(&self) -> HeadLen {
|
2023-02-17 22:09:49 +00:00
|
|
|
self.0.nonce_len()
|
|
|
|
}
|
2023-02-17 22:30:19 +00:00
|
|
|
/// Decrypt a paket. Nonce and Tag are taken from the packet,
|
|
|
|
/// while you need to provide AAD (Additional Authenticated Data)
|
2023-02-21 21:06:17 +00:00
|
|
|
pub fn decrypt<'a>(
|
|
|
|
&self,
|
|
|
|
aad: AAD,
|
2023-05-17 08:26:39 +00:00
|
|
|
data: &'a mut [u8],
|
|
|
|
) -> Result<&'a [u8], Error> {
|
2023-02-17 22:30:19 +00:00
|
|
|
self.0.decrypt(aad, data)
|
|
|
|
}
|
2023-05-26 13:02:21 +00:00
|
|
|
/// return the underlying cipher id
|
|
|
|
pub fn kind(&self) -> CipherKind {
|
|
|
|
self.0.kind()
|
|
|
|
}
|
2023-02-17 22:09:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Send only cipher
|
|
|
|
pub struct CipherSend {
|
2023-02-26 09:44:21 +00:00
|
|
|
nonce: NonceSync,
|
2023-02-17 22:09:49 +00:00
|
|
|
cipher: Cipher,
|
|
|
|
}
|
2023-02-26 09:44:21 +00:00
|
|
|
impl ::core::fmt::Debug for CipherSend {
|
|
|
|
fn fmt(
|
|
|
|
&self,
|
|
|
|
f: &mut core::fmt::Formatter<'_>,
|
|
|
|
) -> Result<(), ::std::fmt::Error> {
|
|
|
|
::core::fmt::Debug::fmt("[hidden cipher send]", f)
|
|
|
|
}
|
|
|
|
}
|
2023-02-17 22:09:49 +00:00
|
|
|
|
|
|
|
impl CipherSend {
|
|
|
|
/// Build a new Cipher
|
2023-05-30 08:52:54 +00:00
|
|
|
pub fn new(kind: CipherKind, secret: Secret, rand: &Random) -> Self {
|
2023-02-17 13:59:02 +00:00
|
|
|
Self {
|
2023-03-01 17:20:03 +00:00
|
|
|
nonce: NonceSync::new(rand),
|
2023-02-17 22:09:49 +00:00
|
|
|
cipher: Cipher::new(kind, secret),
|
2023-02-17 13:59:02 +00:00
|
|
|
}
|
|
|
|
}
|
2023-02-23 20:57:21 +00:00
|
|
|
/// Encrypt the given data
|
2023-05-17 08:26:39 +00:00
|
|
|
pub fn encrypt(&self, aad: AAD, data: &mut [u8]) -> Result<(), Error> {
|
2023-02-26 09:44:21 +00:00
|
|
|
let old_nonce = self.nonce.advance();
|
|
|
|
self.cipher.encrypt(&old_nonce, aad, data)?;
|
2023-02-23 20:57:21 +00:00
|
|
|
Ok(())
|
2023-02-17 22:09:49 +00:00
|
|
|
}
|
2023-05-26 13:02:21 +00:00
|
|
|
/// return the underlying cipher id
|
|
|
|
pub fn kind(&self) -> CipherKind {
|
|
|
|
self.cipher.kind()
|
|
|
|
}
|
2023-02-17 22:09:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// 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()),
|
|
|
|
}
|
2023-02-17 13:59:02 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-02-17 22:09:49 +00:00
|
|
|
//
|
|
|
|
// TODO: For efficiency "Nonce" should become a reference.
|
|
|
|
//
|
|
|
|
|
2023-02-16 18:11:45 +00:00
|
|
|
#[derive(Debug, Copy, Clone)]
|
|
|
|
#[repr(C)]
|
|
|
|
#[allow(missing_debug_implementations)]
|
|
|
|
struct NonceNum {
|
|
|
|
high: u32,
|
|
|
|
low: u64,
|
|
|
|
}
|
|
|
|
/// Nonce with sequence for chach20_apoly1305
|
2023-02-17 22:09:49 +00:00
|
|
|
#[derive(Copy, Clone)]
|
2023-02-16 18:11:45 +00:00
|
|
|
#[repr(C)]
|
|
|
|
pub union Nonce {
|
|
|
|
num: NonceNum,
|
2023-02-17 22:09:49 +00:00
|
|
|
raw: [u8; 12],
|
2023-02-16 18:11:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl ::core::fmt::Debug for Nonce {
|
|
|
|
fn fmt(
|
|
|
|
&self,
|
|
|
|
f: &mut core::fmt::Formatter<'_>,
|
|
|
|
) -> Result<(), ::std::fmt::Error> {
|
|
|
|
#[allow(unsafe_code)]
|
|
|
|
unsafe {
|
2023-02-22 11:30:00 +00:00
|
|
|
::core::fmt::Debug::fmt(&self.num, f)
|
2023-02-16 18:11:45 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Nonce {
|
|
|
|
/// Generate a new random Nonce
|
2023-05-30 08:52:54 +00:00
|
|
|
pub fn new(rand: &Random) -> Self {
|
2023-03-01 17:20:03 +00:00
|
|
|
let mut raw = [0; 12];
|
|
|
|
rand.fill(&mut raw);
|
2023-05-27 08:57:15 +00:00
|
|
|
Self { raw }
|
2023-02-16 18:11:45 +00:00
|
|
|
}
|
2023-02-21 21:06:17 +00:00
|
|
|
/// Length of this nonce in bytes
|
2023-02-22 20:10:00 +00:00
|
|
|
pub const fn len() -> usize {
|
2023-02-21 21:06:17 +00:00
|
|
|
return 12;
|
|
|
|
}
|
2023-02-17 22:09:49 +00:00
|
|
|
/// Get reference to the nonce bytes
|
|
|
|
pub fn as_bytes(&self) -> &[u8] {
|
2023-02-16 18:11:45 +00:00
|
|
|
#[allow(unsafe_code)]
|
|
|
|
unsafe {
|
2023-02-17 22:09:49 +00:00
|
|
|
&self.raw
|
2023-02-16 18:11:45 +00:00
|
|
|
}
|
|
|
|
}
|
2023-02-17 22:09:49 +00:00
|
|
|
/// Create Nonce from array
|
|
|
|
pub fn from_slice(raw: [u8; 12]) -> Self {
|
2023-05-27 08:57:15 +00:00
|
|
|
Self { raw }
|
2023-02-16 18:11:45 +00:00
|
|
|
}
|
2023-02-17 22:09:49 +00:00
|
|
|
/// Go to the next nonce
|
|
|
|
pub fn advance(&mut self) {
|
2023-02-16 18:11:45 +00:00
|
|
|
#[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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2023-02-26 09:44:21 +00:00
|
|
|
|
|
|
|
/// Synchronize the mutex acess with a nonce for multithread safety
|
|
|
|
#[derive(Debug)]
|
|
|
|
pub struct NonceSync {
|
|
|
|
nonce: ::std::sync::Mutex<Nonce>,
|
|
|
|
}
|
|
|
|
impl NonceSync {
|
|
|
|
/// Create a new thread safe nonce
|
2023-05-30 08:52:54 +00:00
|
|
|
pub fn new(rand: &Random) -> Self {
|
2023-02-26 09:44:21 +00:00
|
|
|
Self {
|
2023-03-01 17:20:03 +00:00
|
|
|
nonce: ::std::sync::Mutex::new(Nonce::new(rand)),
|
2023-02-26 09:44:21 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
/// 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
|
|
|
|
}
|
|
|
|
}
|