Rathee 三部曲:CrypTFlow2,SIRNN与SecFloat
Background
Notation: 记安全参数为 , 秘密共享的整数长度为 -bit.
MPC方面,假设我们拥有以下基础知识:
- 可以使用基于公钥密码等方式实现 1-out-of- ,单次通信量 ,使用 IKNP 可以做到 。
- 基于 2-party 的加法 secret sharing 的加法与标量乘几乎免费(无通信开销)。
- 可以使用 beaver triple 实现 2-party 的安全乘法操作。一次安全乘法,总通信量为 。
在浮点数储存方面,我们使用 IEEE754 Float32(单精度标准):
- 符号位 1位、指数为 7 位、尾数 为 23 位、
- 四舍六入五成双(round-to-nearest-ties-to-even)
IEEE 754 加法
不失一般性,设 ,为了计算 ,其中:
则步骤如下:
- 指数对齐。若 > ,将 的尾数右移: .
- 尾数相加。考虑的符号情况。若同号,则计算 。若异号,则用大者减小者得到差的绝对值,并附上正确的符号。
- 规范化。显然 ,故右移最多只需一次(对应指数加1即可)。若 ,则直接跳到第四步。其余情况反复左移并自减,直到 时停止。
- 写入结果。最后将 的首位 1 剔除,将最后的 重新作为新的 32 位浮点结果。
- 在 、、以及对右移一位这四个步骤中,会涉及到舍入问题。
IEEE 754 乘法
乘法由于不涉及数的大小比较,因此步骤相对比较简单。
- 符号位异或。.
- 指数相加。.
- 尾数相乘。,结果在 范围内。
- 规范化。如果结果 ,将 自增并将 右移一位,最后剔除 最高位的 1.
- 在 与 右移一位这两步涉及到舍入操作。
舍入方法
我们令 分别为 的最低位(decision bit),被丢弃的最高位(guard bit)和被丢弃的剩余位(sticky bit)。则“四舍六入五成双”的规则可以形式化表述为如下公式:
简单的解释一下,就是只有当 时才可能进位。此时如果 (肯定大于0.5)或者 (刚好但要是偶数),则进位()。
注意舍入之后有可能需要再次规范化。
Building Blocks
下面用自底向上的方式,讲清楚整个协议是如何构建的。
我们需要实现的理想功能是,MUX(b,x)=(b==1?x:0)。即对于布尔 secret share 和算术 secret share ,输出 。步骤如下:
SETUP: 持有 , 持有 。
- 与 各自生成随机数 。
- 根据 的值设置 。若 ,令 ,否则为 。
- 作为发送方与 做一轮 1-out-of-2 OT, 提供两条消息 , 提供 并最终获得 。
- 根据 的值设置 。若 ,令 ,否则为 。
- 再做一轮 OT, 获得 。
- 贡献 , 贡献 。加起来是 。
我们列举出四种可能的 情况:
,
|
|
|
|
|
| 0 |
0 |
|
|
0 |
| 0 |
1 |
|
|
|
| 1 |
0 |
|
|
|
| 1 |
1 |
|
|
0 |
结果刚好等于 ,因此协议正确。
通信量为两轮 IKNP-OT 的开销,也就是 。但 CryptoFlow2 的 3.1.1 节可以将通信开销优化到 。
这是显然的,直接使用 beaver triple 实现,通信量为 。(见 CryptoFlow2 的附录 A1 节)
我们有 。因此令 ,因此每一方直接计算 即可。通信量与 相同,也为 。
我们将 按照 -bit 进行分块。考虑对某个块 进行比较。我们使用 1-out-of- OT:
- 随机选取 ,并对 准备消息 .
- 将 个消息作为 OT 的输入, 输入 ,并获得 .
- 当且仅当 时,我们有 ,这就完成了 的秘密EQ判断。
我们可以通过树状结构与 ,计算 ,将 -bit 比较拓展到 -bit,最后完成整个长度的比较。通信量为 ,轮数为 .
仍然是分块的思路,首先计算块长度为 的结果 ,还是可以使用 1-out-of- OT 完成。 只需随机生成 , 并准备 个消息 .
然后合并的时候高位优先,.
通信成本小于 ,取 时为 ,轮数 .
假设 LUT 有 项,每一项有 -bit.
SETUP: 随机取索引 ,和 LUT 混淆后的 share .
- 对每个 ,构造 .
- 将这 条长度为 -bit 的消息与 (选取 )作 1-out-of- ,成本为 .
- 令 。现在 持有 , 持有 .
- 在线阶段, 发送 , 发送 ,双方同时计算 . 最后 分别保存 ,显然合起来是 。通信成本 可忽略。
如果我们要计算 ,这等同于计算 。因此直接使用 即可。
分别持有布尔共享的一位 ,并最后得到 且 。
- 随机选取 ,生成二元组 , 并与 (持有输入 )执行 1-out-of-2 , 拿到结果 。 设置 .
- 双方本地线性修正, 计算 , 计算 .
验证一下结果,。
- 当 时,,故 。
- 当 时,,故 . 但此时 ,因此 仍然成立。
通信开销为一次 1-out-of-2 ,成本为 .
我们有了前面的 和 ,构造 便是自然的事情。对于 -bit 的加法共享,我们尝试将其零扩展到 -bit 。首先,我们要 check 这两个 share 是否有进位(使用 ),然后将得到的布尔进位 使用 转为 的算术值。
但我们只是做零扩展操作,两个 share 相加,不能在第 位产生进位,因此双方要在 减掉一个 的 share,由于 公开,可以直接本地完成。
成本为 .
既然我们有了从小到大的 ,那自然也有反过来的 。我们假设从 -bit 截断低位的 -bit,并输出最终的高位 -bit(这本质上也等同于 x >> s):
SETUP: 将原来的 share 拆成 ,前者为 位,后者为 位,可以证明:
开销为 .
与用 beaver triple 的安全乘法比较相近,但区别是后者 和 都知道 和 的一部分 share。而 的适用条件是 独占 , 独占 ,最后各自获得长度 的 的 share.
- 将自己的 写成二进制 .
- 对 , 调用 1-out-of-2 : 持有 , 持有 ,生成 满足 .
- 双方本地计算 ,显然两个share加起来就是乘积.
总通信成本为 .
语义是双方持有 ,目标是计算 .
这里还是不能直接做 beaver triple,因为它是 ring agnostic 的,可能会带来未知的 wrap 问题。当然一个办法是将 都扩展到足够大的环,做安全乘法后又截断回去。当然这里有两个问题,一是 是高位截断而不是低位截断,协议需要稍微改一改;二是这种办法运算成本较高,不如接下来介绍的专用 协议。
我们现在要计算,前两者可以分别由 离线完成。而后两者就涉及到刚才的 了。总之,我们有了在 存储的完整 , 存储的 ,以及双方都拥有的 的一部分。 的作用就是把这 4 项安全地拼起来,并且处理 wrap 的问题。
- 首先,我们要考虑 是否溢出的问题,因此需要 进行检测得到两个溢出位 。
- 然后我们可以利用 计算
g=w_y?x:0 与 h=w_x:y:0的 share ,处理可能的溢出差值。
- 最终 输出 .
尝试把两个 share 加起来看看:
而
合在一起,
,刚好抵消。
设,论文给出了具体的开销,数量级为 。相比于 beaver triple 做法(含生成乘法三元组)的 数量级相同,但论文声称 通信少 .
与 原理类似,通信量完全相同,这里就略过了。
作用是将一个 -bit 的 share ,按 -bit 分块,拆分成 个 ,其中 . 显然,本地进行这个操作会导致 wrap 问题。
当然解决这个问题的思路也容易想到,首先使用 判断 的两个 share 是否溢出,并得到溢出结果的 share ,令 ,并将目前双方的高位 加上 ,最后递归执行这一过程即可。
复杂度相当于 次 的成本,也就是 .
检测第 个 -bit 分块中最高非零位对应的全局 index,也可以理解成 ,这里的具体实现方法是直接 解决。但有一个问题是当 时右边这个式子与左边实现的语义并不相同,作者这里选择让 的结果未定义,交给上层协议来解决。
这一点我不是很理解, 的情况也可以在 的情况直接解决啊,又不会增加通信成本。
总之,通信量等价于一个 1-out-of- 实现,因此通信量为 .
前者的意思是判断一个长度为 的向量(或者 -bit 数)是否为零。而后者是将一个 的 share,转化为一个长度为 的向量 share,满足和为 ,其中仅有第 个向量为 1.
这两个分别可以用 1-out-of- 和 1-out-of- 实现,成本分别为 和 。虽然后者成本较高,但只在父协议 的最后调用一次,因此总成本还能接受。
含义是给定一个输入 ,计算出 的最高零位的值 ,输出向量 share 满足和为 ,其中仅有第 个向量为 1。 为了简单起见,这里我假设 是定义良好的(也就是处理了 的情况)。令 :
- 首先计算输入 做 得到 ,然后对每个 调用 得到 .
- 调用 得到布尔分享 ,用 (也就是 beaver triple)对于任意 做 . 这样 . 这样最高非零位的值便是满足 的从大到小的最后一个。
- 对最高位 digit,令 ,对于剩下 ,.
- 最后本地算出 ,并调用 得到最终的布尔向量 。
总结
| 原语 |
依赖的原语 |
功能 |
通信开销 |
|
|
长度位的三目运算符 |
|
|
|
逻辑或 |
|
|
|
长度位的算术相等 |
|
|
|
长度位的算术比较 |
|
|
|
长度位,结果 位的查找表 |
|
|
|
位零扩展到 位 |
|
|
|
位截取高 位 |
|
|
|
长度位的无/有符号乘 |
|
|
|
长度位的最高非零位index |
|
Primitives
我们终于构建了 secfloat 论文对应的所有基础协议,现在我们转入本篇论文构造的重要原语。
用来检查浮点数在浮点参数为 (IEEE754 默认 )的情况下是否上溢或者下溢。这是本文的一个创新点,就是可以自己选取浮点参数,不一定遵照 IEEE754 的格式。
α = (z, s, e, m)
if 1{e > 2^{p-1} - 1} then
m = 2^q; e = 2^{p-1}
if 1{z = 1} ∨ 1{e < 2 - 2^{p-1}} then
m = 0; e = 1 - 2^{p-1}; z = 1
Return (z, s, e, m)
这个逻辑符合论文对溢出的处理方式,这里大概描述一下协议是怎么跑的:
- 第二行由于 是公开的,这是一个 操作;第四行还有一个 操作。
- 第三行和第五行的赋值操作,由于赋值常数是公开的,不存在隐私问题,因此直接 即可。
- if 分支本质就是 的具体语义。
在 的基础上,增加了对浮点数粘滞位 的进位判定。当被舍去的低 位只要有任意一位为 ,且 ,则执行进位操作。其功能等同于 (x >> s) | (x & (2**s - 1) != 0)。右移还是用 搞定(或者说是结合 与 ),但之后还要做一个 的操作,成本较高。
由于 也等价于判断两个 share 的和是否为 ,或者两者都为,因此其本质也是比较操作(或者 操作)。因此作者选择将 和 捆绑实现成 ,成本仅仅相当于一个比较操作和一个 的成本(这里论文有 typo,不是两个 的成本)。
等价于带舍入的右移 。在前文中我们讲过 IEEE 的舍入逻辑如下:
我们首先用 TRS(x, r-2) 确认 后面的位是否为 ,如果是就加回来以确保粘滞位判定逻辑正确。此时 的后三位就分别是。然后用 将这个 8-bit 的舍入逻辑硬编码到结果 中。最后再调用 TR(x, 2) 并加上舍入结果 即可。
给出浮点数 中的 ,将已规格化的尾数 从 位精度舍入到 位精度,并处理舍入导致的进位溢出。注意这里 采用了定点数形式,也就是尾数 对应定点数 。因此 的取值范围是 。协议逻辑如下所示:
if 1{m >= 2^{Q+1} − 2^{Q−q−1}} then
Return (e+1, 2^q)
else
Return (e, m >>R (Q−q))
以下对该代码逻辑的正确性进行说明。尾数分为两种情况:
- 其一,当尾数的前 位都为1,并且第 位(也就是对应了 )也为1时。这刚好满足 RNTE 的最小舍入条件,因此这整个 位都将舍入到 。做一次规范化后 ,并且尾数 回到原来的 位精度规范化形式 。
- 其二,当不存在四舍五入后需要再次规范化的大多数情况,直接按照先前的 协议进行带舍入右移 位即可。
在前置知识一节,我们已经概括了普通浮点加法的办法。下面我们将该方法与伪代码对应:
- 指数对齐。若 > ,将 的尾数左移: .
- 尾数相加。考虑的符号情况。若同号,则计算 。若异号,则用大者减小者得到差的绝对值,并附上正确的符号。
这里考虑同号与异号的情况。同号时, 与 的符号位异或为 ,否则为 。因此当 时,将 变号完成相减操作。
- 规范化。这里我们找到加法结果 中最高有效位,为了保证有足够的空间舍入,协议在第一步时将精度为 的 零扩展为 位(但注意精度是 )。假设 MSNZB 的结果为 ,要将高位的规范化(也就是对齐后面 Round* 协议的 位)需要左移 位,对应乘 。
至于指数位的计算,由于先左移了 位,同时又因为精度变化又增加 ,因此 。
- 写入结果。最后做一轮 的舍入,由于 ,符号位一定由 决定。最后检查规范化的结果是否在浮点数有效范围内。
- 符号位异或。.(第7行)
- 指数相加。.(第1行,论文这里令 )
- 尾数相乘。,结果在 范围内。(第2行)
- 规范化。如果结果 ,将 自增并将 右移一位,最后剔除 最高位的 1.
结果 走第 6 行 else 分支,否则走第四行 if 分支。
- 在 与 右移一位这两步涉及到舍入操作。(也就是第4、6行的 )
Math Functions
在《学数学,就这么简单》这本书中,有这样一个桥段:
基于这样的思路,我们实际上可以细化出计算机是如何计算 的。
- 首先利用三角函数诱导公式,将 的范围缩减到 . (Range reduction)
这是论文的做法,但实际上舍入到 再利用二倍角公式,误差会小一个数量级。
- 使用泰勒展开进行近似,例如 . (Polynomial Evaluation)
- 使用秦九韶算法 ,减少乘法次数并降低误差。
这些运算就只需要浮点加法和乘法可以搞定,至于多项式的系数由于相对固定,可以通过查表解决。
我们现在对应到具体的原语 上,也就是计算 。步骤如下:
- 首先处理特殊情况:当 时,由于 ,超过了尾数的精度,因此 一定是整数。。另一种特殊情况是当 时,,其误差在 本身浮点表示的误差以内。
- range reduction 步骤:目的是从输入 算出奇偶位 以及区间 。令 ,若 ,则 ,否则 。故 .
m = α.m * 2^{α.e + 14}
a = TR(m, q+14); n = m mod 2^{q+14}
将 变成一个整数。由于 ,故 . 则 为 的整数部分, 为 的小数部分。
f = (n > 2^{q+13} ? 2^{q+14} - n : n)
k,K = MSNZB(f); f = f * K
z = 1{f=0}; e = (z ? -2^{p-1}+1 : k - q - 14)
第一行保证 。第二行找到 的最高有效位,从而将 规范化,并在第三行设定正确的指数位 。
当 时,。将零位 设为 ,并按照论文约定将指数位 设为 .
δ = (z, 0, e, TR(f, q+14−Q))
最后重新将定点数 以 位精度存储,与其他参数合并成浮点数 。
- polynomial evaluation 步骤,注意论文使用了 Remez 等精度更高的逼近方法,而不是泰勒展开:
if 1{δ.e < −14} then
µ = Float_{p,Q}(π) ⊗_{p,Q} δ
先前 时的特殊情况,.
if 1{δ.e < −5} then
idx1 = δ.e + 14 mod 2^4
(θ1, θ3, θ5) = GetC_{4,9,5,p,Q}(idx1, Θ1_sin, K1_sin)
考虑 时的情况,这里 GetC 函数提供了一个查表操作,通过选取 K1_sin 这一常数类,在 Θ1_sin 这个表中,通过 index idx1(也就是 δ.e+14 的低4位)获取对应的参数 (θ1, θ3, θ5)。
4 是表的 bit 数,9 是 spline(系数对)的个数,5 是拟合的多项式的次数。
idx2 = 32 · (δ.e + 5 mod 27)
idx2 = idx2 + ZXt(TR(δ.m, Q−5) mod 32, 7)
idx2 = 1{δ.e = −1} ? 127 : idx2
(θ1, θ3, θ5) = GetC_{7,34,5,p,Q}(idx2, Θ2_sin, K2_sin)
在 这个区间,用δ.e + 5的低2位和δ.m的高5位进行查表的区分(这是论文的第二个创新点:分段索引),来调用对应误差最小的 (θ1, θ3, θ5)。对 的区间,进行特判。
- 使用秦九韶算法求出对应的浮点值:
Δ = δ ⊗_{p,Q} δ
µ = ((θ5 ⊗ Δ) ⊞* θ3) ⊗ Δ
µ = (µ ⊞* θ1) ⊗ δ
Return (µ.z, a ⊕ α.s, Round*(µ.e,µ.m))
前文说过:,故 a ⊕ α.s 确定符号位,最后舍入即可得到最终结果。
大概总结一下, 先对输入做范围归约,把问题化到0到0.5的小区间并记录符号补偿;对极大或极小输入走快速分支。随后按指数和尾数位确定分段,用查表选出对应多项式系数,采用秦九韶算法计算近似值,最后做符号恢复并舍入到单精度输出。
这个方法一个潜在的缺点是,这个 GetC 函数用的表是 ad-hoc 的,与前面浮点四则运算的协议不同,如果想要任意指定,或者迁移到 SecDouble,这个系数表需要进行重新计算才能使得误差小于 1ULP。论文为了追求性能与准确度,牺牲了算法的灵活性。
令 ,令 ,则分为两种情况:
- ,令 ,则 ,则 .
- ,此时若 ,会趋近于零,两个大数相减,精度将会受到影响。因此作者在此变形为 。令 ,则 ,再计算 ,这样就不存在两数相减导致的误差问题了。
然后,分别按照 与 构造两组近似多项式 ,,还是按照分段索引法对指定范围的 与 进行查表。
然后通过秦九韶算法(Horner's method)计算出 的值。对于最后加 的计算,首先通过查表方式将 转化为浮点数,再调用 协议即可。
- range reduction 步骤:
a = 1{N = −1}
f = a ? (2^{q+1} − α.m) : (α.m − 2^q)
k,K = MSNZB(f); f = f *_{q+1} K
e = a ? (k − q − 1) : (k − q)
当 时(此时 ),,否则(此时 ),并将 规范化。
z = 1{f = 0}; e = (z ? −2^{p−1}+1 : e);
N = α.e; δ = (z,0,e, f *_{Q+1} 2^{Q−q})
通过 构造出浮点数 ,并将精度从 位提高到 位,为后文 evaluation 准备。
- polynomial evaluation 步骤
if 1{δ.z} then
µ = Float_{p,Q}(0)
当 时,,故返回 。
if 1{δ.e < −5} then
idx1 = (δ.e + 24) mod 2^5
(θa0,θa1,θa2,θa3) = GetC_{5,19,4,p,Q}(idx1, Θ1_log, K1_log)
(θb0,θb1,θb2,θb3) = GetC_{5,18,4,p,Q}(idx1, Θ3_log, K3_log)
当 时,使用 δ.e + 24 的低5位作为 index,对 的情况查表 Θ1_log,对 的情况查表 Θ3_log,这里为了避免数据依赖必须两个都做。
else
idx2 = 16 · (δ.e + 5 mod 2^7)
idx2 = idx2 + ZXt(TR(δ.m, Q−4) mod 16, 7)
(θa0,θa1,θa2,θa3) = GetC_{7,20,4,p,Q}(idx2, Θ2_log, K2_log)
(θb0,θb1,θb2,θb3) = GetC_{7,32,4,p,Q}(idx2, Θ4_log, K4_log)
当 时,使用 δ.e + 5 的低3位与 δ.m 的高4位作为 index,对 的情况查表 Θ2_log,对 的情况查表 Θ4_log。
(θ0,θ1,θ2,θ3) = a ? (θa0,θa1,θa2,θa3) : (θb0,θb1,θb2,θb3)
根据前文对 值的分类讨论,选择最终的多项式系数。
- 秦九韶算法(Horner)步骤及最后加
µ = ((θ3 ⊗ δ) ⊞* θ2) ⊗ δ
µ = ((µ ⊞* θ1) ⊗ δ) ⊞* θ0
计算出三次多项式 的值。
β = LInt2Float(N)
β′ = (β.z,β.s,β.e, β.m *_{Q+1} 2^{Q-6})
将 使用小 LUT,将 转化为低精度(尾数为6位)的浮点形式 ,再转化为高精度 的形式 。
γ = a ? µ : (µ ⊞* β′)
Return (γ.z, γ.s, Round*(γ.e, γ.m))
最后执行浮点加法:
- 若 执行 ,
- 若 执行 。
最终对 舍入得到协议结果。