2021SC@SDUSC PALISADE开源库(五)公钥加密模块的示例程序(一)advanced-real-numbers.cpp

本文详细介绍了CKKS加密方案的两种变体——EXACTRESCALE和APPROXRESCALE,以及它们在处理密文深度和精度上的区别。EXACTRESCALE提供精确的缩放操作,适合快速原型设计,而APPROXRESCALE则通过近似计算提高效率。此外,还讨论了杂交键交换算法中的位数选择对性能和环尺寸的影响。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

2021SC@SDUSC

目录

介绍

EXACTRESCALE和APPROXRESCALE两种变体

void AutomaticRescaleDemo(RescalingTechnique rsTech)

判断是哪一个变体进行操作

相关参数的设置

输入要参加计算的数据

计算 f(x) = x^18 + x^9 + 1

运行结果

EXACTRESCALE

 APPROXRESCALE

 两种不同的密钥交换算法

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;

运行结果

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值