使用 Ring 构建安全的 Rust 应用:现代开发者的内存安全密码学

发布: (2025年12月16日 GMT+8 03:29)
5 min read
原文: Dev.to

Source: Dev.to

引言

当你编写处理机密信息——密码、金融数据、私人消息——的代码时,容不得任何失误。哪怕是一个字节放错位置或是一次计时差异,都可能导致全部崩溃。历史上,密码学一直是个令人望而生畏的任务,因为许多库都是用 C 编写的,完全信任程序员本身。缓冲区溢出、内存泄漏以及侧信道攻击都是常见的陷阱。

为什么选择 Rust 和 Ring?

Rust 在代码运行前就阻止了整类错误。Ring crate 基于 Rust 的安全保证,提供以安全为首要前提的密码学原语。其核心使用安全的 Rust 编写,剩余的少量 C 组件经过严格审查并实现为常量时间,从设计上消除了许多漏洞类别。

核心特性

  • 哈希函数:SHA‑256、SHA‑512 等。
  • 安全随机数生成
  • 数字签名:Ed25519 等。
  • 密钥协商:Diffie‑Hellman、X2550。
  • 密码哈希:PBKDF2。

生成安全随机数

在密码学中,你需要不可预测的随机性。Ring 提供了一个直接的 API,使用操作系统的安全随机源。

use ring::rand;

fn get_secure_random() -> Result {
    let rng = rand::SystemRandom::new();
    let mut random_bytes = [0u8; 32]; // 32 random bytes
    rng.fill(&mut random_bytes)?;
    Ok(random_bytes)
}

SystemRandom 在 Linux 上从 /dev/urandom(或平台特定的源)读取,并保证要么操作成功,要么返回错误——不存在隐藏的失败模式。

使用 PBKDF2 进行密码哈希

永远不要直接存储密码。应存储加盐、故意慢速的哈希,以防止彩虹表攻击。Ring 提供了 PBKDF2 原语。

use ring::{digest, pbkdf2};
use std::num::NonZeroU32;

fn hash_password(password: &str, salt: &[u8]) -> Vec {
    let iterations = NonZeroU32::new(100_000).expect("Non-zero iteration count");
    let mut derived_key = vec![0u8; digest::SHA256.output_len]; // 32‑byte output

    pbkdf2::derive(
        pbkdf2::PBKDF2_HMAC_SHA256,
        iterations,
        salt,
        password.as_bytes(),
        &mut derived_key,
    );

    derived_key
}

fn verify_password(password: &str, salt: &[u8], stored_hash: &[u8]) -> bool {
    let iterations = NonZeroU32::new(100_000).unwrap();
    pbkdf2::verify(
        pbkdf2::PBKDF2_HMAC_SHA256,
        iterations,
        salt,
        password.as_bytes(),
        stored_hash,
    )
    .is_ok()
}

NonZeroU32 确保迭代次数永远不为零,从编译时就防止了关键的安全错误。每个密码都应拥有唯一的、随机的盐,并与派生的哈希一起存储。

数字签名

签名用于验证真实性和完整性。Ring 支持现代算法,如 Ed25519。

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

fn sign_a_message() -> Result> {
    // 1. Secure RNG
    let rng = rand::SystemRandom::new();

    // 2. Generate a private/public key pair (PKCS#8)
    let pkcs8_bytes = signature::Ed25519KeyPair::generate_pkcs8(&rng)?;

    // 3. Create a key pair object
    let key_pair = signature::Ed25519KeyPair::from_pkcs8(pkcs8_bytes.as_ref())?;

    // Public key (shareable)
    let public_key_bytes = key_pair.public_key().as_ref();

    // 4. Sign a message
    let message = b"Critical system update v2.1";
    let signature = key_pair.sign(message);

    // 5. Verification (performed by the receiver)
    let peer_public_key = signature::UnparsedPublicKey::new(&signature::ED25519, public_key_bytes);
    match peer_public_key.verify(message, signature.as_ref()) {
        Ok(()) => println!("Signature is valid. Update is authentic."),
        Err(_) => println!("DANGER: Signature verification failed!"),
    }

    Ok(())
}

私钥(pkcs8_bytes)必须保密,而公钥可以自由分发。API 明确地强制了这种分离。

与 OpenSSL 的对比

OpenSSL 是一个庞大的 C 语言库。其广度带来了复杂性和大量严重漏洞的历史(例如 Heartbleed)。Ring 采用了不同的方式:

  • 更小、更精挑细选的 API:仅提供现代、经过审查的算法。
  • 内存安全:Rust 代码消除了许多错误类别。
  • 常量时间实现:降低侧信道风险。

这种聚焦的设计引导开发者在不受遗留选项负担的情况下,正确且安全地使用密码学功能。

密钥协商(Diffie‑Hellman)

Ring 也提供安全的密钥交换原语(例如 X25519)。虽然完整示例超出本段范围,但典型工作流包括:

  1. 生成临时密钥对。
  2. 交换公钥。
  3. 使用 agreement::agree_ephemeral 派生共享密钥。

所有操作都是常量时间,并使用安全的 Rust 抽象实现。

Back to Blog

相关文章

阅读更多 »