什么是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大业