feat(des): implement basic encryption

This commit is contained in:
2025-10-17 17:14:43 +03:00
parent 9e91e90303
commit 6175305641
22 changed files with 866 additions and 295 deletions

View File

@@ -1,7 +1,7 @@
use thiserror::Error;
#[derive(Debug, Error, Clone, Copy, PartialEq, Eq)]
pub enum CryptoError {
pub enum CipherError {
/// Invalid key size for the cipher
#[error("invalid key size: expected {expected} bytes, got {actual}")]
InvalidKeySize { expected: usize, actual: usize },
@@ -23,7 +23,7 @@ pub enum CryptoError {
InvalidSize { expected: usize, actual: usize },
}
impl CryptoError {
impl CipherError {
/// Creates a key size error
#[inline]
#[must_use]
@@ -97,4 +97,4 @@ impl CryptoError {
}
/// Type alias for clean Result types
pub type CryptoResult<T> = core::result::Result<T, CryptoError>;
pub type CipherResult<T> = core::result::Result<T, CipherError>;

View File

@@ -1,5 +1,9 @@
mod error;
mod traits;
mod types;
pub use error::{CryptoError, CryptoResult};
pub use traits::{BlockCipher, BlockLike, CipherContext, KeyInit, KeyLike, StreamCipher};
pub use {
error::{CipherError, CipherResult},
traits::BlockCipher,
types::CipherAction,
};

View File

@@ -1,192 +1,24 @@
use crate::CryptoResult;
use crate::{CipherAction, CipherError, CipherResult};
/// Minimal trait describing a fixed-size block-like value.
///
/// Concrete block types (e.g. `Block<const N: usize>`) should implement this.
pub trait BlockLike: Sized + Copy + Clone {
/// Size of the block in bytes.
const SIZE: usize;
/// Create from exactly SIZE bytes.
///
/// # Errors
///
/// Returns a `CryptoError::InvalidBlockSize` (or equivalent) when
/// `bytes.len() != Self::SIZE`.
fn from_bytes(bytes: &[u8]) -> CryptoResult<Self>;
/// Immutable view of the underlying bytes.
fn as_bytes(&self) -> &[u8];
/// Mutable view of underlying bytes.
fn as_bytes_mut(&mut self) -> &mut [u8];
/// Create a zeroed block value.
fn zeroed() -> Self;
}
/// Minimal trait describing a fixed-size key-like value.
///
/// Concrete key types (e.g. `Secret<const N: usize>`) should implement this.
pub trait KeyLike: Sized {
/// Size of the key in bytes.
const SIZE: usize;
/// Create key from exactly SIZE bytes.
///
/// # Errors
///
/// Returns a `CryptoError::InvalidKeySize` when `bytes.len() != Self::SIZE`.
fn from_bytes(bytes: &[u8]) -> CryptoResult<Self>;
/// Immutable view of the key bytes.
fn as_bytes(&self) -> &[u8];
}
/// Core block-cipher trait using [`KeyLike`] and [`BlockLike`].
///
/// The primary (performance-oriented) methods are the in-place variants.
/// Default owned-returning methods are implemented in terms of the in-place
/// ones and therefore require `BlockLike: Copy`.
pub trait BlockCipher: Sized {
/// Key and Block concrete associated types
type Key: KeyLike;
type Block: BlockLike;
const BLOCK_SIZE: usize;
/// Construct a cipher instance from a key.
///
/// # Errors
///
/// Returns a `CryptoError` if the key is invalid for the cipher.
fn new(key: Self::Key) -> CryptoResult<Self>;
fn transform_impl(&self, block: &[u8], action: CipherAction) -> CipherResult<Vec<u8>>;
/// Encrypt in-place (primary implementation target).
///
/// # Errors
///
/// Returns a `CryptoError` on failure (e.g. internal state issues).
fn encrypt_inplace(&self, block: &mut Self::Block) -> CryptoResult<()>;
/// Decrypt in-place (primary implementation target).
///
/// # Errors
///
/// Returns a `CryptoError` on failure (e.g. invalid padding after decrypt).
fn decrypt_inplace(&self, block: &mut Self::Block) -> CryptoResult<()>;
/// Encrypt returning a new block.
///
/// Default implementation copies the input block and calls
/// `encrypt_inplace`.
///
/// # Errors
///
/// Propagates errors returned by `encrypt_inplace`.
fn encrypt(&self, block: &Self::Block) -> CryptoResult<Self::Block> {
let mut out = *block;
self.encrypt_inplace(&mut out)?;
Ok(out)
fn transform(&self, block: &[u8], action: CipherAction) -> CipherResult<Vec<u8>> {
if block.len() != Self::BLOCK_SIZE {
return Err(CipherError::invalid_block_size(
Self::BLOCK_SIZE,
block.len(),
));
}
self.transform_impl(block, action)
}
/// Decrypt returning a new block.
///
/// Default implementation copies the input block and calls
/// `decrypt_inplace`.
///
/// # Errors
///
/// Propagates errors returned by `decrypt_inplace`.
fn decrypt(&self, block: &Self::Block) -> CryptoResult<Self::Block> {
let mut out = *block;
self.decrypt_inplace(&mut out)?;
Ok(out)
}
}
/// Helper trait: initialize a cipher from raw key bytes.
///
/// The default implementation converts the slice into `Self::Key` then calls
/// `Self::new`. Implementations may override to perform custom validation.
pub trait KeyInit: BlockCipher + Sized {
/// Construct the cipher from raw key bytes.
///
/// # Errors
///
/// Returns `CryptoError::InvalidKeySize` if the slice length doesn't match
fn new_from_slice(key_bytes: &[u8]) -> CryptoResult<Self> {
let key = <Self::Key as KeyLike>::from_bytes(key_bytes)?;
Self::new(key)
}
}
/// Stream-like cipher/mode trait (CTR, OFB, stream ciphers).
///
/// Implementations apply keystream to arbitrary-length buffers in-place.
pub trait StreamCipher {
/// XOR keystream with `data` in-place (encrypt == decrypt).
fn apply_keystream(&mut self, data: &mut [u8]);
}
/// Small convenience wrapper that stores a cipher and forwards single-block
/// operations using the associated Block type.
pub struct CipherContext<C>
where
C: BlockCipher,
{
cipher: C,
}
impl<C> CipherContext<C>
where
C: BlockCipher,
{
/// Wrap an existing cipher instance.
pub const fn new(cipher: C) -> Self {
Self { cipher }
}
/// Encrypt a block, returning a new block.
///
/// # Errors
///
/// Propagates errors from the cipher.
pub fn encrypt(&self, block: &C::Block) -> CryptoResult<C::Block> {
self.cipher.encrypt(block)
}
/// Encrypt a block in-place.
///
/// # Errors
///
/// Propagates errors from the cipher. /// Encrypt a block in-place.
pub fn encrypt_inplace(&self, block: &mut C::Block) -> CryptoResult<()> {
self.cipher.encrypt_inplace(block)
}
/// Decrypt a block, returning a new block.
///
/// # Errors
///
/// Propagates errors from the cipher. /// Decrypt a block, returning a new block.
pub fn decrypt(&self, block: &C::Block) -> CryptoResult<C::Block> {
self.cipher.decrypt(block)
}
/// Decrypt a block in-place.
///
/// # Errors
///
/// Propagates errors from the cipher. /// Decrypt a block in-place.
pub fn decrypt_inplace(&self, block: &mut C::Block) -> CryptoResult<()> {
self.cipher.decrypt_inplace(block)
}
/// Access underlying cipher
pub const fn cipher(&self) -> &C {
&self.cipher
}
/// Consume and return underlying cipher
pub fn into_inner(self) -> C {
self.cipher
fn encrypt(&self, plaintext: &[u8]) -> CipherResult<Vec<u8>> {
self.transform(plaintext, CipherAction::Encrypt)
}
fn decrypt(&self, ciphertext: &[u8]) -> CipherResult<Vec<u8>> {
self.transform(ciphertext, CipherAction::Decrypt)
}
}

5
cipher-core/src/types.rs Normal file
View File

@@ -0,0 +1,5 @@
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum CipherAction {
Encrypt,
Decrypt,
}