|隐私数据在隐私AI框架中的安全流动( 四 )


P1 本地计算 Z1 = E * B1 + A1 * F+ C1 + E * F 。
正确性可以通过以下恒等式加以验证:
|隐私数据在隐私AI框架中的安全流动
本文插图

|隐私数据在隐私AI框架中的安全流动
本文插图

|隐私数据在隐私AI框架中的安全流动
本文插图

输出:P0,P1 分别持有 Z = X * Y的秘密分享值 Z0,Z1 。
我们可以看到 , 从输入到计算再到输出 , 整个过程中 , 没有泄漏任何隐私数据 。
整个算子计算结束时 , P0单独拥有X0,Y0,A0,B0,C0,E0,F0,E1,F1,E,F,Z0 , P1单独拥有X1,Y1,A1,B1,C1,E0,F0,E1,F1,E,F,Z1,P2单独拥有A0,B0,C0,A1,B1,C1 , 三方都无法单独得到 X 或 Y 或 Z 。
最后 , 我们总结一下(1~5 对应着上面的步骤):
|隐私数据在隐私AI框架中的安全流动
本文插图

|隐私数据在隐私AI框架中的安全流动
本文插图

获取明文结果
从输入到计算再到输出 , 各节点只能看到本地所单独拥有的秘密分享值(一些毫无规律的64位随机数) , 从上文也可以看到 , 节点是无法独自得到任何隐私数据的 。 那么 , 当计算结束后 , 如果想得到结果的明文 , 怎么获取呢?
Rosetta提供了一个恢复明文的接口 , 使用很简单 。
res = sess.run(rtt.SecureReveal(z, receive_party=4)) # 'b0100' means Charleyprint('z:', res)那这个 rtt.SecureReveal 是如何工作的呢?
其实非常简单:如果本方想要知道明文 , 只需要对方把他的秘密分享值发给我 , 然后本地相加即可 。
比如 , 上一节结尾处 , 我们知道了各方(P0/P1/P2)的分享值如下:
结合实例 , Charley(P2)想要获取结果明文 , 那么 , P0,P1将各自的分享值Z0,Z1发给P2 , 然后P2本地相加即可 。 即: 1db35d14c12a + ffffe24ca30611b0 = 1ad2da(1757914) 。 我们将此值进一步再转换为浮点数 , 就可以得到我们想要的用户态的明文值了:1757914 / (1 << 18)=6.7059097
|隐私数据在隐私AI框架中的安全流动
本文插图

效率优化
上面介绍的秘密分享 与 multiply 算子都是基于最基本的算法原理 , 在时间上和通信上的开销还是比较大的 , 在实际工程实现中 , 还可以进一步进行优化以提升性能 。 在介绍优化方案之前 , 先了解一下什么是PRF[3]?
PRF[3] , Pseudo-Random Function 。 简单来说 , 即给定一个随机种子 key , 一个计数器 counter ,执行 PRF(key, counter), 会得到一个相同的随机数 。
补充说明一下 。 例如 ,P0,P1 执行 PRF(key01,counter01) , 会产生一个相同的随机数 。 这里的 key01 ,counter01 对于的 P0,P1 来说是一致的 。 程序会维护这两个值 , 对使用者来说是透明
关于秘密分享
来看看优化版本(假设:P0有一个私有数据x) 。
方案2(优化版):P0,P1 使用 PRF(Key01,counter) 本地同时生成一个相同的随机数r 。 然后P0本地设置x0=x-r , P1本地设置x1=r 。 此时 share(x) = (x0,x1)x=(x = r.r) , 与定义一致 。 此版本无需 P2 的参与 。 没有通信开销 。
目前 Rosetta 开源版本中使用的正是此方案 。
方案3(优化版):P0 设置 x0 = x-r ,P1 设置 X1 = 0 即可 。 此时 share(x) = (x, 0) 。 此版本无需 P2 的参与 。 没有通信开销 , 也没有计算开销 。
上述各个版本(包括方案1) , 都是可行且安全的 。 可以看到P1/P2并不知道 P0 的原始输入值 。


推荐阅读