1. 引言
2025年2月,Zama 发布了 TFHE-rs v1.0,这是 TFHE-rs 库的第一个稳定版本。这标志着一个重要的里程碑,稳定了 x86 CPU 后端的高级 API,同时确保了向后兼容性。------即,现在可以依赖 TFHE-rs API,而不必担心未来更新中出现重大变化。
此版本中最显著的改进是:
- 关键参数的细化,这增强了密码学安全性,保留了性能并优化了它们以用于分布式协议。
- 还引入了官方手册和简化的贡献流程。
值得注意的是,计算错误的概率已降至 2 − 128 2^{ -128} 2−128 以下,同时保持了性能。从实际角度来看,这意味着发生错误的可能性与破坏现代密码学标准一样微不足道。
2. TFHE-rs 手册第一版
随着 TFHE-rs v1.0 的发布,Zama 发布了其TFHE-rs 手册的第一版,详细介绍了后端的所有实现。
TFHE-rs 手册涵盖:
- 底层算法,包括引导等低级操作;
- 实现同态整数级别运算的方法和算法,特别是针对 FheUint 和 FheInt;
- 关键功能的实现细节,如压缩;
- 深入的技术基准,以供进一步分析。
另一个重要点是,与文档不同,该手册重点关注高斯噪声分布,与学术文献中使用的标准定义一致。
3. 细化密码学参数,以实现最高安全级别
此版本的一个核心增强功能是密码学参数的改进,将计算错误的概率从小于 2 − 64 2^{-64} 2−64 降低到小于 2 − 128 2^{ -128} 2−128 。
尽管密码学参数通常对用户隐藏,但它们对于 TFHE-rs 来说至关重要,可确保计算的安全性、效率和正确性。
直到最近,TFHE-rs 的标准参数集才保证了计算正确性,错误概率低于 2 − 64 2^{ -64} 2−64 ------对于传统的client-server用例来说,几乎可以忽略不计。然而,最近的研究表明,在加密和解密数据都可能被访问的情况下,某些理论上的攻击可能会出现。虽然以前的参数集在计算上仍然不可行 ------平均需要 2 63 2^{63} 263 次计算才能成功------但此更新进一步增强了对此类可能性的安全性。
Zama 设计了 TFHE-rs,为所有应用程序提供最高级别的安全性。借助 TFHE-rs v1.0,密码学参数已得到改进,可将计算错误概率降低到 2 − 128 2^{ -128} 2−128 以下,符合标准密码学安全级别并确保稳健性------即使在分布式协议中也是如此。
通常,将故障概率从 2 − 64 2^{-64} 2−64 降低到 2 − 128 2^{ -128} 2−128 至少会使计算时间增加一倍。然而,TFHE-rs 通过实施一种称为drift mitigation漂移缓解的新技术将减速限制在 10% 左右(详情见2024年论文《Drifting Towards Better Error Probabilities in Fully Homomorphic Encryption Schemes》)。
由于 TFHE-rs 是 Zama 区块链协议的支柱,开发人员可以使用 FHE 编写机密智能合约。此新版本旨在更好地支持分布式协议(其中私钥和公钥生成可能由多个用户共享),TFHE-rs v1.0 现在默认使用 TUniform 噪声分布------Uniform 的这种变体避免了高斯分布施加的约束。
对于喜欢传统方法的用户,基于高斯分布的密码学参数仍然可用。
TFHE-rs 文档提供了有关选择密码学参数和理解不同分布的全面指导,详情参见Zama->FHE Computation->Core workflow->Parameters。
为了获得更深入的技术见解,使用新参数集执行的基准测试(包括整数运算和引导等低级操作的基准测试)也可在Zama->Get Started->Benchmarks->CPU Benchmarks->Integer文档中获取。
4. 一个实际的例子
下面是如何在client-server设置中使用 TFHE-rs 的简单演示。
为了使这个例子简单明了,有意省略了一些高级功能,比如公钥加密及其相关的零知识证明------这些功能通常用于分布式环境中,让用户共享一个通用加密密钥并验证密文是否正确形成。
此示例重点关注核心工作流程,模拟客户端和服务器之间的交互:
- 1)客户端加密:客户端使用其私钥加密数据并将密文发送给服务器。
- 2)服务器端计算:服务器在不访问明文的情况下处理加密数据。如果需要,它可以压缩数据进行存储,稍后再解压以进行进一步的同态运算。
- 3)客户端解密:计算完成后,服务器将加密结果返回给客户端,客户端解密得到最终输出。
rust
use tfhe::prelude::*;
use tfhe::shortint::parameters::{COMP_PARAM_MESSAGE_2_CARRY_2, PARAM_MESSAGE_2_CARRY_2};
use tfhe::{
set_server_key, CompressedCiphertextListBuilder, FheAsciiString, FheBool, FheInt64, FheUint16,
FheUint2, FheUint32,
};
fn main() {
// Use aliases for ease of use, do not use aliases for production (not stable through time)
let config = tfhe::ConfigBuilder::with_custom_parameters(PARAM_MESSAGE_2_CARRY_2)
.enable_compression(COMP_PARAM_MESSAGE_2_CARRY_2)
.build();
// On the client side, generate the ClientKey, must remain SECRET
let client_key = tfhe::ClientKey::generate(config);
// Also generate the ServerKey which will allow the server to perform computations, this one is
// sent to the server
let server_key = tfhe::ServerKey::new(&client_key);
// Encrypt some values required by service provider
let ct1 = FheUint32::encrypt(17_u32, &client_key);
let ct2 = FheInt64::encrypt(-1i64, &client_key);
let ct3 = FheBool::encrypt(false, &client_key);
let ct4 = FheUint2::encrypt(3u8, &client_key);
let ct5 = FheAsciiString::encrypt("1.0!", &client_key);
// On the server side, once the server key has been received, set it as the active key
set_server_key(server_key);
// The server does some computations without seeing the data
let ct1_res = ct1 + 25;
let ct2_res = 43 + ct2;
let ct3_res = ct3 & true;
let ct4_res = ct4 - 1;
let ct5_res = ct5.len().into_ciphertext();
// We can compress the data to send or store smaller amounts of data to the client
let compressed_list = CompressedCiphertextListBuilder::new()
.push(ct1_res)
.push(ct2_res)
.push(ct3_res)
.push(ct4_res)
.push(ct5_res)
.build()
.unwrap();
// On the client side after receiving the compressed data
let a: FheUint32 = compressed_list.get(0).unwrap().unwrap();
let b: FheInt64 = compressed_list.get(1).unwrap().unwrap();
let c: FheBool = compressed_list.get(2).unwrap().unwrap();
let d: FheUint2 = compressed_list.get(3).unwrap().unwrap();
let e: FheUint16 = compressed_list.get(4).unwrap().unwrap();
let a: u32 = a.decrypt(&client_key);
assert_eq!(a, 42);
let b: i64 = b.decrypt(&client_key);
assert_eq!(b, 42);
let c = c.decrypt(&client_key);
assert!(!c);
let d: u8 = d.decrypt(&client_key);
assert_eq!(d, 2);
let e: u16 = e.decrypt(&client_key);
assert_eq!(e, 4);
// The compressed data can be reused by the server if it kept it on disk
let recovered_a: FheUint32 = compressed_list.get(0).unwrap().unwrap();
let new_a = recovered_a << 4u32;
// The result can be sent uncompressed to the Client, which can also decrypt this new result
let new_a: u32 = new_a.decrypt(&client_key);
assert_eq!(new_a, a << 4);
}
5. 为 TFHE-rs 做贡献
在 Zama,成为一家开源密码公司不仅仅是一项要求,更是其积极拥抱的理念。开放性推动着创新、协作以及Zama技术的持续改进。
TFHE-rs 正是基于这一理念而建立的,主要通过两种方式欢迎贡献:
- 1)通过 Zama 赏金计划为 TFHE-rs 做出贡献:推进 FHE,一次应对一个挑战
Zama 赏金计划奖励开发人员解决推动 TFHE-rs 发展的技术挑战。每个季度都会开放一个新的 TFHE-rs 赏金计划,参与者的解决方案通常会为库带来有意义的改进。
最近的一个例子是同态字符串功能,它最初是作为赏金贡献而来的,后来被整合到主存储库中。得益于这一新增功能,用户现在可以运行以下代码来检查子字符串是否包含在另一个字符串中:
rust
use tfhe::prelude::*;
use tfhe::{generate_keys, set_server_key, ClearString, ConfigBuilder, FheAsciiString};
fn main() {
let config = ConfigBuilder::default().build();
let (cks, sks) = generate_keys(config);
set_server_key(sks);
// Encrypt without padding, does not hide the string length and has better performance
let string = FheAsciiString::try_encrypt("TFHE-rs rocks!", &cks).unwrap();
// Encrypt with padding, hide the true length of the string
let search_string = FheAsciiString::try_encrypt_with_padding("is meh", 1, &cks).unwrap();
// We can also use clear strings
let clear_search_string = ClearString::new("rocks".to_string());
// Does our initial string contain "is meh"?
let does_not_contain = string.contains(&search_string);
// Does our initial string contain "rocks"?
let contains = string.contains(&clear_search_string);
// Decrypt
let decrypted_does_not_contain = does_not_contain.decrypt(&cks);
let decrypted_contains = contains.decrypt(&cks);
// Check all worked properly
assert!(!decrypted_does_not_contain);
assert!(decrypted_contains);
}
Zama->FHE Computation->Types->Strings文档中可以找到此新功能的优势的详细比较。
其他几项赏金驱动的贡献也被整合到 TFHE-rs 中,包括同态 SHA-256:
-
赏金计划下一季即将开启!关注官方赏金计划仓库Zama Bounty Program参与。
-
2)直接为 TFHE-rs 做出贡献:构建、改进和创新
引入了简化的贡献流程,让开发者和研究人员能够更轻松地为 TFHE-rs 添加新功能和改进。无论是构建应用程序还是试验新颖的原型,都可以按照以下步骤将相应工作集成到库中:
- fork存储库并创建一个新的分支;
- 使你的代码与现有约定保持一致并提交你的更改;
- 运行测试套件来验证正确性;
- rebase到主分支的最新版本并打开拉取请求。
一旦被接受,该贡献将会随着时间的推移而得到维护,就像库中的任何其他功能一样。
6. TFHE-rs 的未来规划
展望未来,TFHE-rs 将朝着两个主要目标发展:
- 1)以用户为中心的功能------通过引入简化更广泛受众使用的功能,使 FHE 更加直观、更易于访问;
- 2)性能和硬件支持------集成最先进的优化来改进同态计算。重点是扩展 GPU 后端,它已经是 TFHE-rs 的一部分,现在有望达到 x86 CPU 后端的稳定性和生产就绪状态。
参考资料
[1] 2025年2月27日博客 TFHE-rs v1.0: Stable CPU Backend