Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

cipher: add block mode traits and wrappers #567

Merged
merged 1 commit into from
Mar 12, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .github/workflows/crypto-common.yml
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,5 @@ jobs:
- run: cargo check --all-features
- run: cargo test
- run: cargo test --features core-api
- run: cargo test --features rand_core
- run: cargo test --features std
- run: cargo test --all-features
9 changes: 6 additions & 3 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,5 @@ members = [
]

[patch.crates-io]
block-buffer = { git = "https://github.com/RustCrypto/utils", branch = "master" }
block-buffer = { git = "https://github.com/RustCrypto/utils", branch = "block_mode" }
block-padding = { git = "https://github.com/RustCrypto/utils", branch = "block_mode" }
3 changes: 3 additions & 0 deletions cipher/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,14 @@ categories = ["cryptography", "no-std"]
generic-array = "0.14"
crypto-common = { version = "0.1", path = "../crypto-common/" }

block-buffer = { version = "0.10.0-pre.2", features = ["block-padding"], optional = true }
blobby = { version = "0.3", optional = true }
rand_core = { version = "0.6", optional = true }

[features]
default = ["mode_wrapper"]
std = ["crypto-common/std"]
mode_wrapper = ["block-buffer"]
dev = ["blobby"]

[package.metadata.docs.rs]
Expand Down
111 changes: 46 additions & 65 deletions cipher/src/block.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
//! Traits used to define functionality of [block ciphers][1].
//! Traits used to define functionality of [block ciphers][1] and [modes of operation][2].
//!
//! # About block ciphers
//!
//! Block ciphers are keyed, deterministic permutations of a fixed-sized input
//! "block" providing a reversible transformation to/from an encrypted output.
//! They are one of the fundamental structural components of [symmetric cryptography][2].
//! They are one of the fundamental structural components of [symmetric cryptography][3].
//!
//! [1]: https://en.wikipedia.org/wiki/Block_cipher
//! [2]: https://en.wikipedia.org/wiki/Symmetric-key_algorithm
//! [2]: https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation
//! [3]: https://en.wikipedia.org/wiki/Symmetric-key_algorithm

use crate::errors::InvalidLength;
use crate::{FromKey, FromKeyNonce};
use core::convert::TryInto;
use crypto_common::FromKey;
use generic_array::{typenum::Unsigned, ArrayLength, GenericArray};

/// Key for an algorithm that implements [`FromKey`].
Expand Down Expand Up @@ -177,80 +179,59 @@ impl<Alg: BlockDecrypt> BlockDecrypt for &Alg {
}
}

/// Trait for types which can be initialized from a block cipher.
pub trait FromBlockCipher {
/// Block cipher used for initialization.
type BlockCipher: BlockCipher;

/// Initialize instance from block cipher.
fn from_block_cipher(cipher: Self::BlockCipher) -> Self;
}

/// Trait for types which can be initialized from a block cipher and nonce.
pub trait FromBlockCipherNonce {
/// Block cipher
/// Block cipher used for initialization.
type BlockCipher: BlockCipher;
/// Nonce size in bytes
/// Nonce size in bytes.
type NonceSize: ArrayLength<u8>;

/// Instantiate a stream cipher from a block cipher
/// Initialize instance from block cipher and nonce.
fn from_block_cipher_nonce(
cipher: Self::BlockCipher,
nonce: &GenericArray<u8, Self::NonceSize>,
) -> Self;
}

/// Implement [`FromKeyNonce`][crate::FromKeyNonce] for a type which implements [`FromBlockCipherNonce`].
#[macro_export]
macro_rules! impl_from_key_nonce {
($name:ty) => {
impl<C: BlockCipher> crate::FromKeyNonce for $name
where
Self: FromBlockCipherNonce,
<Self as FromBlockCipherNonce>::BlockCipher: FromKey,
{
type KeySize = <<Self as FromBlockCipherNonce>::BlockCipher as FromKey>::KeySize;
type NonceSize = <Self as FromBlockCipherNonce>::NonceSize;

fn new(
key: &GenericArray<u8, Self::KeySize>,
nonce: &GenericArray<u8, Self::NonceSize>,
) -> Self {
let cipher = <Self as FromBlockCipherNonce>::BlockCipher::new(key);
Self::from_block_cipher_nonce(cipher, nonce)
}
impl<T> FromKeyNonce for T
where
T: FromBlockCipherNonce,
T::BlockCipher: FromKey,
{
type KeySize = <T::BlockCipher as FromKey>::KeySize;
type NonceSize = T::NonceSize;

fn new_from_slices(
key: &[u8],
nonce: &[u8],
) -> Result<Self, crate::errors::InvalidLength> {
use crate::errors::InvalidLength;
if nonce.len() != Self::NonceSize::USIZE {
Err(InvalidLength)
} else {
<Self as FromBlockCipherNonce>::BlockCipher::new_from_slice(key)
.map_err(|_| InvalidLength)
.map(|cipher| {
let nonce = GenericArray::from_slice(nonce);
Self::from_block_cipher_nonce(cipher, nonce)
})
}
}
}
};
fn new(
key: &GenericArray<u8, Self::KeySize>,
nonce: &GenericArray<u8, Self::NonceSize>,
) -> Self {
Self::from_block_cipher_nonce(T::BlockCipher::new(key), nonce)
}
}

/// Implement [`FromKey`] for a type which implements [`From<C>`][From],
/// where `C` implements [`FromKey`].
#[macro_export]
macro_rules! impl_from_key {
($name:ty) => {
impl<C: FromKey> cipher::FromKey for $name
where
Self: From<C>,
{
type KeySize = C::KeySize;

fn new(key: &GenericArray<u8, Self::KeySize>) -> Self {
C::new(key).into()
}
impl<T> FromKey for T
where
T: FromBlockCipher,
T::BlockCipher: FromKey,
{
type KeySize = <T::BlockCipher as FromKey>::KeySize;

fn new_from_slice(key: &[u8]) -> Result<Self, cipher::errors::InvalidLength> {
C::new_from_slice(key)
.map_err(|_| cipher::errors::InvalidLength)
.map(|cipher| cipher.into())
}
}
};
fn new(key: &GenericArray<u8, Self::KeySize>) -> Self {
Self::from_block_cipher(T::BlockCipher::new(key))
}

fn new_from_slice(key: &[u8]) -> Result<Self, InvalidLength> {
T::BlockCipher::new_from_slice(key)
.map_err(|_| InvalidLength)
.map(Self::from_block_cipher)
}
}
36 changes: 34 additions & 2 deletions cipher/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@

use core::fmt;

pub use crypto_common::InvalidLength;

/// The error type returned when stream cipher has reached the end of a keystream.
#[derive(Copy, Clone, Debug)]
pub struct LoopError;
Expand Down Expand Up @@ -36,3 +34,37 @@ impl From<OverflowError> for LoopError {

#[cfg(feature = "std")]
impl std::error::Error for OverflowError {}

/// The error type returned when key and/or nonce used in the [`FromKey`]
/// and [`FromKeyNonce`] slice-based methods had an invalid length.
///
/// [`FromKey`]: crate::FromKey
/// [`FromKeyNonce`]: crate::FromKeyNonce
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
pub struct InvalidLength;

impl fmt::Display for InvalidLength {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
f.write_str("Invalid Length")
}
}

#[cfg(feature = "std")]
impl std::error::Error for InvalidLength {}

/// The error type returned by the [`BlockModeEncryptWrapper`] and
/// [`BlockModeDecryptWrapper`] types.
///
/// [`BlockModeEncryptWrapper`]: crate::BlockModeEncryptWrapper
/// [`BlockModeDecryptWrapper`]: crate::BlockModeDecryptWrapper
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
pub struct BlockModeError;

impl fmt::Display for BlockModeError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
f.write_str("Invalid Length")
}
}

#[cfg(feature = "std")]
impl std::error::Error for BlockModeError {}
109 changes: 107 additions & 2 deletions cipher/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,113 @@ mod block;
#[cfg(feature = "dev")]
mod dev;
pub mod errors;
mod mode;
mod stream;

pub use crate::{block::*, stream::*};
pub use crypto_common::{FromKey, FromKeyNonce};
#[cfg(feature = "mode_wrapper")]
mod mode_wrapper;

pub use crate::{block::*, mode::*, stream::*};
pub use generic_array::{self, typenum::consts};
#[cfg(feature = "mode_wrapper")]
pub use mode_wrapper::{BlockModeDecryptWrapper, BlockModeEncryptWrapper};

use crate::errors::InvalidLength;
use generic_array::{typenum::Unsigned, ArrayLength, GenericArray};
#[cfg(feature = "rand_core")]
use rand_core::{CryptoRng, RngCore};

// note: ideally the following traits would be defined in the `crypto-common` crate,
// but it would make impossible the generic impls over `T: FromBlockCipher(Nonce)`
// in the `block` module, see the following link for proposal to change it:
// https://internals.rust-lang.org/t/14125

/// Trait for types which can be created from key and nonce.
pub trait FromKeyNonce: Sized {
/// Key size in bytes.
type KeySize: ArrayLength<u8>;

/// Nonce size in bytes.
type NonceSize: ArrayLength<u8>;

/// Create new value from fixed length key and nonce.
fn new(
key: &GenericArray<u8, Self::KeySize>,
nonce: &GenericArray<u8, Self::NonceSize>,
) -> Self;

/// Create new value from variable length key and nonce.
#[inline]
fn new_from_slices(key: &[u8], nonce: &[u8]) -> Result<Self, InvalidLength> {
let kl = Self::KeySize::to_usize();
let nl = Self::NonceSize::to_usize();
if key.len() != kl || nonce.len() != nl {
Err(InvalidLength)
} else {
let key = GenericArray::from_slice(key);
let nonce = GenericArray::from_slice(nonce);
Ok(Self::new(key, nonce))
}
}

/// Generate a random key using the provided [`CryptoRng`].
#[cfg(feature = "rand_core")]
#[cfg_attr(docsrs, doc(cfg(feature = "rand_core")))]
#[inline]
fn generate_key(mut rng: impl CryptoRng + RngCore) -> GenericArray<u8, Self::KeySize> {
let mut key = GenericArray::<u8, Self::KeySize>::default();
rng.fill_bytes(&mut key);
key
}

/// Generate a random nonce using the provided [`CryptoRng`].
#[cfg(feature = "rand_core")]
#[cfg_attr(docsrs, doc(cfg(feature = "rand_core")))]
#[inline]
fn generate_nonce(mut rng: impl CryptoRng + RngCore) -> GenericArray<u8, Self::NonceSize> {
let mut nonce = GenericArray::<u8, Self::NonceSize>::default();
rng.fill_bytes(&mut nonce);
nonce
}

/// Generate random key and nonce using the provided [`CryptoRng`].
#[cfg(feature = "rand_core")]
#[cfg_attr(docsrs, doc(cfg(feature = "rand_core")))]
#[inline]
fn generate_key_nonce(
mut rng: impl CryptoRng + RngCore,
) -> (
GenericArray<u8, Self::KeySize>,
GenericArray<u8, Self::NonceSize>,
) {
(Self::generate_key(&mut rng), Self::generate_nonce(&mut rng))
}
}

/// Trait for types which can be created from key.
pub trait FromKey: Sized {
/// Key size in bytes.
type KeySize: ArrayLength<u8>;

/// Create new value from fixed size key.
fn new(key: &GenericArray<u8, Self::KeySize>) -> Self;

/// Create new value from variable size key.
fn new_from_slice(key: &[u8]) -> Result<Self, InvalidLength> {
if key.len() != Self::KeySize::to_usize() {
Err(InvalidLength)
} else {
Ok(Self::new(GenericArray::from_slice(key)))
}
}

/// Generate a random key using the provided [`CryptoRng`].
#[cfg(feature = "rand_core")]
#[cfg_attr(docsrs, doc(cfg(feature = "rand_core")))]
#[inline]
fn generate_key(mut rng: impl CryptoRng + RngCore) -> GenericArray<u8, Self::KeySize> {
let mut key = GenericArray::<u8, Self::KeySize>::default();
rng.fill_bytes(&mut key);
key
}
}
Loading