当前位置:   article > 正文

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

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

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

说到前面的话

两段官方的话
image.png
image.png
大致意思就是,这个库有门槛,需要先学会同态的概念,提供的例子必须要看要理解。必看的例子如下,
image.png

代码解析

基础加密

参数设置

三个核心参数
首先要知道这里的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);
  • 1

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

size_t poly_modulus_degree = 4096;
parms.set_poly_modulus_degree(poly_modulus_degree);
  • 1
  • 2

多项式系数模设置
接下来设置多项式的系数模,这是多个大素数相乘得到的大整数,每个素数最大为60bits(计算机目前64位,没法直接表示这个大整数,所以用多个相乘),每个素数是由一个类的实例表示的向量,系数的最大位长为它的素数因子位长之和。系数模越大,那么计算能力更强,就是噪声的上限就会越大,因为解密正确的噪声上限 ( q / t ) / 2 (q/t)/2 (q/t)/2(前面文章LWE和RLWE写过),但它的位长受限于多项式的阶数模,如下
image.png
上面的数字可通过查看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));
  • 1

明文模数设置
明文模数在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);
  • 1

参数可用性校验
参数设置完成后,需要校验一下已设置的参数是否可用,本代码的例子计算的是 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;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

校验结果如图
image.png

代码的最后(本来在代码的最后位置,这里我提到这里来了),展示了如果设置的参数有问题,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;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

校验结果如图
image.png

密钥生成

主要位公钥 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);
  • 1
  • 2
  • 3
  • 4

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

Encryptor encryptor(context, public_key);
  • 1

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

Evaluator evaluator(context);
  • 1

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

Decryptor decryptor(context, secret_key);
  • 1
加密

再次说明这里加密的例子是 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;
  • 1
  • 2
  • 3
  • 4

输出结果如下
image.png

加密操作
密文由两个或多个多项式形如( 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);
  • 1
  • 2
  • 3
  • 4

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

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

查看噪声预算

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

调用解密器

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

输出结果如下
image.png
这里没有输出加密后的密文,我试了半天没输出成功(太菜了 本文内容由网友自发贡献,转载请注明出处:https://www.wpsshop.cn/w/我家小花儿/article/detail/997024

推荐阅读
相关标签