Insecure encryption algorithm
Need
Replacement of broken ciphers and hash functions with current authenticated encryption and password hashing
Context
• Usage of Rust 1.75+ for building application services
• Usage of the RustCrypto des and md-5 crates
• Usage of the RustCrypto aes-gcm and argon2 crates
Description
1. Non compliant code
use des::cipher::{BlockEncrypt, KeyInit};
use des::Des;
use md5::{Digest, Md5};
fn encrypt_block(key: [u8; 8], block: &mut [u8; 8]) {
// DES has a 56-bit key and can be brute forced
let cipher = Des::new(&key.into());
cipher.encrypt_block(block.into());...The code below relies on two broken algorithms. `encrypt_block` encrypts data with DES from the `des` crate. DES has a 56-bit key, which dedicated hardware and cloud instances can search exhaustively in hours, and its 64-bit block size makes collisions practical when large amounts of data are encrypted with the same key (Sweet32). Triple DES, RC4 and AES in ECB mode, which encrypts identical blocks into identical ciphertext and reveals patterns in the data, are unsafe for the same reasons. `hash_password` protects passwords with MD5. MD5 has practical collisions, and it is so fast that a single GPU computes billions of hashes per second, so most real passwords are recovered from a leaked database in minutes. SHA-1 and RIPEMD-160 share these problems, including when they are the inner hash of an HMAC. Using bcrypt with a cost below 10 has a similar effect, because it makes guessing thousands of times cheaper than intended.
2. Steps
• Replace DES, Triple DES and RC4 (`des`, `rc4` crates, `openssl::symm::Cipher::des_*` and `rc4`) with AES-256-GCM (`aes-gcm`) or ChaCha20-Poly1305 (`chacha20poly1305`).
• Do not use ECB mode (`ecb` crate or `Cipher::aes_*_ecb`), and prefer an authenticated mode such as GCM over CBC.
• Generate a unique nonce for every encryption with the same key, and store it next to the ciphertext.
• Hash passwords with Argon2id or with bcrypt at a cost of at least 10, never with MD5, SHA-1, RIPEMD-160 or a single round of SHA-256.
• Replace `Hmac<Md5>` and `Hmac<Sha1>` with `Hmac<Sha256>`, and use SHA-256 or SHA-3 for integrity checks.
• Plan the migration of data that is already encrypted or hashed with the old algorithms, for example by re-hashing passwords at the next successful login.
3. Secure code example
use aes_gcm::aead::{Aead, AeadCore, OsRng};
use aes_gcm::{Aes256Gcm, Key, KeyInit};
use argon2::password_hash::{PasswordHasher, SaltString};
use argon2::Argon2;
fn encrypt(key: &Key<Aes256Gcm>, plaintext: &[u8]) -> Result<Vec<u8>, aes_gcm::Error> {
// AES-256-GCM is authenticated encryption; every message gets a fresh nonce
let cipher = Aes256Gcm::new(key);...The corrected code replaces each algorithm with the construction designed for that job. `encrypt` uses AES-256 in Galois/Counter Mode through the `aes-gcm` crate. AES-256 has a 256-bit key and a 128-bit block, and GCM is an authenticated mode: decryption fails if the ciphertext was modified, which also rules out the bit-flipping and padding oracle attacks that affect unauthenticated modes such as CBC. Each call draws a new 96-bit nonce from `OsRng` and prepends it to the output, because reusing a nonce with the same key breaks GCM. `hash_password` uses Argon2id from the `argon2` crate, a memory-hard password hashing function with a random salt per password. Unlike MD5, it is intentionally slow and expensive to parallelize. For integrity checks and message authentication that do not involve passwords, SHA-256 and HMAC-SHA256 from the `sha2` and `hmac` crates are the right replacement for MD5 and SHA-1.
References
• 052. Insecure encryption algorithm