Criptografía en Rust: cifrado y hashes con las bibliotecas ring y sha2
Rust es un lenguaje de programación de sistemas que garantiza seguridad de memoria y alto rendimiento. Gracias a estas cualidades, es ideal para tareas de criptografía. En este artículo, exploraremos dos bibliotecas populares: ring (para cifrado simétrico y hashing) y sha2 (para hashing puro). Aprenderá a cifrar datos con AES-GCM, calcular hashes SHA-256 y crear firmas HMAC.
¿Por qué Rust para criptografía?
Rust elimina clases enteras de vulnerabilidades (desbordamiento de búfer, condiciones de carrera) en tiempo de compilación. Las bibliotecas criptográficas en Rust, como ring, están escritas en ensamblador y C con envoltorios seguros, lo que proporciona velocidad comparable a C, pero con la confiabilidad de Rust.
Configuración del proyecto
Cree un nuevo proyecto y agregue las dependencias en Cargo.toml:
[dependencies]ring = "0.17"sha2 = "0.10"hex = "0.4" # para visualización conveniente de bytesCifrado simétrico: AES-GCM con ring
AES-GCM (Modo Galois/Contador) es un estándar moderno de cifrado simétrico que proporciona tanto confidencialidad como autenticación (verificación de integridad). La biblioteca ring ofrece una API simple y segura.
Cifrado de datos
Para cifrar se necesitan: una clave (256 bits), un nonce (96 bits) y texto plano. El nonce debe ser único para cada cifrado con una misma clave.
use ring::aead::{Aad, LessSafeKey, Nonce, UnboundKey, AES_256_GCM};use ring::rand::{SecureRandom, SystemRandom};
fn encrypt_aes_gcm(key: &[u8; 32], plaintext: &[u8]) -> Vec<u8> { let rng = SystemRandom::new(); let unbound_key = UnboundKey::new(&AES_256_GCM, key).unwrap(); let key = LessSafeKey::new(unbound_key);
// Generación de nonce aleatorio let mut nonce_bytes = [0u8; 12]; rng.fill(&mut nonce_bytes).unwrap(); let nonce = Nonce::assume_unique_for_key(nonce_bytes);
// Cifrado con datos adicionales vacíos (AAD) let mut in_out = plaintext.to_vec(); let tag = key.seal_in_place_separate_tag(nonce, Aad::empty(), &mut in_out).unwrap(); // Devolvemos nonce + datos cifrados + etiqueta de autenticación let mut result = nonce_bytes.to_vec(); result.extend_from_slice(&in_out); result.extend_from_slice(tag.as_ref()); result}Descifrado
Para descifrar, extraemos el nonce, la etiqueta y el texto cifrado, luego llamamos a open_in_place.
fn decrypt_aes_gcm(key: &[u8; 32], ciphertext: &[u8]) -> Option<Vec<u8>> { if ciphertext.len() < 28 { return None; } // 12 (nonce) + 16 (etiqueta) mínimo
let (nonce_bytes, rest) = ciphertext.split_at(12); let (encrypted, tag_bytes) = rest.split_at(rest.len() - 16); let nonce = Nonce::assume_unique_for_key(*nonce_bytes); let unbound_key = UnboundKey::new(&AES_256_GCM, key).unwrap(); let key = LessSafeKey::new(unbound_key);
let mut in_out = encrypted.to_vec(); in_out.extend_from_slice(tag_bytes); // ring espera la etiqueta al final match key.open_in_place(nonce, Aad::empty(), &mut in_out) { Ok(plaintext) => { let plain_len = plaintext.len(); Some(plaintext[..plain_len].to_vec()) }, Err(_) => None // clave incorrecta o datos dañados }}Ejemplo de uso
fn main() { let key = b"0123456789abcdef0123456789abcdef"; // 32 bytes let data = b"¡Mensaje secreto para Rust!"; let encrypted = encrypt_aes_gcm(key, data); println!("Cifrado (hex): {}", hex::encode(&encrypted)); if let Some(decrypted) = decrypt_aes_gcm(key, &encrypted) { println!("Descifrado: {}", String::from_utf8(decrypted).unwrap()) También en la biblioteca
Monitoreo de aplicaciones C++: bibliotecas y herramientas para la recopilación de métricas
Trabajo con CSV en PHP: lectura, escritura y procesamiento de datos
Procesamiento de Markdown en Rust: bibliotecas, analizadores y herramientas
Clap — potente framework CLI para Rust