“Fidelius开发教程”的版本间差异
ChenminWang(讨论 | 贡献) |
|||
(未显示4个用户的100个中间版本) | |||
第4行: | 第4行: | ||
Fidelius 提供了自定义算法的能力。此处的算法用于分析数据,并将分析结果交付给用户。 | Fidelius 提供了自定义算法的能力。此处的算法用于分析数据,并将分析结果交付给用户。 | ||
Fidelius 的算法实现基于 C/C++ | Fidelius 的算法实现基于 C/C++11。一个算法是指一个运行在可信执行环境(TEE)中的代码,由于目前主要的支持硬件为 Intel SGX,因此编程方式符合 | ||
Intel SGX 的编程规范。 | Intel SGX 的编程规范。 | ||
第10行: | 第10行: | ||
== Hello World == | == Hello World == | ||
就像其他开发教程一样,我们先从写一个最简单的“Hello World”程序开始。 | |||
详见以下章节《如何基于Fidelius开发算法》进行简单开发和运行程序。 | |||
下面来看一个完整的 hello world 程序。 | 下面来看一个完整的 hello world 程序。 | ||
#include ”corecommon/ crypto / stdeth . h” | #include ”corecommon/crypto/stdeth.h” | ||
#include ”stbox/tsgx/log.h” | |||
#include ”ypc_t/analyzer/algo_wrapper.h” | |||
#include ”ypc_t/analyzer/macro.h” | |||
class hello_world { | |||
public: | |||
inline stbox::bytes do_parse(const stbox::bytes ¶m) { | |||
LOG(INFO) << ”hello world”; | |||
return param; | |||
} | |||
}; | |||
ypc::algo_wrapper<ypc::crypto::eth_sgx_crypto, ypc::noinput_data_stream, | |||
hello_world, | |||
ypc::local_result> pw; | |||
YPC_PARSER_IMPL(pw); | |||
ypc : : algo_wrapper<ypc : : crypto : : eth_sgx_crypto , | |||
ypc : : noinput_data_stream , | |||
hello_world , ypc : : | |||
pw ; | |||
YPC_PARSER_IMPL(pw ) ; | |||
== algo_wrapper == | == algo_wrapper == | ||
template<typename Crypto , typename DataSession , typename ParserT , | template<typename Crypto, typename DataSession, typename ParserT, | ||
typename Result, typename ModelT = void, | |||
typename Result , typename ModelT = void , | template<typename> class DataAllowancePolicy = ignore_data_allowance, | ||
template<typename> class ModelAllowancePolicy = ignore_model_allowance> | |||
template <typename> | class algo_warpper ; | ||
template <typename> | |||
代码来源<code>core/include/ypc_t/analyzer/algo_wrapper.h</code> | |||
:* Crypto:密码协议簇,目前支持<code>ypc::crypto::eth_sgx_crypto</code>,兼容以太坊。 | |||
:* DataSession:数据源方式,支持<code>noinput_data_stream, raw_data_stream, sealed_data_stream, multi_data_stream</code>。 | |||
:* ParserT:表示自定义的算法类。 | |||
:* Result:表示结果的类型,支持<code>local_result, onchain_result, offchain_result, forward_result</code>。 | |||
:* ModelT:表示模型的类型,是<code>ff::util::ntobject<...></code>。 | |||
:* DataAllowancePolicy:表示数据源的许可验证策略,支持<code>ignore_data_allowance, check_data_allowance</code>。 | |||
:* ModelAllowancePolicy:表示模型的许可验证策略,支持<code>ignore_model_allowance, check_model_allowance</code>。 | |||
通过对上述参数的选择和组合,可以支持不同的场景。 | 通过对上述参数的选择和组合,可以支持不同的场景。 | ||
第89行: | 第70行: | ||
== Iris 为例 == | == Iris 为例 == | ||
class enclave_iris_means_parser { | |||
public: | |||
public : | enclave_iris_means_parser(ypc::data_source ∗source):m_source(source){}; | ||
inline stbox::bytes do_parse(const stbox::bytes ¶m) { | |||
enclave_iris_means_parser ( ypc : : data_source | ypc::to_type<extra_nt_t> converter(m_source); | ||
transform_format trans(&converter); | |||
: m_source ( source ){}; | typedef hpda::algorithm::kmeans::kmeans_processor< | ||
hpda::ntobject<iris_data, species>, | |||
iris_data, double, iid> kmeans_t; | |||
kmeans_t km(&trans, 3, 0.001); | |||
ypc : : to_type<extra_nt_t> | hpda::output::memory_output<iid, kmeans_t::mean_point, kmeans_t::average_distance> mo(km.means_stream()); | ||
mo.get_engine()−>run(); | |||
transform_format | ... | ||
} | |||
typedef hpda : : algorithm : : kmeans : : kmeans_processor< | protected: | ||
ypc::data_source ∗m_source; | |||
hpda : : ntobject | }; | ||
kmeans_t ; | |||
kmeans_t km(&trans , 3 , 0 . | |||
hpda : : output : : memory_output < | |||
kmeans_t : : average_distance > | |||
mo(km. means_stream ( ) ) ; | |||
mo. get_engine()−>run ( ) ; | |||
. . . | |||
} | |||
protected : | |||
ypc : : data_source ∗m_source ; | |||
}; | |||
该算法可以表示为如下的图 | 该算法可以表示为如下的图 | ||
第143行: | 第98行: | ||
边的数据类型为 | 边的数据类型为 | ||
template<typename . . . ARGS> | <code> | ||
template<typename ...ARGS> ff::util::ntobject; | |||
</code> | |||
[[文件:开发者教程-图2.png|600PX|center]] | [[文件:开发者教程-图2.png|600PX|center]] | ||
== data_source == | == data_source == | ||
第157行: | 第111行: | ||
处理单元用于完成对数据的转换、处理等。目前系统已有的处理单元包括: | 处理单元用于完成对数据的转换、处理等。目前系统已有的处理单元包括: | ||
filter 用于过滤,仅输出满足条件的数据。 | :* filter 用于过滤,仅输出满足条件的数据。 | ||
concat 用于连接不同的数据,在一个输入源结束后,输出另一个输入源。 | :* concat 用于连接不同的数据,在一个输入源结束后,输出另一个输入源。 | ||
group 用于在指定的域相同的数据上完成相应的操作,这些操作包括取平均 (avg),求最大 (max),求最小(min),求和(sum),求数量(count)。 | :* group 用于在指定的域相同的数据上完成相应的操作,这些操作包括取平均 (avg),求最大 (max),求最小(min),求和(sum),求数量(count)。 | ||
split 用于将一个输入复制为多个输出。 | :* split 用于将一个输入复制为多个输出。 | ||
kmeans_processor 用于在指定的数据上完成 kmeans 聚类算法。 | :* kmeans_processor 用于在指定的数据上完成 kmeans 聚类算法。 | ||
== memory_output == | == memory_output == | ||
memory_output是一个将结果存放在内存的输出单元。可以使用如下方式遍历 | |||
所有的输出 | 所有的输出 | ||
for ( auto | for(auto it : mo.values()) { | ||
... | |||
. . . | } | ||
} | |||
== 自定义处理单元 == | == 自定义处理单元 == | ||
通常,系统内置的处理单元并不能满足特定的数据处理需求,因此,需要自定义处理单元。此时需要继承hpda中的处理单元基类,如下: | |||
typedef | typedef ff::util::ntobject<sepal_len,sepal_wid,petal_len,petal_wid,species> extra_nt_t; | ||
class transform_format | |||
: public hpda::processor::processor_base_t<extra_nt_t, user_item_t> { | |||
public: | |||
transform_format(::hpda::processor_with_output_t<extra_nt_t> *t) | |||
: hpda::processor::processor_base_t<extra_nt_t, user_item_t>(t) {} | |||
virtual bool process() { | |||
if (!has_input_value()) { | |||
return false; | |||
} | |||
auto t = base::input_value(); | |||
m_data.get<iris_data>().set<sepal_len, sepal_wid, petal_len, petal_wid>( | |||
t.get<sepal_len>(), t.get<sepal_wid>(), t.get<petal_len>(), | |||
t.get<petal_wid>()); | |||
m_data.set<species>(t.get<species>()); | |||
base::consume_input_value(); | |||
return true; | |||
} | |||
virtual user_item_t output_value() { return m_data; } | |||
protected: | |||
user_item_t m_data; | |||
}; | |||
= 高级 = | |||
== 直接访问上下文 == | |||
上下文是指算法在执行过程中的信息,包括该算法的 hash,与 keymgr 的会话等。可以在算法类中添加如下方法以获得上下文的指针 | |||
: | void set_context(ypc::analyzer_context ∗context){ | ||
m_context = context; | |||
} | |||
algo_wrapper会自动检测到该方法的存在,并编译生成相应的代码。 | |||
== 自定义输入数据格式 == | |||
Fidelius 内置了对于 CSV,mysql 的支持,可以通过定义一个简单的 JSON 描述文件完成文件的加密、读取。然而,对于自定义的文件格式,开发者需要额外的开发工作。 | |||
首先,该数据必须可以描述为一个 bytes 的数组,这在大多数场景下是适用的。例如,视频可以描述为一个数组,数组的每一个元素为一帧图像;一个图片可以表示为一个数组,数组的每一个元素为图片中的一个像素点。Fidelius 的算法每次读如数组中的一个或多个元素。受限于 Intel SGX 的内存限制,一个元素的大小通常不超过 128KB。 | |||
其次,对于自定义的数据格式,需要实现如下的方法,并编译为.so 的插件库。 | |||
void ∗create_item_reader(const char ∗extra_param, int len); | |||
int reset_for_read(void ∗handle); | |||
int read_item_data(void ∗handle, char ∗buf, int ∗len); | |||
int close_item_reader(void ∗handle); | |||
uint64_t get_item_number(); | |||
= 如何基于Fidelius开发算法 = | |||
== 下载 Fidelius 源码 == | |||
$ git clone https://github.com/YeeZTech/YeeZ-Privacy-Computing.git | |||
== 编译 Fidelius == | |||
$ cd YeeZ-Privacy-Computing | |||
$ ./build.sh compile-project prerelease | |||
'''注意''': 开发者可以根据需求修改编译选项,支持的编译模式有 Debug、PreRelease、Release。 | |||
Debug、Prelease 模式下会自动对一些 Enclave 进行签名。 | |||
但是,在 Release 编译选项下还需要手动地对一些 Enclave 签名,这些 Enclave 包括密钥管理的 Enclave libkeymgr.so 和算法样例的 Enclave libiris_parser.so。 | |||
手动签名操作需要依赖 openssl 工具,需事先安装 openssl ,签名步骤如下: | |||
=== 生成 RSA-3072 的私钥和公钥(仅 Release 模式下需要)=== | |||
$ cd YeeZ-Privacy-Computing/lib | |||
$ openssl genrsa -out private_key.pem -3 3072 | |||
$ openssl rsa -in private_key.pem -pubout -out public_key.pem | |||
生成的私钥文件为 private_key.pem,公钥文件为 public_key.pem,私钥文件用于对 Enclave 签名,公钥文件会 append 到 签名后的 Enclave 中。 | |||
=== 签名两个 Enclave(仅 Release 模式下需要)=== | |||
$ openssl dgst -sha256 -out keymgr_sig.hex -sign private_key.pem -keyform PEM keymgr_hash.hex | |||
$ openssl dgst -sha256 -out iris_parser_sig.hex -sign private_key.pem -keyform PEM iris_parser_hash.hex | |||
文件 keymgr_sig.hex 与 iris_parser_sig.hex 为私钥签名后的文件。 | |||
=== 生成签名后的 Enclave(仅 Release 模式下需要)=== | |||
$ /opt/intel/sgxsdk/bin/x64/sgx_sign catsig -enclave libkeymgr.so -config ../keymgr/default/enclave/ekeymgr.config.xml -out keymgr.signed.so -key public_key.pem -sig keymgr_sig.hex -unsigned keymgr_hash.hex | |||
$ /opt/intel/sgxsdk/bin/x64/sgx_sign catsig -enclave libiris_parser.so -config ../example/iris/analyzer/enclave/enclave.config.xml -out iris_parser.signed.so -key public_key.pem -sig iris_parser_sig.hex -unsigned iris_parser_hash.hex | |||
签名后的 Enclave 实际上包括:算法原始文件、Enclave 配置文件、RSA-3072公钥、签名文件、算法哈希文件。 | |||
== 运行算法示例 == | |||
$ cd YeeZ-Privacy-Computing/test/integrate | |||
$ cd js && npm install && cd .. | |||
$ python3 test_iris.py | |||
= Q&A = | = Q&A = |
2023年3月8日 (三) 07:33的最新版本
介绍
简介
Fidelius 提供了自定义算法的能力。此处的算法用于分析数据,并将分析结果交付给用户。
Fidelius 的算法实现基于 C/C++11。一个算法是指一个运行在可信执行环境(TEE)中的代码,由于目前主要的支持硬件为 Intel SGX,因此编程方式符合 Intel SGX 的编程规范。
本文介绍的算法开发基于 Fidelius 的最新版本,请检查自己的版本。
Hello World
就像其他开发教程一样,我们先从写一个最简单的“Hello World”程序开始。
详见以下章节《如何基于Fidelius开发算法》进行简单开发和运行程序。
下面来看一个完整的 hello world 程序。
#include ”corecommon/crypto/stdeth.h” #include ”stbox/tsgx/log.h” #include ”ypc_t/analyzer/algo_wrapper.h” #include ”ypc_t/analyzer/macro.h” class hello_world { public: inline stbox::bytes do_parse(const stbox::bytes ¶m) { LOG(INFO) << ”hello world”; return param; } };
ypc::algo_wrapper<ypc::crypto::eth_sgx_crypto, ypc::noinput_data_stream, hello_world, ypc::local_result> pw; YPC_PARSER_IMPL(pw);
algo_wrapper
template<typename Crypto, typename DataSession, typename ParserT, typename Result, typename ModelT = void, template<typename> class DataAllowancePolicy = ignore_data_allowance, template<typename> class ModelAllowancePolicy = ignore_model_allowance> class algo_warpper ;
代码来源core/include/ypc_t/analyzer/algo_wrapper.h
- Crypto:密码协议簇,目前支持
ypc::crypto::eth_sgx_crypto
,兼容以太坊。
- Crypto:密码协议簇,目前支持
- DataSession:数据源方式,支持
noinput_data_stream, raw_data_stream, sealed_data_stream, multi_data_stream
。
- DataSession:数据源方式,支持
- ParserT:表示自定义的算法类。
- Result:表示结果的类型,支持
local_result, onchain_result, offchain_result, forward_result
。
- Result:表示结果的类型,支持
- ModelT:表示模型的类型,是
ff::util::ntobject<...>
。
- ModelT:表示模型的类型,是
- DataAllowancePolicy:表示数据源的许可验证策略,支持
ignore_data_allowance, check_data_allowance
。
- DataAllowancePolicy:表示数据源的许可验证策略,支持
- ModelAllowancePolicy:表示模型的许可验证策略,支持
ignore_model_allowance, check_model_allowance
。
- ModelAllowancePolicy:表示模型的许可验证策略,支持
通过对上述参数的选择和组合,可以支持不同的场景。
HPDA
算法的主体通常使用 HPDA(High Performance Data Analysis)完成。HPDA 算法由输入(input)、处理单元(processor)、输出(output)三种不同的功能单元 组成,其中输入、处理有一个或多个,输出仅有一个。
一个 HPDA 算法可以表示一个如下的有向图,其中节点表示功能单元,即输入、处理单元或输出,边表示数据。自然地,输入只有出边,输出只有入边, 处理单元有一个或多个入边,且有一个或多个出边。
算法的执行过程一般分为连个阶段:1)构造 HPDA 的图;2)运行算法。算法一旦开始运行,则会开始读取输入、产生输出,直到所有的输入读取完成。
Iris 为例
class enclave_iris_means_parser { public: enclave_iris_means_parser(ypc::data_source ∗source):m_source(source){}; inline stbox::bytes do_parse(const stbox::bytes ¶m) { ypc::to_type<extra_nt_t> converter(m_source); transform_format trans(&converter); typedef hpda::algorithm::kmeans::kmeans_processor< hpda::ntobject<iris_data, species>, iris_data, double, iid> kmeans_t; kmeans_t km(&trans, 3, 0.001); hpda::output::memory_output<iid, kmeans_t::mean_point, kmeans_t::average_distance> mo(km.means_stream()); mo.get_engine()−>run(); ... } protected: ypc::data_source ∗m_source; };
该算法可以表示为如下的图
图中的边—数据类型
在 HPDA 所表示的有向图中,一条边可以表示为 (A, B),我们称 A 是 B 的输入源,A 的输出数据类型和 B 的输入数据类型必须一致,否则会产生编译错误。我们将 A 的输出数据类型(或 B 的输入数据类型)称为边 (A, B) 上的数据类型。
边的数据类型为
template<typename ...ARGS> ff::util::ntobject;
data_source
data_source 是一个表示输入的虚基类,其输出为 bytes 类型。一般后面会跟一个 converter,用于将 bytes 转换为自定义的结构。
处理单元(processor)
处理单元用于完成对数据的转换、处理等。目前系统已有的处理单元包括:
- filter 用于过滤,仅输出满足条件的数据。
- concat 用于连接不同的数据,在一个输入源结束后,输出另一个输入源。
- group 用于在指定的域相同的数据上完成相应的操作,这些操作包括取平均 (avg),求最大 (max),求最小(min),求和(sum),求数量(count)。
- split 用于将一个输入复制为多个输出。
- kmeans_processor 用于在指定的数据上完成 kmeans 聚类算法。
memory_output
memory_output是一个将结果存放在内存的输出单元。可以使用如下方式遍历
所有的输出
for(auto it : mo.values()) { ... }
自定义处理单元
通常,系统内置的处理单元并不能满足特定的数据处理需求,因此,需要自定义处理单元。此时需要继承hpda中的处理单元基类,如下:
typedef ff::util::ntobject<sepal_len,sepal_wid,petal_len,petal_wid,species> extra_nt_t; class transform_format : public hpda::processor::processor_base_t<extra_nt_t, user_item_t> { public: transform_format(::hpda::processor_with_output_t<extra_nt_t> *t) : hpda::processor::processor_base_t<extra_nt_t, user_item_t>(t) {} virtual bool process() { if (!has_input_value()) { return false; } auto t = base::input_value(); m_data.get<iris_data>().set<sepal_len, sepal_wid, petal_len, petal_wid>( t.get<sepal_len>(), t.get<sepal_wid>(), t.get<petal_len>(), t.get<petal_wid>()); m_data.set<species>(t.get<species>()); base::consume_input_value(); return true; } virtual user_item_t output_value() { return m_data; } protected: user_item_t m_data; };
高级
直接访问上下文
上下文是指算法在执行过程中的信息,包括该算法的 hash,与 keymgr 的会话等。可以在算法类中添加如下方法以获得上下文的指针
void set_context(ypc::analyzer_context ∗context){ m_context = context; }
algo_wrapper会自动检测到该方法的存在,并编译生成相应的代码。
自定义输入数据格式
Fidelius 内置了对于 CSV,mysql 的支持,可以通过定义一个简单的 JSON 描述文件完成文件的加密、读取。然而,对于自定义的文件格式,开发者需要额外的开发工作。
首先,该数据必须可以描述为一个 bytes 的数组,这在大多数场景下是适用的。例如,视频可以描述为一个数组,数组的每一个元素为一帧图像;一个图片可以表示为一个数组,数组的每一个元素为图片中的一个像素点。Fidelius 的算法每次读如数组中的一个或多个元素。受限于 Intel SGX 的内存限制,一个元素的大小通常不超过 128KB。
其次,对于自定义的数据格式,需要实现如下的方法,并编译为.so 的插件库。
void ∗create_item_reader(const char ∗extra_param, int len); int reset_for_read(void ∗handle); int read_item_data(void ∗handle, char ∗buf, int ∗len); int close_item_reader(void ∗handle); uint64_t get_item_number();
如何基于Fidelius开发算法
下载 Fidelius 源码
$ git clone https://github.com/YeeZTech/YeeZ-Privacy-Computing.git
编译 Fidelius
$ cd YeeZ-Privacy-Computing $ ./build.sh compile-project prerelease
注意: 开发者可以根据需求修改编译选项,支持的编译模式有 Debug、PreRelease、Release。
Debug、Prelease 模式下会自动对一些 Enclave 进行签名。
但是,在 Release 编译选项下还需要手动地对一些 Enclave 签名,这些 Enclave 包括密钥管理的 Enclave libkeymgr.so 和算法样例的 Enclave libiris_parser.so。
手动签名操作需要依赖 openssl 工具,需事先安装 openssl ,签名步骤如下:
生成 RSA-3072 的私钥和公钥(仅 Release 模式下需要)
$ cd YeeZ-Privacy-Computing/lib $ openssl genrsa -out private_key.pem -3 3072 $ openssl rsa -in private_key.pem -pubout -out public_key.pem
生成的私钥文件为 private_key.pem,公钥文件为 public_key.pem,私钥文件用于对 Enclave 签名,公钥文件会 append 到 签名后的 Enclave 中。
签名两个 Enclave(仅 Release 模式下需要)
$ openssl dgst -sha256 -out keymgr_sig.hex -sign private_key.pem -keyform PEM keymgr_hash.hex $ openssl dgst -sha256 -out iris_parser_sig.hex -sign private_key.pem -keyform PEM iris_parser_hash.hex
文件 keymgr_sig.hex 与 iris_parser_sig.hex 为私钥签名后的文件。
生成签名后的 Enclave(仅 Release 模式下需要)
$ /opt/intel/sgxsdk/bin/x64/sgx_sign catsig -enclave libkeymgr.so -config ../keymgr/default/enclave/ekeymgr.config.xml -out keymgr.signed.so -key public_key.pem -sig keymgr_sig.hex -unsigned keymgr_hash.hex $ /opt/intel/sgxsdk/bin/x64/sgx_sign catsig -enclave libiris_parser.so -config ../example/iris/analyzer/enclave/enclave.config.xml -out iris_parser.signed.so -key public_key.pem -sig iris_parser_sig.hex -unsigned iris_parser_hash.hex
签名后的 Enclave 实际上包括:算法原始文件、Enclave 配置文件、RSA-3072公钥、签名文件、算法哈希文件。
运行算法示例
$ cd YeeZ-Privacy-Computing/test/integrate $ cd js && npm install && cd .. $ python3 test_iris.py