0%

Pyfhel学习:BFV整数加密

Pyfhel库中包含的内容十分丰富,可以从Pyfhel/examples入手,来体验如何使用Pyfhel。下面具体展示其中的BFV方案使用实例。

BFV实例

BFV (Brakerski-Fan-Vercauteren) 方案是一种基于环上的学习困难问题的同态加密方案。BFV方案允许对编码后的整数进行加密,然后进行同态加密操作(如加法和乘法),最后能够解密出一个近似的结果。论文:BFV

声明参数

1
2
3
4
5
6
7
8
HE = Pyfhel()
bfv_params = {
'scheme': 'BFV',
'n': 2 ** 13,
't': 65537,
't_bits': 20,
'sec': 128,
}
  • ‘n’:环的度数。环度指的是同态加密使用的多项式的最大次数加1,因为多项式的次数是从0开始的。这里的值是$2^{13}$,意味着多项式最大次数为$2^{13} - 1$。可以看成是明文数组的长度(明文槽的数量),这些数组中的元素会被编码为一个单一的密文

    注意:环度越大,表示能处理的数据量也越大,但计算复杂度也会增加

  • ‘t’:明文模数。通常用于将明文编码为多项式以及从多项式解码。必须是一个素数满足$t - 1$能被$2^n$整除

  • ‘t_bits’:$t$的比特数。用于生成合适的$t$。如果指定该值就会覆盖上述的$t$值

  • ‘sec’:安全参数。AES密钥的等效长度(以比特位为单位)。用于设置密文模数$q$,可在{128,192,256}中选择。越大意味着越安全,但更低的计算效率

构造密钥

1
2
3
4
5
6
7
8
9
10
11
# 生成BFV方案的context
HE.contextGen(**bfv_params)

# 生成公/私钥对
HE.keyGen()

# 生成旋转密钥 --> 允许旋转/移动
HE.rotateKeyGen()

# 生成重线性化密钥
HE.relinKeyGen()

生成测试数据

  • 定义两个1D的整型数组,编码并加密
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 生成整型数组

# arr1 = [0, 1, ... n-1] (length n)
# 最大可能值为t/2-1
arr1 = np.arange(bfv_params['n'], dtype=np.int64)

# arr2 = [-t//2, -1, 1] (length 3) --> 编码时将数组剩余部分填充0
# 最小可能值为-t/2
arr2 = np.array([-bfv_params['t'] // 2, -1, 1], dtype=np.int64)

# 将arr1编码为PyPtxt的明文
ptxt1 = HE.encodeInt(arr1)

# arr2的长度为3,将arr2用0填充为长度n,再编码为PyPtxt的明文
ptxt2 = HE.encodeInt(arr2)

# 加密明文ptxt1和ptxt2,返回PyCtxt密文
ctxt1 = HE.encryptPtxt(ptxt1)
ctxt2 = HE.encryptPtxt(ptxt2)

同态计算

密文/明文必须建立在同一个context下

  • 密文-密文操作

    1
    2
    3
    4
    5
    6
    7
    8
    ccSum = ctxt1 + ctxt2
    ccSub = ctxt1 - ctxt2
    ccMul = ctxt1 * ctxt2
    cNeg = -ctxt1
    cSq = ctxt1 ** 2
    cPow = ctxt1 ** 3
    cRotR = ctxt1 >> 2
    cRotL = ctxt1 << 2
  • 密文-明文操作

    1
    2
    3
    4
    cpSum = ctxt1 + ptxt2
    cpSum1 = ptxt2 + ccMul
    cpSub = ctxt1 - ptxt2
    cpMul = ctxt1 * ptxt2
  • 乘法重线性化:密文-密文之间的乘法会导致生成的密文多项式的次数增加。为了防止这种增长,通过使用重现性化技术(通常在每次c-c mult之后),从而降低密文多项式的次数到最小规模(两个多项式c0 & c1)

    1
    2
    3
    4
    5
    6
    print("Relinearization-> Right after each multiplication.")
    print(f"ccMul before relinearization (size {ccMul.size()}): {ccMul}")
    ~ccMul
    print(f"ccMul after relinearization (size {ccMul.size()}): {ccMul}")

    print(f"cPow after 2 mult&relin rounds: (size {cPow.size()}): {cPow}")

解码并解密

  • 将密文信息进行解码&解密,并进行显示输出
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
r1     = HE.decryptInt(ctxt1)
r2 = HE.decryptInt(ctxt2)
rccSum = HE.decryptInt(ccSum)
rccSub = HE.decryptInt(ccSub)
rccMul = HE.decryptInt(ccMul)
rcSq = HE.decryptInt(cSq )
rcNeg = HE.decryptInt(cNeg )
rcPow = HE.decryptInt(cPow )
rcRotR = HE.decryptInt(cRotR)
rcRotL = HE.decryptInt(cRotL)
rcpSum = HE.decryptInt(cpSum)
rcpSub = HE.decryptInt(cpSub)
rcpMul = HE.decryptInt(cpMul)

print("Decrypting results")
print(" Original ciphertexts: ")
print(" ->\tctxt1 --(decr)--> ", r1)
print(" ->\tctxt2 --(decr)--> ", r2)
print(" Ciphertext-ciphertext Ops: ")
print(" ->\tctxt1 + ctxt2 = ccSum --(decr)--> ", rccSum)
print(" ->\tctxt1 - ctxt2 = ccSub --(decr)--> ", rccSub)
print(" ->\tctxt1 * ctxt2 = ccMul --(decr)--> ", rccMul)
print(" Single ciphertext: ")
print(" ->\tctxt1**2 = cSq --(decr)--> ", rcSq )
print(" ->\t- ctxt1 = cNeg --(decr)--> ", rcNeg )
print(" ->\tctxt1**3 = cPow --(decr)--> ", rcPow )
print(" ->\tctxt1 >> 2 = cRotR --(decr)--> ", rcRotR)
print(" ->\tctxt1 << 2 = cRotL --(decr)--> ", rcRotL)
print(" Ciphertext-plaintext ops: ")
print(" ->\tctxt1 + ptxt2 = cpSum --(decr)--> ", rcpSum)
print(" ->\tctxt1 - ptxt2 = cpSub --(decr)--> ", rcpSub)
print(" ->\tctxt1 * ptxt2 = cpMul --(decr)--> ", rcpMul)

结语

以上实例具体在Pyfhel/examples中,有需要可以自行查阅源码。

-------------    本文结束  感谢阅读    -------------