2 个版本
0.0.5 | 2023 年 11 月 15 日 |
---|---|
0.0.4 | 2023 年 11 月 11 日 |
0.0.3 |
|
0.0.2 |
|
0.0.1 |
|
#1085 在 密码学
每月 32 次下载
100KB
2K SLoC
雅达查
概述
请参阅 yadacha.com 获取快速入门指南。
雅达查是一种对称加密算法,它结合了 ChaCha20 和非常大的私有密钥。所有现代加密都依赖于未经证实的假设(参见‘指数时间假设’或‘计算困难假设’)。这通常被简化为‘P 与 NP 问题’。
唯一可以证明安全的加密是 一次性密码。由于需要一种安全的方式来传输垫(即私有密钥),其大小需要与明文相同,所以您也可以直接安全地传输明文。
雅达查允许垫(即私有密钥)小于明文,通过使用 ChaCha20 的变体来扩展它,使其随着每条消息而改变。
未来可能会出现量子计算 NP 求解器(它可能已经存在,参见 Jarek Duda 的 2WQC)。这样的算法很可能会破坏所有具有小型私有密钥的算法。雅达查通过使用巨大的私有密钥来防止这种情况,这些私有密钥在量子计算机中可以持续很长时间:最大的密钥是 10 TB(是的,10 太字节),这意味着您需要一台 80 tera-qubits 的量子计算机才能仅保存该密钥。
这不会很快发生。
雅达查的最小密钥是 16 KB,足够小,甚至可以在小型嵌入式板上运行(已测试)。要破解它需要一台拥有超过十万个量子比特的量子计算机。
这也不会很快发生。
尽管国家黑客可能能够通过极细微的手段窃取非常小的密钥以避免检测,但窃取一个 16 KB 的密钥要困难得多,更不用说 10 TB 的密钥了。
标准密钥大小为
- 16 KB:适用于大多数使用情况,甚至在低功耗硬件上也能正常运行
- 8 MB 密钥:可适应您的服务器 L3 缓存,适用于更关键的通信
- 1 TB 密钥:可存储在便携式、移动 SSD 驱动器中
- 10 TB 密钥:用于在关键位置之间进行安全通信
技术细节
灵感来源于 D. J. Bernstein 的 ChaCha20。另请参阅 rfc8439。
Yadacha通过在每个双轮的最后应用64个替换盒来扩展ChaCha20。常规ChaCha20对所有私钥只有一个SAT形式。添加随机替换盒使得将算法转换为SAT形式变得不可能,因为位之间的约束现在依赖于替换盒,它们现在是私钥:因此,SAT形式取决于密钥。虽然添加64个替换盒需要64*256=16 KB(对于最小的yadacha密钥大小),但由于一些s-box无效,单个替换盒的实际熵约为log_2(256!) ~= 1684位,而不是256*8=2048位。因此,整个私钥的熵为64*1684/8=13.2 KB。对于更大的密钥大小,效果类似。
尽管只应用一次替换盒仍可防止SAT形式转换,但在每个双轮应用它们确保它们相互深入交互。
为什么修改ChaCha20而不是AES256?ChaCha20在无硬件支持的情况下性能更好,因此无SAT ChaCha20将比假设的无SAT AES256表现得更好。
请注意,Yadacha目前不是常数时间的,因此在不适当考虑潜在的时序攻击的情况下不应使用它。
yadacha私钥的特殊属性
任何单个比特翻转都将将有效私钥转换为无效私钥。为了使双比特翻转不被检测到,第二个翻转需要发生在特定比特上。即使对于最小的密钥,双比特翻转也有超过99.999%的几率被捕获,前提是它们彼此独立。三比特翻转(以及所有奇数个比特翻转)将被捕获,并且偶数比特翻转(与奇数比特翻转相反)将以高概率被捕获,前提是它们彼此独立。
由单个比特翻转无效的私钥可以被修复为两个不同的有效密钥:然后可以测试哪一个才是正确的。这留给读者作为练习。
关于代码的备注
Yadacha提供为纯Rust no_std库(无依赖项),附带一个利用mmap处理大密钥的cli工具(cli工具有2个依赖项)。测试套件需要rug crate进行常量规范。
如果您想深入了解代码,请先了解lib.rs和yadacha16k.rs,然后按顺序继续:yadacha8m.rs,yadacha1t.rs和yadacha10t.rs。
代码是单线程的,目前不是典型的Rust代码,但应该很容易理解,特别是如果您熟悉ChaCha20。
关于cli工具的1TB和10TB密钥的备注
如果您机器上有足够的RAM,可以通过验证密钥来预加载密钥,然后设置ALWAYS_MMAP=1环境变量以获得加密/解密的合理速度。
快速示例
Cargo.toml
[package]
name = "ytest"
version = "0.1.0"
edition = "2021"
[dependencies]
yadacha = "0.0.5"
getrandom = "0.2.10"
main.rs
use yadacha::*;
struct RandomSource {}
impl yadacha::SeedRNG for RandomSource {
fn fill(&mut self, buf: &mut [u8]) {
getrandom::getrandom(buf).unwrap();
// or rdrand
}
}
fn main() {
let mut data: &mut [u8] = &mut [2, 3, 5, 7, 11, 13, 17, 19];
println!("data (plaintext): {:?}", data);
let mut rng = RandomSource{}; // to use getrandom
//let mut rng = prng::new_fixed_yadarng(42); // to use fixed rng
let mut key_16k: Key16k = [[0u8; 256]; 64];
yadacha16k::init_key_16k(&mut rng, &mut key_16k);
let nonce : Nonce16k = yadacha16k::new_nonce_16k(&mut rng);
let msg_nonce : MsgNonce16k = yadacha16k::new_msg_nonce_16k(&mut rng, data);
let mut yada = yadacha16k::new_yadacha16k(&key_16k, &nonce, &msg_nonce);
println!("key_16k (part): {:?}", &key_16k[0][..8]);
println!("nonce: {:?}", nonce);
println!("msg_nonce: {:?}", msg_nonce);
let associated_data = (data.len() as u64).to_le_bytes(); // or something else
println!("associated_data: {:?}", associated_data);
yada.init_encode(&associated_data);
yada.encode(&mut data);
let tag = yada.finalize();
// data is encrypted, tag is [u8;64].
println!("data (ciphertext): {:?}", data);
println!("tag (part): {:?}", &tag[..8]);
let mut yada = yadacha16k::new_yadacha16k(&key_16k, &nonce, &msg_nonce);
yada.init_decode(&associated_data);
yada.decode(&mut data);
let valid = yada.validate(tag);
assert!(valid);
// data is decrypted
println!("data (decrypted): {:?}", data);
}
使用getrandom rng的示例输出
(您的输出将不同)
data (plaintext): [2, 3, 5, 7, 11, 13, 17, 19]
key_16k (part): [23, 122, 130, 212, 72, 85, 52, 134]
nonce: [876659832, 2034512187, 4227600364, 1904335117, 2528694799, 741854543, 3945783584, 3561167336]
msg_nonce: [3767118128, 1500355652]
associated_data: [8, 0, 0, 0, 0, 0, 0, 0]
data (ciphertext): [206, 86, 63, 114, 232, 20, 222, 69]
tag (part): [221, 209, 249, 239, 197, 16, 152, 120]
data (decrypted): [2, 3, 5, 7, 11, 13, 17, 19]
data (plaintext): [2, 3, 5, 7, 11, 13, 17, 19]
key_16k (part): [98, 81, 95, 24, 184, 192, 215, 251]
nonce: [1756477226, 3512108898, 3482418452, 2734284285, 3102698299, 1823011375, 2586904859, 3783255701]
msg_nonce: [216179676, 2814286635]
associated_data: [8, 0, 0, 0, 0, 0, 0, 0]
data (ciphertext): [80, 105, 31, 147, 114, 121, 203, 196]
tag (part): [145, 41, 188, 227, 153, 158, 93, 79]
data (decrypted): [2, 3, 5, 7, 11, 13, 17, 19]
[...]
使用固定rng的输出
(在v0.0.5版本中,当使用prng::new_fixed_yadarng(42)时,应该完全匹配)
data (plaintext): [2, 3, 5, 7, 11, 13, 17, 19]
key_16k (part): [218, 254, 24, 241, 6, 43, 167, 152]
nonce: [4063157125, 3856611287, 16257720, 3825573552, 1180332863, 3315114498, 2462000061, 3047591357]
msg_nonce: [2158674, 4156766091]
associated_data: [8, 0, 0, 0, 0, 0, 0, 0]
data (ciphertext): [69, 192, 68, 122, 241, 72, 195, 65]
tag (part): [146, 19, 75, 193, 224, 7, 92, 31]
data (decrypted): [2, 3, 5, 7, 11, 13, 17, 19]
许可协议
Yadacha根据您的选择受Apache License,Version 2.0或MIT许可证的许可。
。
贡献
除非您明确声明,否则,根据Apache-2.0许可证定义,您有意提交的任何贡献,包括但不限于提交到工作的内容,应如上所述双重许可,不附加任何额外条款或条件。
变更日志
- v0.0.5 添加测试,外观更改
- v0.0.4 修复测试
- v0.0.3重大更改 - 对加密的一些小调整,一些清理
- v0.0.2 添加Cargo.toml中缺失的存储库
- v0.0.1 首次发布
依赖项
~0–300KB