Tenseal库

博客 动态
0 152
优雅殿下
优雅殿下 2022-05-19 21:59:27
悬赏:0 积分 收藏

Tenseal库

在此记录Tenseal的学习笔记

介绍

在张量上进行同态计算的库,是对Seal的python版实现,给开发者提供简单的python接口,无需深究底层密码实现。

当前最新版本:3.11
位置:A library for doing homomorphic encryption operations on tensors

具备以下特点:

  • BFV方案的加解密(整数)
  • CKKS方案的加解密(浮点数)
  • 密文-密文、密文-明文的加法和乘法运算(同态计算)
  • 点积和矩阵乘法
  • 将Seal封装为tenseal.sealapi

安装

环境:MacOS + python3.9

pip安装

此方法安装出来的是Tenseal的库,是编译好的,是直接拿来用的,但不能源码修改,这种方法对于源码学习者,不建议。

前提:安装pip,也就是需要安装python,这里安装的是3.2版,、
一键安装: python3 pip install tenseal


举例:
(1)新建test.py文件

import tenseal as ts# Setup TenSEAL contextcontext = ts.context(            ts.SCHEME_TYPE.CKKS,            poly_modulus_degree=8192,            coeff_mod_bit_sizes=[60, 40, 40, 60]          )context.generate_galois_keys()context.global_scale = 2**40v1 = [0, 1, 2, 3, 4]v2 = [4, 3, 2, 1, 0]# encrypted vectors【编码和加密】enc_v1 = ts.ckks_vector(context, v1)  enc_v2 = ts.ckks_vector(context, v2)# 密文+密文result = enc_v1 + enc_v2result.decrypt() # ~ [4, 4, 4, 4, 4]# 点积:<密文,密文>result = enc_v1.dot(enc_v2)print(result.decrypt()) # ~ [10]matrix = [  [73, 0.5, 8],  [81, -5, 66],  [-100, -78, -2],  [0, 9, 17],  [69, 11 , 10],]# 密文向量*明文矩阵result = enc_v1.matmul(matrix)print(result.decrypt()) # ~ [157, -90, 153]

(2)执行:python3 test.py

cmake 安装

手动cmake安装,适合阅读源码者,这里安装的是最新版:3.11
(1)下载

git clone git://github.com/OpenMined/TenSEAL.git

(2)编译

mkdir buildcmake ..

翻好墙,耐心等待就行!

开始

Tenseal中很多细节都封装了,比如代码中就没有出现密钥生成算法!

同态加密

同态加密(HE)是一种加密技术,它允许对密文进行计算,并生成解密后与对明文进行相同计算的结果一致。

image

下面举个例子:

x = 7y = 3x_encrypted = HE.encrypt(x)y_encrypted = HE.encrypt(y)z_encrypted = x_encrypted + y_encrypted# z should now be x + y = 10z = HE.decrypt(z_encrypted)

TenSEALContext对象

TenSEALContext对象保存密钥和参数。
(1)下面创建一个TenSEALContext:

import tenseal as tscontext = ts.context(ts.SCHEME_TYPE.BFV, poly_modulus_degree=4096, plain_modulus=1032193)context输出:<tenseal.enc_context.Context object at 0x7fcd0b2e88b0>

需要指定要使用的HE方案(此处为BFV)及其参数。
(2)TenSEALContext现在持有私钥,可以其传递给需要私钥的函数。

public_context = ts.context(ts.SCHEME_TYPE.BFV, poly_modulus_degree=4096, plain_modulus=1032193)print("Is the context private?", ("Yes" if public_context.is_private() else "No"))//私钥为不空返回 Trueprint("Is the context public?", ("Yes" if public_context.is_public() else "No"))//私钥为空返回 Truesk = public_context.secret_key()//暂存私钥# the context will drop the secret-key at this point,删除私钥public_context.make_context_public()print("Secret-key dropped")print("Is the context private?", ("Yes" if public_context.is_private() else "No"))print("Is the context public?", ("Yes" if public_context.is_public() else "No"))输出:Is the context private? YesIs the context public? NoSecret-key droppedIs the context private? NoIs the context public? Yes

(3)TenSEALContext包含的属性很多,因此值得一提的是其他一些有趣的属性。比如用于设置自动重新线性化、重新缩放(仅适用于CKK)和模数切换的属性。这些属性默认启用,如下所示:

print("Automatic relinearization is:", ("on" if context.auto_relin else "off"))print("Automatic rescaling is:", ("on" if context.auto_rescale else "off"))print("Automatic modulus switching is:", ("on" if context.auto_mod_switch else "off"))输出:Automatic relinearization is: onAutomatic rescaling is: onAutomatic modulus switching is: on

(4)TenSEALContext 还提供一个全局默认的scale(在使用CKKS方案时),当用户不提供时,默认使用这个

# this should throw an error as the global_scale isn't defined yettry:    print("global_scale:", context.global_scale)except ValueError:    print("The global_scale isn't defined yet")    # you can define it to 2 ** 20 for instancecontext.global_scale = 2 ** 20print("global_scale:", context.global_scale)输出:The global_scale isn't defined yetglobal_scale: 1048576.0

加密和计算

(1)创建一个加密的整数向量。

plain_vector = [60, 66, 73, 81, 90]encrypted_vector = ts.bfv_vector(context, plain_vector)print("We just encrypted our plaintext vector of size:", encrypted_vector.size())encrypted_vector输出:We just encrypted our plaintext vector of size: 5<tenseal.tensors.bfvvector.BFVVector object at 0x7f8446d27e50>

这里是将一个明文向量加密(编码、加密)为一个BFV密文向量
(2)进行密文加法、减法和乘法。

#密文+明文add_result = encrypted_vector + [1, 2, 3, 4, 5]print(add_result.decrypt())#密文-明文sub_result = encrypted_vector - [1, 2, 3, 4, 5]print(sub_result.decrypt())#密文*明文mul_result = encrypted_vector * [1, 2, 3, 4, 5]print(mul_result.decrypt())#密文+密文encrypted_add = add_result + sub_resultprint(encrypted_add.decrypt())#密文-密文encrypted_sub = encrypted_add - encrypted_vectorprint(encrypted_sub.decrypt())#密文*密文encrypted_mul = encrypted_add * encrypted_subprint(encrypted_mul.decrypt())输出:[60, 66, 73, 81, 90]We just encrypted our plaintext vector of size: 5[61, 68, 76, 85, 95][59, 64, 70, 77, 85][60, 132, 219, 324, 450][120, 132, 146, 162, 180][60, 66, 73, 81, 90][7200, 8712, 10658, 13122, 16200]

(3)c2p比c2c计算快的多

ciphertext to plaintext (c2p) and ciphertext to ciphertext (c2c)

import tenseal as tsfrom time import time# Setup TenSEAL contextcontext = ts.context(            ts.SCHEME_TYPE.CKKS,            poly_modulus_degree=8192,            coeff_mod_bit_sizes=[60, 40, 40, 60]          )context.generate_galois_keys()context.global_scale = 2**40v1 = [0, 1111, 2222, 3333, 4444]v2 = [4444, 3333, 2222, 1111, 0]# encrypted vectors【编码和加密】enc_v1 = ts.ckks_vector(context, v1)  enc_v2 = ts.ckks_vector(context, v2)t_start = time()_ = enc_v1 * enc_v2 #密文*密文t_end = time()print("c2c multiply time: {} ms".format((t_end - t_start) * 1000))t_start = time()_ = enc_v1 * v2 #密文*明文t_end = time()print("c2p multiply time: {} ms".format((t_end - t_start) * 1000))t_start = time()_ = enc_v1.dot(enc_v2) #<密文,密文>t_end = time()print(_.decrypt())print("<c,c>  time: {} ms".format((t_end - t_start) * 1000))t_start = time()_ = enc_v1.dot_(v2) #<密文,明文>t_end = time()print(_.decrypt())print("<c,p> multiply time: {} ms".format((t_end - t_start) * 1000))输出:c2c multiply time: 10.8489990234375 msc2p multiply time: 3.325939178466797 ms[12343211.655333618]<c,c>  time: 27.49800682067871 ms[12343211.655338768]<c,p> multiply time: 22.28689193725586 ms

在密文上的逻辑回归训练和计算

待补充

近似计算(CKKS)

本节介绍CKKS方案原理及其实现,详细的CKKS解读请参考:
'Part 1, Vanilla Encoding and Decoding'.
'Part 2, Full Encoding and Decoding'.
'Part 3, Encryption and Decryption'.
'Part 4, Multiplication and Relinearization'.
'Part 5, Rescaling'.

CKKS原理

中文参考:
CKKS Part1:普通编码和解码
CKKS Part2: CKKS的编码和解码
CKKS Part3: CKKS的加密和解密
CKKS Part4: CKKS的乘法和重线性化
CKKS Part5: CKKS的重缩放

大致方案流程:
image

参数

(1)缩放因子(scaling factor)
CKKS方案的第一步是将实数向量编码为明文多项式。
缩放因子指的是编码精度,用数字二进制表示。直观地说,我们讨论的是二进制精度,如下图所示:
image
(2)模多项式的级数(poly_modulus_degree)
即多项式环上的\(Z_q=Z_q[X]/F(X)\)\(F(X)\)的级数\(N\)
\(N\)产生的影响:

  • 明文多项式的系数个数
  • 密文元素的大小
  • 方案的计算性能(越大越差)
  • 安全级别(越大越好)

在TenSEAL中,就像在Microsoft SEAL中一样,多项式模的次数必须是2的幂,比如:(1024,2048,4096,8192,16384,32768)

(3)模多项式的系数模数(coefficient modulus sizes)
多项式的系数模数(素数列表),即\(q\)
\(q\)产生的影响:

  • 密文元素的大小
  • 方案的安全级数\(L\),即乘法次数
  • 安全级别(越大越好)

在TenSEAL中,就像在Microsoft SEAL中一样,系数模数中的每个素数必须最多为60位,并且必须满足mod 2*poly_modulus_degree=1

密钥

(1)私钥
用于解密,不共享,在TenSEALContext对象中
(2)公钥
用于加密
(3)计算密钥(relinearization keys)
用于重线性化(密钥交换),在乘法后用于降低密文维数。可公开
(4)伽罗瓦密钥(Galois Keys)
用于批处理密文的旋转。可公开

批处理向量的旋转的应用是密文求和

内部计算

这些操作由TenSEAL自动执行。
(1)重线性化(Relinearization)
该操作在密文乘法后由TenSEAL自动执行,将密文的维数降到2维。若密文的维数维\(K+1\),则计算密钥的维数为\(K-1\)
(2)重缩放(Rescaling)
每次在密文密文或者密文明文后由TenSEAL自动执行。

计算误差随同态乘法次数增多呈指数增长。为了克服这个问题,大多数HE方案通常使用模交换(module switching)技术。CKKS中,使用重缩放,相当于模数切换。可以降低误差。在同态乘法后使用重缩放,误差线性增长,而不是指数增长。

即给定密文的模数为\(q_1,...,q_k\),经过重缩放后,密文模数变为\(q_1,..,q_{k-1}\),所相应的缩小密文中的“明文值”。

此步骤消耗系数模数\(q_1,...,q_k\)中的一个素数。当你消耗掉所有的时候,你将无法执行更多的乘法运算,即Leveled-FHE方案。

使用

引入

import torchfrom torchvision import transformsfrom random import randintimport picklefrom PIL import Imageimport numpy as npfrom matplotlib.pyplot import imshowfrom typing import Dictimport tenseal as ts

Context

首先生成Context:

ctx = ts.context(ts.SCHEME_TYPE.CKKS, 8192, coeff_mod_bit_sizes=[60, 40, 40, 60])

其中:

  • 方案类型:ts.SCHEME_TYPE.CKKS
  • poly_modulus_degree:8192
  • coeff_mod_bit_sizes:系数模数大小,这里的[60, 40, 40, 60]表示系数模数将包含4个素数,分别为60位、40位、40位和60位。
  • global_scale:缩放因子(scaling factor),即\(2^{40}\)

TenSEAL支持在公钥和对称加密之间切换。默认情况下使用公钥加密。
默认情况下,会自动执行重线性化后和重缩放。通过generate_galois_keys产生伽罗瓦密钥(Galois Keys)

def context():    context = ts.context(ts.SCHEME_TYPE.CKKS, 8192, coeff_mod_bit_sizes=[60, 40, 40, 60])    context.global_scale = pow(2, 40)    context.generate_galois_keys()    return contextcontext = context()

明文张量(PlainTensor)

张量:可以看成一种数据存储格式
PlainTensor类作为一个转换层,将普通数据类型(例如List,array等)转换为tenseal所支持的明文形式
image

import numpy as npplain1 = ts.plain_tensor([1,2,3,4], [2,2])print(" First tensor: Shape = {} Data = {}".format(plain1.shape, plain1.tolist()))plain2 = ts.plain_tensor(np.array([5,6,7,8]).reshape(2,2))print(" Second tensor: Shape = {} Data = {}".format(plain2.shape, plain2.tolist()))输出:First tensor: Shape = [2, 2] Data = [[1.0, 2.0], [3.0, 4.0]]Second tensor: Shape = [2, 2] Data = [[5.0, 6.0], [7.0, 8.0]]

从上面可以看出:plain1和plain2就是张量形式,包含数据和形状(shape)

加密

CKKS由于明文空间是浮点数或实数,而计算是在多项式环上,所以加密前需要先编码。
(1)编码
编码分为两步:浮点数 -》实数多项式 -》整数多项式

假设,模多项式的级数为\(N\),那么将\(N/2\)个浮点数编码到明文元素中,然后加密,同态计算就是对密文(多项式)上的系数计算(逐coefficient (一个系数就是一个slot?)),从而实现SIMD操作。整个过程叫做"打包"(batching
image
(2)加/解密
加密:对一个明文多项式加密
image

下面举一个例子:将明文张量(PlainTensor)加密为密文张量(encrypted tensor)

为了创建密文张量,TenSEAL会自动执行编码和加密。这适用于CKKS和BFV方案。
将明文张量(PlainTensor)加密为密文张量(encrypted tensor),存储形式为【密文、shape】

下面有几种密文张量形式:

  • BFVVector:1D(1维)整数数组
  • CKKSVector:1D(1维)浮点数数组
  • CKKSTensor:N维浮点数数组,支持密文张量的reshaping或者broadcasting操作

image

import tenseal as tsimport numpy as np# Setup TenSEAL contextcontext = ts.context(            ts.SCHEME_TYPE.CKKS,            poly_modulus_degree=8192,            coeff_mod_bit_sizes=[60, 40, 40, 60]          )context.generate_galois_keys()context.global_scale = 2**40plain1 = ts.plain_tensor([1,2,3,4], [2,2])print(" First tensor: Shape = {} Data = {}".format(plain1.shape, plain1.tolist()))plain2 = ts.plain_tensor(np.array([5,6,7,8]).reshape(2,2))print(" Second tensor: Shape = {} Data = {}".format(plain2.shape, plain2.tolist()))encrypted_tensor1 = ts.ckks_tensor(context, plain1)encrypted_tensor2 = ts.ckks_tensor(context, plain2)print(" Shape = {}".format(encrypted_tensor1.shape))print(" Encrypted Data = {}.".format(encrypted_tensor1))encrypted_tensor_from_np = ts.ckks_tensor(context, np.array([5,6,7,8]).reshape([2,2]))print(" Shape = {}".format(encrypted_tensor_from_np.shape))输出:First tensor: Shape = [2, 2] Data = [[1.0, 2.0], [3.0, 4.0]]Second tensor: Shape = [2, 2] Data = [[5.0, 6.0], [7.0, 8.0]]Shape = [2, 2]Encrypted Data = <tenseal.tensors.ckkstensor.CKKSTensor object at 0x7f9ddd530400>.Shape = [2, 2]

从上面看出,将普通数据(list:[1,2,3,4])转换为明文张量(plain1),再加密为密文张量(encrypted_tensor1),内部存储【密文数据,shape】

同态计算

下面是CKKS所支持的密文张量计算:
image

下面举例:

import tenseal as tsimport numpy as np# Setup TenSEAL contextcontext = ts.context(            ts.SCHEME_TYPE.CKKS,            poly_modulus_degree=8192,            coeff_mod_bit_sizes=[60, 40, 40, 60]          )context.generate_galois_keys()context.global_scale = 2**40def decrypt(enc):    return enc.decrypt().tolist()plain1 = ts.plain_tensor([1,2,3,4], [2,2])print("First tensor: Shape = {} Data = {}".format(plain1.shape, plain1.tolist()))plain2 = ts.plain_tensor(np.array([5,6,7,8]).reshape(2,2))print("Second tensor: Shape = {} Data = {}".format(plain2.shape, plain2.tolist()))encrypted_tensor1 = ts.ckks_tensor(context, plain1)encrypted_tensor2 = ts.ckks_tensor(context, plain2)#密文(张量)+ 密文(张量)result = encrypted_tensor1 + encrypted_tensor2print("Plain equivalent: {} + {}\nDecrypted result: {}.".format(plain1.tolist(), plain2.tolist(), decrypt(result)))#密文(张量)- 密文(张量)result = encrypted_tensor1 - encrypted_tensor2print("Plain equivalent: {} - {}\nDecrypted result: {}.".format(plain1.tolist(), plain2.tolist(), decrypt(result)))#密文(张量)* 密文(张量)result = encrypted_tensor1 * encrypted_tensor2print("Plain equivalent: {} * {}\nDecrypted result: {}.".format(plain1.tolist(), plain2.tolist(), decrypt(result)))#密文(张量)* 明文(张量)plain = ts.plain_tensor([5,6,7,8], [2,2])result = encrypted_tensor1 * plainprint("Plain equivalent: {} * {}\nDecrypted result: {}.".format(plain1.tolist(), plain.tolist(), decrypt(result)))#取反:密文(张量)result = -encrypted_tensor1 print("Plain equivalent: -{}\nDecrypted result: {}.".format(plain1.tolist(), decrypt(result)))#求幂:密文(张量)^3result = encrypted_tensor1 ** 3print("Plain equivalent: {} ^ 3\nDecrypted result: {}.".format(plain1.tolist(), decrypt(result)))#多项式计算(整数):1 + X^2 + X^3,X是密文(张量)result = encrypted_tensor1.polyval([1,0,1,1])print("X = {}".format(plain1.tolist()))print("1 + X^2 + X^3 = {}.".format(decrypt(result)))#多项式计算(浮点数):1 + X^2 + X^3,X是密文(张量)result = encrypted_tensor1.polyval([0.5, 0.197, 0, -0.004])print("X = {}".format(plain1.tolist()))print("0.5 + 0.197 X - 0.004 x^X = {}.".format(decrypt(result)))输出:First tensor: Shape = [2, 2] Data = [[1.0, 2.0], [3.0, 4.0]]Second tensor: Shape = [2, 2] Data = [[5.0, 6.0], [7.0, 8.0]]Plain equivalent: [[1.0, 2.0], [3.0, 4.0]] + [[5.0, 6.0], [7.0, 8.0]]Decrypted result: [[6.000000000510762, 7.99999999944109], [10.000000000176103, 11.999999999918177]].Plain equivalent: [[1.0, 2.0], [3.0, 4.0]] - [[5.0, 6.0], [7.0, 8.0]]Decrypted result: [[-3.999999998000314, -3.9999999987240265], [-4.0000000013643, -4.0000000013791075]].Plain equivalent: [[1.0, 2.0], [3.0, 4.0]] * [[5.0, 6.0], [7.0, 8.0]]Decrypted result: [[5.000000678675058, 12.000001612431278], [21.000002812898412, 32.000004287986336]].Plain equivalent: [[1.0, 2.0], [3.0, 4.0]] * [[5.0, 6.0], [7.0, 8.0]]Decrypted result: [[5.000000676956037, 12.000001612473657], [21.000002810086173, 32.00000428474004]].Plain equivalent: -[[1.0, 2.0], [3.0, 4.0]]Decrypted result: [[-1.0000000012552241, -2.000000000358531], [-2.9999999994059015, -3.999999999269536]].Plain equivalent: [[1.0, 2.0], [3.0, 4.0]] ^ 3Decrypted result: [[1.0000008094463497, 8.000006439159353], [27.000021714154222, 64.00005146475934]].X = [[1.0, 2.0], [3.0, 4.0]]1 + X^2 + X^3 = [[3.000000945752252, 13.000006978595758], [37.00002291844665, 81.000053606697]].X = [[1.0, 2.0], [3.0, 4.0]]0.5 + 0.197 X - 0.004 x^X = [[0.6930000194866153, 0.8620000226394146], [0.9829999914891329, 1.0319998662943677]].

其中密文张量乘法后需要重线性化:
image
其中多项式计算(浮点数),来自:Logistic regression over encrypted data from fully homomorphic encryption

demo

下面对MNIST数据集的分类,使用一个卷积和两个完全连接的层以及一个平方激活函数。
它是同态加密的一个重要用例:来自:https://github.com/youben11/encrypted-evaluation
image

对卷积不了解,后期补充!

性能测试

下面将提供一些关于如何对同态加密应用程序进行基准测试的提示,并选择最合适的参数。

序列化:通信传输时需要序列化,比如:读写就是序列化

代码和结果:https://github.com/OpenMined/TenSEAL/blob/main/tutorials/Tutorial 3 - Benchmarks.ipynb

Context 序列化

结果:

  • 对称加密方案创建的Context比公钥加密方案创建的Context更小。
  • 减少系数模数(coefficient modulus)的长度会减少Context的大小,但也会减少可用乘法的深度\(L\),也会影响精度(对于CKKS)。
  • Galois密钥只会增加公共Context的大小(没有私钥)。仅当需要执行密文旋转时发送它们。
  • 重新线性密钥只会增加公共Context的大小。仅当需要执行密文乘法时才发送它们。
  • 当我们发送私钥时,可以重新生成重新线性化/伽罗瓦密钥,而无需发送它们。

密文(Ciphertext)序列化

设置的参数不同,会影响密文的序列化
对称或者公钥加密方案实际上并不影响密文的大小,只影响Context的大小。
下面结果是针对堆成加密场景:
【明文数据大小:8.8 KB】

  • 多项式模\(N\)的增加导致密文的增加。
  • 系数模数(coefficient modulus)的长度影响密文大小。
  • 系数模数大小的值会影响密文大小以及精度。
  • 对于一组固定的多项式模数\(N\)和系数模数,更改精度不会影响密文大小。

MNIST上的加密卷积

后续补充!

总结

posted @ 2022-05-19 21:26 PamShao 阅读(0) 评论(0) 编辑 收藏 举报
回帖
    优雅殿下

    优雅殿下 (王者 段位)

    2018 积分 (2)粉丝 (47)源码

    小小码农,大大世界

     

    温馨提示

    亦奇源码

    最新会员