SIMD编码
同态加密的一个比较大的缺点就是密文太大,计算一次密文运算的时间长。直接降低一次密文操作的开销是困难的,于是很自然的一个想法就是能不能提高吞吐量。
SIMD的意思是,一次操作可以同时处理多个数据。我们将多个数映射到同一个明文多项式。BGV的明文域实际上是多项式。当我们的编码是同态的时候,我们就得到了一个SIMD的同态操作。
比如简单的系数打包
f
1
(
x
)
=
a
0
+
a
1
x
+
a
2
x
2
,
f
2
(
x
)
=
b
0
+
b
1
x
+
b
2
x
2
f_1(x)=a_0+a_1x+a_2x^2,f_2(x)=b_0+b_1x+b_2x^2
f1(x)=a0+a1x+a2x2,f2(x)=b0+b1x+b2x2.
我们计算
f
1
(
x
)
+
f
2
(
x
)
=
(
a
0
+
b
0
)
+
(
a
1
+
b
1
)
x
+
(
a
2
+
b
2
)
x
2
f_1(x)+f_2(x)=(a_0+b_0)+(a_1+b_1)x+(a_2+b_2)x^2
f1(x)+f2(x)=(a0+b0)+(a1+b1)x+(a2+b2)x2.
这样,我们计算了一次多项式的加法,就得到了三个数据的和
a
0
+
b
0
,
a
1
+
b
1
,
a
2
+
b
2
a_0+b_0,a_1+b_1,a_2+b_2
a0+b0,a1+b1,a2+b2.
但是系数打包并不是乘法同态的,乘法的时候,不同次项的系数会混在一起。
PackEncoding
假设BGV处理的多项式环为 R = Z p [ x ] / ( x N + 1 ) R=\mathbb{Z}_p[x]/(x^N+1) R=Zp[x]/(xN+1),其中 N = 2 d N=2^d N=2d, d d d是一个整数。明文域和密文域的多项式模是一样的,不同的是系数模。这里的 p p p是明文系数模,也就是我们需要计算的数据实际上是 Z p \mathbb{Z}_p Zp上的运算。
由于 Φ ( x ) = x N + 1 \Phi(x)= x^N+1 Φ(x)=xN+1是一个 2 N 2N 2N次分圆多项式,所以我们可以利用分圆多项式的特殊性质来构造 R R R的一个同态分解。
假设 w w w是 Φ ( x ) \Phi(x) Φ(x)的单位原根,也就是 w N = − 1 w^N=-1 wN=−1. 当 p = 2 N k + 1 , k ∈ Z + p=2Nk+1, k \in \mathbb{Z}^+ p=2Nk+1,k∈Z+并且 p p p是一个素数时。 Φ ( x ) \Phi(x) Φ(x)刚好有 N N N个单元原根。 w = w 1 , w 2 , ⋯ , w N w=w_1,w_2,\cdots,w_N w=w1,w2,⋯,wN.
所以,我们可以根据这个构造打包方法。 f ( w 1 ) = a 1 , f ( w 2 ) = a 2 , ⋯ , f ( w N ) = a N f(w_1)=a_1,f(w_2)=a_2,\cdots,f(w_N)=a_N f(w1)=a1,f(w2)=a2,⋯,f(wN)=aN,这样根据多项式插值,可以得到唯一的一个 N − 1 N-1 N−1次多项式,也就是我们的明文多项式 f ( x ) f(x) f(x).
由于我们在计算多项式的乘法的时候,多项式的次数会增加,而我们是在换上进行的运算,所以会模多项式
x
N
+
1
x^N+1
xN+1. 也就是将结果多项式中的
x
N
x^N
xN替换为
−
1
-1
−1,因为在环
R
R
R上
x
N
+
1
=
0
x^N+1=0
xN+1=0.
但是我们注意到
w
i
N
=
−
1
w_i^N=-1
wiN=−1,所以,模多项式并不会影响在原根处的求值结果。
现在我们证明这样是全同态的,设
f
1
(
w
i
)
=
a
i
,
f
2
(
w
i
)
=
b
i
f_1(w_i)=a_i,f_2(w_i)=b_i
f1(wi)=ai,f2(wi)=bi,
加法:
a
+
b
=
f
1
(
w
i
)
+
f
2
(
w
i
)
=
(
f
1
+
f
2
)
(
w
i
)
a+b=f_1(w_i)+f_2(w_i)=(f_1+f_2)(w_i)
a+b=f1(wi)+f2(wi)=(f1+f2)(wi).
乘法:
a
b
=
f
1
(
w
i
)
f
2
(
w
i
)
=
(
f
1
f
2
)
(
w
i
)
ab=f_1(w_i)f_2(w_i)=(f_1f_2)(w_i)
ab=f1(wi)f2(wi)=(f1f2)(wi).
OpenFHE代码示例
OpenFHE中的PackEncoding使用的就是上面的方法。注意的是,我们在选择参数的时候,明文模需要满足上述条件, 2 N 2N 2N必须整除$p-1.
这里用的是openfhe的python库openfhe-python
from openfhe import *
parameters = CCParamsBGVRNS()
parameters.SetPlaintextModulus(536903681)
parameters.SetMultiplicativeDepth(4)
crypto_context = GenCryptoContext(parameters)
# Enable features that you wish to use
crypto_context.Enable(PKESchemeFeature.PKE)
crypto_context.Enable(PKESchemeFeature.KEYSWITCH)
crypto_context.Enable(PKESchemeFeature.LEVELEDSHE)
# Sample Program: Step 2: Key Generation
# Generate a public/private key pair
key_pair = crypto_context.KeyGen()
crypto_context.EvalMultKeyGen(key_pair.secretKey)
vector_of_ints1 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
plaintext1 = crypto_context.MakePackedPlaintext(vector_of_ints1)
# Second plaintext vector is encoded
vector_of_ints2 = [-1,-2,-3,-4,-5,-6,-7,-8,-9,-10,-11,-12]
plaintext2 = crypto_context.MakePackedPlaintext(vector_of_ints2)
c1 = crypto_context.Encrypt(key_pair.publicKey, plaintext1)
c2 = crypto_context.Encrypt(key_pair.publicKey, plaintext2)
# Homomorphic additions
ciphertext_add12 = crypto_context.EvalAdd(c1, c2)
# Homomorphic Multiplication
ciphertext_mult12 = crypto_context.EvalMult(c1, c2)
plaintext_add_result = crypto_context.Decrypt(ciphertext_add12,key_pair.secretKey)
plaintext_mult_result = crypto_context.Decrypt(ciphertext_mult12,key_pair.secretKey)
print(plaintext_add_result)
print(plaintext_mult_result)