7个稳定版本
3.0.0 | 2023年1月26日 |
---|---|
2.0.2 | 2023年1月29日 |
2.0.1 | 2022年9月8日 |
1.1.0 | 2022年9月5日 |
1.0.1 | 2022年4月12日 |
#97 in 密码学
15,889 每月下载量
用于 rgp
240KB
5K SLoC
classic-mceliece-rust
这是一个纯Rust safe-rust实现的Classic McEliece后量子方案。
- Classic McEliece是一种基于码的密钥封装机制(KEM)
- 该实现基于NIST第4轮的Classic McEliece参考实现
- 实现没有使用任何并发技术(SIMD/线程/…,除了可能在CPU上自动向量化)
- 它依赖于
sha3
作为SHA-3实现,以及aes
作为AES分组密码实现(用作RNG) - 它通过了C参考实现的100个测试用例
- 它实现了Classic McEliece KEM的所有10种变体
- 实现运行在现代计算机上需要100毫秒(
mceliece348864
)到500毫秒(mceliece8192128f
) - 在软件指令级别上实现为常数时间
- 随机数生成器基于AES256计数模式
- 首次在1978年描述,这个密码方案在安全性分析方面有着丰富的历史。然而,其大的公钥大小通常限制了其采用。
这10种变体有以下指定的标识符
mceliece348864
mceliece348864f
mceliece460896
mceliece460896f
mceliece6688128
mceliece6688128f
mceliece6960119
mceliece6960119f
mceliece8192128
mceliece8192128f
谁应该使用它?
任何想要使用Classic McEliece在双方之间协商密钥的人。
如何使用它来在堆上存储密钥(默认功能alloc
)?
将以下内容添加到您的Cargo.toml
[dependencies]
classic-mceliece-rust = "3.0"
要使用特定的Classic McEliece变体,您需要使用相应的功能标志导入它
[dependencies]
classic-mceliece-rust = { version = "3.0", features = ["mceliece6960119"] }
假设此依赖项,使用堆分配密钥和*_boxed
KEM步骤函数是使用库的最简单和最便捷的方法。首先,我们导入它们
#[cfg(feature = "alloc")]
use classic_mceliece_rust::{keypair_boxed, encapsulate_boxed, decapsulate_boxed};
接下来,我们运行KEM并提供相应的密钥。这里,我们考虑了一个在单独的线程中运行它的例子(请注意,该示例也依赖于rand crate)
#[cfg(feature = "alloc")] {
use classic_mceliece_rust::{keypair_boxed, encapsulate_boxed, decapsulate_boxed};
fn run_kem() {
let mut rng = rand::thread_rng();
// Alice computes the keypair
let (public_key, secret_key) = keypair_boxed(&mut rng);
// Send `secret_key` over to Bob.
// Bob computes the shared secret and a ciphertext
let (ciphertext, shared_secret_bob) = encapsulate_boxed(&public_key, &mut rng);
// Send `ciphertext` back to Alice.
// Alice decapsulates the ciphertext...
let shared_secret_alice = decapsulate_boxed(&ciphertext, &secret_key);
// ... and ends up with the same key material as Bob.
assert_eq!(shared_secret_bob.as_array(), shared_secret_alice.as_array());
}
fn main() {
std::thread::Builder::new()
// This library needs quite a lot of stack space to work
.stack_size(2 * 1024 * 1024)
.spawn(run_kem)
.unwrap()
.join()
.unwrap();
}
}
请注意,Classic McEliece中的公钥非常大(介于255 KB和1.3 MB之间)。因此,运行该算法需要大量内存。您需要考虑在哪里存储它。在这个API中,优点是……
- 您不需要手动处理内存
- 在Windows上,对
keypair
的调用比默认提供的堆栈要大。可以通过堆分配API避免这种堆栈大小限制(请参阅下面的Windows说明)。
如果禁用了分配功能(alloc
,这是一个禁用的功能),如何使用它来存储堆栈上的密钥?
另一个选项是不使用堆分配API,而使用提供的堆栈分配API。其优点是
- 堆栈分配在
no_std
环境中也有效。 - 在一些微控制器平台上,没有可用的堆。
- 通常,堆栈(解)分配比堆(解)分配要快
因此,在本节中,我们想向您展示如何在没有堆的情况下使用此API。为此,您需要禁用默认启用的功能alloc
(此行保留了默认功能zeroize
,但删除了默认功能alloc
)
classic-mceliece-rust = { version = "3.0", default-features = false, features = ["zeroize"] }
那么如何使用API呢(请注意,该示例也依赖于rand crate)?
use classic_mceliece_rust::{keypair, encapsulate, decapsulate};
use classic_mceliece_rust::{CRYPTO_BYTES, CRYPTO_PUBLICKEYBYTES, CRYPTO_SECRETKEYBYTES};
fn main() {
let mut rng = rand::thread_rng();
// Please mind that `public_key_buf` is very large.
let mut public_key_buf = [0u8; CRYPTO_PUBLICKEYBYTES];
let mut secret_key_buf = [0u8; CRYPTO_SECRETKEYBYTES];
let (public_key, secret_key) = keypair(&mut public_key_buf, &mut secret_key_buf, &mut rng);
let mut shared_secret_bob_buf = [0u8; CRYPTO_BYTES];
let (ciphertext, shared_secret_bob) = encapsulate(&public_key, &mut shared_secret_bob_buf, &mut rng);
let mut shared_secret_alice_buf = [0u8; CRYPTO_BYTES];
let shared_secret_alice = decapsulate(&ciphertext, &secret_key, &mut shared_secret_alice_buf);
assert_eq!(shared_secret_bob.as_array(), shared_secret_alice.as_array());
}
在这里,您可以看到密钥是如何显式分配的。
关于Windows的说明
如果您希望您的程序可以与堆栈分配兼容并且不会意外崩溃,您可能需要在具有足够大的堆栈大小的专用线程中运行整个密钥交换。此代码片段显示了这一思路
std::thread::Builder::new()
.stack_size(4 * 1024 * 1024)
.spawn(|| {/* Run the KEM here */})
.unwrap();
功能kem:RustCrypto API
如果启用了kem
功能,密钥封装和解封装也可以通过kem
crate中的标准特质来完成。
功能zeroize:从内存中清除秘密
如果启用了zeroize
功能(默认情况下是启用的),则包含任何秘密内容的所有密钥类型都实现了Zeroize
和ZeroizeOnDrop
。这使得它们在超出作用域时清除内存,从而降低了秘密密钥材料以某种方式泄露的风险。
请注意,这当然使得您传递给库的任何缓冲区对读取密钥都无用了。而不是尝试从您传入的缓冲区中检索密钥材料,请从as_array
方法中获取。
#[cfg(not(windows))] {
use classic_mceliece_rust::keypair;
use classic_mceliece_rust::{CRYPTO_PUBLICKEYBYTES, CRYPTO_SECRETKEYBYTES};
let mut rng = rand::thread_rng();
let mut pk_buf = [0u8; CRYPTO_PUBLICKEYBYTES];
// Initialize to non-zero to show that it has been set to zero by the drop later
let mut sk_buf = [255u8; CRYPTO_SECRETKEYBYTES];
// This is the WRONG way of accessing your keys. The buffer will
// be cleared once the `PrivateKey` returned from `keypair` goes out of scope.
// You should not rely on that array for anything except providing a temporary storage
// location to this library.
#[cfg(feature = "zeroize")]
{
let (_, secret_key) = keypair(&mut pk_buf, &mut sk_buf, &mut rng);
drop(secret_key);
// Ouch! The array only has zeroes now.
assert_eq!(sk_buf, [0; CRYPTO_SECRETKEYBYTES]);
}
// Correct way of getting the secret key bytes if you do need them. However,
// if you want the secrets to stay secret, you should try to not read them out of their
// storage at all
{
let (_, secret_key) = keypair(&mut pk_buf, &mut sk_buf, &mut rng);
assert_ne!(secret_key.as_array(), &[0; CRYPTO_SECRETKEYBYTES]);
}
}
如何运行它?
本库附带两个示例
$ cargo run --example basic
输出使用Alice/Bob对消息进行注释,以说明哪些数据由哪一方处理。《katkem》示例实现了NIST PQC框架的一部分经典请求/响应文件结构。
$ cargo run --example katkem PQCkemKAT_935.req PQCkemKAT_935.rsp
$ cargo run --example katkem PQCkemKAT_935.rsp
可以通过功能标志启用不同的变体
$ cargo run --example katkem --features mceliece6960119 -- PQCkemKAT_1450.req PQCkemKAT_1450.rsp
mceliece348864
是默认变体。您不能同时启用两个变体。
它有多快?
所有数据都使用时钟周期作为单位(越小越好)。v2.0.0的rust实现产生了以下运行时结果
完整的KEM | 密钥对 | 加密 | 解密 | |
mceliece348864 | 460,062,191 | 439,682,143 | 222,424 | 42,046,357 |
mceliece348864f | 244,943,900 | 203,564,820 | 215,971 | 41,648,773 |
mceliece460896 | 1,326,425,784 | 1,434,864,061 | 487,522 | 111,547,716 |
mceliece460896f | 789,636,856 | 652,117,200 | 553,301 | 106,521,703 |
mceliece6688128 | 3,188,205,266 | 2,596,052,574 | 785,763 | 202,774,928 |
mceliece6688128f | 1,236,809,020 | 1,059,087,715 | 826,899 | 203,907,226 |
mceliece6960119 | 2,639,852,573 | 2,532,146,126 | 3,864,285 | 203,959,009 |
mceliece6960119f | 1,165,079,187 | 965,134,546 | 3,416,795 | 197,089,546 |
mceliece8192128 | 3,129,183,262 | 2,754,933,130 | 965,822 | 247,083,745 |
mceliece8192128f | 1,342,438,451 | 1,150,297,595 | 1,068,317 | 242,545,160 |
C参考实现产生了以下运行时结果
完整的KEM | 密钥对 | 加密 | 解密 | |
mceliece348864 | 434,103,000 | 437,187,000 | 187,557 | 73,801,300 |
mceliece348864f | 252,423,000 | 180,235,000 | 189,522 | 73,668,000 |
mceliece460896 | 760,993,000 | 894,497,000 | 298,041 | 154,507,000 |
mceliece460896f | 606,225,000 | 44,906,000 | 297,743 | 154,013,000 |
mceliece6688128 | 1,568,900,000 | 1,780,660,000 | 425,504 | 29,575,000 |
mceliece6688128f | 109,471,000 | 760,298,000 | 414,358 | 298,173,000 |
mceliece6960119 | 3,405,730,000 | 1,694,410,000 | 840,598 | 287,154,000 |
mceliece6960119f | 1,311,130,000 | 942,987,000 | 984,660 | 303,543,000 |
mceliece8192128 | 1,635,550,000 | 760,619,000 | 428,112 | 361,999,000 |
mceliece8192128f | 1,772,530,000 | 1,222,720,000 | 534,503 | 392,729,000 |
测试是在联想Thinkpad x260(Intel Core i5-6200U CPU @ 2.30GHz)上进行的。对于rust,使用了在benches/
中给出的criterion 0.3.5
,对于C,使用了具有PFM支持的Google的benchmark和禁用CPU频率缩放的选项。您可以使用bench
子命令运行基准测试套件,并可选地添加一些变体功能标志
$ cargo bench --features mceliece348864
为了获得最佳的基准测试结果,您可以使用以下命令运行Rust基准测试,并使用CPU优化:RUSTFLAGS="-C target-cpu=native"
,将Cargo基准优化配置为opt-level = 3
,并将链接时间优化设置为lto = "fat"
。有关Cargo基准配置的更多信息,请参阅官方的cargo基准配置文档。
这是正确的吗?
是的,除了通过单元测试(由C实现派生)外,生成的KAT KEM测试文件还具有等效的MD5散列。具体如下…
变体 | 预期的MD5散列 |
mceliece348864 | 11fd67ba1e2b93cceaec6f5e6fe4ddd1 |
mceliece348864f | 7a6f5262fa013fe7eedda0765a625789 |
mceliece460896 | c9acefa82aa705cd324f12df532744c2 |
mceliece460896f | cb08e0e3f2122c62692111d684f1cbe7 |
mceliece6688128 | 7e300cc0990b05f5edca3219ac769023 |
mceliece6688128f | 6d959c2bf54f7d3576a8e49475a74df5 |
mceliece6960119 | b4960a35e249d55fd48371f793608aa5 |
mceliece6960119f | 2f5d759cb579c6f85c1ee1306082ffdf |
mceliece8192128 | 26a47e6d01eec28e91abfdbdf19c3067 |
mceliece8192128f | a4cd676dc2c774d644f18de05762c51c |
源代码在哪里?
在github上。
内容的许可是什么?
变更日志
请参阅变更日志
我可以在哪里要求您修复一个错误?
在github上。
依赖项
~1.5MB
~15K SLoC