开发新算法#
HEU 支持添加自定义 PHE 算法,添加新的 PHE 算法后,上层所有应用都可以无缝切换到新的算法上,用户无需修改任何业务代码。
HEU 定义了一套 SPI (Service Provider Interface),任何符合 SPI 定义的算法都可接入到 HEU 中,HEU 的算法分配器(dispatcher)是 O(1) 效率的,也就是算法分配的开销与底层算法种类无关,您可以放心向 HEU 添加多种算法而无需担心对 HEU 造成负担。
下文以添加一种 Mock 算法为例,介绍如何在 HEU 中添加、实现一种新的 PHE 算法。新算法的代码文件请存放到 heu/library/algorithms/ 路径下。
一些前置说明
开发一个新算法需要实现
Plaintext、Ciphertext、PublicKey、SecretKey、KeyGenerator、Encryptor、Decryptor、Evaluator共 8 个 Class,请勿修改 Class name,所有 Class 都必须放置在heu::lib::algorithms::your_algonamespace 下,其中your_algo使用实际的算法名称替换,否则将编译失败SPI 只规定了这 8 个 Class 必须要实现的 public 方法,SPI 并没有限制您添加其它方法与字段,您可以根据算法需要自由添加。
定义数据结构#
一般而言,PHE 的值域空间都比较大,远超 C++ int128 范围,因此您需要选定一个大整数运算库。HEU SPI 并没有绑定在任何一个大整数运算库上,您可以任意选定一个合适的大整数库。
选定大数库后,下一步定义 Plaintext 与 Ciphertext 结构。
定义 Plaintext#
Plaintext class 的 SPI 接口如下:
1#include "yacl/base/byte_container_view.h"
2
3#include "heu/library/algorithms/util/spi_traits.h"
4
5namespace heu::lib::algorithms::your_algo {
6
7class Plaintext {
8 public:
9 explicit Plaintext() = default;
10
11 // Plaintext -> primitive type
12 // T could be (u)int8/16/32/64/128
13 template <typename T>
14 T Get() const;
15
16 // Set primitive value
17 // T could be (u)int8/16/32/64/128
18 template <typename T>
19 void Set(T value);
20 // Set big number by string
21 void Set(const std::string &num, int radix);
22
23 yacl::Buffer Serialize() const;
24 void Deserialize(yacl::ByteContainerView buffer);
25
26 std::string ToString() const;
27 friend std::ostream &operator<<(std::ostream &os, const Plaintext &pt);
28 std::string ToHexString() const;
29
30 yacl::Buffer ToBytes(size_t byte_len, Endian endian = Endian::native) const;
31 void ToBytes(unsigned char *buf, size_t buf_len,
32 Endian endian = Endian::native) const;
33
34 size_t BitCount() const;
35
36 Plaintext operator-() const;
37 void NegateInplace();
38
39 bool IsZero() const; // [SPI: Critical]
40 bool IsPositive() const; // [SPI: Important]
41 bool IsNegative() const; // [SPI: Important]
42
43 Plaintext operator+(const Plaintext &op2) const;
44 Plaintext operator-(const Plaintext &op2) const;
45 Plaintext operator*(const Plaintext &op2) const;
46 Plaintext operator/(const Plaintext &op2) const;
47 Plaintext operator%(const Plaintext &op2) const;
48 Plaintext operator&(const Plaintext &op2) const;
49 Plaintext operator|(const Plaintext &op2) const;
50 Plaintext operator^(const Plaintext &op2) const;
51 Plaintext operator<<(size_t op2) const;
52 Plaintext operator>>(size_t op2) const;
53
54 Plaintext operator+=(const Plaintext &op2);
55 Plaintext operator-=(const Plaintext &op2);
56 Plaintext operator*=(const Plaintext &op2);
57 Plaintext operator/=(const Plaintext &op2);
58 Plaintext operator%=(const Plaintext &op2);
59 Plaintext operator&=(const Plaintext &op2);
60 Plaintext operator|=(const Plaintext &op2);
61 Plaintext operator^=(const Plaintext &op2);
62 Plaintext operator<<=(size_t op2);
63 Plaintext operator>>=(size_t op2);
64
65 bool operator>(const Plaintext &other) const;
66 bool operator<(const Plaintext &other) const;
67 bool operator>=(const Plaintext &other) const;
68 bool operator<=(const Plaintext &other) const;
69 bool operator==(const Plaintext &other) const;
70 bool operator!=(const Plaintext &other) const;
71
72 // static helper functions //
73 // Generates a uniformly distributed random number of "bit_size" size
74 static void RandomExactBits(size_t bit_size, Plaintext *r);
75 // Generates a uniformly distributed random number in [0, N)
76 static void RandomLtN(const Plaintext &n, Plaintext *r);
77};
78
79} // namespace heu::lib::algorithms::your_algo
Plaintext class 的接口比较多,这是因为 HEU 并没有绑定到任何大数库,所有明文计算都依赖 Plaintext 类接口完成,这些接口您需要一一实现。
关于 SPI 接口中出现的 ToBytes 方法,此处有一些补充说明:
该接口的功能与 python int.to_bytes 方法类似。
该接口与序列化接口的区别:序列化接口用于在不同 HEU 实例之间传递信息,因此序列化的 buffer 只需要 HEU 自己能解析即可,一般与 Bignumber 内存存储格式接近以最大化性能;ToBytes 接口用于在 HEU 与其它模块之间传递信息,因此 Buffer 格式必须公认,目前模块之间统一使用 numpy 的格式,numpy 底层调用 Python built-in type int 的 to_bytes() 接口,因此本质上 Plaintext 需要实现一个 python int to_bytes() 类似的接口,把 big number 转换成指定字节大小的大/小端存储,因为存在转换,ToBytes 性能一般比序列化低。另一个区别是 ToBytes 是单向的,只需要实现 Plaintext 到 bytes 转换,无需 bytes 到 Plaintext 转换。
定义 Ciphertext#
Ciphertext 需要实现以下这些方法:
1#include <ostream>
2#include <string>
3
4#include "yacl/base/byte_container_view.h"
5
6namespace heu::lib::algorithms::your_algo {
7
8// SPI: Do not change class name.
9class Ciphertext {
10 public:
11 Ciphertext() = default;
12
13 std::string ToString() const;
14 friend std::ostream &operator<<(std::ostream &os, const Ciphertext &c);
15
16 bool operator==(const Ciphertext &other) const;
17 bool operator!=(const Ciphertext &other) const;
18
19 yacl::Buffer Serialize() const;
20 void Deserialize(yacl::ByteContainerView in);
21};
22
23} // namespace heu::lib::algorithms::your_algo
定义 KEYS#
下一步定义 Key 相关数据结构,其 SPI 如下:
PublicKey class:
1#include "heu/library/algorithms/clean_template/plaintext.h"
2
3namespace heu::lib::algorithms::your_algo {
4
5class PublicKey {
6 public:
7 bool operator==(const PublicKey &other) const;
8 bool operator!=(const PublicKey &other) const;
9
10 std::string ToString() const;
11
12 // Valid plaintext range: (max_int_, -max_int_)
13 const Plaintext &PlaintextBound() const &;
14
15 yacl::Buffer Serialize() const;
16 void Deserialize(yacl::ByteContainerView in);
17};
18
19} // namespace heu::lib::algorithms::your_algo
SecretKey class:
1#include "yacl/base/byte_container_view.h"
2
3namespace heu::lib::algorithms::your_algo {
4
5class SecretKey {
6 public:
7 bool operator==(const SecretKey &other) const;
8 bool operator!=(const SecretKey &other) const;
9
10 std::string ToString() const;
11
12 yacl::Buffer Serialize() const;
13 void Deserialize(yacl::ByteContainerView in);
14};
15
16} // namespace heu::lib::algorithms::your_algo
KeyGenerator class:
1#include "heu/library/algorithms/clean_template/public_key.h"
2#include "heu/library/algorithms/clean_template/secret_key.h"
3
4namespace heu::lib::algorithms::your_algo {
5
6class KeyGenerator {
7 public:
8 // Generate PHE key pair
9 static void Generate(int key_size, SecretKey* sk, PublicKey* pk);
10};
11
12} // namespace heu::lib::algorithms::your_algo
定义操纵单元#
基本数据结构和公私钥定义完成后,下一步就可以定义操纵单元,分别是 Encryptor、Decryptor、Evaluator。为了支持不同特性的算法,HEU 提供了两套截然不同的接口(SPI),分别是 Scalar SPI 和 Vectorized SPI。
Scalar SPI:适合于大多数算法,一次调用只处理一个明文或密文。
Vectorized SPI:适合于使用硬件加速的算法,例如 GPU 等,此类硬件对算法并行度有要求,Vectorized SPI 实现了 SIMD 的调用模式,从而使得算法实现层面可以做并行优化。
Scalar SPI 和 Vectorized SPI 算法只需要实现其中一套即可,HEU O(1) dispatcher 会根据算法实现的实际情况自动决定调用哪一套 SPI,当然,如果您同时实现 Scalar SPI 和 Vectorized SPI 那将会更好,此时 HEU O(1) dispatcher 会根据用户使用场景自动在两套 SPI 中切换,算法将获得最佳性能。
HEU O(1) Dispatcher 同时适配两套 SPI 接口的原理
为了更好地帮助算法开发者理解 SPI 接口切换的机制,此处介绍一下 HEU O(1) Dispatcher 检测接口是否实现的原理。
HEU O(1) Dispatcher 通过 std::experimental::is_detected 工具检测某个类是否实现了某个方法,以下是原理示意:
1#include <experimental/type_traits>
2
3// Check if T has a member function .Serialize()
4template <typename T>
5using kHasSerializeMethod = decltype(std::declval<T&>().Serialize());
6
7void foobar(const CLAZZ &obj) {
8 if constexpr (std::experimental::is_detected_v<kHasSerializeMethod, CLAZZ>) {
9 obj.Serialize();
10 } else {
11 obj.Other();
12 }
13}
只实现 Scalar SPI#
Scalar SPI 需要实现以下接口。
Encryptor class:
1#include "heu/library/algorithms/clean_template/ciphertext.h"
2#include "heu/library/algorithms/clean_template/plaintext.h"
3#include "heu/library/algorithms/clean_template/public_key.h"
4
5namespace heu::lib::algorithms::your_algo {
6
7class Encryptor {
8 public:
9 explicit Encryptor(const PublicKey& pk);
10
11 Ciphertext EncryptZero() const;
12 Ciphertext Encrypt(const Plaintext& m) const;
13
14 std::pair<Ciphertext, std::string> EncryptWithAudit(const Plaintext& m) const;
15};
16
17} // namespace heu::lib::algorithms::your_algo
Encryptor 涉及一个比较特殊的 EncryptWithAudit 接口,这是因为某些机构对隐语有特殊要求,要求隐语所有的行为具备事后审计能力,即所有的计算过程可还原。对于 PHE 加密,此处需要把加密用到的随机数返回给上层以便落入日志。
EncryptWithAudit 返回的 audit string 中至少要包含明文、密文以及加密所用的随机数三个信息,对于 string 的格式则没有要求,只要是程序可解析的即可,如果能做到 human readable 则更好,您可以通过以下方法查看实际的 audit string 样例:
1from heu import phe
2
3kit = phe.setup(phe.SchemaType.ZPaillier, 2048)
4print(kit.encryptor().encrypt_with_audit(kit.plaintext(1)))
Decryptor class:
1#include <utility>
2
3#include "heu/library/algorithms/clean_template/ciphertext.h"
4#include "heu/library/algorithms/clean_template/plaintext.h"
5#include "heu/library/algorithms/clean_template/public_key.h"
6#include "heu/library/algorithms/clean_template/secret_key.h"
7
8namespace heu::lib::algorithms::your_algo {
9
10class Decryptor {
11 public:
12 explicit Decryptor(const PublicKey& _, const SecretKey& sk);
13
14 void Decrypt(const Ciphertext& ct, Plaintext* out) const;
15 Plaintext Decrypt(const Ciphertext& ct) const;
16};
17
18} // namespace heu::lib::algorithms::your_algo
Evaluator class:
1#include "heu/library/algorithms/clean_template/ciphertext.h"
2#include "heu/library/algorithms/clean_template/plaintext.h"
3#include "heu/library/algorithms/clean_template/public_key.h"
4
5namespace heu::lib::algorithms::your_algo {
6
7class Evaluator {
8 public:
9 explicit Evaluator(const PublicKey& pk);
10
11 void Randomize(Ciphertext* ct) const;
12
13 Ciphertext Add(const Ciphertext& a, const Ciphertext& b) const;
14 Ciphertext Add(const Ciphertext& a, const Plaintext& b) const;
15 Ciphertext Add(const Plaintext& a, const Ciphertext& b) const;
16 Plaintext Add(const Plaintext& a, const Plaintext& b) const;
17 void AddInplace(Ciphertext* a, const Ciphertext& b) const;
18 void AddInplace(Ciphertext* a, const Plaintext& b) const;
19 void AddInplace(Plaintext* a, const Plaintext& b) const;
20
21 Ciphertext Sub(const Ciphertext& a, const Ciphertext& b) const;
22 Ciphertext Sub(const Ciphertext& a, const Plaintext& b) const;
23 Ciphertext Sub(const Plaintext& a, const Ciphertext& b) const;
24 Plaintext Sub(const Plaintext& a, const Plaintext& b) const;
25 void SubInplace(Ciphertext* a, const Ciphertext& b) const;
26 void SubInplace(Ciphertext* a, const Plaintext& p) const;
27 void SubInplace(Plaintext* a, const Plaintext& b) const;
28
29 Ciphertext Mul(const Ciphertext& a, const Plaintext& b) const;
30 Ciphertext Mul(const Plaintext& a, const Ciphertext& b) const;
31 Plaintext Mul(const Plaintext& a, const Plaintext& b) const;
32 void MulInplace(Ciphertext* a, const Plaintext& b) const;
33 void MulInplace(Plaintext* a, const Plaintext& b) const;
34
35 // out = -a
36 Ciphertext Negate(const Ciphertext& a) const;
37 void NegateInplace(Ciphertext* a) const;
38};
39
40} // namespace heu::lib::algorithms::your_algo
只实现 Vectorized SPI#
Vectorized SPI 的功能与 Scalar SPI 一致的,变化的是函数形参的形式,从单个明文/密文传递变成了明文列表/密文列表传递。
当算法只实现 Vectorized SPI 的时候,Dispatcher 会将所有计算都转换成 SIMD 形式调用,具体来说:
如果用户执行的是标量计算,Dispatcher 会将参数转换成长度为 1 的列表调用 Vectorized SPI。
如果用户执行的是矩阵计算,Dispatcher 会将矩阵拆分转换成列表调用 Vectorized SPI。
假设用户要做两个矩阵的元素级(element-wise)运算,例如元素加法、元素减法或元素乘法,Dispatcher 会把 broadcast 之后的矩阵元素拆解成多个等分,每个等分封装成一个列表,并分配一个线程执行。一个列表内的元素会一次性传递给 Vectorized SPI 执行,从而算法可以获得足够大的并发空间。
如果是 Matmul 运算,则 Dispatcher 会把矩阵按行列转换成多个列表,并行调用 Vectorized SPI 完成计算。
Vectorized SPI 传递列表用的是 Span<T> 和 ConstSpan<T> 两种类型,其中:
Span<T> 表示一个只读数组,每个元素是指向 T 的指针
ConstSpan<T> 表示一个只读数组,每个元素是指向 T 的常量指针
Encryptor class:
1#include "heu/library/algorithms/clean_template/ciphertext.h"
2#include "heu/library/algorithms/clean_template/plaintext.h"
3#include "heu/library/algorithms/clean_template/public_key.h"
4#include "heu/library/algorithms/util/spi_traits.h"
5
6namespace heu::lib::algorithms::your_algo {
7
8class Encryptor {
9 public:
10 explicit Encryptor(const PublicKey& pk);
11
12 std::vector<Ciphertext> EncryptZero(int64_t size) const;
13 std::vector<Ciphertext> Encrypt(ConstSpan<Plaintext> pts) const;
14
15 std::pair<std::vector<Ciphertext>, std::vector<std::string>> EncryptWithAudit(
16 ConstSpan<Plaintext> pts) const;
17};
18
19} // namespace heu::lib::algorithms::your_algo
Decryptor class:
1#include "heu/library/algorithms/clean_template/ciphertext.h"
2#include "heu/library/algorithms/clean_template/plaintext.h"
3#include "heu/library/algorithms/clean_template/public_key.h"
4#include "heu/library/algorithms/clean_template/secret_key.h"
5#include "heu/library/algorithms/util/spi_traits.h"
6
7namespace heu::lib::algorithms::your_algo {
8
9class Decryptor {
10 public:
11 explicit Decryptor(const PublicKey& _, const SecretKey& sk);
12
13 std::vector<Plaintext> Decrypt(ConstSpan<Ciphertext> cts) const;
14 void Decrypt(ConstSpan<Ciphertext> in_cts, Span<Plaintext> out_pts) const;
15};
16
17} // namespace heu::lib::algorithms::your_algo
Evaluator class:
1#include "heu/library/algorithms/clean_template/ciphertext.h"
2#include "heu/library/algorithms/clean_template/plaintext.h"
3#include "heu/library/algorithms/clean_template/public_key.h"
4#include "heu/library/algorithms/util/spi_traits.h"
5
6namespace heu::lib::algorithms::your_algo {
7
8class Evaluator {
9 public:
10 explicit Evaluator(const PublicKey& pk);
11
12 void Randomize(Span<Ciphertext> ct) const;
13
14 std::vector<Ciphertext> Add(ConstSpan<Ciphertext> a,
15 ConstSpan<Ciphertext> b) const;
16 std::vector<Ciphertext> Add(ConstSpan<Ciphertext> a,
17 ConstSpan<Plaintext> b) const;
18 std::vector<Ciphertext> Add(ConstSpan<Plaintext> a,
19 ConstSpan<Ciphertext> b) const;
20 std::vector<Plaintext> Add(ConstSpan<Plaintext> a,
21 ConstSpan<Plaintext> b) const;
22
23 void AddInplace(Span<Ciphertext> a, ConstSpan<Ciphertext> b) const;
24 void AddInplace(Span<Ciphertext> a, ConstSpan<Plaintext> b) const;
25 void AddInplace(Span<Plaintext> a, ConstSpan<Plaintext> b) const;
26
27 std::vector<Ciphertext> Sub(ConstSpan<Ciphertext> a,
28 ConstSpan<Ciphertext> b) const;
29 std::vector<Ciphertext> Sub(ConstSpan<Ciphertext> a,
30 ConstSpan<Plaintext> b) const;
31 std::vector<Ciphertext> Sub(ConstSpan<Plaintext> a,
32 ConstSpan<Ciphertext> b) const;
33 std::vector<Plaintext> Sub(ConstSpan<Plaintext> a,
34 ConstSpan<Plaintext> b) const;
35
36 void SubInplace(Span<Ciphertext> a, ConstSpan<Ciphertext> b) const;
37 void SubInplace(Span<Ciphertext> a, ConstSpan<Plaintext> p) const;
38 void SubInplace(Span<Plaintext> a, ConstSpan<Plaintext> b) const;
39
40 std::vector<Ciphertext> Mul(ConstSpan<Ciphertext> a,
41 ConstSpan<Plaintext> b) const;
42 std::vector<Ciphertext> Mul(ConstSpan<Plaintext> a,
43 ConstSpan<Ciphertext> b) const;
44 std::vector<Plaintext> Mul(ConstSpan<Plaintext> a,
45 ConstSpan<Plaintext> b) const;
46
47 void MulInplace(Span<Ciphertext> a, ConstSpan<Plaintext> b) const;
48 void MulInplace(Span<Plaintext> a, ConstSpan<Plaintext> b) const;
49
50 // out = -a
51 std::vector<Ciphertext> Negate(ConstSpan<Ciphertext> a) const;
52 void NegateInplace(Span<Ciphertext> a) const;
53};
54
55} // namespace heu::lib::algorithms::your_algo
混合实现 Scalar/Vectorized SPI#
HEU Dispatcher 会在函数级别探测算法实现了哪些 SPI,因此在同一个 class 中混合实现(或者同时实现)两种 API 也是可以的,例如 Encryptor class 中的 EncryptZero 您可以只实现 Scalar SPI,而 Encrypt 方法同时实现两种 SPI,此时 Dispatcher 会在函数粒度上选择最合适的 SPI 调用。
1#include "heu/library/algorithms/clean_template//ciphertext.h"
2#include "heu/library/algorithms/clean_template/plaintext.h"
3#include "heu/library/algorithms/clean_template/public_key.h"
4#include "heu/library/algorithms/util/spi_traits.h"
5
6namespace heu::lib::algorithms::your_algo {
7
8class Encryptor {
9 public:
10 explicit Encryptor(const PublicKey& pk) {}
11
12 // Scalar SPI only
13 Ciphertext EncryptZero() const { YACL_THROW("To be implemented"); }
14
15 // Scalar SPI + Vectorized SPI
16 Ciphertext Encrypt(const Plaintext& m) const {
17 YACL_THROW("To be implemented");
18 }
19 std::vector<Ciphertext> Encrypt(ConstSpan<Plaintext> pts) const {
20 YACL_THROW("To be implemented");
21 }
22
23 // Vectorized SPI only
24 std::pair<std::vector<Ciphertext>, std::vector<std::string>> EncryptWithAudit(
25 ConstSpan<Plaintext> pts) const {
26 YACL_THROW("To be implemented");
27 }
28};
29
30} // namespace heu::lib::algorithms::your_algo
注册新算法#
算法主体开发完成后,还需要在 HEU 中注册新算法,注册的位置位于以下两个文件:
请依照文件中注释的引导注册您的算法,有 [SPI: Please register your algorithm here] 标记的位置就是需要修改的地方,总共有5处,其中 schema.h 有4处需要修改,schema.cc 有1处需要修改。
条件编译(可选)#
我们鼓励开发者编写可移植性强,平台依赖低的代码,以便您的算法可以被不同平台的用户所使用。一般而言,隐语至少要求算法支持 Linux、Mac(Intel)、Mac(arm)三个平台,但是对于某些涉及硬件加速的算法支持全平台确实存在困难,则可以考虑条件编译。
条件编译支持在编译时完全剥除您的算法,仅在支持的平台上包含您的算法,避免因为一个算法的加入导致整个 HEU 在某个平台上编译失败。
条件编译的步骤为:
在算法的入口头文件中定义
ENABLE_YOUR_ALGO宏,例如 ipcl.h在 schema.h/cc 注册算法时引用
ENABLE_YOUR_ALGO宏,即不要写死 true,这一步也可以参考 IPCL 的做法
测试和使用#
算法开发完毕后,您还需要编写一个编译脚本,HEU 使用的是 bazel 编译系统,您需要在算法同级目录 heu/library/algorithms/your_algo/ 下放置一个 BUILD.bazel 文件。
BUILD.bazel文件的写法可以参考 此处更多 Bazel 教程可以参考 Bazel 官方文档
Bazel 脚本编写完成后,请把您的算法作为依赖项加入到 schema.h/cc 对应的 BUILD.bazel 文件中。
您可以通过以下命令测试编译是否通过:
bazel test --verbose_failures heu/library/...
备注
所有代码开发完成后,请立即更新 CHANGELOGS 文件。
单元测试#
编写自己的单测(可选)#
HEU 单元测试使用 GoogleTest 框架,有关 GoogleTest 的用法请查阅 GoogleTest 官方文档
单元测试编写完毕后,您可以使用以下命令运行单元测试:
bazel test --verbose_failures heu/library/algorithms/your_algo/...
运行已有单测(必须)#
即使您没有编写任何单测,HEU 也会自动测试您的算法,单测请在 Python 3.8 环境下执行,请执行以下命令测试整个 HEU:
bazel test --verbose_failures heu/...
性能测试#
以下命令可以对您的算法做性能测试:
# 测试算法在 scalar 运算场景下的性能
# Test the performance of your algorithm in scalar computing scenarios
bazel run -c opt heu/library/benchmark:phe -- --schema=your_algo_name_or_alias
# 测试算法在矩阵运算场景下的性能
# Test the performance of your algorithm in matrix operation scenarios
bazel run -c opt heu/library/benchmark:np -- --schema=your_algo_name_or_alias
如果不加 --schema 参数,则默认运行所有算法的性能测试,以便您与其它算法对比性能:
bazel run -c opt heu/library/benchmark:phe
bazel run -c opt heu/library/benchmark:np
算法应用#
单元测试通过后,请执行以下命令编译、安装 Pip 包,以便在 Python 环境中使用您的新算法:
# Please switch to Python 3.8 environment
pip install pybind11 # Only need to run once
sh heu/pylib/reinstall.sh
基本使用:
1from heu import phe
2
3kit = phe.setup(phe.SchemaType.YOUR_ALGO, 2048)
4c1 = kit.encryptor().encrypt_raw(3)
5c2 = kit.evaluator().add(c1, c1)
6print(kit.decryptor().decrypt_raw(c2)) #6
祝贺
如果一切顺利的话,此时您的算法已经可以在隐语中使用了,并且隐语上层所有基于 PHE 的应用都可以无缝切换到您的算法上,例如 WOE、HESS-LR、SecureBoost 等等,相信这些上层应用一定可以从您的算法中获益,感谢您对隐语做出的贡献。