I've noticed that Rust seems to lack a well-maintained, comprehensive crate for common cryptographic algorithms, similar to Python's Crypto.
While there is rust-crypto, it hasn't been maintained for a long time. I tried using its aes-256-cbc, but the results were incorrect. It could be an issue with how I used it, but since it lacks documentation and has been neglected for years, but I eventually stopped using it entirely.
I then tried cbc, hoping it could meet my need for aes-256-cbc. However, the example code in the documentation doesn't even compile.
In the end, I resorted to implementing aes-256-cbc and aes-256-ecb myself using the aes.
I find this situation quite annoying. I'm not interested in cryptography at all, I just need these algorithms fot my project. I simply want to use them in a straightforward way, like Crypto.xxx in Python.
Does anyone else share this frustration? Or am I missing something in Rust’s ecosystem?
Do you know about the RustCrypto project? Unrelated to the rust-crypto crate you shared as far as I'm aware. They are responsible for the cbc and aes crates you linked though.
Rust ecosystem doesn't use big all-batteries-included packages.
The rust-crypto crate has been replaced by dozens of small crates implementing one algorithm each. This way programs can include only what they need, and don't need to build all of the algorithms, and don't have to include unsafe or non-certified algorithms as part of a larger package.
Having said that, there is a downside in the small crates, especially in the crypto ecosystem — they use traits to offer a common API and compose different algorithms together. Traits are less user-friendly (harder to discover the API, and you need to import them), and also versions of different packages being combined must match, so a v0.8 hash won't work with a v0.9 hmac trait, etc.
You should absolutely never use aes-ecb! Much less implement it yourself. aes-cbc also has multiple vulnerabilities and pitfalls. It's not absolutely 100% broken like aes-ecb, but you still should almost certainly never use it. Use either AES-GCM, or ChaCha Poly1305.
We don't use "comprehensive crates" here in Rust. Find a well-supported crate which implements the specific algorithm you need. Why would you want a kitchen-sink crate to do it?
The example is checked as part of the crate CI, so it compiles perfectly fine. It's likely that you've used incompatible versions or haven't added the aes crate to your dependencies. Try to compile the example with cbc v0.1 and aes v0.8.
I get your point, and you're right that it's not the best choice.
Unfortunately, the data im getting is encrypted with AES-ECB, and I don’t have control over which encryption they use.
So for now, I have to work with it as is.
Yes, I made a stupid mistake.
The compilation error I encountered was due to using the preview version of cbc with the stable version of aes. I just tested again, and both combinations work fine:
Preview version of cbc with preview version of aes (0.2.0-pre.2 + 0.9.0-pre.2)
Stable version of cbc with stable version of aes (0.1.2 + 0.8.4)
You are goddamn right!
I ran into version issues immediately and couldn’t make sense of the error messages at all, thanks to my lack of cryptography knowledge and those user-unfriendly traits
// aes = { version = "0.8.4" }
// cbc = { version = "0.2.0-pre.2" }
// hex-literal = { version = "0.4.1" }
fn main() {
use aes::cipher::{block_padding::Pkcs7, BlockDecryptMut, BlockEncryptMut, KeyIvInit};
use hex_literal::hex;
type Aes128CbcEnc = cbc::Encryptor<aes::Aes128>;
type Aes128CbcDec = cbc::Decryptor<aes::Aes128>;
let key = [0x42; 16];
let iv = [0x24; 16];
let plaintext = *b"hello world! this is my plaintext.";
let ciphertext = hex!(
"c7fe247ef97b21f07cbdd26cb5d346bf"
"d27867cb00d9486723e159978fb9a5f9"
"14cfb228a710de4171e396e7b6cf859e"
);
// encrypt/decrypt in-place
// buffer must be big enough for padded plaintext
let mut buf = [0u8; 48];
let pt_len = plaintext.len();
buf[..pt_len].copy_from_slice(&plaintext);
let ct = Aes128CbcEnc::new(&key.into(), &iv.into())
.encrypt_padded_mut::<Pkcs7>(&mut buf, pt_len)
.unwrap();
assert_eq!(ct, &ciphertext[..]);
let pt = Aes128CbcDec::new(&key.into(), &iv.into())
.decrypt_padded_mut::<Pkcs7>(&mut buf)
.unwrap();
assert_eq!(pt, &plaintext);
// encrypt/decrypt from buffer to buffer
let mut buf = [0u8; 48];
let ct = Aes128CbcEnc::new(&key.into(), &iv.into())
.encrypt_padded_b2b_mut::<Pkcs7>(&plaintext, &mut buf)
.unwrap();
assert_eq!(ct, &ciphertext[..]);
let mut buf = [0u8; 48];
let pt = Aes128CbcDec::new(&key.into(), &iv.into())
.decrypt_padded_b2b_mut::<Pkcs7>(&ct, &mut buf)
.unwrap();
assert_eq!(pt, &plaintext);
}
All I know is to use use cbc::cipher::block_padding::Pkcs7 to fix the Pkcs7 import error.
The other errors are very hard for me to understand.
Though actually just switching the cbc version to 0.1.2 solves the problem
Here are some of the error messages
error[E0599]: the function or associated item `new` exists for struct `Encryptor<Aes128>`, but its trait bounds were not satisfied
--> src/main.rs:28:28
|
28 | let ct = Aes128CbcEnc::new(&key.into(), &iv.into())
| ^^^ function or associated item cannot be called on `Encryptor<Aes128>` due to unsatisfied trait bounds
|
::: C:\Users\lanye\.cargo\registry\src\index.crates.io-6f17d22bba15001f\cbc-0.2.0-pre.2\src\encrypt.rs:16:1
|
16 | pub struct Encryptor<C>
| ----------------------- doesn't satisfy `Encryptor<Aes128>: Sized`, `Encryptor<Aes128>: aes::cipher::InnerIvInit`, `Encryptor<Aes128>: aes::cipher::KeyIvInit` or `_: InnerUser`
|
= note: the following trait bounds were not satisfied:
`Encryptor<Aes128>: Sized`
which is required by `Encryptor<Aes128>: aes::cipher::KeyIvInit`
`Encryptor<Aes128>: aes::cipher::InnerIvInit`
which is required by `Encryptor<Aes128>: aes::cipher::KeyIvInit`
`Encryptor<Aes128>: aes::cipher::crypto_common::InnerUser`
which is required by `Encryptor<Aes128>: aes::cipher::KeyIvInit`
`&Encryptor<Aes128>: aes::cipher::InnerIvInit`
which is required by `&Encryptor<Aes128>: aes::cipher::KeyIvInit`
`&Encryptor<Aes128>: aes::cipher::crypto_common::InnerUser`
which is required by `&Encryptor<Aes128>: aes::cipher::KeyIvInit`
`&mut Encryptor<Aes128>: aes::cipher::InnerIvInit`
which is required by `&mut Encryptor<Aes128>: aes::cipher::KeyIvInit`
`&mut Encryptor<Aes128>: aes::cipher::crypto_common::InnerUser`
which is required by `&mut Encryptor<Aes128>: aes::cipher::KeyIvInit`
error[E0277]: the trait bound `Aes128: BlockCipherEncrypt` is not satisfied
--> src/main.rs:28:14
|
28 | let ct = Aes128CbcEnc::new(&key.into(), &iv.into())
| ^^^^^^^^^^^^ the trait `BlockCipherEncrypt` is not implemented for `Aes128`
|
= help: the trait `BlockCipherEncrypt` is implemented for `&Alg`