#stream-cipher #stream #cipher #keccak #ascon

no-std seekable-stream-cipher

可寻址流密码和加密/解密

9个版本

0.2.3 2024年6月7日
0.2.2 2024年6月7日
0.1.4 2024年6月5日

#574密码学

Download history 575/week @ 2024-06-04 34/week @ 2024-06-11

263 每月下载次数

MIT 许可证

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