9个版本
0.2.3 | 2024年6月7日 |
---|---|
0.2.2 | 2024年6月7日 |
0.1.4 | 2024年6月5日 |
#574 在 密码学
263 每月下载次数
24KB
351 行
Rust的可寻址流密码和加密/解密
此crate从密钥生成一个确定性2^64字节的密钥流,并允许应用程序从任何偏移量随机读取该流。
密钥流还可以用于加密或解密大型消息的任意范围。
API文档
使用示例
使用可寻址密钥流填充缓冲区
use seekable_stream_cipher::keccak::StreamCipher;
/// Create a new 32-byte secret key using a secure random number generator
let mut key = [0u8; StreamCipher::KEY_LENGTH];
getrandom::getrandom(&mut key).unwrap();
/// Initialize the stream cipher using the key, and a context.
/// A key used with different contexts produces different key streams.
let st = StreamCipher::new(&key, b"fill test");
/// Fill a buffer with 10000 bytes from the key stream starting at offset 10
let mut out = [0u8; 10000];
st.fill(&mut out, 10).unwrap();
/// Fill another buffer with 50 bytes from key key stream starting at offset 100
let mut out2 = [0u8; 50];
st.fill(&mut out2, 100).unwrap();
/// Verify that the second buffer is a slice of the first one, since the ranges overlap.
assert_eq!(out[90..][..50], out2);
加密/解密任意范围
use seekable_stream_cipher::keccak::StreamCipher;
/// Create a new 32-byte secret key using a secure random number generator
let mut key = [0u8; StreamCipher::KEY_LENGTH];
getrandom::getrandom(&mut key).unwrap();
/// Initialize the stream cipher using the key, and a context.
/// A key used with different contexts produces different key streams.
let st = StreamCipher::new(&key, b"encryption test");
/// Create a large message filled with random junk
let mut msg = [0u8; 10000];
getrandom::getrandom(&mut msg).unwrap();
/// Create a copy of the message
let mut msg2 = msg.clone();
/// Encrypt range 5..500 of that message
st.apply_keystream(&mut msg2[5..500], 5);
/// Now, that range is encrypted.
assert!(msg != msg2);
/// Decrypt range 5.500 by calling the same function as for encryption, with the same parameters
st.apply_keystream(&mut msg2[5..500], 5);
/// Check that we recovered the original message.
assert_eq!(msg, msg2);
原语选择
理由
原语选择的主要标准是
- 能够从任意偏移量以字节粒度加密,且无需访问先前加密的内容。
- 在WebAssembly环境中的安全和效率。
第一个限制条件排除了任何大于8位的块密码。
在WebAssembly上,基于AES的构造要么非常慢,要么容易受到侧信道攻击的保护。
这让我们只剩下了不常见/已弃用的流密码、ChaCha20变体或基于标准公开排列的流密码。
由于Rust和WebAssembly的限制,ChaCha20的性能与本地优化实现相比并不出色,尤其是在x86_64平台上的Wasmtime(不知何故,在aarch64上的结果更一致)。
Keccak和Ascon更适合WebAssembly。它们只在64位字上使用位运算,需要最少的临时寄存器,并且可以被大多数编译器有效地调度。此外,它们不使用查找表,因此与AES等相比,它们在本质上更安全,不易受到侧信道攻击。
使用Keccak,可以通过Keccak-f[1600]使用12轮和一个320位容量构建至少128位安全性的流密码,留下160字节用于速率。通过将速率减少到128字节,可以实现256位安全性。
Ascon排列更小。我们使用Ascon-PRF构造的参数,它将上下文作为32字节块吸收,但最大输出速率是16字节。
基准测试
WebAssembly (Wasmtime, Zen4 CPU)
原语 | 吞吐量 |
---|---|
Keccak | 398.54 M/s |
Ascon | 232.58 M/s |
ChaCha20/20 | 69.33 M/s |
ChaCha20/12 | 107.81 M/s |
ChaCha20/8 | 147.85 M/s |
AES-128 | 77.55 M/s |
在WebAssembly上,Keccak和Ascon是最快的选项,Keccak略占优势。但Ascon可能更适合非常小的输出,并且需要更少的内存。
即使经过圆式缩减,ChaCha20 仍然很慢。这可能是由于编译器优化效率低下,但在 Zig 中,基于 Ascon 和 Keccak 的构造在 WebAssembly 上的性能比其他构造要好。
这个包实现了基于 Ascon 的流密码和基于 Keccak 的流密码。这两种选择都是 WebAssembly 的不错选择,并且建立在传统的 NIST 认可的构建块之上。
可以通过使用 SIMD 指令来提高性能,但它们在 WebAssembly 运行时还不稳定,也不被普遍支持。
依赖项
~39KB