同态加密库OpenFHE之代码详解

0 摘要

        本文主要分析OpenFHE库中测试用例所使用的代码,通过学习测试用例的编程思路,了解OpenFHE的使用方法以及调用逻辑。

        这段代码采用了BFVrns方案以进行整数数据的同态运算,接下来我将会对各个段落的功能进行分析。

1 代码详解

1.1 引入OpenFHE库

#include "openfhe.h"
using namespace lbcrypto;

        这部分代码引入了OpenFHE库,并使用其命名空间。

1.2 定义主函数

int main() {

        定义主函数,在这部分进行同态加密操作。

1.3 设置加密参数

    // Sample Program: Step 1: Set CryptoContext
    CCParams<CryptoContextBFVRNS> parameters;
    parameters.SetPlaintextModulus(65537);
    parameters.SetMultiplicativeDepth(2);

    CryptoContext<DCRTPoly> cryptoContext = GenCryptoContext(parameters);
    cryptoContext->Enable(PKE);
    cryptoContext->Enable(KEYSWITCH);
    cryptoContext->Enable(LEVELEDSHE);

        此代码段设置了加密上下文,指定了明文模数和乘法深度等参数,并启用了公钥加密(PKE)、密钥切换(KEYSWITCH)和分层同态加密(LEVELEDSHE)功能。

1.4 密钥生成

    // Sample Program: Step 2: Key Generation
    KeyPair<DCRTPoly> keyPair;
    keyPair = cryptoContext->KeyGen();
    cryptoContext->EvalMultKeyGen(keyPair.secretKey);
    cryptoContext->EvalRotateKeyGen(keyPair.secretKey, {1, 2, -1, -2});

        生成公私钥对、重新线性化密钥和旋转评估密钥。

1.5 加密

    // Sample Program: Step 3: Encryption
    std::vector<int64_t> vectorOfInts1 = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};
    Plaintext plaintext1 = cryptoContext->MakePackedPlaintext(vectorOfInts1);
    std::vector<int64_t> vectorOfInts2 = {3, 2, 1, 4, 5, 6, 7, 8, 9, 10, 11, 12};
    Plaintext plaintext2 = cryptoContext->MakePackedPlaintext(vectorOfInts2);
    std::vector<int64_t> vectorOfInts3 = {1, 2, 5, 2, 5, 6, 7, 8, 9, 10, 11, 12};
    Plaintext plaintext3 = cryptoContext->MakePackedPlaintext(vectorOfInts3);

    auto ciphertext1 = cryptoContext->Encrypt(keyPair.publicKey, plaintext1);
    auto ciphertext2 = cryptoContext->Encrypt(keyPair.publicKey, plaintext2);
    auto ciphertext3 = cryptoContext->Encrypt(keyPair.publicKey, plaintext3);

       首先创建三个明文向量plaintext1、plaintext2、plaintext3,用于进行编码后加密形成密文。

       用Encrypt函数对刚刚创建的3个向量进行加密处理。

        这里可以看到,加密的过程需要keyPair.publickey也就是用刚刚生成的公私钥对中的公钥进行加密。

1.6 计算

    // Sample Program: Step 4: Evaluation
    auto ciphertextAdd12 = cryptoContext->EvalAdd(ciphertext1, ciphertext2);
    auto ciphertextAddResult = cryptoContext->EvalAdd(ciphertextAdd12, ciphertext3);

    auto ciphertextMul12 = cryptoContext->EvalMult(ciphertext1, ciphertext2);
    auto ciphertextMultResult = cryptoContext->EvalMult(ciphertextMul12, ciphertext3);

    auto ciphertextRot1 = cryptoContext->EvalRotate(ciphertext1, 1);
    auto ciphertextRot2 = cryptoContext->EvalRotate(ciphertext1, 2);
    auto ciphertextRot3 = cryptoContext->EvalRotate(ciphertext1, -1);
    auto ciphertextRot4 = cryptoContext->EvalRotate(ciphertext1, -2);

        在1.5中,代码把生成的长度为12的向量(plaintext1、plaintext2、plaintext3)加密后赋值给ciphertext1、ciphertext2、ciphertext3。

        在计算过程中,先对ciphertext1、ciphertext2进行密文加运算,将运算结果赋值给ciphertextAdd12,然后用ciphertextAdd12与ciphertext3再次进行密文加运算,得到ciphertext1、ciphertext2、ciphertext3密文加的结果,并赋值给ciphertextAddResult。

        同理,下面乘法运算也是分步骤进行的,先对ciphertext1、ciphertext2进行密文乘运算,后利用第一步得出的结果再次和ciphertext3进行密文乘运算,得出密态的结果,赋值给ciphertextMultResult。

        接下来就是对ciphertext1的四次轮转操作,在EvalRotate函数中,第一个值为要轮转的向量,第二个值为轮转的方向,正数轮转代表向量向左轮转(左移),负数轮转代表向量向右轮转(右移)。

        这样说很抽象,举个例子:

        假设我们有一个向量 α[1, 2, 3, 4, 5],我们对这个向量进行不同的轮转操作:

        1.EvalRotate(α, 1)=[2, 3, 4, 5, 1]

                EvalRotate的意义是向左轮转一位,2会取代原来1的位置,3取代2,以此类推,最左边的1则会到最右边去。可以把向量的轮转看成是一个循环队列,轮转后队头可能成为队尾,队尾也可能成为新的队头。

        2.EvalRotate(α, -1)=[5, 1, 2, 3, 4]

                很明显,这是上一个函数的逆运算,即向右轮转一位,跟上面说的一样,位于队尾的5经过轮转之后会跑到队头的位置,剩下4个元素依次向右轮转。

        3.EvalRotate(α, 2)=[3, 4, 5, 1, 2]

                向左轮转两位,第一次轮转结果[2, 3, 4, 5, 1],第二次轮转得到最终结果[3, 4, 5, 1, 2]。

        4.EvalRotate(α, -2)=[4, 5, 1, 2, 3]

                向右轮转两位,第一次轮转结果[5, 1, 2, 3, 4],第二次轮转得到最终结果[4, 5, 1, 2, 3]。

        至此,相信你对密文的加乘以及轮转已经有了一定的认识,让我们看看下面的代码写了些什么。

1.7 解密

    // Sample Program: Step 5: Decryption
    Plaintext plaintextAddResult;
    cryptoContext->Decrypt(keyPair.secretKey, ciphertextAddResult, &plaintextAddResult);

    Plaintext plaintextMultResult;
    cryptoContext->Decrypt(keyPair.secretKey, ciphertextMultResult, &plaintextMultResult);

    Plaintext plaintextRot1;
    cryptoContext->Decrypt(keyPair.secretKey, ciphertextRot1, &plaintextRot1);
    Plaintext plaintextRot2;
    cryptoContext->Decrypt(keyPair.secretKey, ciphertextRot2, &plaintextRot2);
    Plaintext plaintextRot3;
    cryptoContext->Decrypt(keyPair.secretKey, ciphertextRot3, &plaintextRot3);
    Plaintext plaintextRot4;
    cryptoContext->Decrypt(keyPair.secretKey, ciphertextRot4, &plaintextRot4);

    plaintextRot1->SetLength(vectorOfInts1.size());
    plaintextRot2->SetLength(vectorOfInts1.size());
    plaintextRot3->SetLength(vectorOfInts1.size());
    plaintextRot4->SetLength(vectorOfInts1.size());

        首先,声明一个名为plaintextAddResult的明文对象,用于存储解密后的加法结果。

        再声明一个名为plaintextMultResult的明文对象,用于存储解密后的乘法结果。

        调用 Decrypt 方法使用私钥 keyPair.secretKey 解密密文 ciphertextAddResult,并将结果存储在 plaintextAddResult 中。

        乘法与加法同理,调用 Decrypt 方法使用私钥 keyPair.secretKey 解密密文 ciphertextMultResult,并将结果存储在 plaintextMultResult 中。

        随后声明四个明文变量plaintext1、plaintext2、plaintext3、plaintext4,用来存储轮转计算后的明文结果。

        调用 Decrypt 方法使用私钥 keyPair.secretKey 分别解密密文 ciphertextRot1、ciphertextRot2、ciphertextRot3 和 ciphertextRot4,并将结果分别存储在 plaintextRot1、plaintextRot2、plaintextRot3 和 plaintextRot4 中。

        最后这几行代码将 plaintextRot1 到 plaintextRot4 的长度设置为 vectorOfInts1 的大小。这样确保解密后的明文长度与原始向量一致。

1.8 输出结果

    std::cout << "Plaintext #1: " << plaintext1 << std::endl;
    std::cout << "Plaintext #2: " << plaintext2 << std::endl;
    std::cout << "Plaintext #3: " << plaintext3 << std::endl;

    std::cout << "\nResults of homomorphic computations" << std::endl;
    std::cout << "#1 + #2 + #3: " << plaintextAddResult << std::endl;
    std::cout << "#1 * #2 * #3: " << plaintextMultResult << std::endl;
    std::cout << "Left rotation of #1 by 1: " << plaintextRot1 << std::endl;
    std::cout << "Left rotation of #1 by 2: " << plaintextRot2 << std::endl;
    std::cout << "Right rotation of #1 by 1: " << plaintextRot3 << std::endl;
    std::cout << "Right rotation of #1 by 2: " << plaintextRot4 << std::endl;

    return 0;
}

        这一步不需要做过多的解释,就是把刚才赋值好的变量输出到控制台。

2 总结

        本文是对OpenFHE中测试用例simple-intergers做出详细解释,简而言之,先预定义3个向量作为密态运算的项,随后进行密态加法、密态乘法、密态轮转三种运算,最后将3种运算的结果进行解密,最后输出。

        这个测试用例只是简单的对OpenFHE中最基础的几个函数进行调用,然后把密态运算的结果解密后输出,对于测试用例而言比较完整,但是根据这个代码框架,可以玩的东西一下就变得很多了。

        比如可以添加交互代码,让用户自己输入3个数字,然后对这三个数字进行上述的运算,随后解密输出,这也是我能想到的比较初级的玩法,需要解决的问题是如何让密态运算变得可见,我认为思路是先将未解密的密文输出到控制台,让用户有“密态”运算的概念。

        现在的成果是比较直观的,我认为作为一个用户而言运算过程中的密文处理是透明的,用户只是看到了简单的“计算器”功能,这一点我最近也在思考如何让密文在运算中“可见”,可能在不久的将来会用代码实现。

        如果你对我的内容感兴趣,请点赞收藏,这是我更新的最大动力!

  • 21
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值