2021SC@SDUSC
2021-12-12
从本篇开始,接上一篇的BFV分析,这里我们继续分析BFV源码。争取本篇结束战斗。
与之前的工作相比,这些代码显得更具整体性,也能更好体现出SEAL的工作原理以及流程,算是对前面的集大成者了,因此很有分析的必要。
本篇我们将继续分析examples/1_bfv_basics.cpp。看一看SEAL对于bfv是如何实现的。
先看整体的运行效果:
来自example.cpp,输入1即可得到BFV方案的运行示例。
Microsoft SEAL version: 3.7.1
+---------------------------------------------------------+
| The following examples should be executed while reading |
| comments in associated files in native/examples/. |
+---------------------------------------------------------+
| Examples | Source Files |
+----------------------------+----------------------------+
| 1. BFV Basics | 1_bfv_basics.cpp |
| 2. Encoders | 2_encoders.cpp |
| 3. Levels | 3_levels.cpp |
| 4. CKKS Basics | 4_ckks_basics.cpp |
| 5. Rotation | 5_rotation.cpp |
| 6. Serialization | 6_serialization.cpp |
| 7. Performance Test | 7_performance.cpp |
+----------------------------+----------------------------+
[ 0 MB] Total allocation from the memory pool
> Run example (1 ~ 7) or exit (0): 1
+-------------------------------------+
| Example: BFV Basics |
+-------------------------------------+
Line 132 --> Set encryption parameters and print
/
| Encryption parameters :
| scheme: BFV
| poly_modulus_degree: 4096
| coeff_modulus size: 109 (36 + 36 + 37) bits
| plain_modulus: 1024
\
Parameter validation (success): valid
~~~~~~ A naive way to calculate 4(x^2+1)(x+1)^2. ~~~~~~
Line 212 --> Express x = 6 as a plaintext polynomial 0x6.
Line 223 --> Encrypt x_plain to x_encrypted.
+ size of freshly encrypted x: 2
+ noise budget in freshly encrypted x: 55 bits
+ decryption of x_encrypted: 0x6 ...... Correct.
Line 270 --> Compute x_sq_plus_one (x^2+1).
+ size of x_sq_plus_one: 3
+ noise budget in x_sq_plus_one: 33 bits
+ decryption of x_sq_plus_one: 0x25 ...... Correct.
Line 300 --> Compute x_plus_one_sq ((x+1)^2).
+ size of x_plus_one_sq: 3
+ noise budget in x_plus_one_sq: 33 bits
+ decryption of x_plus_one_sq: 0x31 ...... Correct.
Line 315 --> Compute encrypted_result (4(x^2+1)(x+1)^2).
+ size of encrypted_result: 5
+ noise budget in encrypted_result: 4 bits
NOTE: Decryption can be incorrect if noise budget is zero.
~~~~~~ A better way to calculate 4(x^2+1)(x+1)^2. ~~~~~~
Line 353 --> Generate relinearization keys.
Line 361 --> Compute and relinearize x_squared (x^2),
then compute x_sq_plus_one (x^2+1)
+ size of x_squared: 3
+ size of x_squared (after relinearization): 2
+ noise budget in x_sq_plus_one: 33 bits
+ decryption of x_sq_plus_one: 0x25 ...... Correct.
Line 376 --> Compute x_plus_one (x+1),
then compute and relinearize x_plus_one_sq ((x+1)^2).
+ size of x_plus_one_sq: 3
+ noise budget in x_plus_one_sq: 33 bits
+ decryption of x_plus_one_sq: 0x31 ...... Correct.
Line 390 --> Compute and relinearize encrypted_result (4(x^2+1)(x+1)^2).
+ size of encrypted_result: 3
+ size of encrypted_result (after relinearization): 2
+ noise budget in encrypted_result: 11 bits
NOTE: Notice the increase in remaining noise budget.
Line 407 --> Decrypt encrypted_result (4(x^2+1)(x+1)^2).
+ decryption of 4(x^2+1)(x+1)^2 = 0x54 ...... Correct.
Line 425 --> An example of invalid parameters
/
| Encryption parameters :
| scheme: BFV
| poly_modulus_degree: 2048
| coeff_modulus size: 109 (36 + 36 + 37) bits
| plain_modulus: 1024
\
Parameter validation (failed): parameters are not compliant with HomomorphicEncryption.org security standard
+---------------------------------------------------------+
| The following examples should be executed while reading |
| comments in associated files in native/examples/. |
+---------------------------------------------------------+
| Examples | Source Files |
+----------------------------+----------------------------+
| 1. BFV Basics | 1_bfv_basics.cpp |
| 2. Encoders | 2_encoders.cpp |
| 3. Levels | 3_levels.cpp |
| 4. CKKS Basics | 4_ckks_basics.cpp |
| 5. Rotation | 5_rotation.cpp |
| 6. Serialization | 6_serialization.cpp |
| 7. Performance Test | 7_performance.cpp |
+----------------------------+----------------------------+
[ 8 MB] Total allocation from the memory pool
下面我们可以分析BFV源码了。
在此示例中,我们演示了使用 BFV 加密方案对加密整数执行简单计算(多项式评估)。
第一个任务是设置 EncryptionParameters 类的实例。
了解不同参数的行为方式、它们如何影响加密方案、性能和安全级别至关重要。需要设置三个加密参数:
- poly_modulus_degree(多项式模数的程度);
- coeff_modulus([密文]系数模数);
- plain_modulus(明文模数;仅适用于 BFV 方案)。
BFV 方案不能对加密数据执行任意计算。相反,每个密文都有一个特定的数量,称为“不变噪声预算”——或简称“噪声预算”——以比特为单位。新加密密文中的噪声预算(初始噪声预算)由加密参数决定。同态运算以同样由加密参数确定的速率消耗噪声预算。在 BFV 中,允许对加密数据进行的两个基本操作是加法和乘法,其中加法通常可以被认为是几乎无代价的,与乘法相比的噪声预算消耗相比。由于噪声预算消耗在顺序乘法中复合,因此选择适当加密参数的最重要因素是用户想要对加密数据进行评估的算术电路的乘法深度。一旦密文的噪声预算达到零,它就会变得太损坏而无法解密。因此,必须选择足够大的参数以支持所需的计算;否则即使使用密钥也无法理解结果。
EncryptionParameters parms(scheme_type::bfv);
我们设置的第一个参数是“多项式模数”的次数。 这必须是 2 的正幂,表示二次分圆多项式的次数;没有必要理解这意味着什么。
较大的 poly_modulus_degree 使密文大小更大且所有操作更慢,但可以实现更复杂的加密计算。 推荐值为1024、2048、4096、8192、16384、32768,但也有可能超出这个范围。
在本例中,我们使用相对较小的多项式模数。 任何比这更小的东西都将只适用非常有限的加密计算。
size_t poly_modulus_degree = 4096;
parms.set_poly_modulus_degree(poly_modulus_degree);
这个函数如下:
inline void set_poly_modulus_degree(std::size_t poly_modulus_degree)
{
if (scheme_ == scheme_type::none && poly_modulus_degree)
{
throw std::logic_error("poly_modulus_degree is not supported for this scheme");
}
// Set the degree
poly_modulus_degree_ = poly_modulus_degree;
// Re-compute the parms_id
compute_parms_id();
}
接下来我们设置[密文]“系数模数”(coeff_modulus)。该参数是一个大整数,它是不同质数的乘积,每个质数最多 60 位。它表示为这些素数的向量,每个素数都由 Modulus 类的一个实例表示。 coeff_modulus 的位长是指其质因数的位长之和。
更大的 coeff_modulus 意味着更大的噪声预算,因此更加密的计算能力。但是,coeff_modulus 的总比特长度的上限由 poly_modulus_degree 确定,如下所示:
+------------------------------------------------- ---+
| poly_modulus_degree |最大 coeff_modulus 位长 |
+---------------------+---------------------------- ---+
| 1024 | 27 |
| 2048 | 54 |
| 4096 | 109 |
| 8192 | 218 |
| 16384 | 438 |
| 32768 | 881 |
+---------------------+---------------------------- ---+
这些数字也可以在函数 SEAL_HE_STD_PARMS_128_TC 中编码的 native/src/seal/util/hestdparms.h 中找到,也可以从函数CoeffModulus::MaxBitCount(poly_modulus_degree)。
例如,如果 poly_modulus_degree 为 4096,则 coeff_modulus 可以由三个 36 位素数(108 位)组成。
Microsoft SEAL 附带用于选择 coeff_modulus 的辅助函数。
对于新用户,最简单的方法是简单地使用 CoeffModulus::BFVDefault(poly_modulus_degree),它返回 std::vector,其中包含对给定 poly_modulus_degree 的一个通常不错的选择。
parms.set_coeff_modulus(CoeffModulus::BFVDefault(poly_modulus_degree));
函数具体实现如下:
inline void set_coeff_modulus(const std::vector<Modulus> &coeff_modulus)
{
// Check that a scheme is set
if (scheme_ == scheme_type::none)
{
if (!coeff_modulus.empty())
{
throw std::logic_error("coeff_modulus is not supported for this scheme");
}
}
else if (coeff_modulus.size() > SEAL_COEFF_MOD_COUNT_MAX || coeff_modulus.size() < SEAL_COEFF_MOD_COUNT_MIN)
{
throw std::invalid_argument("coeff_modulus is invalid");
}
coeff_modulus_ = coeff_modulus;
// Re-compute the parms_id
compute_parms_id();
}
下一步是设置明文模数参数。