全同态加密-SEAL库CKKS工程搭建

全同态加密-SEAL库CKKS工程搭建

WHU W.F.
2020.12.30
由于博主本人不再从事相关工作,请大家注意时效性。
部分资源在下载区,我已取消下载限制(1积分)
2023.4.30

1.序

本文的目的并非深入探究CKKS原理,而是力求帮助开发者在完全不懂原理的情况下,快速构建CKKS工程,并掌握基本的使用技巧。
阅读本文要求读者:

  • 了解公钥加密基本概念
  • 能读懂C++代码
  • 已配置好SEAL的开发环境

如无特殊说明,本文用m代指明文,c代指密文

2.标准化构建流程

看代码之前我们先回顾一些概念:
首先,CKKS是一个公钥加密体系,具有公钥加密体系的一切特点,例如公钥加密、私钥解密等。因此,我们的代码中需要以下组件:

密钥生成器 keygenerator
加密模块 encryptor
解密模块 decryptor

其次,CKKS是一个(level)全同态加密算法(level表示其运算深度仍然存在限制),可以实现数据的“可算不可见”,因此我们还需要引入:

密文计算模块 evaluator

最后,加密体系都是基于某一数学困难问题构造的,CKKS所基于的数学困难问题在一个“多项式环”上(没有数论知识也没有关系,只需要明白环上的元素与实数并不相同),因此我们需要引入:

编码器 encoder

来实现数字和环上元素的相互转换。
总结下来,整个构建过程为:
① 选择CKKS参数 parms
② 生成CKKS框架 context
③ 构建CKKS模块 keygenerator,encoder,encryptor,evaluator和decryptor
④ 使用encoder 将 数据n 编码为 明文m
⑤ 使用encryptor 将 明文m 加密为 密文c
⑥ 使用evaluator 对 密文c 运算为 密文c’
⑦ 使用decryptor 将 密文c’ 解密为 明文m’
⑧ 使用encoder 将 明文m’ 解码为 数据n’

同态加密算法最直观的应用是云计算,其基本流程为:
①发送方利用公钥pk 加密 明文m 为 密文c
②发送方把密文c发送到服务器
③服务器执行密文运算,生成结果密文 c’
④服务器将结果密文c’发送给接收方
⑤接收方利用私钥sk 解密密文c’为明文结果m’
当发送方与接收方相同时,则该客户利用全同态加密算法完成了一次安全计算,即既利用了云计算的算力,又保障了数据的安全性,这对云计算的安全应用有重要意义。

3.样例代码

下面的例子实现了上述情景:

#include "examples.h"
/*
This file can be found in SEAL/native/example/ 
该文件可以在SEAL/native/example目录下找到
*/
#include <vector>
using namespace std;
using namespace seal;
#define N 3
//本例的目的是计算x,y,z的乘积

int main(){
//客户端的视角: 要进行计算的数据
vector<double> x, y, z;
	x = { 1.0, 2.0, 3.0 };
	y = { 2.0, 3.0, 4.0 };
	z = { 3.0, 4.0, 5.0 };

//构建参数容器 parms
EncryptionParameters parms(scheme_type::CKKS);
/*CKKS有三个重要参数:
1.poly_module_degree(多项式模数)
2.coeff_modulus(参数模数)
3.scale(规模)
下一小节会详细解释*/

size_t poly_modulus_degree = 8192;
parms.set_poly_modulus_degree(poly_modulus_degree);
parms.set_coeff_modulus(CoeffModulus::Create(poly_modulus_degree, { 60, 40, 40, 60 }));
//选用2^40进行编码
double scale = pow(2.0, 40);

//用参数生成CKKS框架context
auto context = SEALContext::Create(parms);

//构建各模块
//首先构建keygenerator,生成公钥、私钥和重线性化密钥
KeyGenerator keygen(context);
	auto public_key = keygen.public_key();
	auto secret_key = keygen.secret_key();
	auto relin_keys = keygen.relin_keys();

//构建编码器,加密模块、运算器和解密模块
//注意加密需要公钥pk;解密需要私钥sk;编码器需要scale
	Encryptor encryptor(context, public_key);
	Evaluator evaluator(context);
	Decryptor decryptor(context, secret_key);

	CKKSEncoder encoder(context);
//对向量x、y、z进行编码
	Plaintext xp, yp, zp;
	encoder.encode(x, scale, xp);
	encoder.encode(y, scale, yp);
	encoder.encode(z, scale, zp);
//对明文xp、yp、zp进行加密
	Ciphertext xc, yc, zc;
	encryptor.encrypt(xp, xc);
	encryptor.encrypt(yp, yc);
	encryptor.encrypt(zp, zc);
/*对密文进行计算,要说明的原则是:
1.加法可以连续运算,但乘法不能连续运算
2.密文乘法后要进行relinearize操作
3.执行乘法后要进行rescaling操作
4.进行运算的密文必需执行过相同次数的rescaling(位于相同level)
*/
//基于上述原则进行运算

//至此,客户端将pk、CKKS参数发送给服务器,服务器开始运算
//服务器的视角:先设中间变量
	Ciphertext temp;
	Ciphertext result_c;
//计算x*y,密文相乘,要进行relinearize和rescaling操作
	evaluator.multiply(xc,yc,temp);
	evaluator.relinearize_inplace(temp, relin_keys);
	evaluator.rescale_to_next_inplace(temp);

//在计算x*y * z之前,z没有进行过rescaling操作,所以需要对z进行一次乘法和rescaling操作,目的是 make x*y and z at the same level
	Plaintext wt;
	encoder.encode(1.0, scale, wt);
//我们可以查看框架中不同数据的层级:
cout << "    + Modulus chain index for zc: "
<< context->get_context_data(zc.parms_id())->chain_index() << endl;
cout << "    + Modulus chain index for temp(x*y): "
<< context->get_context_data(temp.parms_id())->chain_index() << endl;
cout << "    + Modulus chain index for wt: "
<< context->get_context_data(wt.parms_id())->chain_index() << endl;

//执行乘法和rescaling操作:
	evaluator.multiply_plain_inplace(zc, wt);
	evaluator.rescale_to_next_inplace(zc);

//再次查看zc的层级,可以发现zc与temp层级变得相同
cout << "    + Modulus chain index for zc after zc*wt and rescaling: "
<< context->get_context_data(zc.parms_id())->chain_index() << endl;

//最后执行temp(x*y)* zc(z*1.0)
	evaluator.multiply_inplace(temp, zc);
	evaluator.relinearize_inplace(temp,relin_keys);
	evaluator.rescale_to_next(temp, result_c);
//计算完毕,服务器把结果发回客户端

//客户端进行解密和解码
	Plaintext result_p;
	decryptor.decrypt(result_c, result_p);
//注意要解码到一个向量上
	vector<double> result;
	encoder.decode(result_p, result);
//得到结果 
//正确的话将输出:{6.000,24.000,60.000,...,0.000,0.000,0.000}
	cout << "结果是:" << endl;
	print_vector(result,3,3);
return 0;
}

4.参数解释

读完样例代码,对CKKS同态加密计算流程应该有了基本的了解。
本小节对三个参数进行简单的解释

(1)poly_modulus_degree(polynomial modulus)

该参数必须是2的幂,如1024, 2048, 4096, 8192, 16384, 32768,当然再大点也没问题。
更大的 poly_modulus_degree 会增加密文的尺寸,这会让计算变慢, 但也能让你执行更复杂的计算,关于这点稍后会有更直观的理解。
每个密文和明文(即Plaintext和Cipertext)本质上是一个长为poly_modules/2的向量,如果要利用例子中的“包加密技术”将一个向量直接加密到密文上,就需要保证向量的长度不超过poly_modules/2,在本例中为4096。否则就要先进行拆分再加密(即用for循环遍历数据,逐一加密),或选取更大的poly_modules参数。

(2)[ciphertext] `coefficient modulus

这是一组重要参数,因为rescaling操作依赖于coeff_modules。
简单来说,coeff_modules的个数决定了你能进行rescaling的次数,进而决定了你能执行的乘法操作的次数。

coeff_modules的最大位数与poly_modules有直接关系,列表如下:

poly_modulus_degreemax coeff_modulus bit-length
102427
204854
4096109
8192218
16384438
32768881

本文例子中的{60,40,40,60}有以下含义:
① coeff_modules总位长200(60+40+40+60)位
② 最多进行两次(两层)乘法操作

该系列数字的选择不是随意的,有以下要求:
① 总位长不能超过上表限制
② 最后一个参数为特殊模数,其值应该与中间模数的最大值相等
③ 中间模数与scale尽量相近

本例中,每次Rescaling的示意图如下:
\ special prime:60
coeff_modulus: { 60, 40, 40, 60 } ±–+ Level 3 (key level)
----------------------rescaling↓--------------------------------------
coeff_modulus: { 60, 40, 40 } ±–+ Level 2 (data level)
-----------------rescaling↓---------------------------------------------
coeff_modulus: { 60, 40 } ±–+ Level 1
------------rescaling↓--------------------------------------------
coeff_modulus: { 60 } ±–+ Level 0 (lowest level)

(3)Scale

Encoder利用该参数对浮点数进行缩放,每次相乘后密文的scale都会翻倍,因此需要执行rescaling操作约减一部分,约模的大素数位长由coeff_modules中的参数决定。

Scale不应太小,虽然大的scale会导致运算时间增加
但能确保噪声在约模的过程中被正确地舍去,同时不影响正确解密。

因此,两组推荐的参数为:

Poly_module_degree = 8196; coeff_modulus={60,40,40,60};scale = 2^40
Poly_module_degree = 8196; coeff_modulus={50,30,30,30.50};scale = 2^30

5.计算注意事项

如示例代码中所述,每次进行运算前,要保证参与运算的数据位于同一“level”上。
加法不需要进行rescaling操作,因此不会改变数据的level
数据的level只能降低无法升高,所以要小心设计计算的先后顺序

可以通过输出p.scale()、p.parms_id()以及
context->get_context_data(p.parms_id())->chain_index()
来确认即将进行操作的数据满足:
1)用同一组参数进行加密
2)位于(chain)上的同一level
3)scale相同

的计算条件。

要想把不同level的数据拉到同一level,可以利用乘法单位元1把层数较高的操作数拉到较低的level(如本例),也可以通过内置函数进行直接转换,但笔者推荐前者,有利于提高代码的可读性,便于维护。

目前,SEAL提供了reverse、square等有限的计算操作,大部分复杂运算需要自己编写代码实现,在实现过程中要根据数据量把握好精度和性能的取舍。

  • 16
    点赞
  • 67
    收藏
    觉得还不错? 一键收藏
  • 13
    评论
好的,关于你的问题,我会尽力回答。不过需要说明的是,我是一个语言模型,并不能直接进行代码实现和运行。以下是基于同态加密SEAL实现同态算法BFV并验证其满足同态性的流程: 1. 安装SEAL并配置环境。SEAL是一个用于实现同态加密C++,可以在官网(https://www.microsoft.com/en-us/research/project/simple-encrypted-arithmetic-library-seal/)下载。安装完成后需要进行环境配置,主要是设置编译器和SEAL的路径等。 2. 生成加密参数。BFV算法需要一些参数来进行加密和计算,如多项式次数、扰动项数量、模数等。可以使用SEAL提供的工具程序生成这些参数,也可以手动设置。 3. 加密数据。使用SEAL提供的API对需要计算的数据进行加密,得到密文。注意,加密后的数据格式与原始数据不同,需要进行转换。 4. 进行同态计算。使用SEAL提供的API对密文进行同态计算,得到结果密文。BFV算法支持加法和乘法运算,可以进行多次计算。 5. 解密结果。使用SEAL提供的API将结果密文解密得到明文结果。 6. 检验同态性。对于加法和乘法运算,需要验证其是否满足同态性质。即对于两个明文x和y,其加密结果密文为c1和c2,加法的结果为c3,有decrypt(c3) = decrypt(c1) + decrypt(c2);乘法的结果为c3,有decrypt(c3) = decrypt(c1) * decrypt(c2)。 以上就是基于同态加密SEAL实现同态算法BFV并验证其满足同态性的流程。需要注意的是,BFV算法虽然可以实现同态,但是其计算效率较低,且只能处理有限的数据范围。因此在实际应用中需要根据具体情况选择适当的同态加密算法。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 13
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值