ring для Rust: полное руководство с примерами

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

ring для Rust: полное руководство с примерами

Что это и зачем нужна

ring — это криптографическая библиотека для языка Rust, предоставляющая безопасные, проверенные и высокопроизводительные реализации основных криптографических примитивов. Она основана на коде BoringSSL (форк OpenSSL от Google) и спроектирована с фокусом на безопасность, простоту использования и минимизацию поверхности атаки. В отличие от более общих библиотек, таких как openssl или rust-crypto, ring намеренно ограничивает набор поддерживаемых алгоритмов, исключая устаревшие или небезопасные (например, DES, RC4, MD5), что снижает риск случайного использования слабой криптографии.

Зачем нужна ring? Если вы разрабатываете приложение на Rust, которому требуется выполнять хеширование (SHA-256, SHA-512), симметричное шифрование (AES-GCM, ChaCha20-Poly1305), асимметричное шифрование и подписи (RSA, ECDSA, Ed25519), генерацию случайных чисел или согласование ключей (X25519, ECDH), ring — это оптимальный выбор. Она активно используется в таких проектах, как rustls (реализация TLS на Rust), reqwest (HTTP-клиент) и webpki (проверка сертификатов). Библиотека не требует внешних зависимостей (кроме libc на Linux/macOS) и компилируется с помощью Cargo, что делает её идеальной для встраиваемых систем и кросс-компиляции.

Основные преимущества ring: безопасность (постоянный аудит, отсутствие небезопасных алгоритмов), производительность (использование оптимизированных ассемблерных вставок из BoringSSL) и простота API (минимальное количество типов и функций, понятная документация). ring не предоставляет высокоуровневых абстракций, таких как работа с сертификатами или протоколами — для этого существуют отдельные крейты. Вместо этого она даёт строительные блоки, которые можно безопасно комбинировать.

Установка

Для добавления ring в проект на Rust выполните команду в корневой директории проекта:

cargo add ring

Или вручную добавьте в файл Cargo.toml:

[dependencies]
ring = "0.17"

На момент написания статьи актуальная версия — 0.17.x. Убедитесь, что у вас установлен Rust (rustc 1.70+). ring требует наличия компилятора C (gcc, clang или msvc) для сборки нативных компонентов BoringSSL. На Windows может потребоваться установка Build Tools for Visual Studio.

Быстрый старт — минимальный рабочий пример

Создайте новый проект и замените содержимое src/main.rs на следующий код, который вычисляет SHA-256 хеш строки:

use ring::digest::{Context, SHA256};
use ring::rand::SystemRandom;

fn main() {
    // Хеширование SHA-256
    let mut context = Context::new(&SHA256);
    context.update("Hello, ring!".as_bytes());
    let digest = context.finish();
    println!("SHA-256: {:?}", digest);

    // Генерация случайных байтов
    let rng = SystemRandom::new();
    let mut random_bytes = [0u8; 32];
    ring::rand::fill(&rng, &mut random_bytes).unwrap();
    println!("Random bytes: {:02x?}", random_bytes);
}

Запустите программу командой cargo run. Вы увидите хеш SHA-256 и 32 случайных байта в шестнадцатеричном формате.

Основные методы/функции

1. ring::digest::digest

Сигнатура: pub fn digest(algorithm: &'static Algorithm, data: &[u8]) -> Digest

Описание: Вычисляет хеш от данных data с использованием указанного алгоритма (SHA256, SHA384, SHA512, SHA1). Это однопроходная функция для простых случаев.

use ring::digest::{digest, SHA256};

let data = b"Hello, world!";
let result = digest(&SHA256, data);
println!("SHA-256: {:?}", result);

2. ring::digest::Context

Сигнатура: pub struct Context { /* fields omitted */ }
Методы: new(algorithm: &'static Algorithm) -> Self, update(&mut self, data: &[u8]), finish(self) -> Digest

Описание: Позволяет вычислять хеш по частям (потоковый режим). Полезно для больших файлов или сетевых данных.

use ring::digest::{Context, SHA256};

let mut ctx = Context::new(&SHA256);
ctx.update(b"Part 1 ");
ctx.update(b"Part 2 ");
ctx.update(b"Part 3");
let digest = ctx.finish();
println!("Streaming SHA-256: {:?}", digest);

3. ring::hmac::sign

Сигнатура: pub fn sign(key: &Key, data: &[u8]) -> Tag

Описание: Вычисляет HMAC (хеш-код аутентификации сообщения) для данных data с использованием ключа key. Поддерживает SHA256, SHA384, SHA512.

use ring::hmac;

let key = hmac::Key::new(hmac::HMAC_SHA256, b"secret_key");
let tag = hmac::sign(&key, b"message");
println!("HMAC: {:?}", tag);

4. ring::aead::seal_in_place

Сигнатура: pub fn seal_in_place(key: &LessSafeKey, nonce: Nonce, aad: Aad<&[u8]>, data: &mut [u8], tag_out: &mut [u8]) -> Result<(), error::Unspecified>

Описание: Шифрует данные в режиме AEAD (Authenticated Encryption with Associated Data). Поддерживает AES-GCM и ChaCha20-Poly1305. Шифрует данные на месте, добавляя тег аутентификации.

use ring::aead::{Aad, LessSafeKey, Nonce, UnboundKey, AES_256_GCM};
use ring::rand::{SecureRandom, SystemRandom};

let rng = SystemRandom::new();
let mut key_bytes = [0u8; 32];
rng.fill(&mut key_bytes).unwrap();
let unbound_key = UnboundKey::new(&AES_256_GCM, &key_bytes).unwrap();
let key = LessSafeKey::new(unbound_key);

let nonce_bytes = [0u8; 12]; // В реальности должен быть уникальным
let nonce = Nonce::assume_unique_for_key(nonce_bytes);

let mut data = b"Hello, AES-GCM!".to_vec();
let tag_len = AES_256_GCM.tag_len();
let mut tag = vec![0u8; tag_len];
key.seal_in_place(nonce, Aad::empty(), &mut data, &mut tag).unwrap();
println!("Encrypted: {:02x?}", data);
println!("Tag: {:02x?}", tag);

5. ring::aead::open_in_place

Сигнатура: pub fn open_in_place(key: &LessSafeKey, nonce: Nonce, aad: Aad<&[u8]>, data: &mut [u8], tag: &[u8]) -> Result<&mut [u8], error::Unspecified>

Описание: Расшифровывает данные, зашифрованные с помощью seal_in_place. Проверяет тег аутентификации и возвращает расшифрованные данные.

// Продолжение предыдущего примера
let mut ciphertext = data.clone();
let mut tag_copy = tag.clone();
let plaintext = key.open_in_place(nonce, Aad::empty(), &mut ciphertext, &mut tag_copy).unwrap();
println!("Decrypted: {:?}", std::str::from_utf8(plaintext).unwrap());

6. ring::signature::KeyPair::generate

Сигнатура: pub fn generate(alg: &'static SigningAlgorithm, rng: &dyn SecureRandom) -> Result

Описание: Генерирует новую пару ключей для асимметричной подписи. Поддерживает Ed25519, ECDSA (P-256, P-384) и RSA.

use ring::signature::{Ed25519KeyPair, KeyPair};
use ring::rand::SystemRandom;

let rng = SystemRandom::new();
let key_pair = Ed25519KeyPair::generate(&rng).unwrap();
let public_key = key_pair.public_key().as_ref();
println!("Ed25519 public key: {:02x?}", public_key);

7. ring::signature::Signer::sign

Сигнатура: pub fn sign(&self, msg: &[u8]) -> Signature

Описание: Подписывает сообщение msg с использованием закрытого ключа. Возвращает подпись, которую можно проверить с помощью соответствующего открытого ключа.

use ring::signature::{Ed25519KeyPair, Signature};
use ring::rand::SystemRandom;

let rng = SystemRandom::new();
let key_pair = Ed25519KeyPair::generate(&rng).unwrap();
let message = b"Important message";
let signature = key_pair.sign(message);
println!("Signature: {:02x?}", signature.as_ref());

8. ring::signature::UnparsedPublicKey::verify

Сигнатура: pub fn verify(&self, message: &[u8], signature: &[u8]) -> Result<(), error::Unspecified>

Описание: Проверяет подпись signature для сообщения message с использованием открытого ключа. Возвращает Ok(()) при успешной проверке.

use ring::signature::{Ed25519KeyPair, UnparsedPublicKey, ED25519};
use ring::rand::SystemRandom;

let rng = SystemRandom::new();
let key_pair = Ed25519KeyPair::generate(&rng).unwrap();
let message = b"Test message";
let signature = key_pair.sign(message);

let public_key = UnparsedPublicKey::new(&ED25519, key_pair.public_key().as_ref());
public_key.verify(message, signature.as_ref()).unwrap();
println!("Signature verified!");

9. ring::agreement::EphemeralPrivateKey::generate

Сигнатура: pub fn generate(alg: &'static AgreementAlgorithm, rng: &dyn SecureRandom) -> Result

Описание: Генерирует эфемерный закрытый ключ для протокола согласования ключей (X25519 или ECDH). Используется для создания общего секрета между двумя сторонами.

use ring::agreement::{EphemeralPrivateKey, X25519};
use ring::rand::SystemRandom;

let rng = SystemRandom::new();
let private_key = EphemeralPrivateKey::generate(&X25519, &rng).unwrap();
let public_key_bytes = private_key.compute_public_key().unwrap();
println!("X25519 public key: {:02x?}", public_key_bytes.as_ref());

10. ring::agreement::agree_ephemeral

Сигнатура: pub fn agree_ephemeral(private_key: EphemeralPrivateKey, peer_public_key: &[u8], alg: &'static AgreementAlgorithm, kdf: &dyn Kdf) -> Result, error::Unspecified>

Описание: Выполняет согласование ключа по протоколу ECDH. Принимает свой закрытый ключ и открытый ключ другой стороны, возвращает общий секрет.

use ring::agreement::{EphemeralPrivateKey, X25519};
use ring::rand::SystemRandom;

let rng = SystemRandom::new();
let alice_private = EphemeralPrivateKey::generate(&X25519, &rng).unwrap();
let alice_public = alice_private.compute_public_key().unwrap();

let bob_private = EphemeralPrivateKey::generate(&X25519, &rng).unwrap();
let bob_public = bob_private.compute_public_key().unwrap();

let shared_secret = ring::agreement::agree_ephemeral(
    alice_private,
    bob_public.as_ref(),
    &X25519,
    ring::agreement::UnspecifiedKdf::new(),
).unwrap();
println!("Shared secret: {:02x?}", shared_secret);

Реальный практический пример

Ниже представлен полноценный пример защищённого обмена сообщениями между двумя сторонами (Алисой и Бобом) с использованием шифрования ChaCha20-Poly1305 и согласования ключей X25519. Пример включает генерацию ключей, шифрование, расшифровку и проверку подписи.

use ring::aead::{Aad, LessSafeKey, Nonce, UnboundKey, CHACHA20_POLY1305};
use ring::agreement::{EphemeralPrivateKey, X25519};
use ring::digest::{digest, SHA256};
use ring::rand::{SecureRandom, SystemRandom};
use ring::signature::{Ed25519KeyPair, UnparsedPublicKey, ED25519};

/// Генерирует ключевую пару Ed25519 для подписи
fn generate_signing_keypair(rng: &SystemRandom) -> Ed25519KeyPair {
    Ed25519KeyPair::generate(rng).unwrap()
}

/// Выполняет согласование ключа X25519 и возвращает общий секрет
fn perform_key_exchange(
    rng: &SystemRandom,
    peer_public_key: &[u8],
) -> (Vec
                
    
    

Рекомендаций