什么是HElib?
HElib是一个基于C++语言的同态加密开源软件库,底层依赖于NTL数论运算库和GMP多精度运算库实现,主要开发者为IBM的Halevi,目前最新版本为1.0.2,实现了支持“Bootstrapping”的BGV方案和基于近似数的CKKS方案。 HElib在上述原始方案中引入了许多优化以加速同态运算,包括Smart-Vercauteren密文打包技术和Gentry-Halevi-Smart优化,提升了算法的整体运行效率。
HElib提供了一种“同态加密汇编语言”,支持“set”、“add”、“multiply”、“shift”等基本操作指令,此外还提供了自动噪声管理、改进的“Bootstrapping”方法、多线程等功能。
HElib的github主页:https://github.com/homenc/HElib
如何安装?
//CentOS 8安装HElib
##一般先决条件
GNU make
//-GNU make> = 3.82
yum install make

pthreads
什么是Pthreads?
参考:链接
git
//-git> = 1.8.3(需要构建和运行HElib测试套件)
yum install git

** Linux环境:**
g ++
// -g ++> = 7.3.1
yum install gcc-c++

cmake
// -cmake> = 3.10.2
yum install cmake

第一种方法
自动下载并构建GMP和NTL依赖关系并打包可重定位文件夹中的库
此选项将HElib及其依赖项(NTL和GMP)捆绑在一个目录中,然后可以在系统上自由移动。 NTL和GMP将自动获取和编译。 它可以全局安装(即在/ usr / local下),如果未指定CMAKE_INSTALL_PREFIX,这是默认选项,但是由于要覆盖现有版本的NTL,GMP或HElib,因此应谨慎操作。在这种情况下,还需要另外两个前提条件:
m4
什么是m4?
m4是一个宏处理器,将输入拷贝到输出,同时将宏展开。宏可以是内嵌的,也可以是用户定义的,它还有一些内建函数,可以引用文件、执行命令和计算等。m4既可以作为编译器的前端,也可以单独作为一个宏处理器。
更多:链接
// -m4> = 1.4.16
yum install m4

patchelf
什么是patchelf ?
patchelf 是一个用来修改elf格式的动态库和可执行程序的小工具,可以修改动态链接库的库名字,以及链接库的RPATH。
更多:链接
// -patchelf> = 0.9(如果在Linux上构建)
yum install patchelf

PS:一般用云服务器的话,镜像里没有 patchelf 的话,会自动去github上下载
如果用的是服务器测试的话,一般是yum 不了patchelf的,所以请采用其他方式安装
参考:链接
开始1
1、下载
wget https://github.com/homenc/HElib/archive/v2.1.0.tar.gz
2、解压
tar -zxvf v2.1.0.tar.gz

3、构建目录
mv HElib-2.1.0/ HElib cd HElib mkdir build cd build
4、运行cmake
cmake -DPACKAGE_BUILD=ON -DCMAKE_INSTALL_PREFIX=/home/alice/helib_install ..
PS:运行cmake配置步骤,指定要构建软件包(通过-DPACKAGE_BUILD = ON)并说出您希望安装到的位置,例如,要在`/home/alice/helib_install`中安装
可以在此处指定其他选项,例如使用以下命令启用HElib测试 -DENABLE_TEST = ON
5、编译
编译,并指定可选数量的线程(在此示例中为16),其输出将在可重定位文件夹“ helib_pack”中
make -j16

PS:1核 2GB 20Mbps 的服务器,CPU直接100%,需要稍微高点的配置。或者输入小的进程数,例如: make -j5
1核 4GB 20Mbps的服务器make时:

6、可选
(1)如果步骤2是通过-DENABLE_TEST = ON执行的,则HElib测试可以运行如下:
ctest
可以在以下位置找到详细的HElib特定测试日志 :`Testing / Temporary / LastTest.log`
(2)运行安装步骤,将文件夹“ helib_pack”复制到 :$ {CMAKE_INSTALL_PREFIX}(在此示例中为/home/alice/helib_install)
make install
当然,如果将“ CMAKE_INSTALL_PREFIX”保留为默认的“ /usr /local”,或者其他系统范围的路径,步骤6(2)可能需要`sudo`特权
默认按上面安装的流程,此时已安装完毕:
1、解压后的目录:

examples:里面有三个工程项目,稍后在下面可以演示
tests:里面有很多小例子,均引用了该库,后期会运行演示
2、安装目录
/home/alice/helib_install/helib_pack

所有的头文件都在 include 中
第二种方法
MacOS10.15下安装HElib库

安装并在系统中可用的依赖项
此选项涉及自行构建HElib,并链接到系统上预先存在的依赖项(NTL和GMP)。这样,可以移动HElib库,但不能依赖其依赖项(NTL和GMP),因为它们是绝对路径。对于此选项,您必须自己构建GMP> = 6.0.0和NTL> = 11.4.3。
在整个安装选项中,都假定将环境变量$ GMPDIR和$ NTLDIR设置为分别指向GMP和NTL的安装目录。请注意,如果从软件包构建更改为库构建,则更安全使用一个干净的构建目录
GMP
GMP多精度运算库,许多发行版都预装有GMP。如果没有,您可以将GMP安装为如下:
1、下载,确保版本GMP> = 6.0.0
2、解压缩并cd到gmp目录
3、GMP以标准的unix方式编译
./configure make sudo make install
默认情况下,这会将GMP安装到`/ usr / local`中
NTL
NTL数论运算库,您可以按如下方式安装NTL:
1、下载,确保版本NTL> = 11.4.3
2、解压缩并cd进入目录,例如`ntl-11.4.3 / src`
3、以标准的Unix方式配置,构建和安装NTL(但请记住指定以下标志以进行“配置”):
./configure NTL_GMP_LIP=on SHARED=on NTL_THREADS=on NTL_THREAD_BOOST=on make sudo make install
这应该将NTL安装到`/usr/local`中
注意:如果要链接到非系统GMP,请将`GMP_PREFIX = <path / to / gmp>`传递给 `/configure`步骤
参数介绍
通用:
-`BUILD_SHARED = ON / OFF`(默认为`OFF`):构建为共享库。请注意,如果未将NTL构建为共享库,则构建HElib(无论“ BUILD_SHARED”如何)都将失败。 NTL的默认值是静态库,要在共享库中构建NTL,请在步骤1中使用`./configure SHARED = on`。 -`CMAKE_BUILD_TYPE` :(默认值为`RelWithDebInfo`):选择构建类型,选项包括:`Debug`,`RelWithDebInfo`,`Release`,`MinSizeRel`。 -`CMAKE_INSTALL_PREFIX`:HElib的所需安装目录。 -`ENABLE_TEST = ON / OFF`(默认为`OFF`):启用测试构建。这将包括针对Google测试框架稳定版本(googletest v1.10.0)的自动下载步骤。 -`ENABLE_THREADS = ON / OFF`(默认为`ON`):启用线程支持。当且仅当NTL是使用`NTL_THREADS = ON`构建的时,此选项才必须打开。 -`PEDANTIC_BUILD = ON / OFF`(默认为`ON`):使用`-Wall -Wpedantic -Wextra -Werror`在构建期间。 -`HELIB_DEBUG = ON / OFF`(默认为`OFF`):构建HElib时激活调试模块(通过定义`HELIB_DEBUG`宏)。当调试模块处于活动状态时,这会生成用于调试目的的其他信息。 当使用cmake时,`HELIB_DEBUG`会传播到使用HElib的程序中。启用此功能后,使用HElib的程序将在配置过程中生成警告。这是为了提醒用户,如果未正确初始化调试模块,则可能会导致诸如“ sigsegv”之类的问题。
方法1:
-`PACKAGE_DIR`:软件包内部版本的安装位置。默认为`$ {CMAKE_INSTALL_PREFIX} / helib_pack`。
-`FETCH_GMP`:是否获取和构建GMP。默认为ON。如果设置为“ OFF”,则应该存在系统安装的GMP库,或者“ GMP_DIR”应指向有效的GMP前缀。
-`GMP_DIR`:GMP库的前缀。如果`FETCH_GMP = ON`被忽略
方法2:
- `GMP_DIR`: Prefix of the GMP library. - `NTL_DIR`: Prefix of the NTL library.
第三种方法
采用Docker容器安装,请参考:FHE-Toolkit 安装
如何使用?
在项目中使用HElib
前提
在方法1或方法2中运行“ make install”后,可以在lib中找到要链接的必需共享库文件,并在 include 中找到头文件【我这里是 /home/alice/helib_install/helib_pack/include】
例子的使用参考:examples/README.md
examples
1、介绍
`examples'目录包含教程和独立的示例程序表示各种API以及使用HElib的简单用例。
2、提供了什么
[tutorials]要涵盖CKKS方案,并包含8个记录在案的示例从基本操作到更复杂的序列化。
提供的示例程序使用BGV方案,包括:
-[BGV_binary_arithmetic](二进制算法)
-[BGV_country_db_lookup](BGV国家数据库查找)
-[BGV_packed_arithmetic](BGV压缩算法)

3、安装
要编译示例,您必须在系统上已经安装了HElib。这个过程是纯CMake的。首先,创建一个构建目录并移入该目录。在“ examples / build”中运行CMake,
mkdir build cd build cmake -Dhelib_DIR=/share/cmake/helib ..
PS:我这里的目录不是 /share/cmake/helib,而是 /home/alice/helib_install/helib_pack/share/cmake/helib 故应该:
cmake -Dhelib_DIR=/home/alice/helib_install/helib_pack/share/cmake/helib ..
4、编译
例如 BGV_country_db_lookup,进入 BGV_country_db_lookup 目录
cd BGV_country_db_lookup make -j6

100%,显示编译完成,下面可以运行试试看
5、运行
进入/bin
cd ../bin ./BGV_country_db_lookup

此时应该会报错,大概的意思就是没有找到 countries_dataset.csv 文件,现在要将该文件复制到该位置即可,文件位置在:examples/BGV_country_db_lookup
cp countries_dataset.csv ../bin
再次运行即可

其他两个例子也如此!
examples/tests
1、示例的所有测试均以bats(bash的测试框架)编写,并且需要bats-core。
2、请注意,测试要求示例已在build目录中成功编译并且可在build / bin中使用。 要运行测试,只需在examples / tests目录中执行脚本即可。
3、要运行所有测试,请键入以下命令。 (可选)-j标志可以使用线程来并行化测试。 注意,这需要与GNU并行,请参阅bats文档以获取更多信息。
前提安装:bats
举例运行:
cd test bats BGV_binary_arithmetic.bats
有报错,暂时不知道咋回事!

examples/tutorial
1、在examples目录下
make

2、在bin下执行
cd ../bin
例如:01_ckks_basics
/* Copyright (C) 2020-2021 IBM Corp.
* This program is Licensed under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License. See accompanying LICENSE file.
*/
// In the CKKS encryption scheme, plaintexts are vectors of real or complex
// numbers. The length, n, of these vectors is determined by the choice of
// parameters, as discussed below. We often refer to the components of these
// vectors as "slots", which are indexed 0, ..., n-1. We can add, subtract, or
// multiply two ciphertexts, and the corresponding operations are carried out
// slot by slot. This is sometimes referred to as a "SIMD operation" (SIMD
// means Single Instruction Multiple Data), since we can effectively perform
// the same scalar operation in parallel on all n slots.
#include <helib/helib.h>
using namespace std;
using namespace helib;
int main(int argc, char* argv[])
{
// To get started, we need to choose some parameters. This is done by
// initializing a Context object. Since there are a lot of parameters, many
// of them optional, HElib provides a "builder pattern" than lets you provide
// these parameters "by name".
Context context =
// initialize a Context object using the builder pattern
ContextBuilder<CKKS>()
.m(16 * 1024)
// m is the "cyclotomic index". For CKKS, m must be a power of 2. As
// m increases, you get more security and more slots, but the
// performance degrades and the size of a ciphertext increases. See
// table below for more information.
.bits(119)
// bits specifies the number of bits in the "ciphertext modulus". As
// bits increases, you get less security, but you can perform deeper
// homomorphic computations; in addition, the size of a ciphertext
// increases. See table below for more information. Also see
// 02_depth.cpp for more information about how depth and bits are
// related.
.precision(20)
// precision specifies the number of bits of precision when data is
// encoded, encrypted, or decrypted. More precisely, each of these
// operations are designed to add an error term of at most
// 2^{-precision} to each slot. As precision increases, the allowed
// depth of homomorphic computations decreases (but security and
// performance are not affected). It is not recommended to use
// precision greater than about 40 or so.
.c(2)
// c specifies the number of columns in key-switching matrices. Yes,
// it sounds very technical, and it is. However, all you have to know
// about this parameter is that as c increases, you get a little more
// security, but performance degrades and the memory requirement for
// the public key increases. c must be at least 2 and it is not
// recommended to set c higher than 8. See table below for more
// information.
.build();
// last step of the builder pattern
// The following table lists settings of m, bits, and c that yield (at least)
// 128-bit security. It is highly recommended to only use settings from this
// table.
//
// m bits c
// 16384 119 2
// 32768 358 6
// 32768 299 3
// 32768 239 2
// 65536 725 8
// 65536 717 6
// 65536 669 4
// 65536 613 3
// 65536 558 2
// 131072 1445 8
// 131072 1435 6
// 131072 1387 5
// 131072 1329 4
// 131072 1255 3
// 131072 1098 2
// 262144 2940 8
// 262144 2870 6
// 262144 2763 5
// 262144 2646 4
// 262144 2511 3
// 262144 2234 2
// We can print out the estimated security level.
// This estimate is based on the LWE security estimator.
cout << "securityLevel=" << context.securityLevel() << "
";
// Get the number of slots, n. Note that for CKKS, we always have n=m/4.
long n = context.getNSlots();
// Construct a secret key. A secret key must be associated with a specific
// Context, which is passed (by reference) to the constructor. Programming
// note: to avoid dangling pointers, the given Context object must not be
// destroyed while any objects associated with it are still in use.
SecKey secretKey(context);
// Constructing a secret key does not actually do very much. To actually
// build a full-fledged secret key, we have to invoke the GenSecKey method.
secretKey.GenSecKey();
// In HElib, the SecKey class is actually a subclass if the PubKey class. So
// one way to initialize a public key object is like this:
const PubKey& publicKey = secretKey;
// TECHNICAL NOTE: Note the "&" in the declaration of publicKey. Since the
// SecKey class is a subclass of PubKey, this particular PubKey object is
// ultimately a SecKey object, and through the magic of C++ polymorphism,
// encryptions done via publicKey will actually use the secret key, which has
// certain advantages. If one left out the "&", then encryptions done via
// publicKey will NOT use the secret key.
//===========================================================================
// Let's encrypt something!
// HElib provides a number of idioms for encrypting and decrypting. We focus
// on one particular idiom here.
// We start by declaring a vector of length n, and we fill it with some
// arbitrary numbers. Note that PI is defined by HElib.
vector<double> v0(n);
for (long i = 0; i < n; i++)
v0[i] = sin(2.0 * PI * i / n);
// Next, we load the plaintext vector v0 into a special type of container,
// called a PtxtArray. Note that a PtxtArray is associated with a Context
// object, which is passed (by reference) to the constructor.
PtxtArray p0(context, v0);
// Note that many types of vectors can be loaded into a PtxtArray object
// (including, vectors of int, long, double, or even complex<double>). Also
// note that constructing p0 and loading v0 into could have been done in two
// separate steps:
// PtxtArray p0(context); p0.load(v0);
// Next, we construct a ciphertext c0. A ciphertext is associated with a
// PubKey object, which is passed (by reference) to the constructor.
// Programming note: to avoid dangling pointers, the given PubKey object must
// not be destroyed while any objects associated with it are still in use.
Ctxt c0(publicKey);
// Finally, we can encrypt p0 and store it in c0:
p0.encrypt(c0);
// Note that since a ciphertext is always associated with a public key, there
// is no need to pass a public key as a separate parameter to the encryption
// method.
//===========================================================================
// We next create another ciphertext c1, in a slightly different way.
// First, we construct another PtxtArray p1:
PtxtArray p1(context);
// Next, we fill all n slots of p1 with random numbers in the interval [0,1]:
p1.random();
// Finally, we encrypt p1 and store it in c1, as above:
Ctxt c1(publicKey);
p1.encrypt(c1);
//===========================================================================
// We next create a ciphertext c2, in the same as was we did c1:
PtxtArray p2(context);
p2.random();
Ctxt c2(publicKey);
p2.encrypt(c2);
//===========================================================================
// Now we homorphically compute c3 = c0*c1 + c2*1.5:
Ctxt c3 = c0;
c3 *= c1;
Ctxt c4 = c2;
c4 *= 1.5;
c3 += c4;
// When this is done, if we denote the i-th slot of a ciphertext c by c[i],
// then we have c3[i] = c0[i]*c1[i] + c2[i]*1.5 for i = 0..n-1.
// More generally, for a Ctxt c, one can perform c *= d, c += d, or c -= d,
// where d can be (among other things) a long, a double, or even a PtxtArray.
//===========================================================================
// Next we decrypt c3.
// First, we construct a new PtxtArray pp3.
PtxtArray pp3(context);
// Next, we decrypt c3, storing the plaintext in p3:
pp3.decrypt(c3, secretKey);
// Finally, we store the PtxtArray p3 into a standard vector v3:
vector<double> v3;
pp3.store(v3);
//===========================================================================
// If we like, we can test the accuracy of the computation.
// First, we perform the same computation directly on plaintexts.
// The PtxtArray class allows this to be done very easily:
PtxtArray p3 = p0;
p3 *= p1;
PtxtArray p4 = p2;
p4 *= 1.5;
p3 += p4;
// Then, we compute the distance between p3 (computed on plaintexts) and pp3
// (computed homomorphically on ciphertexts). This is computed as
// max{ |p3[i]-pp3[i]| : i = 0..n-1 }
double distance = Distance(p3, pp3);
cout << "distance=" << distance << "
";
return 0;
}
运行结果:
securityLevel=157.866 distance=2.82273e-06
其余的例子类似
参考
1、官方安装文档
2、官方例子
3、不懂球的2大业