同态加密库HElib快速入门

HElib是IBM用C++写成的一个开源的同态加密库。至于同态加密是什么,网上有一堆教程,无须赘述。

对于同态加密库HElib而言,所有的输入输出都表示成为vector<long>的形式,vector<long>的大小和HElib的初始化参数有关。需要将原始数据全部全部转换成vector<long>的形式,至于如何转换,与具体的业务逻辑有关系,HElib并不关心具体的转换方式,HElib所做的只是对vector<long>进行加解密,以及进行加减乘等运算。

首先先介绍一下HElib的几个重要的类:

FHEcontext类:初始化所必须的类,程序根据初始化的参数生成FHEcontext对象,进而通过FHEcontext对象生成同态加密的公私钥。

ZZX类:生成EncryptedArray所必须的。

FHESecKey类:同态加密私钥类

FHEPubKey类:同态加密公钥类

EncryptedArray类:贯穿整个同态加密的代码,用于将vector<long>编码成同态加密的明文对象,对于数据进行同态加密和解密,用于将明文对象解码乘vector<long>。

NewPlaintextArray类:明文对象,输入vector<long>转换成明文对象进而进一步被HElib处理,如果利用cout将NewPlaintextArray对象输出的话也是以向量形式输出,维度与对应位置的元素与vector<long>相同。提供了加、减、乘、移位、异或等运算。加减乘运算即向量对应位置的元素进行加减乘。

Ctxt对象:同态加密的密文对象,NewPlaintextArray类进行同态加密以后得到Ctxt对象。提供了和NewPlaintextArray一样的运算。

示例代码:

#include <NTL/ZZ.h>
#include <NTL/BasicThreadPool.h>
#include "FHE.h"
#include "timing.h"
#include "EncryptedArray.h"
#include <NTL/lzz_pXFactoring.h>

#include <cassert>
#include <cstdio>

int main(){
	long R=1;
	long p=619;
	long r=1;
	long d=1;
	long c=2;
	long k=80;
	long w=64;
  long L=6;
	long m=7781;
	Vec<long> gens;
	Vec<long> ords;//以上都是进行同态加密初始化的参数,其中p参数比较重要,它是Ctxt类中Plaintextspace成员的值,决定了我们进行同态加密运算的数据范围并且p必须为素数。例如本例中p=619,则进行同态加密运算的数据不得超过619,明文运算的结果也不能超过619,否则没有办法解密出正确的结果。
    vector<long> gens1, ords1;
    convert(gens1, gens);
    convert(ords1, ords);
  	FHEcontext context(m, p, r, gens1, ords1);
  	buildModChain(context, L, c);

  	ZZX G;
  	if (d == 0)
    	G = context.alMod.getFactorsOverZZ()[0];
  	else
    	G = makeIrredPoly(p, d);//进行HElib的初始化

  	//生成同态加密的公私钥,进行同态加密运算的密文对象必须是经过同一公钥加密的数据,否则会报错
        FHESecKey secretKey(context);
  	const FHEPubKey& publicKey = secretKey;
  	secretKey.GenSecKey(w); // A Hamming-weight-w secret key
  	addSome1DMatrices(secretKey); // compute key-switching matrices that we need

  	EncryptedArray ea(context, G);
  	long nslots = ea.size();
		
	NewPlaintextArray plain_text1(ea);
	vector<long> data1;
	data1.resize(nslots);
	data1[0]=7;
	data1[1]=8;//向量数据
	encode(ea,plain_text1,data1);//将向量编码成明文对象
	cout<<"plain_text1="<<plain_text1<<endl;
	Ctxt cipher_text1(publicKey);
	cout<<"encrypt plain_text1"<<endl;
	ea.encrypt(cipher_text1, publicKey, plain_text1);
	

	NewPlaintextArray plain_text2(ea);
	data1[0]=5;
	data1[1]=4;
	encode(ea,plain_text2,data1);
	cout<<"plain_text2="<<plain_text2<<endl;
	Ctxt cipher_text2(publicKey);
	ea.encrypt(cipher_text2,publicKey,plain_text2);

	NewPlaintextArray plain_text3(ea);
	data1[0]=1;
	data1[1]=2;
	encode(ea,plain_text3,data1);
	cout<<"plain_text3="<<plain_text3<<endl;
	Ctxt cipher_text3(publicKey);
	ea.encrypt(cipher_text3,publicKey,plain_text3);

	cout<<"密文相加"<<endl;
	cipher_text1+=cipher_text2;
	cipher_text1+=cipher_text3;
	cout<<"结束"<<endl;

	NewPlaintextArray de(ea);
	cout<<"解密:"<<endl;
	ea.decrypt(cipher_text1, secretKey, de);
	vector<long> array2;
	array2.resize(ea.size());
	decode(ea,array2,de);//把解密的结果还原成向量
	cout<<"decrypt:"<<array2<<endl;

}

最终打印出解密结果,即三个向量对应位置的元素相加。

此外,在进行乘法运算的过程中,存在一个我也不知道是不是BUG的问题:

有两个NewPlaintextArray对象plain1和plain2,加密成Ctxt对象cipher1,cipher2,接着进行运算cipher1×=cipher2,此时对cipher1进行解密可以得到正确的结果。但是调用cipher1的write函数将cipher1写入文件以后,再读文件读入cipher3,此时再对cipher3进行解密无法解密出正确的结果。

我比较了一下cipher1和cipher3,打印出他们所有的成员,发现了cipher1的PrimeSet的成员=[1,2]但是cipher3的PrimeSet成员=[0,1,2],我调用了cipher3的getPrimeSet方法得到了PrimeSet成员,调用该成员的remove(0)方法删去0,接着进行解密可以解密出正确的结果,但目前不确定该方法是否具有通用性。

猜你喜欢

转载自blog.csdn.net/LOVETEDA/article/details/84952037
今日推荐