Cryptography in Rust: Encryption and Hashing with ring and sha2 Libraries
Rust is a systems programming language that guarantees memory safety and high performance. Thanks to these qualities, it is ideal for cryptography tasks. In this article, we will explore two popular libraries: ring (for symmetric encryption and hashing) and sha2 (for pure hashing). You will learn how to encrypt data using AES-GCM, compute SHA-256 hashes, and create HMAC signatures.
Why Rust for Cryptography?
Rust eliminates entire classes of vulnerabilities (buffer overflow, data races) at compile time. Cryptographic libraries in Rust, such as ring, are written in assembly and C with safe wrappers, providing speed comparable to C but with Rust's reliability.
Project Setup
Create a new project and add dependencies to Cargo.toml:
[dependencies]ring = "0.17"sha2 = "0.10"hex = "0.4" # for convenient byte displaySymmetric Encryption: AES-GCM with ring
AES-GCM (Galois/Counter Mode) is a modern symmetric encryption standard that provides both confidentiality and authentication (integrity checking). The ring library provides a simple and secure API.
Data Encryption
For encryption you need: a key (256 bits), a nonce (96 bits), and plaintext. The nonce must be unique for each encryption with the same key.
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);
// Generate random nonce let mut nonce_bytes = [0u8; 12]; rng.fill(&mut nonce_bytes).unwrap(); let nonce = Nonce::assume_unique_for_key(nonce_bytes);
// Encryption with empty additional data (AAD) let mut in_out = plaintext.to_vec(); let tag = key.seal_in_place_separate_tag(nonce, Aad::empty(), &mut in_out).unwrap(); // Return nonce + encrypted data + authentication tag let mut result = nonce_bytes.to_vec(); result.extend_from_slice(&in_out); result.extend_from_slice(tag.as_ref()); result}Decryption
For decryption, extract the nonce, tag, and ciphertext, then call open_in_place.
fn decrypt_aes_gcm(key: &[u8; 32], ciphertext: &[u8]) -> Option<Vec<u8>> { if ciphertext.len() < 28 { return None; } // 12 (nonce) + 16 (tag) minimum
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 expects the tag at the end 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 // wrong key or corrupted data }}Usage Example
fn main() { let key = b"0123456789abcdef0123456789abcdef"; // 32 bytes let data = b"Secret message for Rust!"; let encrypted = encrypt_aes_gcm(key, data); println!("Encrypted (hex): {}", hex::encode(&encrypted)); if let Some(decrypted) = decrypt_aes_gcm(key, &encrypted) { println!("Decrypted: {}", String::from_utf8(decrypted).unwrap()) Also in library
Working with Excel in C++: Libraries and Tools for Reading and Writing
HTTP Client for Swift (iOS/macOS): Top Libraries 2024
PDF Generation in C++: Overview of Libraries and Tools
Redis Client for C++: Connection, Installation, and Usage Examples
Database Migrations in TypeScript: TypeORM, Prisma, and Drizzle