目录
- 一、加密参数 EncryptionParameters类
- 1. 三个重要的参数
- 2. 参数的作用
- 3. 同态加密方案
- 4. 多项式模数的度 poly_modulus_degree (n)
- 5. 密文模数 coeff_modulus (q)
- 6. 明文模数 plain_modulus (t,这是 BFV 方案才有的,CKKS 没有)
- 二、上下文 SEALContext 类
- 三、密钥生成 KeyGenerator 类
- 四、加密 Encryptor 类
- 五、评估 Evaluator 类
- 六、解密 Decryptor 类
- 七、实例
一、加密参数 EncryptionParameters类
1. 三个重要的参数
表示 | 含义 |
---|---|
poly_modulus_degree | n 多项式模数的度 |
coeff_modulus | q 密文模数 |
plain_modulus | t 明文模数 |
2. 参数的作用
- 每个密文都有一个特定的量,称为 “噪声预算”,以 位 为单位测量。
- 噪声预算 是由 加密参数 决定的。
- 同态操作 消耗噪声预算的速率 也由 加密参数 决定。
3. 同态加密方案
在 SEAL 中,同态加密方案的表示为:
- BFV 方案:
scheme_type::bfv
- CKKS 方案:
scheme_type::ckks
EncryptionParameters parms(scheme_type::bfv);
例如:EncryptionParameters parms(scheme_type::bfv);
。
4. 多项式模数的度 poly_modulus_degree (n)
- 这必须是 2 的正整数幂,表示 2 的幂次分圆多项式的度。暂时没有必要理解这意味着什么。
- n 越大,密文大小 越大,同态操作 越慢,支持更复杂的加密计算。
- 推荐的值有:1024、2048、4096 … …
size_t poly_modulus_degree = 4096;parms.set_poly_modulus_degree(poly_modulus_degree);
5. 密文模数 coeff_modulus (q)
- 这个参数是一个 大整数,它是 不同素数的 乘积,每个素数 最多 60 位。它表示为 这些素数的 向量,每个素数由 Modulus类 的实例表示。coeff_modulus 的 位长度 意味着其 素因子位长度的 总和。
- q 越大,噪声预算 越大。
- q 的总位长 由 n 决定,可自己定,也可以通过
CoeffModulus::BFVDefault(poly_modulus_degree)
来自动返回一个好的选择。
parms.set_coeff_modulus(CoeffModulus::BFVDefault(poly_modulus_degree));
6. 明文模数 plain_modulus (t,这是 BFV 方案才有的,CKKS 没有)
- 明文模数可以是 任何正整数,但是实际上,在许多情况下,人们可能希望它是素数。
- 新的加密密文的 噪声预算大小 的计算方法为: l o g 2 ( c o e f f _ m o d u l u s p l a i n _ m o d u l u s ) = l o g 2 ( q t ) ( 位 ) log_2(\frac{coeff\_modulus}{plain\_modulus}) = log_2(\frac{q}{t}) \ \ \ (位) log2(plain_moduluscoeff_modulus)=log2(tq) (位)所以为了获得最佳性能,尽量保持 明文数据类型尽可能小 是至关重要的。
parms.set_plain_modulus(1024);
二、上下文 SEALContext 类
- 所有加密 参数 设置好后,就可以构造 SEALContext 对象 了,它会检查 加密参数 的 兼容性和安全性。
SEALContext context(parms);/*打印我们选择的参数。*/print_line(__LINE__);cout << "设置加密参数并打印" << endl;print_parameters(context);/*当参数用于创建SEALContext时,Microsoft SEAL将首先验证这些参数。这里选择的参数是有效的。*/cout << "参数验证(成功):" << context.parameter_error_message() << endl;
三、密钥生成 KeyGenerator 类
- 我们现在准备生成 私钥 和 公钥。为此我们需要 KeyGenerator 类 的实例。
- 构造 KeyGenerator 会自动生成 私钥。然后我们可以使用
KeyGenerator::create_public_key
为其创建 任意多个 公钥。 - 注意
KeyGenerator::create_public_key
有另一个重载,它不接受参数并返回Serializable<PublicKey>
对象。我们将在 ‘6 serialization’ 中讨论这一点。
// 创建密钥生成器,构造时自动生成私钥KeyGenerator keygen(context);// 获取自动生成的私钥SecretKey secret_key = keygen.secret_key();// 声明公钥对象PublicKey public_key;// 基于秘钥生成对应的公钥keygen.create_public_key(public_key);
四、加密 Encryptor 类
- 为了能够加密,我们需要构造 Encryptor 实例。注意 Encryptor 只需要 公钥,这是预期的。
// 创建加密器,使用公钥进行加密操作Encryptor encryptor(context, public_key);
五、评估 Evaluator 类
- 对密文的计算 使用 Evaluator 类 执行。
- 在实际用例中,Evaluator 不会由 持有秘钥 的同一方构造。
// 创建计算器,用于对密文执行同态运算Evaluator evaluator(context);
六、解密 Decryptor 类
- 构造 Decryptor 的实例。注意 Decryptor 需要 私钥。
// 创建解密器,使用秘钥进行解密操作Decryptor decryptor(context, secret_key);
七、实例
cout << "~~~~~~ 计算4(x^2+1)(x+1)^2的更好方法。~~~~~~" << endl;/*噪声预算已达到 0,这意味着不能期望解密给出正确结果。这是因为密文x_sq_plus_one和x_plus_one_sq由于先前的平方运算都由3个多项式组成,对大密文的同态运算比对小密文的计算消耗更多噪声预算。对较小密文的计算在计算上也显著更便宜。"重线性化"是一种操作,它在乘法后将密文的大小减小回初始大小2。因此,在下一次乘法之前重线性化一个或两个输入密文可以对噪声增长和性能产生巨大的积极影响,尽管重线性化本身有显著的计算成本。只能将大小为3的密文重线性化为大小2,所以通常用户会想在每次乘法后重线性化以保持密文大小为2。重线性化需要特殊的"重线性化密钥",可以被认为是一种公钥。重线性化密钥可以用KeyGenerator轻松创建。重线性化在BFV和CKKS方案中使用类似,但在这个示例中我们继续使用BFV。我们重复之前的计算,但这次在每次乘法后重线性化。*/print_line(__LINE__);cout << "生成重线性化密钥。" << endl;// 声明重线性化密钥对象RelinKeys relin_keys;// 生成重线性化密钥keygen.create_relin_keys(relin_keys);/*我们现在重复计算,在每次乘法后重线性化。*/print_line(__LINE__);cout << "计算并重线性化x_squared (x^2)," << endl;cout << string(13, ' ') << "然后计算x_sq_plus_one (x^2+1)" << endl;// 声明存储x^2的密文对象Ciphertext x_squared;// 计算x^2evaluator.square(x_encrypted, x_squared);cout << " + x_squared的大小:" << x_squared.size() << endl;// 重线性化:将大小从3减小到2(就地操作)evaluator.relinearize_inplace(x_squared, relin_keys);cout << " + x_squared的大小(重线性化后):" << x_squared.size() << endl;// 计算x^2 + 1evaluator.add_plain(x_squared, plain_one, x_sq_plus_one);cout << " + x_sq_plus_one中的噪声预算:" << decryptor.invariant_noise_budget(x_sq_plus_one) << "位"<< endl;cout << " + x_sq_plus_one的解密:";decryptor.decrypt(x_sq_plus_one, decrypted_result);cout << "0x" << decrypted_result.to_string() << " ...... 正确。" << endl;// 声明存储x+1的密文对象Ciphertext x_plus_one;cout << "计算x_plus_one (x+1)," << endl;cout << string(13, ' ') << "然后计算并重线性化x_plus_one_sq ((x+1)^2)。" << endl;// 计算x + 1evaluator.add_plain(x_encrypted, plain_one, x_plus_one);// 计算(x + 1)^2evaluator.square(x_plus_one, x_plus_one_sq);cout << " + x_plus_one_sq的大小:" << x_plus_one_sq.size() << endl;// 重线性化evaluator.relinearize_inplace(x_plus_one_sq, relin_keys);cout << " + x_plus_one_sq中的噪声预算:" << decryptor.invariant_noise_budget(x_plus_one_sq) << "位"<< endl;cout << " + x_plus_one_sq的解密:";decryptor.decrypt(x_plus_one_sq, decrypted_result);cout << "0x" << decrypted_result.to_string() << " ...... 正确。" << endl;cout << "计算并重线性化encrypted_result (4(x^2+1)(x+1)^2)。" << endl;// 将x_sq_plus_one乘以4evaluator.multiply_plain_inplace(x_sq_plus_one, plain_four);// 执行最终乘法evaluator.multiply(x_sq_plus_one, x_plus_one_sq, encrypted_result);cout << " + encrypted_result的大小:" << encrypted_result.size() << endl;// 重线性化最终结果evaluator.relinearize_inplace(encrypted_result, relin_keys);cout << " + encrypted_result的大小(重线性化后):" << encrypted_result.size() << endl;cout << " + encrypted_result中的噪声预算:" << decryptor.invariant_noise_budget(encrypted_result) << "位" << endl;cout << endl;cout << "注意:注意剩余噪声预算的增加。" << endl;/*重线性化明显改善了我们的噪声消耗。我们仍然有大量的噪声预算剩余,所以我们可以期望解密时得到正确答案。*/print_line(__LINE__);cout << "解密encrypted_result (4(x^2+1)(x+1)^2)。" << endl;decryptor.decrypt(encrypted_result, decrypted_result);cout << " + 4(x^2+1)(x+1)^2的解密 = 0x" << decrypted_result.to_string() << " ...... 正确。" << endl;cout << endl;/*对于x=6,4(x^2+1)(x+1)^2 = 7252。由于明文模数设置为1024,这个结果在整数模1024下计算。因此预期输出应该是7252 % 1024 == 84,或十六进制的0x54。*/