2021SC@SDUSC PALISADE开源库(四)对CKKS源码分析(一)实现简单的加减乘

2021SC@SDUSC

目录

介绍

源码分析

生成公钥

 实现密文之间的加法

实现密文之间的减法

实现密文之间的乘法

总结


介绍

        这篇文章我将从CKKS的源码进行入手,进一步理解CKKS的内部加密解密以及简单的加减乘等操作。毕竟是我第一次大规模的看别人的开源代码,有什么分析不对的地方希望大家指正,一起加油,一起进步!!!

源码分析

生成公钥

        在我之前博客中 PALISADE开源库(二)CKKS讲解系列(三)加密和解密 大体介绍了加密解密以及简单的加法,如果不是很清楚大体过程的同学可以再去回顾一下,以便于了解。

生成组成公钥的要素 “ a ” 

Element a(dug, elementParams, Format::EVALUATION);

生成秘钥 “ s ”

Element s;

给秘钥s进行赋值 

    不使用预先计算池中的随机多项式,分两步完成。支持离散高斯分布(RLWE),三元均匀分布(优化)和稀疏分布(sparse)情况。
switch (cryptoParams->GetMode()) {
    case RLWE:
      s = Element(dgg, elementParams, Format::COEFFICIENT);
      break;
    case OPTIMIZED:
      s = Element(tug, elementParams, Format::COEFFICIENT);
      break;
    case SPARSE:
      s = Element(tug, elementParams, Format::COEFFICIENT, 64);
      break;
    default:
      break;
  }
  s.SetFormat(Format::EVALUATION);

生成并设置公钥

// privateKey->MakePublicKey(a, publicKey);
  Element e(dgg, elementParams, Format::COEFFICIENT);
  e.SetFormat(Format::EVALUATION);

  //公钥b的生成
  Element b = e - a * s;

调用上述方法给kp赋值并返回结果

kp.secretKey->SetPrivateElement(std::move(s));
  kp.publicKey->SetPublicElementAtIndex(0, std::move(b));
  kp.publicKey->SetPublicElementAtIndex(1, std::move(a));

  // 给kp的是三个参数进行赋值 kp = (b, a)=(- a.s + e, a)
  return kp;

 实现密文之间的加法

主函数调用下面的方法

template <class Element>
Ciphertext<Element> LPAlgorithmSHECKKS<Element>::EvalAddCore(
    ConstCiphertext<Element> ciphertext1,
    ConstCiphertext<Element> ciphertext2) const {
  Ciphertext<Element> result = ciphertext1->Clone();
//EvalAddCoreInPlace方法下面进行了定义
  EvalAddCoreInPlace(result, ciphertext2);
  return result;
}

EvalAddCoreInPlace 方法的定义

1)给出两个要进行计算的密文

void LPAlgorithmSHECKKS<Element>::EvalAddCoreInPlace(
    Ciphertext<Element> &ciphertext1,
    ConstCiphertext<Element> ciphertext2)

2)判断两个密文的深度是否相等,如果不相等,返回报错语句

 if (ciphertext1->GetDepth() != ciphertext2->GetDepth()) {
    PALISADE_THROW(config_error, "Depths of two ciphertexts do not match.");
  }

3)判断两个密文的级别,如果第一个密文的级别小于第二个密文的级别,返回报错语句

 if (ciphertext1->GetLevel() < ciphertext2->GetLevel()) {
    PALISADE_THROW(config_error,
                   "EvalAddCoreInPlace cannot add ciphertexts with ciphertext1 "
                   "level less than ciphertext2 level.");
  }

4)将两个密文分别拆分到两个容器内,以便进行操作

std::vector<Element> &cv1 = ciphertext1->GetElements();
  const std::vector<Element> &cv2 = ciphertext2->GetElements();

5)分别计算两个密文容器长度大小,并记录长度小的数量

  size_t c1Size = cv1.size();
  size_t c2Size = cv2.size();
  size_t cSmallSize = std::min(c1Size, c2Size);

6)将两个密文容器中长度小的范围内的数据进行相加操作

for (size_t i = 0; i < cSmallSize; i++) {
    cv1[i] += cv2[i];
  }

7)对长度大的密文容器中剩余的数据进行操作

    如果第一个密文容器的长度大于第二个密文容器的长度的话,我们可以知道,cv1[i] += cv2[i];这样的话,不需要进行任何操作,cv1剩余的部分不需要进行操作就可以了。

    但是,如果第一个密文容器的长度小于于第二个密文容器的长度的话,我们就要将第一个密文容器进行扩充到第二个密文容器的大小,然后将第二个密文容器内剩余的部分复制到第一个密文容器的空充补分就可以了。
if (c1Size < c2Size) {
    cv1.reserve(c2Size);
    for (size_t i = c1Size; i < c2Size; i++) {
      cv1.emplace_back(cv2[i]);
    }
  }

实现密文之间的减法

1)给出两个要进行计算的密文

Ciphertext<Element> LPAlgorithmSHECKKS<Element>::EvalSubCore(
    ConstCiphertext<Element> ciphertext1,
    ConstCiphertext<Element> ciphertext2)

2)判断两个密文的深度是否相等,如果不相等,返回报错语句

if (ciphertext1->GetDepth() != ciphertext2->GetDepth()) {
    PALISADE_THROW(config_error,
                   "LPAlgorithmSHECKKS<Element>::EvalSubCore - Depths of two "
                   "ciphertexts do not match.");
  }

3)判断两个密文的级别是否相等,如果不相等,返回报错语句

if (ciphertext1->GetLevel() != ciphertext2->GetLevel()) {
    PALISADE_THROW(config_error,
                   "EvalSubCore cannot sub ciphertexts with different number "
                   "of CRT components.");
  }

4)设置一个计算结果的空容器

Ciphertext<Element> result = ciphertext1->CloneEmpty();

5)将两个密文分别拆分到两个容器内,以便进行操作

  const std::vector<Element> &cv1 = ciphertext1->GetElements();
  const std::vector<Element> &cv2 = ciphertext2->GetElements();

6)判断两个密文容器的大小,并记录密文容器大的长度和密文容器小的长度

size_t c1Size = cv1.size();
  size_t c2Size = cv2.size();
  size_t cSmallSize, cLargeSize;
  if (c1Size < c2Size) {
    cSmallSize = c1Size;
    cLargeSize = c2Size;
  } else {
    cSmallSize = c2Size;
    cLargeSize = c1Size;
  }

7)设置一个减法分段计算的密文空间

std::vector<Element> cvSub;

8)将两个密文容器内共有长度的数据进行相减操作

for (size_t i = 0; i < cSmallSize; i++) {
    cvSub.push_back(std::move(cv1[i] - cv2[i]));
  }

9)将长度大的密文容器内剩余的数据进行操作

for (size_t i = cSmallSize; i < cLargeSize; i++) {
      //被减数的长度小于减数的长度,将减数剩余部分取相反数添加到减法分段的密文空间
    if (c1Size < c2Size)
      cvSub.push_back(std::move(cv2[i].Negate()));
    //被减数的长度大于减数的长度,将被减数剩余部分直接添加到减法分段的密文空间
    else
      cvSub.push_back(cv1[i]);
  }

10)返回相减的计算结果

result->SetElements(std::move(cvSub));

  result->SetDepth(ciphertext1->GetDepth());
  result->SetScalingFactor(ciphertext1->GetScalingFactor());
  result->SetLevel(ciphertext1->GetLevel());

实现密文之间的乘法

1)给出两个要进行计算的密文

Ciphertext<Element> LPAlgorithmSHECKKS<Element>::EvalMultCore(
    ConstCiphertext<Element> ciphertext1,
    ConstCiphertext<Element> ciphertext2) 

2)判断两个密文的数据是否准确,如果不准确,返回报错语句

if (ciphertext1->GetElements()[0].GetFormat() == Format::COEFFICIENT ||
      ciphertext2->GetElements()[0].GetFormat() == Format::COEFFICIENT) {
    PALISADE_THROW(not_available_error,
                   "EvalMult cannot multiply in COEFFICIENT domain.");
  }

3)判断两个密文的级别是否相等,如果不相等,返回报错语句

if (ciphertext1->GetLevel() != ciphertext2->GetLevel()) {
    PALISADE_THROW(config_error,
                   "EvalMultCore cannot multiply ciphertexts with different "
                   "number of CRT components.");
  }

4)设置一个计算结果的空容器

Ciphertext<Element> result = ciphertext1->CloneEmpty();

5)将两个密文分别拆分到两个容器内,以便进行操作

  std::vector<Element> cv1 = ciphertext1->GetElements();
  const std::vector<Element> &cv2 = ciphertext2->GetElements();

6)设置一个乘法分段计算的密文容器以及其长度

  size_t cResultSize = cv1.size() + cv2.size() - 1;
  std::vector<Element> cvMult(cResultSize);

7)进行相关的乘法操作(两种情况)

if (cv1.size() == 2 && cv2.size() == 2) {
    // cvMult[0] = cv1[0] * cv2[0];
    // cvMult[1] = (cv1[0] *= cv2[1]) + (cv2[0] * cv1[1]);
    // cvMult[2] = (cv1[1] *= cv2[1]);
    cvMult[2] = (cv1[1] * cv2[1]);
    cvMult[1] = (cv1[1] *= cv2[0]);
    cvMult[0] = (cv2[0] * cv1[0]);
    cvMult[1] += (cv1[0] *= cv2[1]);
  } else {
    bool isFirstAdd[cResultSize];
    std::fill_n(isFirstAdd, cResultSize, true);

    for (size_t i = 0; i < cv1.size(); i++) {
      for (size_t j = 0; j < cv2.size(); j++) {
        if (isFirstAdd[i + j] == true) {
          cvMult[i + j] = cv1[i] * cv2[j];
          isFirstAdd[i + j] = false;
        } else {
          cvMult[i + j] += cv1[i] * cv2[j];
        }
      }
    }
  }

8)返回密文相乘的结果

 result->SetElements(std::move(cvMult));
  result->SetDepth(ciphertext1->GetDepth() + ciphertext2->GetDepth());
  result->SetScalingFactor(ciphertext1->GetScalingFactor() *
                           ciphertext2->GetScalingFactor());
  result->SetLevel(ciphertext1->GetLevel());

总结

        这篇文章只是简单介绍了加密以及密文与密文之间的加减乘,并没有深入展开扩展加减乘等一系列操作,下一篇文章将展开进行扩展源码介绍,这篇文章就到此为止吧。

### 关于 CKKS 同态加密库的使用和文档 CKKS(Cheon-Kim-Kim-Song)是种近似同态加密方案,允许对密文执行加法和乘法操作而无需解密。这种技术广泛应用于隐私保护计算领域,特别是在机器学习模型训练和推理过程中。 以下是关于 CKKS 库的些关键信息: #### 1. 安装 CKKS 库 大多数实现 CKKS 的开源项目基于 C++ 或 Python 缠绕接口开发。以下是些常见的安装方法: - **Microsoft SEAL**: Microsoft 提供了个流行的同态加密库,支持 CKKS 方案[^3]。 ```bash git clone https://github.com/microsoft/SEAL.git cd SEAL && cmake . && make ``` - **HElib**: 另个常用的同态加密库也实现了部分 CKKS 功能[^4]。 ```bash git clone https://github.com/homenc/HElib.git cd HElib && ./bootstrap.sh && ./configure && make ``` #### 2. 使用示例 下面是简单的 Python 示例,展示如何利用 Microsoft SEAL 实现基本的 CKKS 加密运算[^5]: ```python from seal import * parms = EncryptionParameters(scheme_type.CKKS) poly_modulus_degree = 8192 coeff_mod_bit_sizes = [40, 20, 40] parms.set_poly_modulus_degree(poly_modulus_degree) parms.set_coeff_modulus(CoeffModulus.Create( poly_modulus_degree, coeff_mod_bit_sizes)) context = SEALContext(parms) print(f"Set encryption parameters and print them:") print_parameters(context) keygen = KeyGenerator(context) public_key = keygen.public_key() secret_key = keygen.secret_key() encryptor = Encryptor(context, public_key) evaluator = Evaluator(context) decryptor = Decryptor(context, secret_key) encoder = CKKSEncoder(context) scale = pow(2.0, 40) slots = encoder.slot_count() plain = Plaintext() encrypted_result = Ciphertext() vector_of_floats = [0, 1, 2, 3, 4, 5, 6, 7] encoder.encode(vector_of_floats, scale, plain) encryptor.encrypt(plain, encrypted_result) output = DoubleVector() decoder.decode(decryptor.decrypt(encrypted_result), output) print("Decrypted plaintext:", list(output)) ``` #### 3. 文档资源 官方文档通常是最权威的学习材料之。对于 CKKS 的具体用法,可以参考以下链接: - **Microsoft SEAL Documentation**: 提供详细的教程和技术细节[^6]。 - **PALISADE Homomorphic Encryption Library**: 这也是个强大的工具包,提供了丰富的 API 和示例代码[^7]。 #### 4. 性能优化技巧 为了提高 CKKS 密码系统的性能,开发者需要注意以下几个方面: - 调整多项式的模数阶次 `poly_modulus_degree` 来平衡安全性和效率[^8]。 - 控制系数模数大小以减少噪声增长速度[^9]。 --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值