快速入门#
HEU 当前仅可作为加法同态加密 Library 使用,使用前请先参考 安装说明 安装 HEU Python 包。
备注
本文所讲的内容全部位于 heu.phe 模块中
加解密#
HEU 的操作对象有3种:
对象 |
数据类型 |
描述 |
|---|---|---|
Cleartext(原文) |
python 原生数据类型 |
Python 原生的整数、浮点数 |
Plaintext(明文) |
phe.Plaintext |
编码后的原文,一定为整数 |
Ciphertext(密文) |
phe.Ciphertext |
密文 |
三种对象之间的转换方法如下:
HEU 基本使用展示
1from heu import phe
2
3kit = phe.setup(phe.SchemaType.ZPaillier, 2048)
4encryptor = kit.encryptor()
5evaluator = kit.evaluator()
6decryptor = kit.decryptor()
7
8c1 = encryptor.encrypt_raw(3)
9c2 = encryptor.encrypt_raw(5)
10evaluator.add_inplace(c1, c2) # c1 += c2
11
12decryptor.decrypt_raw(c1) # output 8
备注
encrypt_raw 和 decrypt_raw 支持高精度,其精度与同态加密算法本身支持的值域范围相同,具体值域因算法而异,但一般都 远大于 C++ int128 表达范围。
编码#
当前 HEU 提供了五种 Encoder:
phe.IntegerEncoder: 编码 128bits 以内的整数phe.FloatEncoder: 编码双精度浮点数phe.BigintEncoder: 编码高精度整数,支持任意精度phe.BatchIntegerEncoder: 将两个整数原文编码到一个明文中phe.BatchFloatEncoder: 将两个浮点数原文编码到一个明文中
创建 Encoder 的方法(以``phe.IntegerEncoder``为例)
方法一:
encoder = phe.IntegerEncoder(phe.SchemaType.ZPaillier)
方法二:
kit = phe.setup(phe.SchemaType.ZPaillier, 2048)
encoder = phe.IntegerEncoder(kit.get_schema())
方法三:
kit = phe.setup(phe.SchemaType.ZPaillier, 2048)
encoder = kit.integer_encoder()
IntegerEncoder 和 FloatEncoder#
phe.IntegerEncoder 和 FloatEncoder 原理类似,都是将原文乘上一个 scale 后转换成明文,因此 scale 大小决定了计算的精度。如果创建 IntegerEncoder/FloatEncoder 对象时不提供参数,则使用默认的 scale 1e6
警告
phe.IntegerEncoder 有数值大小上限,请确保编码后的明文小于 128 比特
警告
phe.FloatEncoder 有数值大小上限,请确保编码后的明文大小在双精度浮点数(double)表示范围内
1from heu import phe
2
3encoder = phe.IntegerEncoder(phe.SchemaType.ZPaillier)
4pt = encoder.encode(3.5)
5print(type(pt)) # heu.phe.Plaintext
6print(pt) # 3000000
7print(encoder.decode(pt)) # 3
8
9encoder = phe.FloatEncoder(phe.SchemaType.ZPaillier)
10pt = encoder.encode(3.5)
11print(encoder.decode(pt)) # 3.5
BigintEncoder#
BigintEncoder 类似于 IntegerEncoder(scale=1),但不受精度限制,支持编码任意精度的整数,为了方便用户使用,BigintEncoder 是隐式的,如果用户没有指定 encoder,都默认使用该 encoder。
备注
BigintEncoder 编码 int128 原文性能非常高,但是超过 128bits 后性能会有显著降低
1from heu import phe
2
3encoder = phe.BigintEncoder(phe.SchemaType.ZPaillier)
4int64_max = 9223372036854775807
5pt = encoder.encode(int64_max**10)
6print(encoder.decode(pt) == int64_max**10) # True
BatchIntegerEncoder 和 BatchFloatEncoder#
BatchIntegerEncoder 和 BatchFloatEncoder 是上述 IntegerEncoder、FloatEncoder 的 batch 版本,功能类似,但是 BatchEncoder 支持将两个原文(Cleartext,int64整数)打包加密到一个明文(Plaintext)中,实现 SIMD 功能。
警告
phe.BatchIntegerEncoder 有数值大小上限,每个原文不大于 64 比特
小心
BatchIntegerEncoder、BatchFloatEncoder 并不完全兼容密态减法,仅当密文中所有元素都是正整数时才可以使用,如果您无法确定元素数值范围,应当避免使用密态减法。
1from heu import phe
2
3kit = phe.setup(phe.SchemaType.ZPaillier, 2048)
4encryptor = kit.encryptor()
5evaluator = kit.evaluator()
6decryptor = kit.decryptor()
7
8bc = kit.batch_integer_encoder()
9pt1 = bc.encode(123, 456)
10pt2 = bc.encode(789, 101112)
11
12ct1 = encryptor.encrypt(pt1)
13ct2 = encryptor.encrypt(pt2)
14
15# output: (912, 101568)
16print(bc.decode(decryptor.decrypt(evaluator.add(ct1, ct2))))
17# When using batch encoding, please pay special attention to subtraction,
18# which can only be used when all elements in ciphertext are positive integers.
19# output: (-666, -100656)
20print(bc.decode(decryptor.decrypt(evaluator.sub(ct1, ct2))))
备注
BatchEncoder 当前一次仅支持打包2个原文,如果您有打包更多数字的需求,欢迎提 Issue 或者直接参与共建。
持久化#
实际场景中,隐私计算往往涉及多个参与方,这就涉及到对象的序列化和反序列化,HEU 的对象持久化功能依赖 Pickle。
在一个典型的使用场景中,Client 是数据提供方,Server 是算力提供方,Client 并不信任 Server,因此 Client 可以把数据加密发送给 Server,Server 在密文数据上做计算,并把结果返回给 Client。
1import pickle
2from heu import phe
3
4# client: encrypt
5client_he = phe.setup(phe.SchemaType.ZPaillier, 2048)
6pk_buffer = pickle.dumps(client_he.public_key())
7
8ct1_buffer = pickle.dumps(client_he.encryptor().encrypt_raw(123))
9ct2_buffer = pickle.dumps(client_he.encryptor().encrypt_raw(456))
10
11# server: calc ct1 - ct2
12# server_he supports encryption and cryptographic operations, but doesn't support decryption
13server_he = phe.setup(pickle.loads(pk_buffer))
14ct3 = server_he.evaluator().sub(pickle.loads(ct1_buffer), pickle.loads(ct2_buffer))
15ct3_buffer = pickle.dumps(ct3)
16
17# client: decrypt
18ct_x = pickle.loads(ct3_buffer)
19print(client_he.decryptor().decrypt_raw(ct_x)) # -333