Криптография в Rust: шифрование и хеширование с библиотеками ring и sha2
Rust — это системный язык программирования, который обеспечивает безопасность памяти и производительность, сравнимую с C/C++. Благодаря своей строгой системе типов и модели владения, Rust идеально подходит для реализации криптографических алгоритмов, где ошибки могут привести к серьёзным уязвимостям. В этой статье мы рассмотрим, как использовать популярные библиотеки ring и sha2 для выполнения базовых криптографических операций: симметричного шифрования, хеширования и вычисления HMAC.
Библиотека ring: универсальный инструмент для криптографии
Библиотека ring — это одна из самых популярных криптографических библиотек для Rust. Она предоставляет безопасные реализации многих алгоритмов, включая AES-GCM для шифрования, SHA-256 для хеширования и HMAC для аутентификации сообщений. ring написана на Rust и C, что обеспечивает высокую производительность и безопасность.
Для начала работы добавьте зависимость в ваш Cargo.toml:
[dependencies]
ring = "0.17"
Симметричное шифрование с AES-GCM
AES-GCM (Galois/Counter Mode) — это режим шифрования, который обеспечивает как конфиденциальность, так и аутентификацию данных. Он широко используется в современных протоколах, таких как TLS.
Пример шифрования и дешифрования строки с помощью 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);
// Генерация случайного nonce (12 байт)
let mut nonce_bytes = [0u8; 12];
rng.fill(&mut nonce_bytes)?;
let nonce = Nonce::assume_unique_for_key(nonce_bytes);
// Буфер для зашифрованных данных + тег аутентификации
let mut in_out = plaintext.to_vec();
key.seal_in_place_append_tag(nonce, Aad::empty(), &mut in_out)?;
// Возвращаем nonce + зашифрованные данные
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);
// Извлекаем nonce (первые 12 байт)
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 байта для AES-256
let data = b"Секретное сообщение";
let encrypted = encrypt(data, key).unwrap();
println!("Зашифровано: {:?}", encrypted);
let decrypted = decrypt(&encrypted, key).unwrap();
println!("Расшифровано: {}", String::from_utf8(decrypted).unwrap());
}
Этот код демонстрирует полный цикл шифрования: генерацию случайного nonce, шифрование с тегом аутентификации и последующее дешифрование. Обратите внимание, что nonce должен быть уникальным для каждого шифрования с одним и тем же ключом.
Хеширование с библиотекой sha2
Библиотека sha2 предоставляет реализации алгоритмов семейства SHA-2, включая SHA-256, SHA-384 и SHA-512. Хеширование используется для проверки целостности данных, хранения паролей и создания цифровых подписей.
Добавьте зависимость:
[dependencies]
sha2 = "0.10"
Пример хеширования строки с SHA-256:
use sha2::{Sha256, Digest};
fn hash_string(input: &str) -> String {
let mut hasher = Sha256::new();
hasher.update(input.as_bytes());
let result = hasher.finalize();
format!("{:x}", result)
}
fn main() {
let data = "Привет, мир!";
let hash = hash_string(data);
println!("SHA-256 хеш: {}", hash);
// Вывод: SHA-256 хеш: 3f7c6b9b8c8e5a3d0b2f1c4e8a9d6b7