RustCrypto / block-ciphers

Collection of block cipher algorithms written in pure Rust
662 stars 130 forks source link

feature request: add block_size method to BlockCipher #131

Closed hxzhao527 closed 2 years ago

hxzhao527 commented 4 years ago

describe & question

BlockCipher.encrypt work in-place, and no allocate. So I need to calculate the buffer size manually.
my question :

my code

use std::error;

use aes::block_cipher::{generic_array::typenum::Unsigned, BlockCipher};
use aes::Aes128;
use block_modes::{block_padding::Pkcs7, BlockMode, Cbc};

type Aes128Cbc = Cbc<Aes128, Pkcs7>;
type Byte = u8;

fn aes128_cbc_encrypt(
    plaintext: &[Byte],
    key: &[Byte],
    iv: &[Byte],
) -> Result<Vec<Byte>, Box<dyn error::Error>> {
    // pading只是做对齐, 但是这个对齐是in-place, 这个buf还是需要自己申请大小以满足条件
    // pading just fill, no allocate
    // =======================================
    let block_size = <Aes128 as BlockCipher>::BlockSize::to_usize();
    let padding_length = block_size - plaintext.len() % block_size;
    // =======================================
    let cipher = Aes128Cbc::new_var(&key, &iv)?;

    let mut buffer = Vec::with_capacity(padding_length + plaintext.len());
    buffer.resize(padding_length + plaintext.len(), 0u8);
    // unsafe { buffer.set_len(padding_length+plaintext.len()); }
    buffer[..plaintext.len()].copy_from_slice(plaintext);

    //let ciphertext = cipher.encrypt(&mut buffer, plaintext.len())?;
    cipher.encrypt(&mut buffer, plaintext.len())?;
    Ok(buffer)
}

Cargo.toml

[dependencies]
block-modes="0.4.0"
aes="0.4.0
tarcieri commented 4 years ago

I think adding a block_size() method makes sense.

Kind of curious what you're trying to do there specifically and if we could add some additional higher level methods to help. Do you specifically need (unauthenticated) AES-CBC encryption?

hxzhao527 commented 4 years ago

Just transform golang to rust. 😂

In go, https://golang.org/pkg/crypto/cipher/#Block, provide a method BlockSize(), it is convenient.

tarcieri commented 4 years ago

Eventually they'll just be associated consts (after const generics land), but I agree for now having a method is easier than <Aes128 as BlockCipher>::BlockSize::to_usize()

hxzhao527 commented 4 years ago

you mean something like this?

pub trait BlockCipher {
    const Size: usize;
    fn block_size() -> usize {
        Self::Size
    }

    fn new() -> Self;
}

pub struct Aes {}

impl BlockCipher for Aes {
    const Size: usize = 1;
    fn new() -> Self {
        Self {}
    }
}

pub struct Cbc<C: BlockCipher> {
    cipher: C,
}
impl<C> Cbc<C>
where
    C: BlockCipher,
{
    fn encrypt(self, data: &str) {
        for _ in 0..C::block_size() {
            println!("data: {}", data);
        }
    }

    fn new() -> Self {
        Self { cipher: C::new() }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    type AesCbc = Cbc<Aes>;
    #[test]
    pub fn test_size() {
        println!("block_size: {}", Aes::block_size());
        let encryptor = AesCbc::new();
        encryptor.encrypt("hello");
    }
}

image

tarcieri commented 4 years ago

What I'm talking about involves changes to the Rust language. Here's a blog post I wrote on the topic:

https://tonyarcieri.com/rust-in-2019-security-maturity-stability#const-generics-and-cryptography_2

hxzhao527 commented 4 years ago

Got it.

====== For others: The Rust RFC Book : 2000-const_generics Tracking issue for const generics (RFC 2000)