Seal库官方示例(一):bfv_basics.cpp解析

这篇具有很好参考价值的文章主要介绍了Seal库官方示例(一):bfv_basics.cpp解析。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

尽量理论来理解代码。
完整代码或者\native\examples里面

说到前面的话

两段官方的话
seal库 例,密码学-隐私计算,同态加密,安全
seal库 例,密码学-隐私计算,同态加密,安全
大致意思就是,这个库有门槛,需要先学会同态的概念,提供的例子必须要看要理解。必看的例子如下,
seal库 例,密码学-隐私计算,同态加密,安全

代码解析

基础加密

参数设置

三个核心参数
首先要知道这里的BFV代码是基于RLWE的,也就是涉及到多项式运算,同时它只能加密整数。
回忆一下RLWE的实例 ( b = a s + e , a ) (b=as+e,a) (b=as+e,a)中的部分是取自于多项式环 R q = Z q [ x ] / f ( x ) R_q = \mathbb{Z}_q[x]/f(x) Rq=Zq[x]/f(x)(其中 f ( x ) = x N + 1 f(x)=x^N+1 f(x)=xN+1),明文空间是 R t R_t Rt
那么由此将得到三个十分重要的加密参数,即多项式阶数模 N N N、多项式系数模数 q q q、明文模数 t t t
第三个参数仅有bfv需要(BGV的明文空间是 R 2 R_2 R2,CKKS是浮点数和复数)。

  • poly_modulus_degree (degree of polynomial modulus);
  • coeff_modulus ([ciphertext] coefficient modulus);
  • plain_modulus (plaintext modulus; only for the BFV scheme);

同态方案设置
设置当前使用的方案是bfv

EncryptionParameters parms(scheme_type::bfv);

多项式阶数模设置
多项式的阶数模必须是2的正整数次幂,设置得越大,那么密文将越大,相应的计算就会更慢,但是能使用更复杂的计算。另外,官方推荐的至少大于等于1024。这个例子用的4096。

size_t poly_modulus_degree = 4096;
parms.set_poly_modulus_degree(poly_modulus_degree);

多项式系数模设置
接下来设置多项式的系数模,这是多个大素数相乘得到的大整数,每个素数最大为60bits(计算机目前64位,没法直接表示这个大整数,所以用多个相乘),每个素数是由一个类的实例表示的向量,系数的最大位长为它的素数因子位长之和。系数模越大,那么计算能力更强,就是噪声的上限就会越大,因为解密正确的噪声上限 ( q / t ) / 2 (q/t)/2 (q/t)/2(前面文章LWE和RLWE写过),但它的位长受限于多项式的阶数模,如下
seal库 例,密码学-隐私计算,同态加密,安全
上面的数字可通过查看native/src/seal/util/hestdparms.h 或者调用函数

  • CoeffModulus::MaxBitCount(poly_modulus_degree);

也可以直接调用默认的

  • CoeffModulus::BFVDefault(poly_modulus_degree);

所以设置多项式系数模就可以像下面一样设置

parms.set_coeff_modulus(CoeffModulus::BFVDefault(poly_modulus_degree));

明文模数设置
明文模数在bfv中不受限制,可以是任意的正整数,比如这里就取2的幂次可以使得运算简化。
明文的模数决定明文数据类型的size和乘法中的噪声预算的消耗,回想一下bfv中的密文是使用了类似模交换操作的,也就是乘以了 t / q t/q t/q来进行噪声缩放,从而达到控制噪声的目的,所以容易知道明文模数 t t t越大,将导致每一次乘法的噪声越大,也就是噪声预算消耗得越大,所以需要它尽可能的小来获取更好的性能。
这里估算的噪声预算大致是log2(coeff_modulus/plain_modulus) (bits)( log ⁡ q / t \log q/t logq/t
每一次乘法消耗的噪声预算大致是log2(plain_modulus) + (other terms)()

parms.set_plain_modulus(1024);

参数可用性校验
参数设置完成后,需要校验一下已设置的参数是否可用,本代码的例子计算的是 4 ( x 2 + 1 ) ( x + 1 ) 2 = 4 x 4 + 8 x 3 + 8 x 2 + 8 x + 4 4(x^2+1)(x+1)^2=4x^4+8x^3+8x^2+8x+4 4(x2+1)(x+1)2=4x4+8x3+8x2+8x+4

SEALContext context(parms);
/*Print the parameters that we have chosen.*/
print_line(__LINE__);
cout << "Set encryption parameters and print" << endl;
print_parameters(context);
cout << "Parameter validation (success): " << context.parameter_error_message() << endl;
cout << endl;
cout << "~~~~~~ A naive way to calculate 4(x^2+1)(x+1)^2. ~~~~~~" << endl;

校验结果如图
seal库 例,密码学-隐私计算,同态加密,安全

代码的最后(本来在代码的最后位置,这里我提到这里来了),展示了如果设置的参数有问题,seal库会输出为什么有问题。。。相当于一个不错的辅助作用,能帮助修改参数。

print_line(__LINE__);
cout << "An example of invalid parameters" << endl;
parms.set_poly_modulus_degree(2048);
context = SEALContext(parms);
print_parameters(context);
cout << "Parameter validation (failed): " << context.parameter_error_message() << endl << endl;

校验结果如图
seal库 例,密码学-隐私计算,同态加密,安全

密钥生成

主要位公钥 s k = s sk=s sk=s、私钥 p k = ( b = a s + e , a ) pk=(b=as+e,a) pk=(b=as+e,a)、评估密钥evk的生成,首先需要一个密钥生成类的实例来创建一个自动生成私钥的生成器,接着利用私钥来生成公钥。

公钥和私钥生成

KeyGenerator keygen(context);
SecretKey secret_key = keygen.secret_key();
PublicKey public_key;
keygen.create_public_key(public_key);

创建加密器
创建加密器,实例化一个加密器类

Encryptor encryptor(context, public_key);

同态计算生成
同态计算密文的时候需要用到这个类(实际运用中,它不会由私钥拥有方来构建)

Evaluator evaluator(context);

创建解密器
创建解密器,实例化一个解密器类

Decryptor decryptor(context, secret_key);
加密

再次说明这里加密的例子是 4 x 4 + 8 x 3 + 8 x 2 + 8 x + 4 4x^4+8x^3+8x^2+8x+4 4x4+8x3+8x2+8x+4,这里设置了x=6。在RLWE问题中我们可以把明文消息看作多项式的系数,这里的明文就是6,也就是只有一项常数项。另外,别忘了明文的模数是1024。
明文创建
下面的代码创建了一个包含常量6的明文多项式,接着将明文多项式变为一个字符串,其中多项式的系数为十六进制。

print_line(__LINE__);
uint64_t x = 6;
Plaintext x_plain(uint64_to_hex_string(x));
cout << "Express x = " + to_string(x) + " as a plaintext polynomial 0x" + x_plain.to_string() + "." << endl;

输出结果如下
seal库 例,密码学-隐私计算,同态加密,安全

加密操作
密文由两个或多个多项式形如( c t = ( c 0 , c 1 ) ) c_t=(c_0,c_1)) ct=(c0,c1)))组成的,系数要模多项式系数模数,也就是模( q q q

print_line(__LINE__);
Ciphertext x_encrypted;
cout << "Encrypt x_plain to x_encrypted." << endl;
encryptor.encrypt(x_plain, x_encrypted);

密文的大小由Ciphertext::size()给出,新加密的密文大小都是2

cout << "    + size of freshly encrypted x: " << x_encrypted.size() << endl;

查看噪声预算

cout << "    + noise budget in freshly encrypted x: " << decryptor.invariant_noise_budget(x_encrypted) << " bits"
         << endl;
解密

调用解密器

Plaintext x_decrypted;
cout << "    + decryption of x_encrypted: ";
decryptor.decrypt(x_encrypted, x_decrypted);
cout << "0x" << x_decrypted.to_string() << " ...... Correct." << endl;

输出结果如下
seal库 例,密码学-隐私计算,同态加密,安全
这里没有输出加密后的密文,我试了半天没输出成功(太菜了😇),找了一下原因是输出结果是二进制形式不是可阅读的,所以Ciphertexts类里面只提供了save(可以通过stringstream来保存为一个文件)和load(从文件里导出)方法,它没有提供一个类似Plaintext类里的to_string方法。
这是Ciphertexts里的save方法的说明。
seal库 例,密码学-隐私计算,同态加密,安全

同态计算

因为噪声预算会随着乘法深度(次数)的增加而增加,所以为了能够完成更多次的计算,一般选择最小化乘法次数
所以这里计算多项式 4 x 4 + 8 x 3 + 8 x 2 + 8 x + 4 4x^4+8x^3+8x^2+8x+4 4x4+8x3+8x2+8x+4的时候,选择使用 4 ( x 2 + 1 ) ( x + 1 ) 2 4(x^2+1)(x+1)^2 4(x2+1)(x+1)2,这样对比直接计算能将乘法的深度从4降到了2。
首先是计算 x 2 + 1 x^2+1 x2+1

print_line(__LINE__);
cout << "Compute x_sq_plus_one (x^2+1)." << endl;
Ciphertext x_sq_plus_one;
evaluator.square(x_encrypted, x_sq_plus_one);//Squares a ciphertext
Plaintext plain_one("1");
evaluator.add_plain_inplace(x_sq_plus_one, plain_one);//Add a ciphertext and a plaintext

同态乘法会导致密文扩张,精确的说,如果输出的两个密文大小为M和N,那么同态乘法后的密文大小将变为M+N-1,下面的代码输出了密文扩张大小和噪声消耗。

cout << "    + size of x_sq_plus_one: " << x_sq_plus_one.size() << endl;
cout << "    + noise budget in x_sq_plus_one: " << decryptor.invariant_noise_budget(x_sq_plus_one) << " bits"
      << endl;

输出一下解密的结果

Plaintext decrypted_result;
cout << "    + decryption of x_sq_plus_one: ";
decryptor.decrypt(x_sq_plus_one, decrypted_result);
cout << "0x" << decrypted_result.to_string() << " ...... Correct." << endl;

输出结果如下
seal库 例,密码学-隐私计算,同态加密,安全
可以看到这里乘法后,密文从2项变到了3项,而噪声消耗了55-33=22个比特,因为还有噪声预算能够消耗,也就是还没达到噪声界限,所以能够正确解密。

接着计算 ( x + 1 ) 2 (x+1)^2 (x+1)2

print_line(__LINE__);
cout << "Compute x_plus_one_sq ((x+1)^2)." << endl;
Ciphertext x_plus_one_sq;
evaluator.add_plain(x_encrypted, plain_one, x_plus_one_sq);
evaluator.square_inplace(x_plus_one_sq);
cout << "    + size of x_plus_one_sq: " << x_plus_one_sq.size() << endl;
cout << "    + noise budget in x_plus_one_sq: " << decryptor.invariant_noise_budget(x_plus_one_sq) << " bits"
     << endl;
cout << "    + decryption of x_plus_one_sq: ";
decryptor.decrypt(x_plus_one_sq, decrypted_result);
cout << "0x" << decrypted_result.to_string() << " ...... Correct." << endl;

这里需要提一下的是add_plain_inplace()、square_inplace(0与add_plain()、square()函数的区别,带有inplace的是需要指定一个参数用来接收结果的,不带的就是直接保存在第一个参数里的。
输出结果如下
seal库 例,密码学-隐私计算,同态加密,安全
这里的噪声消耗也是22比特

最后是计算两个的乘积

print_line(__LINE__);
cout << "Compute encrypted_result (4(x^2+1)(x+1)^2)." << endl;
Ciphertext encrypted_result;
Plaintext plain_four("4");
evaluator.multiply_plain_inplace(x_sq_plus_one, plain_four);
evaluator.multiply(x_sq_plus_one, x_plus_one_sq, encrypted_result);
cout << "    + size of encrypted_result: " << encrypted_result.size() << endl;
cout << "    + noise budget in encrypted_result: " << decryptor.invariant_noise_budget(encrypted_result) << " bits"
     << endl;
cout << "NOTE: Decryption can be incorrect if noise budget is zero." << endl;

cout << endl;
cout << "~~~~~~ A better way to calculate 4(x^2+1)(x+1)^2. ~~~~~~" << endl;

这里本来是没有输出结果的,看到噪声预算还有剩余,就加了点代码看看结果。输出结果如下
seal库 例,密码学-隐私计算,同态加密,安全
大概消耗了33-4=29比特的噪声预算,式子越复杂消耗的越多。两个三项的相乘会得到五项。
x = 6 x=6 x=6,表达式是 4 x 4 + 8 x 3 + 8 x 2 + 8 x + 4 = 4 ( x 2 + 1 ) ( x + 1 ) 2 4x^4+8x^3+8x^2+8x+4=4(x^2+1)(x+1)^2 4x4+8x3+8x2+8x+4=4(x2+1)(x+1)2,我们期待解密结果是 4 × 37 × 49 = 7252 m o d    1024 = 84 4\times37\times49=7252 \mod 1024 = 84 4×37×49=7252mod1024=84换算为十六进制就是0x54,所以解密正确。

重线性化

重线性化是为了减少密文的规模扩张,并将密文规模还原到近似乘法之前的状况。
重线性化需要重线性化密钥rlk(其他方案里也叫做evk),它是由一层一层计算出来的一连串的密钥链,每一层都需要对应每一层的密钥,主要是用来消除密钥乘积的二次项,但是会诞生一定的噪声。
假设一个二阶的密文为 [ c 0 , c 1 , c 2 ] [\mathbf c_0,\mathbf c_1,\mathbf c_2] [c0,c1,c2],线性化的目的是将它变成一阶的 [ c 0 ′ , c 1 ′ ] [\mathbf c'_0,\mathbf c'_1] [c0,c1] [ c 0 + c 1 ⋅ s + c 2 ⋅ s 2 ] q = [ c 0 ′ + c 1 ′ ⋅ s + r ] q \left[\mathbf{c}_{0}+\mathbf{c}_{1} \cdot \mathbf{s}+\mathbf{c}_{2} \cdot \mathbf{s}^{2}\right]_{q}=\left[\mathbf{c}_{0}^{\prime}+\mathbf{c}_{1}^{\prime} \cdot \mathbf{s}+\mathbf{r}\right]_{q} [c0+c1s+c2s2]q=[c0+c1s+r]q,其中 r \mathbf r r很小。
首先需要生成重线性化密钥rlk

print_line(__LINE__);
cout << "Generate relinearization keys." << endl;
RelinKeys relin_keys;
keygen.create_relin_keys(relin_keys);

接下来重新计算 4 ( x 2 + 1 ) ( x + 1 ) 2 4(x^2+1)(x+1)^2 4(x2+1)(x+1)2,并在每一次乘法后都调用重线性化,还原密文规模。

print_line(__LINE__);
cout << "Compute and relinearize x_squared (x^2)," << endl;
cout << string(13, ' ') << "then compute x_sq_plus_one (x^2+1)" << endl;
Ciphertext x_squared;
evaluator.square(x_encrypted, x_squared);
cout << "    + size of x_squared: " << x_squared.size() << endl;
evaluator.relinearize_inplace(x_squared, relin_keys);
cout << "    + size of x_squared (after relinearization): " << x_squared.size() << endl;
evaluator.add_plain(x_squared, plain_one, x_sq_plus_one);
cout << "    + noise budget in x_sq_plus_one: " << decryptor.invariant_noise_budget(x_sq_plus_one) << " bits"
     << endl;
cout << "    + decryption of x_sq_plus_one: ";
decryptor.decrypt(x_sq_plus_one, decrypted_result);
cout << "0x" << decrypted_result.to_string() << " ...... Correct." << endl;

print_line(__LINE__);
Ciphertext x_plus_one;
cout << "Compute x_plus_one (x+1)," << endl;
cout << string(13, ' ') << "then compute and relinearize x_plus_one_sq ((x+1)^2)." << endl;
evaluator.add_plain(x_encrypted, plain_one, x_plus_one);
evaluator.square(x_plus_one, x_plus_one_sq);
cout << "    + size of x_plus_one_sq: " << x_plus_one_sq.size() << endl;
evaluator.relinearize_inplace(x_plus_one_sq, relin_keys);
cout << "    + noise budget in x_plus_one_sq: " << decryptor.invariant_noise_budget(x_plus_one_sq) << " bits"
     << endl;
cout << "    + decryption of x_plus_one_sq: ";
decryptor.decrypt(x_plus_one_sq, decrypted_result);
cout << "0x" << decrypted_result.to_string() << " ...... Correct." << endl;

print_line(__LINE__);
cout << "Compute and relinearize encrypted_result (4(x^2+1)(x+1)^2)." << endl;
evaluator.multiply_plain_inplace(x_sq_plus_one, plain_four);
evaluator.multiply(x_sq_plus_one, x_plus_one_sq, encrypted_result);
cout << "    + size of encrypted_result: " << encrypted_result.size() << endl;
evaluator.relinearize_inplace(encrypted_result, relin_keys);
cout << "    + size of encrypted_result (after relinearization): " << encrypted_result.size() << endl;
cout << "    + noise budget in encrypted_result: " << decryptor.invariant_noise_budget(encrypted_result) << " bits"
     << endl;

cout << endl;
cout << "NOTE: Notice the increase in remaining noise budget." << endl;

接着是看看解密的结果

print_line(__LINE__);
cout << "Decrypt encrypted_result (4(x^2+1)(x+1)^2)." << endl;
decryptor.decrypt(encrypted_result, decrypted_result);
cout << "    + decryption of 4(x^2+1)(x+1)^2 = 0x" << decrypted_result.to_string() << " ...... Correct." << endl;
cout << endl;

来看看输出的结果
seal库 例,密码学-隐私计算,同态加密,安全
可以看到由于多了重线性化操作,密文的大小也有5减少到了2,而最终表达式的噪声预算还剩下10比特多于没有重线性化的4比特。(怎么两个因子消耗的预算还是没变化~~~😅,算了,whatever,最后的结果是优化了的)。文章来源地址https://www.toymoban.com/news/detail-526878.html

到了这里,关于Seal库官方示例(一):bfv_basics.cpp解析的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【论文解读】基于神经辐射场NeRF的像素级交互式编辑(Seal-3D)

    来源:投稿 作者:橡皮 编辑:学姐 论文链接:https://arxiv.org/pdf/2307.15131 项目主页:https://windingwind.github.io/seal-3d/ 随着隐式神经表征或神经辐射场(NeRF)的普及,人们迫切需要与隐式三维模型交互的编辑方法,以完成重建场景的后期处理和三维内容创建等任务。虽然以前的作

    2024年02月03日
    浏览(28)
  • 计算机视觉与图形学-神经渲染专题-Seal-3D(基于NeRF的像素级交互式编辑)

    摘要 随着隐式神经表示或神经辐射场 (NeRF) 的流行,迫切需要与隐式 3D 模型交互的编辑方法,以完成后处理重建场景和 3D 内容创建等任务。虽然之前的作品从不同角度探索了 NeRF 编辑,但它们在编辑灵活性、质量和速度方面受到限制,无法提供直接的编辑响应和即时预览。

    2024年02月13日
    浏览(32)
  • TensorRT英伟达官方示例解析(一)

    TensorRT英伟达官方示例解析(一) TensorRT英伟达官方示例解析(二) TensorRT英伟达官方示例解析(三) 官方示例:https://github.com/NVIDIA/trt-samples-for-hackathon-cn/tree/master 官方视频:https://www.bilibili.com/video/BV1jj411Z7wG/?spm_id_from=333.337.search-card.all.clickvd_source=ce674108fa2e19e5322d710724193487

    2024年01月25日
    浏览(32)
  • llama笔记:官方示例解析 example_chat_completion.py

    使用预训练模型生成文本的程序的入口点 ckpt_dir (str) 指向包含预训练模型检查点文件的目录的路径 tokenizer_path (str) 分词器模型的路径,用于文本的编码和解码 temperature (float, optional) 控制生成过程中随机性的温度值。 温度值越高,生成的文本越随机,反之则更确定。 top_p (

    2024年03月22日
    浏览(24)
  • 【opencv】示例-stereo_calib.cpp 基于OpenCV的立体视觉相机校准的完整示例

    这段代码是一个用于执行立体视觉系统校准的应用程序的主函数main。它按以下步骤执行: 初始化用于指定棋盘尺寸、图像列表文件名、是否展示校正结果等参数的变量。 解析命令行输入的参数,其中包括棋盘的宽度、高度、类型、格子大小、Aruco标记大小、Aruco字典名称、额

    2024年04月15日
    浏览(35)
  • 【opencv】示例-grabcut.cpp 使用OpenCV库的GrabCut算法进行图像分割

    left mouse button - set rectangle SHIFT+left mouse button - set GC_FGD pixels CTRL+left mouse button - set GC_BGD pixels 这段代码是一个 使用OpenCV库的GrabCut算法进行图像分割 的C++程序。它允许用户通过交互式方式选择图像中的一个区域,并利用GrabCut算法尝试将其分割出来。代码中包含用户操作指南、

    2024年04月13日
    浏览(36)
  • 【opencv】示例-image_alignment.cpp 利用ECC 算法进行图像对齐

    affine homography 这段代码是一个 利用ECC (Enhanced Correlation Coefficient) 算法进行图像对齐的示例 。代码首先包含了OpenCV库的头文件,并且使用了OpenCV和标准库的命名空间。然后定义了几个函数和宏进行图像变换矩阵的操作,定义了一些用于解析命令行参数的。 main 函数中,

    2024年04月13日
    浏览(32)
  • 全同态加密:BFV

    参考文献: O. Regev. On lattices, learning with errors, random linear codes, and cryptography. In H. N. Gabow and R. Fagin, editors, STOC , pages 84–93. ACM, 2005. Full version in J. ACM 56(6), 2009. V. Lyubashevsky, C. Peikert, and O. Regev. On Ideal Lattices and Learning with Errors over Rings. In Advances in Cryptology - EUROCRYPT 2010 , volume 611

    2023年04月08日
    浏览(29)
  • React井字棋游戏官方示例

    在本篇技术博客中,我们将介绍一个React官方示例:井字棋游戏。我们将逐步讲解代码实现,包括游戏的组件结构、状态管理、胜者判定以及历史记录功能。让我们一起开始吧! 在这个井字棋游戏中,我们有以下组件: Square 组件:表示游戏棋盘上的每一个方格。 Board 组件:

    2024年02月14日
    浏览(26)
  • 【opencv】示例-inpaint.cpp 图像修复是通过填充损坏图像部分从而修复这些损坏的过程...

    原始图像 这段代码展示了一个使用OpenCV库进行图像修复的例子。它首先包含了处理图像编码、解码、显示、处理和照片处理所必要的OpenCV模块的头文件。然后利用cv和std命名空间下的类和方法。通过定义一个鼠标回调函数onMouse来处理图像上的绘图操作,并通过主函数main处理

    2024年04月25日
    浏览(34)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包