2021SC@SDUSC

目录

介绍

大体步骤

1. 设置CryptoContext类

1)指定乘法深度

2)指定缩放因子的位长

3)指定密文中使用的明文槽数

4)指定基于FHE标准的期望安全级别

5)基于指定参数加密上下文

2. 密钥生成

1)生成加密密钥

2)生成重新线性化键

3)生成旋转键

3. 输入后的编码和加密

4. 进行加减乘等操作

5. 解密和输出


介绍

        在这一篇博客中,我们主要是介绍了利用PALISADE的主要API来进行CKKS的同态加密解密的大体步骤以及部分代码,如果想要看完整代码和运行过程及结果的同学,请查看的我的下一篇博客 PALISADE开源库(三)CKKS代码分析(二)简单代码运行及结果 来进行具体的操作。

大体步骤

1. 设置CryptoContext类

1)指定乘法深度

        我们在这里设置的CKKS方案将适用于任何具有乘数深度等于" multidepth "的计算。这是给定乘法的最大可能深度,但不是该方案支持的乘法总数。

        例如,f(x, y) = x^2 + x*y + y^2 + x + y的乘法深度为1,但总共需要3次乘法。另一方面,计算  g(x_i) = x1*x2*x3*x4 可以实现为 g(x_i) = ((x1*x2)*x3)*x4的乘法深度为3的计算,也可以实现为乘深2的计算:g(x_i) = (x1*x2)*(x3*x4)。

        出于性能方面的考虑,通常更可取的做法是尽可能缩短乘法深度。

  uint32_t multDepth = 1;

2)指定缩放因子的位长

        CKKS适用于实数,但这些数字被编码为整数。例如,实数m = 0.01被编码为m' = round(m*D),其中D是称为比例因子的方案参数。假设D = 1000,那么 m' 是 10(一个整数)。假设一个基于 m' 的计算结果是130,然后在解密时,去掉比例因子,使用户看到的实数结果是0.13。

        参数 " scaleFactorBits " 决定了比例因子 D 的位长,而不是比例因子本身。后者是特定于实现的,在CKKS的某些版本(例如EXACTRESCALE)中,它也可能在密文之间有所不同。

        选择 " scaleFactorBits " 取决于所需的计算精度,以及剩余的参数,如 multidepth 或 security 。这是因为其余的参数决定了在计算过程中会产生多少噪声 (记住CKKS是一种近似方案,每次操作都会产生少量的噪声)。缩放因子应该足够大,既能容纳这种噪声,又能支持与所需精度匹配的结果。

uint32_t scaleFactorBits = 50;

3)指定密文中使用的明文槽数

        CKKS在每个密文中包含多个明文值。插槽的最大数目取决于称为环尺寸的安全参数。在本例中,我们不直接指定环的尺寸,而是让库根据我们选择的安全级别、我们希望支持的乘深和比例因子大小为我们选择环的尺寸。

        请使用  GetRingDimension()  方法找出用于这些参数的环的确切尺寸。根据CKKS的工作方式,给出环尺寸N,最大批量尺寸为N/2。

        这就是我上一个系列文章中提到的M和N的来源。

uint32_t batchSize = 8;

4)指定基于FHE标准的期望安全级别

        这个参数可以有四个值。三个可能的值分别对应128位、192位和256位的安全性,第四个值对应“ NotSet ”,这意味着用户负责选择安全性参数。当然,“NotSet”应该只在非生产环境中使用,或者由了解其选择的安全性影响的专家使用。

        如果选择了给定的安全级别,库将参考FHE标准联盟(Introduction – Homomorphic Encryption Standardization) 定义的当前安全参数表,自动选择安全参数。详情请参阅以下参考资料中的“推荐参数表”:​​​​​​http://homomorphicencryption.org/wp-content/uploads/2018/11/HomomorphicEncryptionStandardv1.1.pdf

 SecurityLevel securityLevel = HEStd_128_classic;

5)基于指定参数加密上下文

下面的调用基于上面定义的参数创建一个CKKS加密上下文。

CryptoContext<DCRTPoly> cc =
      CryptoContextFactory<DCRTPoly>::genCryptoContextCKKS(
          multDepth, scaleFactorBits, batchSize, securityLevel);


std::cout << "CKKS scheme is using ring dimension " << cc->GetRingDimension()
          << std::endl
          << std::endl;

启用您希望使用的特性

cc->Enable(ENCRYPTION);
cc->Enable(SHE);

2. 密钥生成

1)生成加密密钥

        它们用于加密/解密,以及生成不同类型的密钥。

auto keys = cc->KeyGen();

2)生成重新线性化键

        在CKKS中,每当有人将两个用密钥s加密的密文相乘时,我们得到一个结果,其中一些组件在密钥s下是有效的,另一个组件在密钥s^2下是有效的。

        在大多数情况下,我们想执行密文乘密文的结果,也就是说,我们要变换的s ^ 2分量密文所以变得有效在最初的关键。为此,我们需要创建 relinearization 键。

cc->EvalMultKeyGen(keys.secretKey);

3)生成旋转键

        CKKS支持旋转打包密文的内容,但要这样做,我们需要创建一个我们称为旋转密钥的东西。这是通过下面的调用完成的,它接受一个向量作为输入,该向量的下标对应于我们想要支持的旋转偏移量。负指标对应右移,正指标对应左移。

cc->EvalAtIndexKeyGen(keys.secretKey, {1, -2});

3. 输入后的编码和加密

输入:

vector<double> x1 = {0.25, 0.5, 0.75, 1.0, 2.0, 3.0, 4.0, 5.0};
vector<double> x2 = {5.0, 4.0, 3.0, 2.0, 1.0, 0.75, 0.5, 0.25};

编码为明文:

Plaintext ptxt1 = cc->MakeCKKSPackedPlaintext(x1);
Plaintext ptxt2 = cc->MakeCKKSPackedPlaintext(x2);

输出编码后的明文:

std::cout << "Input x1: " << ptxt1 << std::endl;
std::cout << "Input x2: " << ptxt2 << std::endl;

加密编码向量:

auto c1 = cc->Encrypt(keys.publicKey, ptxt1);
auto c2 = cc->Encrypt(keys.publicKey, ptxt2);

4. 进行加减乘等操作

密文相加:

auto cAdd = cc->EvalAdd(c1, c2);

密文相减:

auto cSub = cc->EvalSub(c1, c2);

明文与密文想乘:

auto cScalar = cc->EvalMult(c1, 4.0);

密文相乘:

auto cMul = cc->EvalMult(c1, c2);

密文旋转:

auto cRot1 = cc->EvalAtIndex(c1, 1);
auto cRot2 = cc->EvalAtIndex(c1, -2);

5. 解密和输出

Plaintext result;

        为了更好的输出,我们将cout精度设置为8位十进制数字。

        如果你想看到CKKS引入的错误/噪声,将其增加到15,它就会变得很明显了。

std::cout.precision(8);
std::cout << std::endl
          << "Results of homomorphic computations: " << std::endl;

解密加法的结果:

cc->Decrypt(keys.secretKey, cAdd, &result);
result->SetLength(batchSize);
std::cout << "x1 + x2 = " << result;
std::cout << "Estimated precision:" << result->GetLogPrecision()
          << std::endl;

解密减法的结果:

cc->Decrypt(keys.secretKey, cSub, &result);
result->SetLength(batchSize);
std::cout << "x1 - x2 = " << result;
std::cout << "Estimated precision: " << result->GetLogPrecision()
          << std::endl;

解密明文乘密文的结果:

cc->Decrypt(keys.secretKey, cScalar, &result);
result->SetLength(batchSize);
std::cout << "4 * x1 = " << result;
std::cout << "Estimated precision: " << result->GetLogPrecision()
          << std::endl;

解密密文乘密文的结果:

cc->Decrypt(keys.secretKey, cMul, &result);
result->SetLength(batchSize);
std::cout << "x1 * x2 = " << result;
std::cout << "Estimated precision: " << result->GetLogPrecision()
          << std::endl;

解密旋转的结果:

cc->Decrypt(keys.secretKey, cRot1, &result);
result->SetLength(batchSize);
std::cout
    << std::endl
    << "In rotations, very small outputs (~10^-10 here) correspond to 0's:"
    << std::endl;
std::cout << "x1 rotate by 1 = " << result;
std::cout << "Estimated precision: " << result->GetLogPrecision()
          << std::endl;




cc->Decrypt(keys.secretKey, cRot2, &result);
result->SetLength(batchSize);
std::cout << "x1 rotate by -2 = " << result << std::endl;
std::cout << "Estimated precision: " << result->GetLogPrecision()
          << std::endl;


Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐