Cryptography in Rust: Encryption and Hashing with ring and sha2 Libraries
Rust is a systems programming language that provides memory safety and performance comparable to C/C++. Thanks to its strict type system and ownership model, Rust is ideal for implementing cryptographic algorithms, where errors can lead to serious vulnerabilities. In this article, we will look at how to use the popular ring and sha2 libraries to perform basic cryptographic operations: symmetric encryption, hashing, and HMAC computation.
The ring Library: A Versatile Tool for Cryptography
The ring library is one of the most popular cryptographic libraries for Rust. It provides secure implementations of many algorithms, including AES-GCM for encryption, SHA-256 for hashing, and HMAC for message authentication. ring is written in Rust and C, which ensures high performance and security.
To get started, add the dependency to your Cargo.toml:
[dependencies]ring = "0.17"Symmetric Encryption with AES-GCM
AES-GCM (Galois/Counter Mode) is an encryption mode that provides both confidentiality and data authentication. It is widely used in modern protocols such as TLS.
Example of encrypting and decrypting a string using ring:
use ring::aead::{Aad, LessSafeKey, Nonce, UnboundKey, AES_256_GCM};use ring::error::Unspecified;use ring::rand::{SecureRandom, SystemRandom};
fn encrypt(plaintext: &[u8], key: &[u8; 32]) -> Result<Vec<u8>, Unspecified> { let rng = SystemRandom::new(); let unbound_key = UnboundKey::new(&AES_256_GCM, key)?; let key = LessSafeKey::new(unbound_key);
// Generate a random nonce (12 bytes) let mut nonce_bytes = [0u8; 12]; rng.fill(&mut nonce_bytes)?; let nonce = Nonce::assume_unique_for_key(nonce_bytes);
// Buffer for encrypted data + authentication tag let mut in_out = plaintext.to_vec(); key.seal_in_place_append_tag(nonce, Aad::empty(), &mut in_out)?;
// Return nonce + encrypted data let mut result = nonce_bytes.to_vec(); result.extend_from_slice(&in_out); Ok(result)}
fn decrypt(ciphertext: &[u8], key: &[u8; 32]) -> Result<Vec<u8>, Unspecified> { let unbound_key = UnboundKey::new(&AES_256_GCM, key)?; let key = LessSafeKey::new(unbound_key);
// Extract the nonce (first 12 bytes) let (nonce_bytes, encrypted) = ciphertext.split_at(12); let nonce = Nonce::assume_unique_for_key(nonce_bytes.try_into().unwrap());
let mut in_out = encrypted.to_vec(); let plaintext = key.open_in_place(nonce, Aad::empty(), &mut in_out)?; Ok(plaintext.to_vec())}
fn main() { let key = b"01234567890123456789012345678901"; // 32 bytes for AES-256 let data = b"Secret message";
let encrypted = encrypt(data, key).unwrap(); println!("Encrypted: {:?}", encrypted);
let decrypted = decrypt(&encrypted, key).unwrap(); println!("Decrypted: {}", String::from_utf8(decrypted).unwrap());}This code demonstrates the full encryption cycle: generating a random nonce, encrypting with an authentication tag, and subsequent decryption. Note that the nonce must be unique for each encryption with the same key.
Hashing with the sha2 Library
The sha2 library provides implementations of the SHA-2 family of algorithms, including SHA-256, SHA-384, and SHA-512. Hashing is used for data integrity verification, password storage, and creating digital signatures.
Add the dependency:
[dependencies]sha2 = "0.10"Example of hashing a string with SHA-256:
use sha2: Also in library
Task Queues in C# (.NET): System.Threading.Channels and TPL Dataflow
Email Sending in PHP: PHPMailer, SwiftMailer, and mail() — Complete Guide
ExcelJS for JavaScript (Node.js): Complete Guide with Examples