0 简介
之前做过OpenFHE里面测试用例的详解,虽然能通过调用内置的测试用例进行简单的同态运算演示,但我认为并不直观,只是输出了一些已经规定好的数值。于是我想到了另外一个办法---开放输入口,让用户输入想要进行计算的数值,然后经过同态运算后解密输出。
理论可行,开始实践。
1 代码详解
1.1 代码总览
#include "openfhe.h"
#include <iostream>
#include <vector>
#include <stdexcept>
using namespace lbcrypto;
extern "C" {
void perform_homomorphic_operations() {
try {
// 第一步:设置加密上下文
// 创建加密上下文参数对象
CCParams<CryptoContextBFVRNS> parameters;
// 设置明文模数为65537
parameters.SetPlaintextModulus(65537);
// 设置乘法深度为2
parameters.SetMultiplicativeDepth(2);
// 生成加密上下文
CryptoContext<DCRTPoly> cryptoContext = GenCryptoContext(parameters);
// 启用公钥加密功能
cryptoContext->Enable(PKE);
// 启用密钥切换功能
cryptoContext->Enable(KEYSWITCH);
// 启用分层同态加密功能
cryptoContext->Enable(LEVELEDSHE);
// 第二步:生成密钥对
// 生成密钥对对象
KeyPair<DCRTPoly> keyPair;
// 生成密钥对
keyPair = cryptoContext->KeyGen();
// 生成同态乘法所需的密钥
cryptoContext->EvalMultKeyGen(keyPair.secretKey);
// 生成同态旋转所需的密钥,旋转位置为1, 2, -1, -2
cryptoContext->EvalRotateKeyGen(keyPair.secretKey, {1, 2, -1, -2});
// 第三步:获取用户输入
int x;
std::cout << "请输入要输入的值的数量:";
std::cin >> x;
// 检查输入数量是否为正整数
if (x <= 0) {
throw std::invalid_argument("输入的数量必须是正整数");
}
// 创建一个向量来存储用户输入的值
std::vector<int64_t> userInput(x);
std::cout << "请输入 " << x << " 个值:";
for (int i = 0; i < x; ++i) {
std::cin >> userInput[i];
}
// 第四步:加密
// 将用户输入的值转换为明文
Plaintext plaintext = cryptoContext->MakePackedPlaintext(userInput);
// 加密明文,生成密文
auto ciphertext = cryptoContext->Encrypt(keyPair.publicKey, plaintext);
// 第五步:同态运算
// 创建一个包含顺序值 1, 2, ..., x 的向量
std::vector<int64_t> seqValues(x);
for (int i = 0; i < x; ++i) {
seqValues[i] = i + 1;
}
// 将顺序值向量转换为明文
Plaintext seqPlaintext = cryptoContext->MakePackedPlaintext(seqValues);
// 加密顺序值明文,生成密文
auto seqCiphertext = cryptoContext->Encrypt(keyPair.publicKey, seqPlaintext);
// 同态加法
auto ciphertextAdd = ciphertext;
for (int i = 1; i < x; ++i) {
ciphertextAdd = cryptoContext->EvalAdd(ciphertextAdd, seqCiphertext);
}
// 同态乘法
auto ciphertextMult = ciphertext;
for (int i = 1; i < x; ++i) {
ciphertextMult = cryptoContext->EvalMult(ciphertextMult, seqCiphertext);
}
// 第六步:解密
// 解密同态加法结果
Plaintext plaintextAddResult, plaintextMultResult;
cryptoContext->Decrypt(keyPair.secretKey, ciphertextAdd, &plaintextAddResult);
// 解密同态乘法结果
cryptoContext->Decrypt(keyPair.secretKey, ciphertextMult, &plaintextMultResult);
// 输出解密结果
std::cout << "同态加法结果:";
for (int i = 0; i < x; ++i) {
std::cout << plaintextAddResult->GetPackedValue()[i] << " ";
}
std::cout << std::endl;
std::cout << "同态乘法结果:";
for (int i = 0; i < x; ++i) {
std::cout << plaintextMultResult->GetPackedValue()[i] << " ";
}
std::cout << std::endl;
} catch (const std::exception &e) {
// 捕获并打印异常信息
std::cerr << "发生错误:" << e.what() << std::endl;
}
}
}
// 添加 main 函数作为程序入口点
int main() {
perform_homomorphic_operations();
return 0;
}
这里直接附上有详细注释的代码片,有一定基础的人相信可以看懂,下面我来详细解释一下这段代码。
1.2 分步解析
1.2.1 设置加密参数
CCParams<CryptoContextBFVRNS> parameters;
parameters.SetPlaintextModulus(65537);
parameters.SetMultiplicativeDepth(2);
CryptoContext<DCRTPoly> cryptoContext = GenCryptoContext(parameters);
cryptoContext->Enable(PKE);
cryptoContext->Enable(KEYSWITCH);
cryptoContext->Enable(LEVELEDSHE);
- CCParams:创建一个
CCParams
对象,用于存储加密的参数。 - SetPlaintextModulus:设置明文模数为65537,这是明文消息转换为加密形式时使用的模数。
- SetMultiplicativeDepth:设置同态乘法的深度为2,这决定了可以进行的同态乘法操作的次数。
- GenCryptoContext:生成加密参数,它包含了加密、解密和同态运算所需的所有信息。
- Enable:启用不同的功能模块,包括公钥加密(PKE)、密钥切换(KEYSWITCH)和分层同态加密(LEVELEDSHE)。
1.2.2 生成密钥对
KeyPair<DCRTPoly> keyPair;
keyPair = cryptoContext->KeyGen();
cryptoContext->EvalMultKeyGen(keyPair.secretKey);
cryptoContext->EvalRotateKeyGen(keyPair.secretKey, {1, 2, -1, -2});
- KeyPair:创建一个
KeyPair
对象,用于存储公钥和私钥。 - KeyGen:生成公钥和私钥对。
- EvalMultKeyGen:生成用于同态乘法的评估密钥。
- EvalRotateKeyGen:生成用于同态旋转的评估密钥,旋转位置包括1、2、-1和-2。
1.2.3 获取用户输入
int x;
std::cout << "请输入要输入的值的数量:";
std::cin >> x;
if (x <= 0) {
throw std::invalid_argument("输入的数量必须是正整数");
}
std::vector<int64_t> userInput(x);
std::cout << "请输入 " << x << " 个值:";
for (int i = 0; i < x; ++i) {
std::cin >> userInput[i];
}
- 用户输入的数量:提示用户输入要处理的值的数量,并存储在变量
x
中。 - 输入验证:检查输入的数量是否为正整数,如果不是,抛出异常。
- 输入值存储:创建一个向量
userInput
来存储用户输入的值,并提示用户输入这些值。
1.2.4 同态运算
std::vector<int64_t> seqValues(x);
for (int i = 0; i < x; ++i) {
seqValues[i] = i + 1;
}
Plaintext seqPlaintext = cryptoContext->MakePackedPlaintext(seqValues);
auto seqCiphertext = cryptoContext->Encrypt(keyPair.publicKey, seqPlaintext);
// 同态加法
auto ciphertextAdd = ciphertext;
for (int i = 1; i < x; ++i) {
ciphertextAdd = cryptoContext->EvalAdd(ciphertextAdd, seqCiphertext);
}
// 同态乘法
auto ciphertextMult = ciphertext;
for (int i = 1; i < x; ++i) {
ciphertextMult = cryptoContext->EvalMult(ciphertextMult, seqCiphertext);
}
- 创建顺序值向量:创建一个包含顺序值(1, 2, ..., x)的向量。
- 加密顺序值:将顺序值向量转换为明文并加密,生成密文。
- 同态加法:进行同态加法操作,将用户输入的密文和顺序值密文相加。
- 同态乘法:进行同态乘法操作,将用户输入
1.2.5 输出解密后的结果
std::cout << "同态加法结果:";
for (int i = 0; i < x; ++i) {
std::cout << plaintextAddResult->GetPackedValue()[i] << " ";
}
std::cout << std::endl;
std::cout << "同态乘法结果:";
for (int i = 0; i < x; ++i) {
std::cout << plaintextMultResult->GetPackedValue()[i] << " ";
}
std::cout << std::endl;
} catch (const std::exception &e) {
// 捕获并打印异常信息
std::cerr << "发生错误:" << e.what() << std::endl;
这一步没什么好过多解释的,就是print一下结果。
2 如何使用
2.1 写入cpp文件
我的OpenFHE存放在根目录下openfhe-development文件夹下,下面就按照我自己的环境,讲解一下如何导入并使用上面的代码。
首先,进入cpp文件的保存位置:
cd /openfhe-development/src/pke/examples/
这一步如果报错的话就一步一步cd一下,方法是固定的,cpp文件存在openfhe安装位置下的src/pke/examples文件夹下。
进入这个文件夹后,用vi编辑一个新的文档,把上面的代码写进去:
vi jiaohuyunsuan.cpp
vi后面是我自己编的名字,注意后面要跟.cpp的后缀,文件名要记住,调用的时候会用到。
进去之后按“i”,然后把上面的代码粘贴到文件里面,按“esc“键,然后:wq保存。
2.2 编译
编译过程需要进入到openfhe安装路径的build文件夹下,然后直接用make编译文件,我以我的环境举例:
cd openfhe-development/build
make
等编译好以后,就可以进入到/openfhe-development/build/bin/examples/pke/下查看已经编译好的文件。
2.3 运行
刚才起的名字叫jiaohuyunsuan.cpp,调用的时候不用带后缀,代码如下:
cd openfhe-development
cd build
bin/examples/pke/jiaohuyunsuan
结果如图:
如果对你有帮助,请点赞收藏,这是我更新的最大动力。