参考文献:
- 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_compile_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∏giei⋅j∏hjej
ei∈[ord(gi)],ej∈[ord(hj)]}
假设
∣
T
∣
=
l
|T|=l
∣T∣=l,我们令
d
=
ϕ
(
m
)
/
l
d=\phi(m)/l
d=ϕ(m)/l,那么在
(
m
o
d
p
r
)
\pmod{p^r}
(modpr) 意义下,有如下的分圆多项式分解:
Φ
m
(
X
)
=
∏
t
∈
T
F
t
(
X
)
\Phi_m(X) = \prod_{t \in T} F_t(X)
Φm(X)=t∈T∏Ft(X)
易知
1
∈
T
1 \in T
1∈T,对于
r
=
1
r=1
r=1,首先确定任意的不可约因子设为
F
1
(
X
)
F_1(X)
F1(X),然后计算其他的因子
F
t
(
X
)
=
gcd
(
F
1
(
X
t
)
,
Φ
m
(
X
)
)
F_t(X)=\gcd(F_1(X^t),\Phi_m(X))
Ft(X)=gcd(F1(Xt),Φm(X)),这些
l
l
l 个不可约因子次数都为
d
d
d。固定多项式环
R
:
=
Z
p
[
X
]
/
(
F
1
(
X
)
)
R:=\mathbb Z_p[X]/(F_1(X))
R:=Zp[X]/(F1(X)),假如
ρ
\rho
ρ 是
F
1
F_1
F1 的根,那么
ρ
1
/
t
\rho^{1/t}
ρ1/t 的极小多项式是
F
t
(
X
)
F_t(X)
Ft(X)。对于
r
>
1
r>1
r>1,需要使用 Hensel Lifting 给出
Φ
m
(
X
)
\Phi_m(X)
Φm(X) 的分解,各个
F
t
F_t
Ft 之间也有类似的关系。因此,这些明文槽都是同构的。解码过程为:
a
∈
Z
p
r
[
X
]
/
(
Φ
m
(
X
)
)
↦
{
a
(
ζ
t
)
∈
Z
p
r
[
X
]
/
(
F
t
)
}
t
∈
T
a \in \mathbb Z_{p^r}[X]/(\Phi_m(X)) \mapsto \{a(\zeta^t) \in \mathbb Z_{p^r}[X]/(F_t)\}_{t \in T}
a∈Zpr[X]/(Φm(X))↦{a(ζt)∈Zpr[X]/(Ft)}t∈T
其中
ζ
\zeta
ζ 是
Z
p
r
[
X
]
/
(
F
1
)
\mathbb Z_{p^r}[X]/(F_1)
Zpr[X]/(F1) 中的某固定
m
m
m-th 本原单位根。
class PAlgebra
的属性:
m, p, phiM, ordP, nfactors, radm, normBnd, polyNormBnd
,私有属性,管理相关参数std::vector<long> gens
,私有属性,管理 Z m ∗ \mathbb Z_m^* Zm∗ 的生成元NTL::Vec<bool> native
,私有属性,native[i]=true
指示gens[i]
落在子群 ( g 1 , g 2 , ⋯ ) (g_1,g_2,\cdots) (g1,g2,⋯) 内NTL::Vec<long> frob_perturb
,私有属性,frob_perturb[i]=j
指示gens[i]
的乘法阶为 p j p^j pjCubeSignature cube
,私有属性,管理 Z m ∗ / ( p ) \mathbb Z_m^*/(p) Zm∗/(p) 的高阶立方的结构std::vector<long> T, Tidx
,私有属性,前者T[i]=t
记录了各个代表元 t ∈ Z m ∗ / ( p ) t \in \mathbb Z_m^*/(p) t∈Zm∗/(p),后者Tidx[t]=i
是反过来的std::vector<long> zmsIdx, zmsRep
,私有属性,前者zmsIdx[t]=i
记录了 t ∈ Z m ∗ t \in \mathbb Z_m^* t∈Zm∗ 的次序i
,后者是反过来的long getPhiM()
,返回 ϕ ( m ) \phi(m) ϕ(m)(多项式 Φ m ( X ) \Phi_m(X) Φm(X) 的次数)long getRadM()
,返回 R a d ( m ) Rad(m) Rad(m)(不同素因子的乘积)const NTL::ZZX& getPhimX()
,返回 Φ m ( X ) \Phi_m(X) Φm(X)long getNSlots()
,返回明文槽的数量 l l ldouble get_cM()
,返回环常数 c M c_M cM(随机元素在不同 bases 下无穷范数的比值,仅用于自举)long numOfGens()
,返回 Z m ∗ / ( p ) \mathbb Z_m^*/(p) Zm∗/(p) 的生成元个数long ZmStarGen(long i)
,返回 Z m ∗ / ( p ) \mathbb Z_m^*/(p) Zm∗/(p) 的生成元gens[i]
long OrderOf(long i)
,返回gens[i]
的乘法阶(高阶立方cube
第i
维度的规模)long frobeniusPow(long j)
,返回 Frobenius 映射的指数 p j ( m o d m ) p^j \pmod m pj(modm)long ith_rep(long i)
,返回第 i i i 个明文槽的代表T[i]
long indexInZmstar(long t)
,返回元素 t ∈ Z m ∗ t \in \mathbb Z_m^* t∈Zm∗ 的索引zmsIdx[t]
long coordinate(long i, long k)
,返回索引k
在维度i
上的坐标
定义了 class MappingData
,用于管理 encoding/decoding slots 的映射信息
G, degG
,私有属性,域扩张多项式 G ( X ) G(X) G(X),它是 Φ m ( X ) ( m o d p r ) \Phi_m(X) \pmod{p^r} Φm(X)(modpr) 的任意不可约因子maps, matrix_maps, rmaps
,私有属性,存储了映射
定义了 class PAlgebraMod
,用于支持
Z
p
r
[
X
]
\mathbb Z_{p^r}[X]
Zpr[X] 上的 encode/decode slots 过程。各个明文槽的结构为
Z
p
r
[
X
]
/
(
F
t
(
X
)
)
≅
Z
p
r
[
X
]
/
(
F
1
(
X
)
)
\mathbb Z_{p^r}[X]/(F_t(X)) \cong \mathbb Z_{p^r}[X]/(F_1(X))
Zpr[X]/(Ft(X))≅Zpr[X]/(F1(X)),简记
R
[
X
]
:
=
Z
p
r
[
X
]
R[X]:=\mathbb Z_{p^r}[X]
R[X]:=Zpr[X]
首先定义了 template <typename type> class PAlgebraModDerived : public PAlgebraModBase
实例化,包含的属性有:
const PAlgebra& zMStar
,私有属性,管理 Z m ∗ \mathbb Z_m^* Zm∗ 的高阶立方结构r, pPowR, PhimXMod
,私有属性,管理相关的参数const vec_RX& getFactors()
,返回 Φ m ( X ) ( m o d p r ) \Phi_m(X) \pmod{p^r} Φm(X)(modpr) 的分解 F t ∈ R [ X ] F_t \in R[X] Ft∈R[X]const vec_RX& getCrtCoeffs()
,返回 CRT 基 ( ∏ j ≠ i F j ) − 1 ( m o d F i ) (\prod_{j \neq i}F_j)^{-1} \pmod{F_i} (∏j=iFj)−1(modFi)CRT_decompose, CRT_reconstruct
,计算多项式RX H
的 CRT 分解vector<RX> crt
void mapToSlots(MappingData<type>& mappingData, const RX& G)
,根据 G ( X ) G(X) G(X) 计算出到 slots 的映射void embedInSlots(RX& H, const std::vector<RX>& alphas, const MappingData<type>& mappingData)
,将 [ α 0 , ⋯ , α l − 1 ] [\alpha_0,\cdots,\alpha_{l-1}] [α0,⋯,αl−1] 编码到 H ∈ R [ X ] / ( Φ m ( X ) ) H \in R[X]/(\Phi_m(X)) H∈R[X]/(Φm(X)) 的 slots 上
class PAlgebraMod
就是某个 PAlgebraModBase
对象实例的指针,
ClonedPtr<PAlgebraModBase> rep
,私有属性explicit PAlgebraMod(const PAlgebra& zMStar, long r)
,根据 A p \mathbb A_p Ap 和 r r r 构造 A p r \mathbb A_{p^r} Aprconst PAlgebra& getZMStar()
,返回底层的PAlgebra
结构(在rep
里存储)const std::vector<NTL::ZZX>& getFactorsOverZZ()
,返回 Φ m ( X ) ( m o d p r ) \Phi_m(X) \pmod{p^r} Φm(X)(modpr) 的分解(在rep
里存储)
permutatios.h
使用 typedef NTL::Vec<long> Permut
描述简单置换
p
[
i
]
=
π
i
p[i] = \pi_i
p[i]=πi,
void applyPermToVec(std::vector<T>& out, const std::vector<T>& in, const Permut& p1)
,执行一个置换,输出 o u t [ i ] = i n [ p 1 [ i ] ] out[i] = in[p_1[i]] out[i]=in[p1[i]]void applyPermsToVec(std::vector<T>& out, const std::vector<T>& in, const Permut& p2, const Permut& p1)
,连续执行两个置换,输出 o u t [ i ] = i n [ p 2 [ p 1 [ i ] ] ] out[i] = in[p_2[p_1[i]]] out[i]=in[p2[p1[i]]]
定义了 class ColPerm : public HyperCube<long>
,用于置换高阶立方的某一列
定义了 class GeneralBenesNetwork
,使用 Benes Network 实现任意的置换(自同构仅提供简单的移位置换)
定义了 template <typename T> class FullBinaryTree
,代数结构
Z
m
∗
/
(
p
)
\mathbb Z_m^*/(p)
Zm∗/(p) 的各个生成元,都需要一棵完全二叉树,用于高阶立方各个维度的任意置换
定义了 class PermNetwork
,这是一个 “背靠背” 蝴蝶网络,
NTL::Vec<PermNetLayer> layers
,私有属性,记录了置换网络的各层信息PermNetwork(const Permut& pi, const GeneratorTrees& trees)
,根据映射pi
构造置换网络void applyToCtxt(Ctxt& c, const EncryptedArray& ea)
,密文槽的同态置换
定义了 class PermIndepPrecomp
和 class PermPrecomp
,前者是 permutation-independent 预计算,后者是 permutation-dependent 预计算。
PolyModRing.h
定义了 struct PolyModRing
,它是明文槽的环结构,都同构于
Z
[
X
]
/
(
G
(
X
)
,
p
r
)
\mathbb Z[X]/(G(X), p^r)
Z[X]/(G(X),pr),其中
G
=
F
t
G=F_t
G=Ft 是分圆多项式
Φ
m
(
X
)
(
m
o
d
p
r
)
\Phi_m(X) \pmod{p^r}
Φm(X)(modpr) 的任意不可约因子。属性如下:
PolyModRing(long p, long r, const NTL::ZZX& G)
,根据参数构造明文槽的环结构friend std::ostream& operator<<(std::ostream& os, const PolyModRing& ring)
,打印环结构的参数
PolyMod.h
定义了 class PolyMod
,用于存储
Z
p
r
[
X
]
/
(
G
(
X
)
)
\mathbb Z_{p^r}[X]/(G(X))
Zpr[X]/(G(X)) 中的多项式。
PolyMod(const std::vector<long>& input, const std::shared_ptr<PolyModRing>& ringDescriptor)
,根据 PolyModRing 将系数表示(input[i]
表示 x i x^i xi 的系数)转化为环元素==, !=, -, *, +, -, *=, +=, -=
,基本运算friend std::ostream& operator<<(std::ostream& os, const PolyMod& poly)
,打印多项式
Sample.h
定义了随机采样的一些函数,
sampleSmall
,随机的三元分布sampleHWt
,给定汉明重量的三元分布sampleGaussian
,高斯分布sampleUniform
,均匀分布
Crypto Layer
scheme.h
定义了 struct CKKS
和 struct BGV
,用于确定方案的底层代数结构。
- CKKS 方案,明文槽结构
std::complex<double>
,也就是 C \mathbb C C 的浮点数近似 - BGV 方案,明文槽结构
PolyMod
,形如 Z p r [ X ] / ( G ( X ) ) \mathbb Z_{p^r}[X]/(G(X)) Zpr[X]/(G(X)),其中 G G G 是 Φ m ( X ) \Phi_m(X) Φm(X) 的不可约因子
Ptxt.h
定义了明文空间的一些功能,
std::vector<typename Scheme::SlotType> convertDataToSlotVector(const std::vector<From>& data, const Context& context)
,- BGV方案,将
vector<From>
转换为vector<PolyMod>
,这里的From
可以是NTL::ZZX
- CKKS 方案,将
vector<From>
转换为vector<std::complex<double>>
,这里的From
可以是double
- BGV方案,将
定义了 template <typename Scheme> class Ptxt
,
explicit Ptxt(const Context& context)
,根据上下文初始化Ptxt(const Context& context, const SlotType& value)
,将明文槽都设置为value
template <typename T> Ptxt(const Context& context, const std::vector<T>& data)
,将向量data
转化到明文槽中size_t size()
,返回明文槽的数量size
const Context& getContext()
,返回自身的上下文void setData(const SlotType& value)
,设置明文槽的值void decodeSetData(const NTL::ZZX& data)
,使用多项式ZZX
解码后的 slots 设置明文,BGV 专用void clear(), Ptxt<Scheme>& random()
,清理明文、随机明文const std::vector<SlotType>& getSlotRepr()
,获取明文的 slots 表示NTL::ZZX getPolyRepr()
,获取明文的 coeff 表示void encode(EncodedPtxt& eptxt, double mag = -1, OptLong prec = OptLong())
,将自身(solts 形式)编码为EncodedPtxt
(poly 形式)==, !=, *, +, -, *=, +=, -=, negate
,基本运算Ptxt<Scheme>& addConstant(const Scalar& scalar)
,全部明文槽都加常数,BGV 专用Ptxt<Scheme>& addConstantCKKS(const Scalar& scalar)
,全部明文槽都加常数,CKKS 专用multiplyBy, multiplyBy2, square, cube, power
,乘法、幂次Ptxt<Scheme>& rotate(long amount)
,循环右移,槽i
移动到i+amount mod size
Ptxt<Scheme>& rotate1D(long dim, long amount)
,高阶立方体dim
维度上的循环右移,是由 Z m ∗ \mathbb Z_m^* Zm∗ 的循环群结构PAlgebra& zMStar = context->getZMStar()
所诱导的Ptxt<Scheme>& shift(long amount)
,零填充右移Ptxt<Scheme>& shift1D(long dim, long amount)
,高阶立方体dim
维度上的零填充右移Ptxt<Scheme>& automorph(long k)
,自同构映射 a ( X ) ↦ a ( X k ) ( m o d Φ m ( X ) ) a(X) \mapsto a(X^k) \pmod{\Phi_m(X)} a(X)↦a(Xk)(modΦm(X)),其中 k ∈ Z m ∗ k \in \mathbb Z_m^* k∈Zm∗Ptxt<Scheme>& frobeniusAutomorph(long j)
,Frobenius 自同构 a ( X ) ↦ a ( X p j ) ( m o d Φ m ( X ) ) a(X) \mapsto a(X^{p^j}) \pmod{\Phi_m(X)} a(X)↦a(Xpj)(modΦm(X)),BGV 专用Ptxt<Scheme>& replicate(long pos)
,第pos
个明文槽的广播复制complexConj, real, imag
,复数变换,CKKS 专用runningSums, totalSums
,明文槽加和,前者是前缀槽的和,后者是全部槽的和incrementalProduct, totalProduct
,明文槽乘积,前者是前缀槽的积,后者是全部槽的积
EncodedPtxt.h
定义了 class EncodedPtxt_BGV
,
EncodedPtxt_BGV(const zzX& poly_, long ptxtSpace_, const Context& context_)
,构造函数getPoly, getPtxtSpace, getContext
,查看存储的信息
定义了 class EncodedPtxt_CKKS
,
EncodedPtxt_CKKS(const zzX& poly_, double mag_, double scale_, double err_, const Context& context_)
,构造函数getPoly, getMag, getScale, getErr, getContext
,查看存储的信息
定义了 class EncodedPtxt
,它要么存储 EncodedPtxt_BGV
要么存储 EncodedPtxt_CKKS
,存储结构是 zzX poly
eptxt.isBGV(), eptxt.isCKKS()
,判断自己是哪个方案eptxt.getBGV(), eptxt.getCKKS()
,生成只读的EncodedPtxt_BGV
和EncodedPtxt_CKKS
对象引用eptxt.resetBGV(), eptxt.resetCKKS()
,重置EncodedPtxt_BGV
和EncodedPtxt_CKKS
对象
定义了 FatEncodedPtxt_BGV
,class FatEncodedPtxt_CKKS
和 class FatEncodedPtxt
,接口是一样的,但是存储结构为 DoubleCRT dcrt
Ctxt.h
定义了 class SKHandle
,用于描述满足形式
s
r
(
X
t
)
s^r(X^t)
sr(Xt) 的私钥元素(同态乘法导致
s
r
(
X
)
s^r(X)
sr(X),同态自同构导致
s
(
X
t
)
s(X^t)
s(Xt)),属性 powerOfS, powerOfX, secretKeyID
(是私钥句柄,用于描述解密时 CtxtPart
应当乘以哪一个私钥)记录了信息
(
r
,
t
,
i
)
(r,t,i)
(r,t,i),用于辅助解密。句柄之间定义了等性检测和乘法运算。
定义了 class CtxtPart : public DoubleCRT
,用于存储密文中的单个多项式,包含一个 SKHandle
对象,并继承了 DoubleCRT
的属性(以 DCRT 格式存储和计算)。密文形如
(
c
0
,
c
1
,
⋯
,
c
k
)
(c_0,c_1,\cdots,c_k)
(c0,c1,⋯,ck),私钥形如
s
i
(
X
t
)
s_i(X^t)
si(Xt),解密公式为
∑
r
c
r
(
X
)
⋅
s
r
(
X
t
)
\sum_r c_r(X) \cdot s^r(X^t)
∑rcr(X)⋅sr(Xt),因此各个 CtxtPart
记录了私钥句柄
(
r
,
t
,
i
)
(r,t,i)
(r,t,i)
定义了 class Ctxt
,使用 std::vector<CtxtPart> parts
对象来存储密文 c[i] = part[i]
。BGV 密文中加密的消息是
[
q
⋅
m
]
p
r
[q \cdot m]_{p^r}
[q⋅m]pr,在模切换过程中保持因子
[
q
]
p
r
[q]_{p^r}
[q]pr 对应当前的密文模数。密文同态加法时,相同句柄的相加,不同句柄的简单存储;密文同态乘法时,计算 part, part'
的张量积,它们的句柄两两相乘。
context, pubKey
,私有属性,密文相关参数的指针parts, primeSet, ptxtSpace, noiseBound
,私有属性,存储密文各个分量、用到的素数、明文模数、平均噪声估计intFactor, ratFactor, ptxtMag
,私有属性,前者用于 BGV 纠错,后两者用于 CKKS 纠错(尾数部分,指数部分)tensorProduct, subPart, addPart, keySwitchPart, keySwitchDigits
,私有属性,用于同态运算==, !=
,检查两个Ctxt
对象的size, primeSet, intFactor, noiseBound
是否相同negate, +=, -=, *=
,基本的同态运算。支持不同密文模数的加法,结果是素数集的并集;支持不同密文模数的乘法,结果是素数集的交集。这里都是 “high-level” 版本,计算结果自动执行模切换和重线性化。void multLowLvl(const Ctxt& other, bool destructive = false);
自动执行模切换,但是不执行重线性化。addConstant, multByConstant
,前者首先将输入的常数缩放 [ q ] p r [q]_{p^r} [q]pr 然后再加到 c t ( s i ) ct(s_i) ct(si) 的常数项,后者就是简单将 c t ( s ) ct(s) ct(s) 乘以输入的常数。对于频繁使用的常数,可以将它用ea.encode
预先编码为EncodedPtxt
,然后使用FatEncodedPtxt(eptxt, context.fullPrimes())
获取它的 DoubleCRT 表示。xorConstant, nxorConstant
,根据公式 a ⊕ b = a + b − 2 a b a \oplus b = a+b-2ab a⊕b=a+b−2ab 和 ( a ⊕ b ) ‾ = 1 − a − b + 2 a b \overline{(a \oplus b)}=1-a-b+2ab (a⊕b)=1−a−b+2ab,计算任意明文模数下的异或运算(需要同态乘法)void automorph(long k)
,自同构映射 F ( X ) → F ( X k ) F(X) \to F(X^k) F(X)→F(Xk),其中 gcd ( k , m ) = 1 \gcd(k,m)=1 gcd(k,m)=1,这是 “raw” 版本,各个part[i]
句柄的powerOfX
乘以k
void smartAutomorph(long k)
,它根据context
中提供的 KSK 将自同构 X → X k X \to X^k X→Xk 拆分为 X → X k i , i = 1 , ⋯ , t X \to X^{k_i},i=1,\cdots,t X→Xki,i=1,⋯,t,依次执行各个自同构,紧接着自动执行对应的 KS 过程。在生成 KSK 的同时确定了一个表格keySwitchMap[i][k]
,记录了各个k
对应的自同构序列。void frobeniusAutomorph(long j)
,也就是smartAutomorph(p^j mod m)
divideByP, multByP
,简单作用到密文上,使得明文模数分别变为 p r − 1 , r > 1 p^{r-1},r>1 pr−1,r>1 和 p r + 1 , r ≥ 1 p^{r+1},r\ge1 pr+1,r≥1multiplyBy, multiplyBy2, square, cube, power
,高等的同态乘法,它们自动执行模切换和重线性化运算,甚至自动选取计算顺序。void evalPoly(const NTL::ZZX& poly)
,执行同态多项式求值reducePtxtSpace, hackPtxtSpace
,模切换过程,后者在明文高位引入噪声void blindCtxt(const NTL::ZZX& poly)
,盲化 BGV 密文,加上常数poly
的高噪声密文NTL::xdouble modSwitchAddedNoiseBound()
,估计模切换带来的额外噪声modUpToSet, modDownToSet, bingToSet
,模切换过程,前两者添加/删除一些素数(同时缩放对应的倍率,保持明文的因子 [ q ] p r [q]_{p^r} [q]pr),后者先Up
再Down
void Ctxt::reLinearize(long keyID)
,重线性化过程,keyID
确定要切换到的私钥 ( 1 , s i ) (1,s_i) (1,si),采取了 [BV11] 和 [GHS12] 的混合方案,首先 ScaleDown 移除SpecialPrimes
,然后 KS 过程中 ScaleUp 重新引入它们,最终的输出密文模数包含了这些素数(副作用)void dropSmallAndSpecialPrimes()
,移除 Key-Switch 过程的副作用(密文模数放大了SpecialPrimes
),同时会自动添加一些ctxtPrimes
使得模切换的额外噪声可忽略totalNoiseBound, errorBound
,前者估计相位中的噪声(CKKS 要加上ptxtMag * ratFactor
精度损失),后者估计明文中的噪声(BGV 的是零)。同态运算过程中,自动执行启发式的噪声估计,存储在noiseBound
内。capacity, bitcapacity
,两者返回的都是 log 2 ( Q / B ) \log_2(Q/B) log2(Q/B),标志着同态运算的能力,其中B = totalNoiseBound()
是噪声界bool isCorrect()
,判断是否能够正确解密getContext, getPubKey, getPrimeSet, getPtxtSpace, getNoiseBound, getRatFactor, getPtxtMag, getKeyID
,获取本密文的相关信息addedNoiseForCKKSDecryption
,对 CKKS 解密结果添加噪声,防止 Approximate FHE IND-CPA+ 攻击
定义了一些自由函数,
totalProduct, incrementalProduct, innerProduct
,特殊同态乘法的加速实现(二叉树、统一重线性化)conjugate, extractRealPart, extractImPart
,CKKS 专用,利用自同构ctxt.frobeniusAutomorph(1)
(同态复共轭)同态提取实部/虚部void extractDigits(std::vector<Ctxt>& digits, const Ctxt& c, long r = 0)
,BGV 专用,提取明文槽 Z p r [ X ] / ( G ) \mathbb Z_{p^r}[X]/(G) Zpr[X]/(G) 的各个 mod- p p p deigits,其中digit[j]
的明文模数分别是 p r − j p^{r-j} pr−j,但是这些密文的 Level 相同extractBits, extendExtractDigits
,其他的数字分解void CheckCtxt(const Ctxt& c, const char* label)
,打印密文的错误信息
Key.h
定义了 class PubKey
,管理公钥数据以及相关接口,
Ctxt pubEncrKey
,私有属性,公钥就是零的密文 p k = R L W E s ( 0 ) pk = RLWE_s(0) pk=RLWEs(0)std::vector<KeySwitch> keySwitching
,私有属性,记录了多个秘钥切换矩阵 W [ s ′ → s ] W[s' \to s] W[s′→s],其中 s ′ ∈ { s i r ( X t ) } s' \in \{s_i^r(X^t)\} s′∈{sir(Xt)},以及 s ∈ { s j ( X ) } s \in \{s_j(X)\} s∈{sj(X)}std::vector<std::vector<long>> keySwitchMap
,私有属性,这是个指针数组,keySwitchMap[i][e] = j
记录了对 s i ( X e ) s_i(X^e) si(Xe) 重线性化时应当使用keySwitching[j]
Ctxt recryptEkey
,私有属性,存储了自举秘钥void Encrypt(Ctxt& ctxt, const EncodedPtxt& eptxt)
,公钥加密void reCrypt(Ctxt& ctxt), void thinReCrypt(Ctxt& ctxt)
,自举程序,后者要求 slots 内加密的是数值而非多项式void hackPtxtSpace(long p2r)
,强行提升明文模数,在 BGV 中会引入高位噪声
定义了 class SecKey : public PubKey
,管理私钥数据以及相关接口,
std::vector<DoubleCRT> sKeys
,私有属性,存储了若干的私钥 s i s_i si,它们被CtxtPart
句柄中的secretKeyID
索引,用于 Level FHE(移除循环安全假设)GenSecKey, GenKeySWmatrix, getRecryptKey
,生成各种秘钥SK, KSK, BSK
void Decrypt(NTL::ZZX& plaintxt, const Ctxt& ciphertxt)
,解密long skEncrypt(Ctxt& ctxt, const zzX& ptxt, long ptxtSpace, long skIdx)
,对称加密
此外,double RLWE(DoubleCRT& c0, DoubleCRT& c1, const DoubleCRT& s, long p, NTL::ZZ* prgSeed = nullptr)
,产生随机的 RLWE 样本,返回的 double
是典范嵌入的无穷范数。
KeySwitching.h
有两种 KS 程序,其一是 [BV11] 的数字分解技术,其二是 [GHS12] 的模数扩展技术。这里的实现是 Hybrid 技巧。
定义了 class KeySwitch
,它存储形如
W
[
s
′
→
s
]
=
(
a
⃗
,
b
⃗
)
T
∈
R
Q
2
×
t
W[s' \to s]=(\vec a, \vec b)^T \in R_Q^{2 \times t}
W[s′→s]=(a,b)T∈RQ2×t 的 KSK 矩阵,其中
Q
Q
Q 是全部的 ctxtPrimes
和 specialPrimes
的乘积,而
t
t
t 是这个模数的数字分解(每个 digit 是若干个素数的乘积,规模
D
i
D_i
Di),形如
b
i
:
=
[
(
∏
j
<
i
D
i
)
⋅
s
′
+
p
r
e
i
−
a
i
s
]
Q
b_i := [(\prod_{j<i}D_i) \cdot s' + p^re_i - a_is]_Q
bi:=[(∏j<iDi)⋅s′+prei−ais]Q,这里的
∏
j
<
i
D
i
\prod_{j<i}D_i
∏j<iDi 就是 Powerof2 的替代品。
SKHandle fromKey
和long toKeyID
,分别记录了 s ′ s' s′ 和 s s s 的句柄/索引,形如 ( r , t , i ) (r,t,i) (r,t,i) 和 j j jNTL::ZZ prgSeed
和std::vector<DoubleCRT> b
,分别记录了 a ⃗ \vec a a(PRG seed)和 b ⃗ \vec b b
在 SecKey
内构造添加关于 KeySwitch
的自由函数,每一个私钥
s
i
s_i
si 都携带一张有向图
G
i
G_i
Gi,它由 keySwitchMap
指示 KSK 的路径
s
i
(
X
e
)
→
s
i
(
X
1
)
s_i(X^e) \to s_i(X^1)
si(Xe)→si(X1),用于组合少量的 KS 过程获得任意的 KS 过程。
void addAllMatrices(SecKey& sKey, long keyID = 0)
,生成所有映射 s i ( X e ) ↦ s i ( X ) , ∀ e ∈ Z m ∗ s_i(X^e) \mapsto s_i(X), \forall e \in \mathbb Z_m^* si(Xe)↦si(X),∀e∈Zm∗ 的 KSK 矩阵,但是规模太大了(本身 m m m 就是上万的规模)void addFewMatrices(SecKey& sKey, long keyID = 0)
,生成少量映射的 KSK 矩阵,确保 s i ( X e ) ↦ s i ( X ) , ∀ e ∈ Z m ∗ s_i(X^e) \mapsto s_i(X), \forall e \in \mathbb Z_m^* si(Xe)↦si(X),∀e∈Zm∗ 都可以两步以内被线性化(就是路径 e → 1 e \to 1 e→1 长度至多为 2 2 2),这会导致每个 KS 过程被分解为若干步骤void add1DMatrices(SecKey& sKey, long keyID = 0)
,生成全部形如 s i ( X g ± e ) ↦ s i ( X ) s_i(X^{g^{\large{\pm e}}}) \mapsto s_i(X) si(Xg±e)↦si(X) 映射的 KSK 矩阵(用于rotate1D, shift1D
,进而可以组合出任意的automorph
),其中 Z m ∗ / ( p ) = ( g ) \mathbb Z_m^*/(p) = (g) Zm∗/(p)=(g) 且 e < o r d ( g ) e<ord(g) e<ord(g)。类似的,也有addSome1DMatrices
版本,它生成上述 KSK 的某个子集,将每个 s i ( X g ± e ) ↦ s i ( X ) s_i(X^{g^{\large{\pm e}}}) \mapsto s_i(X) si(Xg±e)↦si(X) 分解为至多两步(因此也支持全部的automorph
)void addFrbMatrices(SecKey& sKey, long keyID = 0)
,生成全部形如 s i ( X p e ) ↦ s i ( X ) s_i(X^{p^e}) \mapsto s_i(X) si(Xpe)↦si(X) 映射的 KSK 矩阵(用于frobeniusAutomorph
)void addMatrices4Network(SecKey& sKey, const PermNetwork& net, long keyID = 0)
,生成置换网络net
所需的那些 KSK 矩阵
recryption.h
对于 BGV 方案,存在 [HS15], [CH18] 两种自举算法。前者是通用的,后者专用于模数是二的幂次。HElib
给出了两种自举,“thick” 和 “thin”,前者是通用的自举程序(速度:数分钟级别),后者专用于明文限制在明文槽的基域/基环(速度:数十秒级别)
定义了 class RecryptData
,用于存储自举相关的数据
NTL::Vec<long> mvec
,参数 m m m 的素分解e, ePrime, skHwt
,明文空间、私钥重量alMod, ea
,明文槽的结构和编码解码接口init, setAE
,配置自举数据
定义了 class ThinRecryptData : public RecryptData
,专用于 “thin” bootstrapping 的自举数据。
在 recryption.cpp
中定义了一些没有在 recryption.h
内声明的自由函数,可能包含在其他的头文件里,
extractDigitsPacked
和extractDigitsThin
,同态数字提取void PubKey::reCrypt(Ctxt& ctxt)
和void PubKey::thinReCrypt(Ctxt& ctxt)
,单个密文的自举程序void packedRecrypt(const CtPtrs& cPtrs, const std::vector<zzX>& unpackConsts, const EncryptedArray& ea)
,利用repack, unpack
将CtPtrs
中的每 deg d \deg d degd 个密文打包在一起,批量自举
multicore.h
用于多线程的加锁和同步,
std::atomic_long, std::atomic_ulong
,原子的整数类型std::mutex, std::lock_guard<std::mutex>
,资源锁
似乎它仅在 bootstrapping
中实现了多线程?
Data-Movement Layer
intraSlot.h
定义了一些关于 G F ( p d ) GF(p^d) GF(pd) 上明文槽的 Packing/Unpacking 功能,
void buildUnpackSlotEncoding(std::vector<zzX>& unpackSlotEncoding, const EncryptedArray& ea)
,预计算解包相关的信息long repack(const CtPtrs& packed, const CtPtrs& unpacked, const EncryptedArray& ea)
,打包函数long unpack(const CtPtrs& unpacked, const CtPtrs& packed, const EncryptedArray& ea, const std::vector<zzX>& unpackSlotEncoding)
,解包函数
EncryptedArray.h
定义了 class EncryptedArrayBase
,这是 virtual
的类,用于明文槽的数据移动
getContext, getPAlgebra, getDegree, getP2R
,获取相关参数rotate, shift, rotate1D, shift1D
,右旋、右移(有两种明文槽的 array view,其一是线性数组,其二是高阶立方,后者关于 Z m ∗ / ( p ) \mathbb Z_m^*/(p) Zm∗/(p) 的循环群结构)encode, decode
,数组和多项式之间的转换decrypt, rawDecrypt, decryptComplex, rawDecryptComplex, rawDecryptReal, decryptReal
,解密函数void buildLinPolyCoeffs(std::vector<NTL::ZZX>& C, const std::vector<NTL::ZZX>& L)
,根据线性映射L
构造线性多项式列表C
,两者都是公开的多项式- 输入向量
L
∈
(
Z
p
r
[
X
]
)
deg
G
L \in (\mathbb Z_p^r[X])^{\deg G}
L∈(Zpr[X])degG,它描述了
Z
p
r
[
X
]
/
(
G
(
X
)
)
\mathbb Z_{p^r}[X]/(G(X))
Zpr[X]/(G(X)) 中的某线性映射,满足
M
:
X
j
m
o
d
G
↦
L
[
j
]
m
o
d
G
,
j
=
0
,
1
,
⋯
,
deg
G
−
1
M: X^j \bmod G \mapsto L[j] \bmod G,\,\, j=0,1,\cdots,\deg G-1
M:XjmodG↦L[j]modG,j=0,1,⋯,degG−1,也就是
L
描述了M
对于标准 Power Basis 的作用 - 输出向量
C
∈
(
Z
p
r
[
X
]
)
deg
G
C \in (\mathbb Z_p^r[X])^{\deg G}
C∈(Zpr[X])degG,其中的每一个多项式都是线性的,用于计算
M
:
h
(
X
)
m
o
d
G
↦
∑
j
=
0
deg
G
−
1
C
[
j
]
⋅
h
(
X
p
j
)
m
o
d
G
M: h(X) \bmod G \mapsto \sum_{j=0}^{\deg G-1} C[j] \cdot h(X^{p^j}) \bmod G
M:h(X)modG↦∑j=0degG−1C[j]⋅h(Xpj)modG,我们称
C
是M
的线性化多项式(Linearized polynomials)
- 输入向量
L
∈
(
Z
p
r
[
X
]
)
deg
G
L \in (\mathbb Z_p^r[X])^{\deg G}
L∈(Zpr[X])degG,它描述了
Z
p
r
[
X
]
/
(
G
(
X
)
)
\mathbb Z_{p^r}[X]/(G(X))
Zpr[X]/(G(X)) 中的某线性映射,满足
M
:
X
j
m
o
d
G
↦
L
[
j
]
m
o
d
G
,
j
=
0
,
1
,
⋯
,
deg
G
−
1
M: X^j \bmod G \mapsto L[j] \bmod G,\,\, j=0,1,\cdots,\deg G-1
M:XjmodG↦L[j]modG,j=0,1,⋯,degG−1,也就是
long coordinate(long i, long k)
,返回索引k
的维度i
上坐标
定义了 template <typename type> class EncryptedArrayDerived : public EncryptedArrayBase
实例化,
R, vec_R, mat_R, RX, vec_RX, RXModulus, RE, vec_RE, mat_RE, REX, vec_REX
,公开属性,管理多项式信息const Context& context
,私有属性,管理各种参数MappingData<type> mappingData
,私有属性,管理 encode/decode slots 信息rotate, shift, rotate1D, shift1D
,循环右移、零填充右移,高阶立方某维度上的右移encode, decode, decrypt, genericEncode, genericDecode, genericDecrypt
,编码,解码,解密
定义了 class EncryptedArray
,定义了多种与密文有关的接口,但是似乎自身并不存储密文。
const PAlgebraMod& alMod
,私有属性,支持 Z p r [ X ] \mathbb Z_{p^r}[X] Zpr[X] 上的 encode/decode slots 过程ClonedPtr<EncryptedArrayBase> rep
,私有属性EncryptedArray(const Context& context, const NTL::ZZX& G = NTL::ZZX(1, 1))
,构造函数,默认情况下的明文空间是明文槽 Z p r [ X ] / ( F t ) \mathbb Z_{p^r}[X]/(F_t) Zpr[X]/(Ft) 的(同构意义下)子环 Z p r [ X ] / ( X ) ≅ Z p r \mathbb Z_{p^r}[X]/(X) \cong \mathbb Z_{p^r} Zpr[X]/(X)≅Zpr,必要时手动设置 G = F 1 G=F_1 G=F1rotate, shift, rotate1D, shift1D
,循环右移、零填充右移,高阶立方某维度上的右移encode, decode
,若干个编码/解码函数encrypt, decrypt, decryptReal, decryptComplex
,若干个加密/解密函数void encodeUnitSelector(EncodedPtxt& eptxt, long i)
,设置位置i
的掩码选择器
首先定义了 class PlaintextArrayDerived : public PlaintextArrayBase
,它携带 std::vector<RX> data
属性存储数据。接着定义了 class PlaintextArray
(弃用) 和 class PtxtArray
(建议),包含了运算接口:
const EncryptedArray& ea
,公开属性,定义了加密接口PlaintextArray pa
,公开属性,用于存储明文数据void encode(EncodedPtxt& eptxt, double mag = -1, OptLong prec = OptLong())
,利用ea
将pa
编码void encrypt(Ctxt& ctxt, double mag = -1, OptLong prec = OptLong())
,利用ea
将pa
加密(Ctxt
携带属性PubKey
)void decrypt(const Ctxt& ctxt, const SecKey& sKey, OptLong prec = OptLong())
,利用ea
将pa
解密(额外输入SecKey
)load, store
,读写GF2X, ZZX, vector<long>, vector<double>
等数据buildLinPolyCoeffs
,根据线性变换L
构造线性化多项式C
定义了一些关于 PtxtArray
的自由函数,
==, !=, +=, -=, *=
,明文的基本运算rotate, shift, rotate1D, shift1D
,循环右移、零填充右移,高阶立方某维度上的右移frobeniusAutomorph, conjugate, extractRealPart, extractImPart
,自同构映射totalSums, runningSums, power
,高级的加法/乘法
定义了一些关于 EncryptedArray
的自由函数,
totalSums, runningSums
,高级的同态加法/乘法void mapTo01(const EncryptedArray& ea, Ctxt& ctxt, bool multithread = true)
,判断ctxt
各个明文槽是否是零void incrementalZeroTest(Ctxt* res[], const EncryptedArray& ea, const Ctxt& ctxt, long n)
,专用于 p − 2 , r = 1 p-2,r=1 p−2,r=1 的情况,密文res[i]
的第j
个槽是零,如果ctxt
的第j
个槽的前0...i
比特是否都是零void applyLinPoly1(const EncryptedArray& ea, Ctxt& ctxt, const std::vector<NTL::ZZX>& C)
,将生成的映射C = ea.buildLinPolyCoeffs
作用到ctxt
的所有槽上void applyLinPolyMany(const EncryptedArray& ea, Ctxt& ctxt, const std::vector<std::vector<NTL::ZZX>>& Cvec)
,将若干个映射Cvec
分别作用到ctxt
不同的明文槽上,其中Cvec[0...nslots-1][0...degree-1]
包含nslots
个长度degree
的行向量(都由ea.buildLinPolyCoeffs
生成)
replicate.h
定义了明文槽复制/广播的接口,
void replicate(const EncryptedArray& ea, Ctxt& ctx, long pos)
,利用 O ( log n ) O(\log n) O(logn) 次rotate1D
,将位置pos
的元素广播到全部的明文槽void replicateAll(std::vector<Ctxt>& v, const EncryptedArray& ea, const Ctxt& ctxt, long recBound = 64, RepAuxDim* repAuxPtr = nullptr)
,使用混合策略(二叉树的顶部拍平,详见 [HS14]),将所有的槽分别广播到独立的密文,在默认的启发式深度recBound
下计算复杂度约为 O ( n ) O(n) O(n)
另外还有 class ReplicateHandler
、class RepAux
等辅助工具。
Algorithm Layer
polyEval.h
定义了同态多项式计算的接口,使用了 BSGS 算法、Paterson-Stockmeyer 算法,
void polyEval(Ctxt& ret, const NTL::Vec<Ctxt>& poly, const Ctxt& x)
,同态计算明文多项式ret = poly(x)
void evalPoly(const NTL::ZZX& poly)
,这是Ctxt
的一个属性void polyEval(Ctxt& ret, const NTL::Vec<Ctxt>& poly, const Ctxt& x)
,同态计算密文多项式,参数Vec<Ctxt>& poly
记录了多项式的系数表示, r e t = ∑ i p o l y [ i ] ⋅ x i ret = \sum_i poly[i] \cdot x^i ret=∑ipoly[i]⋅xi
定义了 class DynamicCtxtPowers
,用于同态计算密文 c = Ctxt(X)
的幂次
std::vector<Ctxt> v
,私有属性,记录 X i X^i Xi 的密文DynamicCtxtPowers(const Ctxt& c, long nPowers)
,构造函数,初始化长度nPowers
的数组,简单存储v[0] = c
Ctxt& getPower(long e)
,如果 X e X^e Xe(存储在v[e-1]
中)还没有被计算,那么执行同态乘法,否则直接输出已有数据bool isPowerComputed(long i)
,判断是否预计算过
binaryArith.h
在 binaryArith.cpp
中实现了 class DAGnode
和 class AddDAG
,用于存储管理布尔加法中的进位信息(同态乘法)。
对于 CtPtrs
对象(加密布尔值的密文列表的指针),定义了一些布尔运算,
void binaryCond(CtPtrs& output, const Ctxt& cond, const CtPtrs& trueValue, const CtPtrs& falseValue)
,根据控制位cond
(在明文槽中)确定output
各个槽的数值(挑选trueValue
或falseValue
),也就是 CMux Gate 运算void binaryMask(CtPtrs& binaryNums, const Ctxt& mask)
,根据掩码mask
(取值 0 / 1 0/1 0/1)将某些明文槽值为零concatBinaryNums, splitBinaryNums
,二进制数的级联和切分leftBitwiseShift, bitwiseRotate
,布尔电路上的移位运算bitwiseXOR, bitwiseOr, bitwiseAnd, bitwiseNot
,布尔电路上的逻辑运算addTwoNumbers, negateBinary, subtractBinary, addManyNumbers, multTwoNumbers, fifteenOrLess4Four
,布尔电路上的算术运算void decryptBinaryNums(std::vector<long>& pNums, const CtPtrs& eNums, const SecKey& sKey, const EncryptedArray& ea, bool twosComplement = false, bool allSlots = true)
,解密二进制数的密文eNums
,获得各个槽的整数明文pNums
,参数twosComplement
用于确定是无符号数(二进制)还是带符号数(二补码)void packedRecrypt(const CtPtrs& a, const CtPtrs& b, std::vector<zzX>* unpackSlotEncoding)
,批量自举两个二进制数的密文
binaryCompare.h
在 binaryCompare.cpp
中定义了 runningSums, compProducts, compEqGt
,然后用线性迭代布尔比较算法(这很慢吧)实现 compareTwoNumbersImplementation
函数。比较之前它会自动地执行 packedRecrypt
,确保乘法深度足够支持比较电路。
定义了布尔比较电路,前者仅执行 comp,后者还执行了 swap,
void compareTwoNumbers(Ctxt& mu, Ctxt& ni, const CtPtrs& a, const CtPtrs& b, bool twosComplement = false, std::vector<zzX>* unpackSlotEncoding = nullptr)
,比较a,b
大小关系,输出 m u = ( a > b ) mu=(a>b) mu=(a>b) 和 n i = ( a < b ) ni=(a<b) ni=(a<b)void compareTwoNumbers(CtPtrs& max, CtPtrs& min, Ctxt& mu, Ctxt& ni, const CtPtrs& a, const CtPtrs& b, bool twosComplement = false, std::vector<zzX>* unpackSlotEncoding = nullptr)
,比较a,b
大小关系,额外输出 max ( a , b ) \max(a,b) max(a,b) 和 min ( a , b ) \min(a,b) min(a,b),也就是 Swap 运算
matmul.h
这个实现考虑 “a Ctxt encoding a vector” 的线性变换:单个密文 Ctxt ctxt
加密了行矢 vector v
,获得加密 v * mat
的单个密文。
首先定义了一些虚拟类 class MatMulFull
、class BlockMatMulFull
、class MatMul1D
、class BlockMatMul1D
,用于记录对于 vector
的线性变换,细分为 “full”、“block”、“1D” 形式。
接着定义了 template <typename type> class MatMulFull_derived : public MatMulFull
实例化,
PA_INJECT(type)
,用于存储矩阵的实际数据bool get(RX& out, long i, long j)
,获取矩阵的mat[i,j]
元素,如果它是零元,则返回值是true
最后定义了 class MatMulFullExec
、class BlockMatMulFullExec
、class MatMul1DExec
、class BlockMatMul1DExec
,用于实际计算矩阵乘法。以 MatMulFullExec
为例:
std::vector<MatMul1DExec> transforms
,公开属性,存储了线性变换的信息MatMulFullExec(const MatMulFull& mat, bool minimal = false)
,构造函数,输入mat
存储了NTL::zzX
形式的矩阵元素。void mul(Ctxt& ctxt)
,密文加密行矢v
,计算它右乘矩阵v * mat
void upgrade()
,更新自身的缓存struct ConstMultiplierCache
Matrix.h
这个实现考虑 “vectors of Ctxts” 的线性变换:密文向量 vector<Ctxt> Cvec
加密了多个行矢 vector<vector> vv
,获得加密 vv * mat
的密文向量。
定义了 template <typename T, std::size_t N> class Tensor
,其中 N
是张量的维度,只考虑了 N=1,2
的情况。
std::shared_ptr<std::vector<T>> elements_ptr
,私有属性,顺序存储张量的各列元素Tensor(std::initializer_list<std::vector<T>> lst)
,构造函数,输入lst
是列表,每个列表元素是vector<T>
向量,表示张量的某一列Tensor<T, 2> getColumn(std::size_t j)
,获取张量的某一列Tensor<T, 2> getRow(std::size_t i)
,获取张量的某一行template <typename T2> Tensor<T, N>& entrywiseOperation(const Tensor<T2, N>& rhs, std::function<T&(T&, const T2&)> operation)
,将函数operation
分别作用到张量的每个元素+=, -=, hadamard, apply, transpose
,张量的一些运算
最后用 template <typename T> using Matrix = Tensor<T, 2>
定义了 class Matrix