参考文献:
- HElib 源码
- HEXL 源码
- googletest 源码
- bats-core 源码
- [HS13] Halevi S, Shoup V. Design and implementation of a homomorphic-encryption library[J]. IBM Research (Manuscript), 2013, 6(12-15): 8-36.
- [HS14] Halevi S, Shoup V. Algorithms in helib[C]//Advances in Cryptology–CRYPTO 2014: 34th Annual Cryptology Conference, Santa Barbara, CA, USA, August 17-21, 2014, Proceedings, Part I 34. Springer Berlin Heidelberg, 2014: 554-571.
- [HS15] Halevi S, Shoup V. Bootstrapping for helib[J]. Journal of Cryptology, 2021, 34(1): 7.
- [HS18] Halevi S, Shoup V. Faster homomorphic linear transformations in HElib[C]//Annual International Cryptology Conference. Cham: Springer International Publishing, 2018: 93-120.
- [HS20] Halevi S, Shoup V. Design and implementation of HElib: a homomorphic encryption library[J]. Cryptology ePrint Archive, 2020.
- 安装 GMP、NTL、CTMalloc
- 全同态加密:BGV
- 全同态加密:CKKS
- Full-RNS BGV/BFV
- FHE 的槽置换:Benes Network
- Level FHE 的快速算法:Double-CRT & Dot Multiplication
- 基于插值的同态比较算法 & Paterson-Stockmeyer 多项式求值算法
HElib
编译
配置环境,
sudo apt-get install patchelf #安装patchelf
cd ./googletest-main #编译安装gtest
cmake ./
sudo make && make install
cd ./bats-core-master #安装bats
sudo ./install.sh /usr/local lib64
安装 Intel/hexl
硬件加速库(我的 Intel® Core™ i9-12900H 不支持 AVX512 指令集,反而 Intel® Core™ i9-11900H 是支持的,本机查看 cat /proc/cpuinfo
),
git config --global url."https://hub.nuaa.cf/".insteadOf "https://github.com/" #首先换个源,git 总是连不上 github
git clone --recurse-submodules https://github.com/intel/hexl.git/
cd ./hexl
cmake -S . -B build #配置 makefile,它会连接 github 拉取一些 cpu-features 文件
cmake --build build #编译 hexl
sudo cmake --install build #安装位置 install_manifest.txt
假如你的 NTL
依赖于 gf2x
,那么还需要修改下 src/CMakeLists.txt
(否则出现未定义函数),添加如下的编译配置:
add_link_options(
-lstdc++ -lm -lntl -pthread -lgf2x
)
find_package(gf2x)
......
target_link_libraries(helib gf2x)
正式编译,
mkdir build
cd ./build
cmake -DPACKAGE_BUILD=OFF -DENABLE_TEST=ON -DENABLE_THREADS=ON -DUSE_INTEL_HEXL=ON -DGMP_DIR="${GMPDIR}" -DNTL_DIR="${NTLDIR}" .. #开启一些选项,使用 hexl 需要关闭 PACKAGE_BUILD 选项
make -j 16 #多线程编译, build/helib_pack
ctest #测试编译是否正确, build/xunit_test_result
sudo make install #安装到计算机
详细安装信息查看 install_manifest.txt
,因为 HElib 没有编写 make uninstall
,可以使用命令 xargs rm < install_manifest.txt
简单删除文件来卸载。
示例
编译示例
cd /examples
mkdir build
cd ./build
cmake ..
make
运行 CKKS
用例:
>>> bin/01_ckks_basics
securityLevel=157.866
distance=3.15527e-06
>>> bin/02_ckks_depth
securityLevel=129.741
c.capacity=328.497 c.errorBound=1.28242e-06
c.capacity=289.748 c.errorBound=2.69368e-06
c.capacity=252.063 c.errorBound=5.73764e-06
c.capacity=213.502 c.errorBound=1.16416e-05
c.capacity=176.579 c.errorBound=2.37458e-05
c.capacity=139.634 c.errorBound=4.79519e-05
distance=4.17908e-05
>>> bin/03_ckks_data_movement
securityLevel=129.741
c.capacity=318.497 c.errorBound=1.25236e-09
c.capacity=310.254 c.errorBound=6.4202e-09
c.capacity=310.254 c.errorBound=1.64714e-08
c.capacity=271.254 c.errorBound=1.71435e-08
c.capacity=232.254 c.errorBound=1.78156e-08
c.capacity=219.254 c.errorBound=0.000145946
distance=0.000135014
GOOD
运行 BGV
用例:
>>> bin/BGV_packed_arithmetic
Initialising context object...
m = 32109, p = 4999, phi(m) = 16560
ord(p) = 690
normBnd = 2.32723
polyNormBnd = 58.2464
factors = [3 7 11 139]
generator 320 has order (== Z_m^*) of 6
generator 3893 has order (== Z_m^*) of 2
generator 14596 has order (== Z_m^*) of 2
T = [ 1 14596 3893 21407 320 14915 25618 11023 6073 20668 9965 27479 16820 31415 10009 27523 20197 2683 24089 9494 9131 23726 2320 19834 ]
r = 1
nslots = 24
hwt = 0
ctxtPrimes = [6,7,8,9,10,11,12,13,14]
specialPrimes = [15,16,17,18,19]
number of bits = 773
security level = 62.4783
Security: 62.4783
Creating secret key...
Generating key-switching matrices...
Number of slots: 24
Initial Plaintext: {"HElibVersion":"2.2.0","content":{"scheme":"BGV","slots":[[0],[1],[2],[3],[4],[5],[6],[7],[8],[9],[10],[11],[12],[13],[14],[15],[16],[17],[18],[19],[20],[21],[22],[23]]},"serializationVersion":"0.0.1","type":"Ptxt"}
Operation: 2(a*a)/(a*a) - 2(a*a)/(a*a) = 0
Decrypted Result: {"HElibVersion":"2.2.0","content":{"scheme":"BGV","slots":[[0],[0],[0],[0],[0],[0],[0],[0],[0],[0],[0],[0],[0],[0],[0],[0],[0],[0],[0],[0],[0],[0],[0],[0]]},"serializationVersion":"0.0.1","type":"Ptxt"}
Plaintext Result: {"HElibVersion":"2.2.0","content":{"scheme":"BGV","slots":[[0],[0],[0],[0],[0],[0],[0],[0],[0],[0],[0],[0],[0],[0],[0],[0],[0],[0],[0],[0],[0],[0],[0],[0]]},"serializationVersion":"0.0.1","type":"Ptxt"}
Operation: Enc{(0 + 1)*1} + (0 + 1)*1
Decrypted Result: {"HElibVersion":"2.2.0","content":{"scheme":"BGV","slots":[[2],[2],[2],[2],[2],[2],[2],[2],[2],[2],[2],[2],[2],[2],[2],[2],[2],[2],[2],[2],[2],[2],[2],[2]]},"serializationVersion":"0.0.1","type":"Ptxt"}
除了 ./examples
中介绍基本用法,./tests
中其实有更详细的示例。
算法模块
HElib 的各个结构好复杂啊,./example
给的例子太简单了,编写工程代码需要更多的接口。花了两天时间翻看 ./include
和 ./src
源码,记录下它们的结构和接口。文章 [HS13] 解释了某些结构的设计原理。
Math Layer
NumbTh.h
实现了许多有用的数学函数:
mcMod, mcDiv
,计算数学意义上的取模运算和除法运算(计算机实现并不正确)long balRem(long a, long q)
,平衡的余数long multOrd(long p, long m)
,乘法阶ppsolve, ppInvert
,求解方阵的方程组,求方阵的逆double log2(const NTL::xdouble& x)
,底 2 的对数void factorize(std::vector<long>& factors, long N)
,较小整数的因子分解void pp_factorize(std::vector<long>& factors, long N)
,分解为素数幂void phiN(long& phiN, std::vector<long>& facts, long N)
,欧拉函数findGenerators
,计算 Z m ∗ / ( p ) \mathbb Z_m^*/(p) Zm∗/(p) 的生成元以及它们的阶FindPrimitiveRoot
,本原单位根long mobius(long n)
,莫比乌斯函数NTL::ZZX Cyclotomic(long N)
,分圆环NTL::ZZX makeIrredPoly(long p, long d)
,不可约多项式long primroot(long N, long phiN)
,本原根PolyRed, vecRed
,取模MulMod, balanced_MulMod
,模乘convert
,一大堆不同数据类型之间的转换函数long computeProd(const std::vector<long>& vec)
,连乘积long is_in(long x, int* X, long sz)
,隶属问题long argminmax(std::vector<T>& v)
,最大值、最小值bool closeToOne(const NTL::xdouble& x, long p)
,是否落在 ( 1 − 1 / p , 1 + 1 / p ) (1-1/p,1+1/p) (1−1/p,1+1/p) 内std::pair<long, long> rationalApprox(double x, long denomBound = 0)
,浮点数的有理数近似void reverse(NTL::Vec<T>& v, long lo, long hi)
,就地的向量翻转void rotate(NTL::Vec<T>& v, long k)
,就地的向量旋转void killVec(std::vector<T>& vec)
,释放向量的内存bool sameObject(const T1* p1, const T2* p2)
,检查两个输入是否是同一个对象void ModComp(NTL::ZZX& res,const NTL::ZZX& g, const NTL::ZZX& h, const NTL::ZZX& f)
,多项式复合 g ( h ) ( m o d f ) g(h)\pmod f g(h)(modf)long polyEvalMod(const NTL::ZZX& f, long x, long p)
,多项式求值 f ( x ) ( m o d p ) f(x) \pmod p f(x)(modp)void interpolateMod(NTL::ZZX& poly, const NTL::vec_long& x, const NTL::vec_long& y, long p, long e = 1)
,拉格朗日插值double Norm(const std::vector<T>& x)
,无穷范数double Distance(const std::vector<T>& x, const std::vector<U>& y)
,无穷范数诱导的距离bool approx_equal(const T& x, const U& y, double tolerance, double floor)
,近似相等double NextPow2(double x)
,下一个二的幂次
Context.h
本模块用于记录重要的上下文信息。首先,定义了评估安全强度的辅助函数:
double lweEstimateSecurity(int n, double log2AlphaInv, int hwt)
,输入维度 n n n,噪声比率 α \alpha α,以及 s k ∈ { 0 , ± 1 } ∗ sk \in \{0,\pm1\}^* sk∈{ 0,±1}∗ 的汉明重量,估计它的安全强度。long FindM(long k, long nBits, long c, long p, long d, long s, long chosen_m, bool verbose = false)
,输入密文模数 Q 0 ≈ 2 n B i t s Q_0 \approx 2^{nBits} Q0≈2nBits,KeySwitch 矩阵列数 c c c,明文空间特征 p p p,明文槽的度数 d d d 和个数 s s s,确定出参数 m m m 的大小。
定义了 struct Context::ModChainParams
,struct Context::BootStrapParams
,struct Context::SerializableContent
,分别记录密文模数、自举参数、序列化(用于读写数据)
定义了 class Context
,用于存储方案的关键信息,下面的其他模块依赖于此。
std::vector<Cmodulus> moduli
,私有属性,用于存储不同的素数,这个列表只增(不减不改)zMStar, alMod, slotRing
,私有属性,记录代数结构 Z m ∗ \mathbb Z_m^* Zm∗, Z [ X ] / ( Φ m ( X ) , p r ) \mathbb Z[X]/(\Phi_m(X),p^r) Z[X]/(Φm(X),pr), Z [ X ] / ( G ( X ) , p r ) \mathbb Z[X]/(G(X),p^r) Z[X]/(G(X),pr)stdev = 3.2, scale = 10
,私有属性,记录噪声和明文缩放因子ctxtPrimes, specialPrimes,smallPrimes
,私有属性,记录 pk 和 ct 的模数,记录 Key-Switch 使用的模数,记录 modulus-Switch 使用的模数std::vector<IndexSet> digits
,私有属性,记录 Key-Switch 中的数字分解,每个digits[i]
是多个素数的乘积ThinRecryptData rcData
,私有属性,记录 “thin” 或者 “thick” 自举信息Context(unsigned long m, unsigned long p, unsigned long r, const std::vector<long>& gens, const std::vector<long>& ords)
,构造函数,CKKS 方案的r
是 bit precision,BGV 方案的r
是 Hensel lifting parameter,gens, ords
是群 Z m ∗ \mathbb Z_m^* Zm∗ 的循环结构getM, getP, getPhiM, getOrdP, getNSlots, getScale, getStdev, getR, getPPowR, getrecision, getSlotRing, getsCtxtPrimes, getsDigit, getRcData
,获取相关参数getZMStar, getAlMod, getEA, sharedEA
,获取PAlgebra zMStar
,PAlgebraMod alMod
,std::shared_ptr<const EncryptedArray> ea
的引用noiseBoundForUniform, noiseBoundForMod, noiseBoundForGaussian, noiseBoundForSmall, noiseBoundForHWt
,高概率的噪声估计stdDevForRecryption, boundForRecryption
,自举噪声估计void enableBootStrapping(const NTL::Vec<long>& mvec, bool build_cache = false, bool alsoThick = true)
,初始化自举数据rcData
,输入的mvec
是参数 m m m 的唯一素分解fullPrimes, allPrimes
,前者获取ctxtPrimes + specialPrimes
,后者还额外获取smallPrimes
,返回值都是IndexSet
句柄ithPrime, ithModulus
,获取特定位置的素数、模数logOfPrime, logOfProduct, bitSizeOfQ
,获取某些数值的规模double securityLevel()
,估计安全强度,其中 s = 3.2 ⋅ m s=3.2 \cdot \sqrt m s=3.2⋅m, α = s / q \alpha=s/q α=s/q, n = ϕ ( m ) n=\phi(m) n=ϕ(m)clearModChain, buildModChain, endBuildModChain
,构造密文模数链
定义了 template <typename SCHEME> class ContextBuilder
,用于构造 Context
对象,
gens_, ords_, m_, p_, r_, c_, ...
,私有的数据属性,存储若干的必要参数m, p, r, precision, scale, stdev, c, gens, ords, bits, skHwt, ...
,公开的函数属性,用于初始化mvec, thinboot, thickboot, buildCache, bootstrappable
,BGV 方案的自举相关Context build()
,根据自身存储的信息,构造上下文对象
primeChain.h
定义了 class ModuliSizes
,用于根据 modulo-size 确定 primeSets
typedef std::pair<double, IndexSet> Entry
,存储(size, set-of-primes)
元组void init(const Context& context)
,初始化素数存储表IndexSet getSet4Size(double low, double high, const IndexSet& fromSet, bool reverse)
,确定指标集formSet
中的最大子集,使得这些素数的乘积满足区间[low, high]
CModulus.h
文件 bluestein.h
和 NumbTh.h
实现了 Bluestein-FFT/NTT 算法,它可以计算任意长度(不只是二的幂次)的卷积运算。渐进复杂度中的常数因子比常规 FFT/NTT 大了 3 3 3 倍。
这个算法的高级接口是 class Cmodulus
,数据类型是 NTL::zz_pX
(也就是 long
)
q, qinv, root, rInv, m_inv, zMStar
,私有属性,记录相关参数powers, powers_aux, Rb, ipowers, ipowers_aux, iRb
,私有属性,用于辅助存储一些预计算数据void FFT(NTL::vec_long& y, const zzX& x)
,正向 FFT/NTT 运算void iFFT(NTL::zz_pX& x, const NTL::vec_long& y)
,逆向 FFT/NTT 运算
DoubleCRT.h
定义了 class DoubleCRT
,构造 L L L 行 ϕ ( m ) \phi(m) ϕ(m) 列矩阵,第 i i i 行使用 p i p_i pi 素数执行 FFT/NTT。所包含的属性:
const Context& context
,私有属性,绑定到某个上下文,记录了ctxtPrimes
和specialPrimes
信息DoubleCRT(const NTL::ZZX& poly, const Context& _context, const IndexSet& indexSet)
,根据上下文(记录了可用的素数context.ithPrime(i)
)以及使用的素数索引 IndexSet(动态数组),将 poly 转变为 DoubleCRT 格式long getOneRow(NTL::zz_pX& row, long idx)
,获取 DoubleCRT 的某一行多项式(系数表示)void toPoly(NTL::ZZX& p, const IndexSet& s, bool positive = false)
,将指定行的数据合成为多项式void addPrimes(const IndexSet& s1, NTL::ZZX* poly_p = 0)
,将 DoubleCRT 扩展一些行(先将现有的数据 IFFT,然后对新添的素数做 FFT)void removePrimes(const IndexSet& s1)
,将 DoubleCRT 删除一些行(简单删除,效果是简单取模)void setPrimes(const IndexSet& s1)
,从当前 CRT 基转换到 IndexSet 指定的基DoubleCRT& SetZero(), DoubleCRT& SetOne()
,设置数值DoubleCRT& Negate(), +=, -=, *=, /=, ==, !=
,基本运算符
hypercube.h
定义了 class CubeSignature
,存储高阶立方(明文槽)的维度信息。
dims, prods
,私有属性,前者存储各个维度的大小,后者存储slice
的规模 p r o d s [ d ] = ∏ j = d n − 1 d i m s [ j ] prods[d] = \prod_{j=d}^{n-1} dims[j] prods[d]=∏j=dn−1dims[j](固定前d
个坐标,遍历后n-d
个坐标;特别地,仅固定第0
个坐标称为col
)getNumDims, getSize, getDim, getProd, numSlices, sliceSize, numCols
,获取dim, slice, col
的信息long getCoord(long i, long d)
,计算索引i
在第d
维上的坐标void getAllCoords(VecType& v, long i)
,获取索引i
的高阶立方上的坐标v
long assembleCoords(VecType& v)
,将坐标v
重构为索引i
long addCoord(long i, long d, long offset)
,确定索引i
在第d
维上的坐标偏移offset
之后的新索引i'
bool incrementCoords(VecType& v)
,根据坐标v
,确定它的索引i
的下一个索引
定义了 template <typename T> class HyperCube
,管理高阶立方中的数据对象。
NTL::Vec<T> data
,存储数据(高阶立方被存储为单个向量)==, !=
,判断两个立方的形状和数据是否完全相同getSig, getData, getDim, getNumDims, getProd, getCoord, numSlices, sliceSize, numCols
,获取相关的维度、规模信息at, []
,获取索引i
的数据引用rotate1D(i, k), shift1D(i, k)
,将维度i
右旋/右移k
位
定义了 class ConstCubeSlice, class CubeSlice
,用于管理某个 slice
另外,getHyperColumn, setHyperColumn
,获取/设置高阶立方的某个 col
PAlgebra.h
定义了 class PAlgebra
,用于支持 Z m ∗ ≅ ( p ) × ( g 1 , g 2 , ⋯ ) × ( f 1 , f 2 , ⋯ ) \mathbb Z_m^* \cong (p) \times (g_1,g_2,\cdots) \times (f_1,f_2,\cdots) Zm∗≅(p)×(g1,g2,⋯)×(f1,f2,⋯),它同构于分圆整数环 A : = Z [ X ] / ( Φ m ( X ) ) \mathbb A:=\mathbb Z[X]/(\Phi_m(X)) A:=Z[X]/(Φm(X)) 的 Galois 群。此结构完全由 m , p m,p m,p 决定,其中 p p p 是满足 p ∤ m p \nmid m p∤m 的素数。群 ( g 1 , g 2 , ⋯ ) (g_1,g_2,\cdots) (g1,g2,⋯) 包含所有的 ”在 Z m ∗ \mathbb Z_m^* Zm∗ 和 Z m ∗ / ( p , g 1 , ⋯ , g i − 1 ) \mathbb Z_m^*/(p,g_1,\cdots,g_{i-1}) Zm∗/(p,g1,⋯,gi−1) 中拥有相同阶“ 的那些元素,群 ( f 1 , f 2 , ⋯ ) (f_1,f_2,\cdots) (f1,f2,⋯) 生成商群 Z m ∗ / ( p , g 1 , g 2 , ⋯ ) \mathbb Z_m^*/(p,g_1,g_2,\cdots) Zm∗/(p,g1,g2,⋯)
查找表 T ⊆ Z m ∗ T \subseteq \mathbb Z_m^* T⊆Zm∗ 是商群 Z m ∗ / ( p ) \mathbb Z_m^*/(p) Zm∗/(p) 的全部陪集的代表,
T : = { ∏ i g i e i ⋅ ∏ j h j e j ∣ e i ∈ [ o r d ( g i ) ] , e j ∈ [ o r d ( h j ) ] } T:=\left\{\prod_i g_i^{e_i} \cdot \prod_j h_j^{e_j} \Big| e_i \in [ord(g_i)], e_j \in [ord(h_j)]\right\} T:={
i∏gie