2021SC@SDUSC
目录
EXACTRESCALE和APPROXRESCALE两种变体
void AutomaticRescaleDemo(RescalingTechnique rsTech)
void HybridKeySwitchingDemo1()
void HybridKeySwitchingDemo2()
介绍
在这篇博客中,我将介绍有关公钥加密的实例程序。
这次是先介绍有关 advanced-real-numbers.cpp 的示例程序。
我们的CKKS实现包括两个变体,分别称为“EXACTRESCALE”和“APPROXRESCALE”。从1.10版本开始,我们添加了APPROXAUTO变体,它的工作方式与APPROXRESCALE相同,但也可以自动缩放。
在我们开始之前,我们需要介绍一下规模操作,这是CKKS的核心。当我们将两个分别加密数字m1*D和m2*D的密文c1和c2相乘时,我们得到的结果看起来像m1*m2*D^2。因为这个数字的比例因子是D^2,我们说结果的深度是2。很明显,深度为2的密文不能加到深度为1的密文中,因为它们的缩放因子是不同的。重缩放取深度为2的密文,通过一个看起来很像除以D=2^p的操作使其深度为1。
出于效率的原因,我们的CKKS实现在RNS空间中工作,这意味着我们避免处理大数字,只处理本地整数。一个复杂的问题是我们只能通过除以某些质数来缩放而不是D=2^p。
有两种方法可以解决这个问题。第一种是选择尽可能接近2^p的质数,并假设比例因子保持不变。这不可避免地会导致一些近似误差,这就是为什么我们将其称为APPROXRESCALE变体。
处理这个问题的第二种方法是跟踪比例因子是如何变化的,并尝试根据它进行调整。这就是CKKS的EXACTRESCALE变种。这样做的代价是EXACTRESCALE计算通常会稍微慢一些(根据我们的经验,根据计算的复杂性,速度会减慢5-35%),因为需要对值进行调整。
我们设计了EXACTRESCALE,所以它隐藏了追踪密文深度的所有细微差别,并不得不调用缩放操作。因此,EXACTRESCALE更适合那些不想深入了解底层加密和数学的细节,或者想要快速构建一个原型的用户。相反,APPROXRESCALE更适合于经过专家优化的生产应用程序。
本演示的前两部分通过使用EXACTRESCALE和APPROXRESCALE实现相同的计算,即函数f(x) = x^18 + x^9 + 1,介绍了这两种变体。
EXACTRESCALE和APPROXRESCALE两种变体
AutomaticRescaleDemo(EXACTRESCALE);
AutomaticRescaleDemo(APPROXAUTO);
void AutomaticRescaleDemo(RescalingTechnique rsTech)
EXACTRESCALE是CKKS的一个变体,它有两个主要特性:
1. 它在每次乘法之前自动执行缩放。这样做是为了让用户更容易编写FHE计算,而不必担心密文的深度或缩放。
2. 它跟踪所有密文的精确比例因子。这意味着EXACTRESCALE中的计算将比APPROXRESCALE中的相同计算更精确。
请记住,这种差异只有在处理大的乘法深度计算时才会变得明显;这是因为较大的乘法深度意味着我们需要找到更多足够接近D=2^p的质数,而随着乘法深度的增加,这变得越来越难。
判断是哪一个变体进行操作
if (rsTech == EXACTRESCALE) {
std::cout << "\n\n\n ===== ExactRescaleDemo ============= " << std::endl;
} else {
std::cout << "\n\n\n ===== ApproxAutoDemo ============= " << std::endl;
}
相关参数的设置
uint32_t multDepth = 5;
uint32_t scaleFactorBits = 50;
uint32_t batchSize = 8;
SecurityLevel securityLevel = HEStd_128_classic;
// 0 means the library will choose it based on securityLevel
uint32_t ringDimension = 0;
CryptoContext<DCRTPoly> cc =
CryptoContextFactory<DCRTPoly>::genCryptoContextCKKS(
multDepth, scaleFactorBits, batchSize, securityLevel, ringDimension,
rsTech);
std::cout << "CKKS scheme is using ring dimension " << cc->GetRingDimension()
<< std::endl
<< std::endl;
cc->Enable(ENCRYPTION);
cc->Enable(SHE);
// When using EXACTRESCALE, LEVELEDSHE has to be enabled because Rescale is
// implicitly used upon multiplication.
cc->Enable(LEVELEDSHE);
auto keys = cc->KeyGen();
cc->EvalMultKeyGen(keys.secretKey);
输入要参加计算的数据
vector<double> x = {1.0, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7};
Plaintext ptxt = cc->MakeCKKSPackedPlaintext(x);
std::cout << "Input x: " << ptxt << std::endl;
auto c = cc->Encrypt(keys.publicKey, ptxt);
计算 f(x) = x^18 + x^9 + 1
下面我们计算f(x)的乘法深度为5。
结果是正确的,尽管没有调用Rescale()操作。
auto c2 = cc->EvalMult(c, c); // x^2
auto c4 = cc->EvalMult(c2, c2); // x^4
auto c8 = cc->EvalMult(c4, c4); // x^8
auto c16 = cc->EvalMult(c8, c8); // x^16
auto c9 = cc->EvalMult(c8, c); // x^9
auto c18 = cc->EvalMult(c16, c2); // x^18
auto cRes = cc->EvalAdd(cc->EvalAdd(c18, c9), 1.0); // Final result
Plaintext result;
std::cout.precision(8);
cc->Decrypt(keys.secretKey, cRes, &result);
result->SetLength(batchSize);
std::cout << "x^18 + x^9 + 1 = " << result << std::endl;
运行结果
EXACTRESCALE
APPROXRESCALE
两种不同的密钥交换算法
HybridKeySwitchingDemo1();
HybridKeySwitchingDemo2();
void HybridKeySwitchingDemo1()
在这个演示中,我们关注如何选择混合键交换中的位数,以及它如何影响CKKS方案的使用和效率。
相关参数的设置
std::cout << "\n\n\n ===== HybridKeySwitchingDemo1 ============= "
<< std::endl;
uint32_t multDepth = 5;
uint32_t scaleFactorBits = 50;
uint32_t batchSize = 8;
SecurityLevel securityLevel = HEStd_128_classic;
uint32_t ringDimension =
0; // 0 means the library will choose it based on securityLevel
RescalingTechnique rsTech = EXACTRESCALE;
KeySwitchTechnique ksTech = HYBRID;
设置杂交分解中大数位数
dnum是杂交分解中大数位数
如果不提供(或提供值0),默认值设置如下:
如果乘法深度为> 3,则dnum = 3位数字。
如果乘法深度为3,则dnum = 2位。
如果乘法深度< 3,则dnum设置为multDepth+1
uint32_t dnum = 2;
输入要参加计算的数据并参与计算
在CKKS的RNS实现中,每个密文对应一个大数(在RNS中表示为小整数)对密文模Q取模,这个模Q定义为(multDepth+1)素数的乘积:Q = q0 * q1 *…* qL.每个qi被选择接近比例因子D=2^p,因此Q的总大小大约是:
sizeof (Q) = (multDepth + 1) * scaleFactorBits。混合键切换采用一个以Q为模定义的数字d,并执行4个步骤:d分割成dnum数字,每个数字的大小大概是装天花板(sizeof (Q) / dnum) 2 -扩展密文模数从Q Q * P P是特殊的质数的乘积3 -用扩展组件与关键切换键4 -减少密文模数回到Q
没有必要理解所有这些阶段是如何工作的,只要清楚地知道在阶段2中密文模数的大小从sizeof(Q)增加到sizeof(Q)+sizeof(P)。P总是被设置为尽可能小,只要sizeof(P)大于最大数位的大小,即大于ceil(sizeof(Q)/dnum)。因此,P的大小与位数成反比,所以位数越多,那么P更小。
这里的折衷是,数字越多意味着数字分解阶段的代价越高,但密文模量Q*P的最大值会变小。由于Q*P的大小决定了实现一定安全级别所需的环尺寸,在某些情况下,更多的数字意味着我们可以使用更小的环尺寸,从而获得更好的整体性能。
CryptoContext<DCRTPoly> cc =
CryptoContextFactory<DCRTPoly>::genCryptoContextCKKS(
multDepth, scaleFactorBits, batchSize, securityLevel, ringDimension,
rsTech, ksTech, dnum);
std::cout << "CKKS scheme is using ring dimension " << cc->GetRingDimension()
<< std::endl;
std::cout << "- Using HYBRID key switching with " << dnum << " digits"
<< std::endl
<< std::endl;
cc->Enable(ENCRYPTION);
cc->Enable(SHE);
auto keys = cc->KeyGen();
cc->EvalAtIndexKeyGen(keys.secretKey, {1, -2});
// Input
vector<double> x = {1.0, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7};
Plaintext ptxt = cc->MakeCKKSPackedPlaintext(x);
std::cout << "Input x: " << ptxt << std::endl;
auto c = cc->Encrypt(keys.publicKey, ptxt);
TimeVar t;
TIC(t);
auto cRot1 = cc->EvalAtIndex(c, 1);
auto cRot2 = cc->EvalAtIndex(cRot1, -2);
double time2digits = TOC(t);
// Take note and compare the runtime to the runtime
// of the same computation in the next demo.
Plaintext result;
std::cout.precision(8);
cc->Decrypt(keys.secretKey, cRot2, &result);
result->SetLength(batchSize);
std::cout << "x rotate by -1 = " << result << std::endl;
std::cout << " - 2 rotations with HYBRID (2 digits) took " << time2digits
<< "ms" << std::endl;
运行结果
void HybridKeySwitchingDemo2()
相关参数的设置
std::cout << "\n\n\n ===== HybridKeySwitchingDemo2 ============= "
<< std::endl;
uint32_t multDepth = 5;
uint32_t scaleFactorBits = 50;
uint32_t batchSize = 8;
SecurityLevel securityLevel = HEStd_128_classic;
uint32_t ringDimension =
0; // 0 means the library will choose it based on securityLevel
RescalingTechnique rsTech = EXACTRESCALE;
KeySwitchTechnique ksTech = HYBRID;
设置杂交分解中大数位数
这里我们用dnum = 3位数字。尽管3位数以上两个数字在前面的演示和数字分解成本较高,数字意味着单个数字的增加更小,我们可以执行关键切换使用只有一个特殊的' P(而不是两个在前面演示)。
这也意味着密钥交换中密文模量的最大尺寸要小60位,结果证明,这种减小足以保证更小的环尺寸来实现相同的安全级别(128位)。
uint32_t dnum = 3;
输入要参加计算的数据并参与计算
CryptoContext<DCRTPoly> cc =
CryptoContextFactory<DCRTPoly>::genCryptoContextCKKS(
multDepth, scaleFactorBits, batchSize, securityLevel, ringDimension,
rsTech, ksTech, dnum);
// Compare the ring dimension in this demo to the one in
// the previous.
std::cout << "CKKS scheme is using ring dimension " << cc->GetRingDimension()
<< std::endl;
std::cout << "- Using HYBRID key switching with " << dnum << " digits"
<< std::endl
<< std::endl;
cc->Enable(ENCRYPTION);
cc->Enable(SHE);
auto keys = cc->KeyGen();
cc->EvalAtIndexKeyGen(keys.secretKey, {1, -2});
// Input
vector<double> x = {1.0, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7};
Plaintext ptxt = cc->MakeCKKSPackedPlaintext(x);
std::cout << "Input x: " << ptxt << std::endl;
auto c = cc->Encrypt(keys.publicKey, ptxt);
TimeVar t;
TIC(t);
auto cRot1 = cc->EvalAtIndex(c, 1);
auto cRot2 = cc->EvalAtIndex(cRot1, -2);
// The runtime here is smaller than in the previous demo.
double time3digits = TOC(t);
Plaintext result;
std::cout.precision(8);
cc->Decrypt(keys.secretKey, cRot2, &result);
result->SetLength(batchSize);
std::cout << "x rotate by -1 = " << result << std::endl;
std::cout << " - 2 rotations with HYBRID (3 digits) took " << time3digits
<< "ms" << std::endl;
运行结果