authnz-common 0.2.1

Authnz common library (types and utils).
Documentation
//! Encryption module.

#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))]
use crate::{MResult, ServerError};

#[cfg(any(target_arch = "wasm32", target_arch = "wasm64"))]
use crate::{CResult, CliError};

#[derive(Clone)]
/// Encryption key.
pub struct CipherKey {
  inner: InnerCipherKey,
}

#[non_exhaustive]
#[derive(Clone)]
enum InnerCipherKey {
  Chacha20Poly1305([u8; 32]),
}

impl InnerCipherKey {
  fn encrypt(&self, message: &impl serde::Serialize) -> MResult<(Vec<u8>, Vec<u8>)> {
    #[allow(unreachable_patterns)]
    match self {
      Self::Chacha20Poly1305(key) => {
        use chacha20poly1305::{
          ChaCha20Poly1305,
          aead::{Aead, AeadCore, KeyInit, OsRng, generic_array::GenericArray},
          consts::U32,
        };

        let serialized = rmp_serde::to_vec(message).map_err(|e| {
          ServerError::from_private(e)
            .with_private_str("Can't serialize data to encrypt!")
            .with_500()
        })?;
        let key = GenericArray::<u8, U32>::from_slice(key);
        let cipher = ChaCha20Poly1305::new(key);
        let nonce = ChaCha20Poly1305::generate_nonce(&mut OsRng);
        let ciphertext = cipher.encrypt(&nonce, serialized.as_ref()).map_err(|e| {
          ServerError::from_private_str(e.to_string())
            .with_private_str("Can't encrypt given data!")
            .with_500()
        })?;
        Ok((ciphertext, nonce.to_vec()))
      }
      _ => unreachable!(),
    }
  }

  fn decrypt<T: serde::de::DeserializeOwned>(&self, ciphertext: &[u8], nonce: &[u8]) -> MResult<T> {
    #[allow(unreachable_patterns)]
    match self {
      Self::Chacha20Poly1305(key) => {
        use chacha20poly1305::{
          ChaCha20Poly1305,
          aead::{Aead, KeyInit, generic_array::GenericArray},
          consts::{U12, U32},
        };

        let key = GenericArray::<u8, U32>::from_slice(key);
        let cipher = ChaCha20Poly1305::new(key);
        let nonce = GenericArray::<u8, U12>::from_slice(nonce);
        let plaintext = cipher.decrypt(nonce, ciphertext).map_err(|e| {
          ServerError::from_private_str(e.to_string())
            .with_private_str("Can't decrypt given ciphertext!")
            .with_500()
        })?;
        let deserialized = rmp_serde::from_slice::<T>(plaintext.as_slice()).map_err(|e| {
          ServerError::from_private(e)
            .with_private_str("Can't deserialize decrypted data!")
            .with_500()
        })?;
        Ok(deserialized)
      }
      _ => unreachable!(),
    }
  }

  fn pack(&self) -> Vec<u8> {
    #[allow(unreachable_patterns)]
    match self {
      Self::Chacha20Poly1305(key) => {
        let mut packed = b"chacha20poly1305::".to_vec();
        packed.extend_from_slice(key);
        packed
      }
      _ => unreachable!(),
    }
  }

  fn unpack(key: impl AsRef<[u8]>) -> MResult<Self> {
    if key.as_ref().starts_with(b"chacha20poly1305::") {
      use std::mem::MaybeUninit;

      let key = &key.as_ref()[b"chacha20poly1305::".len()..];
      if key.len() != 32 {
        return Err(ServerError::from_private_str("Invalid encryption key length!").with_500());
      }

      let buffer: [MaybeUninit<u8>; 32] = unsafe { MaybeUninit::uninit().assume_init() };
      let mut buffer = unsafe { std::mem::transmute::<[MaybeUninit<u8>; 32], [u8; 32]>(buffer) };
      buffer.copy_from_slice(key);

      return Ok(Self::Chacha20Poly1305(buffer));
    }

    Err(ServerError::from_private_str("Invalid packed key format!").with_500())
  }
}

impl CipherKey {
  /// Generates an encryption (`ChaCha20Poly1305`) key.
  pub fn new_chacha20poly1305() -> Self {
    use rand::Rng;

    let mut arr: [u8; 32] = [0; 32];
    let mut rng = rand::rng();
    rng.fill(arr.as_mut_slice());

    Self {
      inner: InnerCipherKey::Chacha20Poly1305(arr),
    }
  }

  /// Encrypts serializable `message` by encryption key, providing `ciphertext` and its `nonce`.
  pub fn encrypt(&self, message: &impl serde::Serialize) -> MResult<(Vec<u8>, Vec<u8>)> {
    self.inner.encrypt(message)
  }

  /// Decrypts deserializable message from `ciphertext`, its `nonce` and provided key.
  pub fn decrypt<T: serde::de::DeserializeOwned>(&self, ciphertext: &[u8], nonce: &[u8]) -> MResult<T> {
    self.inner.decrypt(ciphertext, nonce)
  }

  /// Packs encryption key.
  pub fn pack(&self) -> Vec<u8> {
    self.inner.pack()
  }

  /// Unpacks encryption key.
  pub fn unpack(key: impl AsRef<[u8]>) -> MResult<Self> {
    Ok(CipherKey {
      inner: InnerCipherKey::unpack(key)?,
    })
  }
}