MPyC 乘积开值信息泄漏的分析、复现与上游修复验证¶
在开展隐私保护航路计算研究时,我使用了 Python 安全多方计算框架 MPyC。对底层协议的检查,让这项应用研究延伸成了一次开源安全贡献:反馈实现中的信息泄漏,构造本地复现,与维护者 Berry Schoenmakers 教授多轮邮件讨论,并验证后续上游修复。
这段经历已经留下了可以公开核查的记录。2026 年 11 月 3 日,教授在一次提交中写道:
Thanks to Kewen Wang from Hubei University of Technology, China for this issue.
随后,在进一步处理相关乘积开值的提交中,又注明:
Follow-up handling Kewen Wang's feedback.
两句话分别出自上游提交 5849396 和 62b0462。本文记录这项贡献的来由、技术问题、协作过程和验证结果;邮件讨论只作概述,不转载私人通信原文。
一、研究为什么会走到 MPyC 底层¶
我的研究场景是多主体协作的航路计算:几个参与方分别持有不宜公开的通行限制与代价记录,希望共同计算一条完整路线,又不把全部私有记录集中交给某一方。
约束还会持续更新。除了约束数值,更新了哪个区段、某次提交是否有效、两次提交是否针对同一位置,也可能反映主体活动。因此,研究既涉及路径状态的复用,也依赖底层秘密计算能否保护这些信息。
MPyC 提供了秘密整数、比较、秘密选择等接口,让应用可以在份额上完成计算。我的航路原型使用三个参与方、阈值 1、SecInt(32) 和统计安全参数 K=80。这里的 K 是 MPyC 的安全参数,与航路方法中的块长不是同一个量。
程序算出正确路线后,还有一个问题需要检查:参与方在执行过程中收到的消息,会不会比接口承诺的结果透露更多信息?
这促使我从应用层继续向下检查比较、零测试及乘积开值的实现。这个过程中使用了 AI 辅助分析和实验工具;具体结论则通过固定版本源码、真实三方运行和对照检查逐项核实。
二、问题出在“公开了什么”¶
2.1 公开结果随机,不代表执行视图没有泄漏¶
以公开零测试为例,预期功能是判断秘密值 \(a\) 是否等于零,而不是公开 \(a\) 本身。一种常见思路是取随机掩码 \(r\),公开乘积 \(ar\)。
在有限域中,若 \(a\ne0\),且 \(r\) 均匀分布,那么 \(ar\) 也均匀分布。仅看这个常数,似乎无法判断具体的非零输入。
但秘密分享协议的参与方不只看到最终的常数。它还持有自己的份额,并在重构过程中接收其他参与方发来的评价点。问题需要放到这个完整视图中分析。
2.2 三方乘法会产生二次多项式¶
在三方、阈值 1 的 Shamir 分享中,输入和掩码可以分别写成一次多项式:
直接相乘得到
其次数至多为二。如果直接按次数 2 重构这个乘积,某一参与方自己的评价点,加上正常收到的另外两个点,就足以确定整个 \(Q(X)\),而不只是常数项 \(ar\)。
对这个多项式进行因式分析,再结合观察方已有的输入份额和掩码份额,可以约束原始分享多项式。在部分输入和随机状态下,安全整数的合法取值范围还能进一步区分候选,恢复本应隐藏的输入。
我的本地复现使用合成输入,观察方只利用自己的份额、正常接收的开值消息,以及自己持有密钥能够计算的 PRSS 分量。这里的参与方仍按协议执行,不需要读取其他进程的秘密数据。
这也说明了为什么该问题值得关注:功能输出正确,与执行过程保护了隐私,是两件需要分别验证的事。 乘积多项式泄漏的风险本身是已知原理;这次工作的价值在于定位具体实现路径、提供复现并协助完成修复闭环。
三、从首轮反馈到后续修复¶
整个过程经历了多轮分析和复测。最初的检查涉及旧实现的中等域分支;教授结合反馈重新分析后,先移除了这一分支,并调整了大域分支的选取条件。
我继续在更新后的代码上检查。原来涉及两个乘积的恢复思路,还可以简化为对单个未随机化乘积的分析:即使不再执行旧中等域分支,直接重构原始乘积多项式的风险仍值得处理。我据此继续反馈,并用本地重新分享方案作过渡性验证。后续交流中,教授进一步分析了本地份额如何消除因式缩放的不确定性,以及合法整数范围如何帮助区分候选。
最终,上游对相关开值路径补充了随机化处理。两个关键公开节点如下,日期统一按北京时间记录:
| 日期 | 上游提交 | 处理内容与公开记录 |
|---|---|---|
| 2026-11-03 | 5849396 |
Drop use of medium-sized fields.,移除中等域分支,并在提交说明中致谢我的反馈 |
| 2026-11-07 | 62b0462 |
Rearrange some reshares vs outputs.,进一步处理相关乘积开值,并注明跟进我的反馈 |
第二个提交的 UTC 日期为 11 月 6 日,换算北京时间为 11 月 7 日。本文用完整提交标识确定验证对象,不仅依赖版本号:它的版本字符串仍为 0.11.3,无法仅凭这一字符串区分修复前后源码。
四、上游怎样修复,我的本地方案又是什么¶
本地过渡方案与最终上游实现有共同目标:在公开结果前,去掉原始乘积多项式中不应暴露的系数关系。但两者并非完全相同的代码。
| 方案或设置 | 开值前的处理 | 三方、阈值 1 下的重构方式 |
|---|---|---|
| 本地过渡方案 | 将乘积重新分享,变成新鲜的一次分享 | 以阈值 1 重构 |
上游 62b0462,PRSS 启用 |
向乘积加入新鲜随机零分享 | 仍以阈值 2 重构 |
上游 62b0462,PRSS 关闭 |
将乘积重新分享 | 以阈值 1 重构 |
重新分享使重构消息不再提供原始二次乘积的评价点。随机零分享则保留要公开的乘积常数,同时随机化多项式系数。
具体到 PRSS 启用的三方情况,若观察方评价点为 \(c\),它不知道的零分享分量具有如下形式:
这个分量在 \(X=0\) 和 \(X=c\) 都为零:不改变公开结果,也不改变观察方本地点,却随机化了剩余的多项式信息。即使观察方扣除自己能够计算的零分享贡献,仍会留下这个未知分量。它所确定的已是随机化后的多项式,不能再直接套用原始乘积的恢复关系。
因此,是否修复不能只看开值阈值是否仍为 2t,还要看开值前有没有足够的新鲜随机化,以及观察方扣除已知信息后还剩下什么。
小域与大域的区别仍然有意义:大域中随机掩码取零的概率很小,零测试可以接受相应的极小误判项;小域则需要另外处理。这个概率问题与乘积多项式是否充分随机化,是两个不同的问题,不能靠调整参数互相替代。
五、我实际验证了哪些内容¶
上游提交后,我没有只停留在阅读差异,而是重新执行了针对原问题的本地三方检查。以下数字来自本地归档的实验记录,完整探针与记录尚未公开发布。
5.1 原语回归¶
测试固定三方、阈值 1、SecInt(32),覆盖安全参数 K=80 和 K=32,以及 PRSS 开启与关闭。测试对象是标量和 NumPy 数组的公开零测试、求逆接口。
| 接口 | 旧提交 5849396:恢复数/观察组合 |
新提交 62b0462:原恢复路径成功数/观察组合 |
|---|---|---|
is_zero_public |
72/72 | 0/72 |
np_is_zero_public |
72/72 | 0/72 |
reciprocal |
72/72 | 0/72 |
np_reciprocal |
72/72 | 0/72 |
每个接口的 72 个组合,来自 9 个非零合成输入、2 名非输入所有者、2 个安全参数和 2 种 PRSS 设置,不能理解为 72 个独立业务样本。关闭 PRSS 后,重新分享使旧二次恢复所需的视图不再出现;开启 PRSS 时,对随机化视图使用原恢复方法,也未恢复真实输入。
为检查测试是否真正识别了修复原因,我还在探针中临时禁用新增零分享。每个接口的 36 个 PRSS 开启观察组合又全部恢复了输入。这一反向对照帮助排除“没有捕获到消息,所以误以为修复成功”的情况。
零与非零测试的功能检查、逆元乘积为 1 的检查均通过。另有 21 项上游单元测试通过;它们是单方功能测试,不与上述三方视图检查混为同一类证据。
5.2 回到航路研究¶
原语检查完成后,我将航路原型统一迁移到原样上游 62b0462,不再叠加此前的本地零测试补丁。配置保持三方、阈值 1、SecInt(32)、K=80、PRSS 启用和 TLS。
在这个版本上重新运行了主规模、增量对照、组件、几何、敏感性、受控延迟及延长使用期实验。正文实验共核验 2,529 次完整输出,另在独立环境完成了 45 次小例输出核验,并同步校准论文表图。
这些检查表明,修复后的上游能够用于我当前的航路研究场景,也阻断了已展示的恢复路径。它们不等于对 MPyC 全库或任意应用的完整安全证明。我的论文仍分别陈述理想原语下的协议隐私、实际后端的条件分析,以及功能与通信实验的证据范围。
六、这项贡献具体体现在哪里¶
这次工作中,我承担的是问题反馈、复现分析、本地缓解验证和上游回归:
- 从实际研究依赖中检查到不应暴露的乘积开值信息,并向维护者报告。
- 在首轮上游调整后继续复测,反馈单乘积路径中的残余恢复问题。
- 用本地重新分享方案验证缓解方向,并检查相关标量与数组接口。
- 跟进最终上游实现,验证修复前后的差异,再将应用原型统一迁回上游。
最终上游代码由 Schoenmakers 教授分析、实现并提交,我的本地补丁没有作为一份由我提交的 Pull Request 合入。公开提交中的实名致谢与后续反馈说明,记录的是这次安全反馈和协作贡献。这样的分工既应写清楚,也值得认真记录。
七、这段经历给我的启发¶
应用研究与底层密码协议之间有很长的实现链。使用成熟框架可以节省大量工作,但仍要核对具体参数、实际分支、开值消息和持续状态,不能把库名本身当成应用安全性的证明。
我也更清楚地体会到,与维护者沟通需要可复查的材料:固定源码版本、明确参与方模型、说明观察者到底知道什么,并同时提供功能检查与修复前后的对照。新的提交到来后,还要继续检查它是否处理了原来的原因。
对我而言,这次经历既是航路研究中的一次实现核查,也是一次从使用开源工具走向参与改进的实践。
致谢¶
谨向我的导师张明武老师致以诚挚谢意。本文所记录的隐私保护航路计算选题由张老师提出;正是在这一研究场景中对底层实现的逐层核查,才引出后续关于 MPyC 乘积开值问题的反馈、复现与上游修复验证。
衷心感谢 MPyC 维护者 Berry Schoenmakers 教授。自首轮问题反馈至后续修复,教授在多轮邮件讨论中耐心解释协议细节、持续分析残余风险,并完成最终的上游实现;本文所引两个公开提交中的致谢与反馈说明,亦出自教授之手。正是这段持续的沟通与协作,使问题得以从本地复现走到上游修复闭环。
公开来源与延伸阅读¶
- MPyC 项目仓库:项目介绍与源码。
- MPyC 官方文档:接口与使用说明;具体修复判断以固定提交为准。
- 提交
5849396:Drop use of medium-sized fields.:首轮调整及公开致谢。 - 提交
62b0462:Rearrange some reshares vs outputs.:后续修复及反馈说明。 - 修复后的
runtime.py固定源码:核对相关开值处理。 - 本站密码协议与应用:安全计算相关的背景阅读。
本文记录截至 2026 年 11 月 7 日的工作。公开链接可以核查上游改动与致谢;实验计数对应上述固定版本的本地记录,不代表全部历史版本的统一结论。