Cryptography in Rust: Encryption and Hashing with ring and sha2 Libraries
The Rust language has earned a reputation as one of the safest systems programming languages. Its strict type system, memory management without a garbage collector, and zero-cost abstractions make it an ideal choice for implementing cryptographic algorithms. In this article, we will look at two key libraries for cryptography in Rust: ring (high-level, secure) and sha2 (pure hash implementation). You will learn how to perform symmetric encryption (AES-GCM) and hashing (SHA-256), and also get acquainted with HMAC.
Why Rust for Cryptography?
Cryptography requires uncompromising reliability. Rust prevents entire classes of vulnerabilities (buffer overflow, data races) at compile time. The ring and sha2 libraries are written with these principles in mind and are actively maintained by the community.
- Memory Safety — Rust guarantees that keys and encrypted data will not be accidentally copied or left in memory.
- Performance — cryptographic operations in Rust are comparable to C/C++, and often faster.
- Ease of Use — the libraries provide a clean API with minimal risk of error (e.g., automatic nonce management).
The ring Library: A Universal Tool
ring is a cross-platform cryptographic library that focuses on security and simplicity. It includes support for AES-GCM, ChaCha20-Poly1305, ECDSA, Ed25519, and many other algorithms. ring does not support outdated or insecure protocols, which protects the developer from accidentally choosing weak encryption.
Installation:
[dependencies]ring = "0.17"Example: AES-GCM Encryption and Decryption
AES-GCM (Galois/Counter Mode) provides both confidentiality and data authentication. In the example below, we generate a random key and nonce, encrypt a message, and then successfully decrypt it.
use ring::{aead, rand};use ring::rand::SecureRandom;
fn main() -> Result<(), Box<dyn std::error::Error>> { // Generate a random key (256 bits) let rng = rand::SystemRandom::new(); let mut key_bytes = [0u8; 32]; rng.fill(&mut key_bytes)?; let key = aead::UnboundKey::new(&aead::AES_256_GCM, &key_bytes)?; let sealing_key = aead::SealingKey::new(key.clone()); let opening_key = aead::OpeningKey::new(key);
// Data to encrypt let plaintext = b"Secret message for PythonLib"; let mut nonce_bytes = [0u8; 12]; // 96 bits for GCM rng.fill(&mut nonce_bytes)?; let nonce = aead::Nonce::assume_unique_for_key(nonce_bytes);
// Encryption (in-place) let mut ciphertext = plaintext.to_vec(); let tag = sealing_key.seal_in_place_separate_tag(nonce, aead::Aad::empty(), &mut ciphertext)?; ciphertext.extend_from_slice(tag.as_ref());
println!("Encrypted: {:02x?}", ciphertext);
// Decryption let mut opened = ciphertext.clone(); let plaintext_len = opening_key.open_in_place(nonce, aead::Aad::empty(), &mut opened)?; let decrypted = &opened[..plaintext_len];
println!("Decrypted: {}", String::from_utf8(decrypted.to_vec())?); Ok(())}Important: The nonce (number used once) must be unique for each encryption with the same key. ring does not generate the nonce automatically — this is the developer's responsibility. In the example, we use SystemRandom to generate a random nonce.
The sha2 Library: Data Hashing
sha2 is a pure implementation of the SHA-2 family of hashes (SHA-224, SHA-256, SHA-384, SHA-512, SHA-512/224, SHA-512/256). This library is written entirely in Rust, which ensures portability and ease of integration. It is ideal for cases where you need a reliable hash function without external dependencies.
Installation:
[dependencies]sha2 = "0.10"Example: SHA-256 Hashing
Let's calculate the SHA-256 hash for a string. This is useful for verifying data integrity, storing passwords (with salt), or creating digital signatures.
use sha2::{Sha256, Digest};
fn main() { // Data for hashing let data = b"Hello, PythonLib readers!";
// Create a Sha256 object let mut hasher = Sha256::new();
// Write input data hasher.update(data);
// Read hash digest and consume hasher let result = hasher.finalize();
println!("SHA-256 hash: {:02x}", result);}The Digest trait provides a unified interface for all hash functions. You can easily replace Sha256 with Sha384 or Sha512 without changing the rest of the code.
HMAC: Authenticating Messages
HMAC (Hash-based Message Authentication Code) allows you to verify both the integrity and authenticity of a message using a secret key. The ring library supports HMAC based on SHA-256 and other algorithms.
Example: HMAC-SHA256
use ring::{hmac, rand};
fn main() -> Result<(), Box<dyn std::error::Error>> { // Generate a random key let rng = rand::SystemRandom::new(); let key_bytes: [u8; 32] = rand::generate(&rng)?.expose(); let key = hmac::Key::new(hmac::HMAC_SHA256, &key_bytes);
// Message to authenticate let message = b"Important data";
// Calculate HMAC let tag = hmac::sign(&key, message);
println!("HMAC tag: {:02x?}", tag.as_ref());
// Verification (should succeed) hmac::verify(&key, message, tag.as_ref())?; println!("Verification successful!");
Ok(())}HMAC is widely used in APIs (for example, in JWT or AWS Signature V4) to protect requests from tampering.
Practical Tips
- Do not use outdated algorithms — avoid DES, MD5, SHA-1 (except for compatibility). Use AES-256-GCM, ChaCha20-Poly1305, SHA-256/SHA-3.
- Store keys securely — use hardware security modules (HSM) or operating system key stores. Do not hardcode keys in the source code.
- Use constant-time comparison — when verifying HMAC or passwords, use
ring::constant_time::verify_slices_are_equalto prevent timing attacks. - Update dependencies — cryptographic libraries regularly receive security patches. Keep them up to date.
Conclusion
Rust provides a powerful and safe foundation for cryptography. The ring and sha2 libraries allow you to implement encryption, hashing, and authentication with minimal effort and maximum reliability. Start with simple examples, gradually moving to more complex scenarios. And remember: in cryptography, it is better to use proven libraries than to implement algorithms yourself.
Subscribe to the PythonLib blog so you don't miss new articles about Rust, Python, and other modern technologies!
Also in library
PDF Generation in JavaScript (Node.js): Libraries and Tools for Creating PDF
Working with Dates in Swift (iOS/macOS): Libraries and Frameworks
Working with XML in Go (Golang): encoding/xml package and alternatives
NetworkExtension for Swift: Complete Guide with Examples
OAuth client for Rust: oauth2 library