#allocator #constant-time #debugging #crypto #overflow #random

secrets

加密密钥的保护内存

10个版本 (3个稳定版)

1.2.0 2022年3月26日
1.1.0 2020年7月27日
1.0.0 2020年3月13日
0.12.1 2019年7月26日
0.9.1 2014年12月16日

#242 in 加密学

Download history 137/week @ 2024-03-14 151/week @ 2024-03-21 108/week @ 2024-03-28 48/week @ 2024-04-04 105/week @ 2024-04-11 74/week @ 2024-04-18 70/week @ 2024-04-25 89/week @ 2024-05-02 109/week @ 2024-05-09 65/week @ 2024-05-16 63/week @ 2024-05-23 705/week @ 2024-05-30 1170/week @ 2024-06-06 62/week @ 2024-06-13 710/week @ 2024-06-20 295/week @ 2024-06-27

2,502 次每月下载
4 个crate中使用 (通过 edcert)

MIT/Apache

120KB
1.5K SLoC

secrets

Build Status

Cargo Crate Docs License

secrets 是一个库,帮助Rust程序员在内存中安全地保存加密密钥。

它主要是对libsodium提供的内存保护工具的便捷包装。

在栈上分配的固定大小缓冲区获得以下保护

  • mlock(2) 被调用来处理底层内存
  • 当不再使用时,底层内存会被清零
  • 它们在其整个生命周期内被借用,因此不能被移动
  • 它们在常数时间内进行比较
  • 它们被Debug阻止打印
  • 它们被阻止进行Clone操作

固定和可变大小的缓冲区可以在堆上分配,并获得以下保护

  • 除非作用域内有活动的借用,否则底层内存通过mprotect(2)受保护,以免被读取或写入
  • mlock(2) 被调用来处理分配的内存
  • 当不再使用时,底层内存会被清零
  • 通过不可访问的守卫页面检测溢出和下溢,导致立即段错误和程序终止
  • 当使用canaries释放内存时,检测到写入内存的短下溢,将导致段错误和程序终止

恐慌安全性

此库明确不是恐慌安全的。为了确保保护内存空间的安全,如果此库无法执行其宣传的保证,它将引发恐慌。

同样,如果(并且仅当)该库检测到某些安全违规时,它将导致段错误。例如,如果某个进程尝试直接读取或写入尚未正确解锁的内存内容,或者如果canaries已被覆盖,则可能会发生这种情况。该库已被编写以确保通过良好的Rust代码不可能引起此类违规,因此,这些违规应仅作为安全漏洞的结果而出现。

示例

示例:生成加密密钥

Secret::<[u8; 16]>::random(|s| {
    // use `s` as if it were a `&mut [u8; 16]`
    //
    // the memory is `mlock(2)`ed and will be zeroed when this closure
    // exits
});

示例:从磁盘加载主密钥并从中生成子密钥

use std::fs::File;
use std::io::Read;

use libsodium_sys as sodium;
use secrets::SecretBox;

const KEY_LEN : usize = sodium::crypto_kdf_KEYBYTES     as _;
const CTX_LEN : usize = sodium::crypto_kdf_CONTEXTBYTES as _;

const CONTEXT : &[u8; CTX_LEN] = b"example\0";

fn derive_subkey(
    key:       &[u8; KEY_LEN],
    context:   &[u8; CTX_LEN],
    subkey_id: u64,
    subkey:    &mut [u8],
) {
    unsafe {
        libsodium_sys::crypto_kdf_derive_from_key(
            subkey.as_mut_ptr(),
            subkey.len(),
            subkey_id,
            context.as_ptr().cast(),
            key.as_ptr()
        );
    }
}

let master_key = SecretBox::<[u8; KEY_LEN]>::try_new(|mut s| {
    File::open("example/master_key/key")?.read_exact(s)
})?;

let subkey_0 = SecretBox::<[u8; 16]>::new(|mut s| {
    derive_subkey(&master_key.borrow(), CONTEXT, 0, s);
});

let subkey_1 = SecretBox::<[u8; 16]>::new(|mut s| {
    derive_subkey(&master_key.borrow(), CONTEXT, 1, s);
});

assert_ne!(
    subkey_0.borrow(),
    subkey_1.borrow(),
);

示例:在内存中安全地存储解密后的密文

use std::fs::File;
use std::io::Read;

use libsodium_sys as sodium;
use secrets::{SecretBox, SecretVec};

const KEY_LEN   : usize = sodium::crypto_secretbox_KEYBYTES   as _;
const NONCE_LEN : usize = sodium::crypto_secretbox_NONCEBYTES as _;
const MAC_LEN   : usize = sodium::crypto_secretbox_MACBYTES   as _;

let mut key        = SecretBox::<[u8; KEY_LEN]>::zero();
let mut nonce      = [0; NONCE_LEN];
let mut ciphertext = Vec::new();

File::open("example/decrypted_ciphertext/key")?
    .read_exact(key.borrow_mut().as_mut())?;

File::open("example/decrypted_ciphertext/nonce")?
    .read_exact(&mut nonce)?;

File::open("example/decrypted_ciphertext/ciphertext")?
    .read_to_end(&mut ciphertext)?;

let plaintext = SecretVec::<u8>::new(ciphertext.len() - MAC_LEN, |mut s| {
    if -1 == unsafe {
        sodium::crypto_secretbox_open_easy(
            s.as_mut_ptr(),
            ciphertext.as_ptr(),
            ciphertext.len() as _,
            nonce.as_ptr(),
            key.borrow().as_ptr(),
        )
    } {
        panic!("failed to authenticate ciphertext during decryption");
    }
});

assert_eq!(
    *b"attack at dawn",
    *plaintext.borrow(),
);

许可证

根据您选择之一许可

任选。

依赖项

~0–4.5MB
~21K SLoC