Criptografía en Rust: cifrado y hashes con las bibliotecas ring y sha2

Entrenador de Python en Línea para Principiantes

Aprende Python fácilmente sin sobrecargarte de teoría. Resuelve tareas prácticas con verificación automática, recibe pistas y escribe código directamente en el navegador, sin instalar nada.

Start Course

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 bytes


Cifrado 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())                    

Recommendations