news 2026/9/13 23:31:58

fhEVM JS SDK 审查计划深度解析:解密许可版本控制、阈值类型与 EIP-712 的正确性保障

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
fhEVM JS SDK 审查计划深度解析:解密许可版本控制、阈值类型与 EIP-712 的正确性保障

fhEVM JS SDK 审查计划深度解析:解密许可版本控制、阈值类型与 EIP-712 的正确性保障

【免费下载链接】fhevmFHEVM, a full-stack framework for integrating Fully Homomorphic Encryption (FHE) with blockchain applications项目地址: https://gitcode.com/GitHub_Trending/fh/fhevm

fhEVM 的 JavaScript SDK(sdk/js-sdk)负责在浏览器/Node 环境中完成加密、解密与 KMS 交互,其中**解密许可(Signed Decryption Permit)**是用户向 KMS 网关授权解密的 EIP-712 签名凭证,其版本、编码与校验直接决定解密流程能否在链上升级后保持正确。本文以仓库内审查文档 sdk/js-sdk/notes/SDK_REVIEW_PLAN.md 为主体,系统解析其对 15 个审查点的 triage 结论,并结合sdk/js-sdk/srchost-contractsgateway-contracts的源码逐一印证;读者可以从中掌握 permit 的 v1/v2 演进脉络、extraData 编码细节、阈值类型缺陷以及一套"先证明不变量、再补测试"的工程化审查方法。

一、审查背景:一份"只读调查"的代码审查计划

该文档定位是一次read-only investigation(只读调查,未改动任何代码),对sdk/js-sdk的 15 个审查点逐一给出四种判定之一:confirmed bug(已确认缺陷)、decision needed(需决策)、additive work(增量工作)、verified-fine(已验证无问题),并附上证据。文档中的File:line引用以审查时点为准,本文在引用时以文件路径为主,并标注了与当前仓库代码的差异。

1.1 关联的合约版本

审查涉及三代 host 合约源码,对应三档协议版本:

协议版本合约源码位置说明
v12sdk/js-sdk/contracts/src/v0.12.0/host-contractsKMSVerifier < 0.4.0,extraData 最高 v1
v13sdk/js-sdk/contracts/src/v0.13.0/host-contracts同上,协议 API 走 v13 读取路径
v14host-contracts/contractsKMSVerifier ≥ 0.4.0,引入protocolConfigAddress与 epoch 概念

SDK 侧对 KMSVerifier 版本与 extraData 上限的对应关系在 kmsExtraData-p.ts 的isKmsExtraDataCompatibleWithKmsVerifier中有精确注释:< 0.2.0→ 协议 v0.11.0 → extraData ≤ v0;0.2.0–0.3.x→ 协议 v0.12.0/v0.13.0 → extraData ≤ v1;0.4.0→ 协议 v0.14.0 → extraData ≤ v2;> 0.4.0(v0.14.1+)则返回true不设上限,理由是"未来的合约版本接受范围无法预知,若链上拒绝,让 revert 本身成为事实来源"。

1.2 Triage 总览表

#审查点判定
1, 2threshold 是 uint256 而非 uint8Confirmed bug
3getResolvedProtocolVersion可能为 undefined?Verified — 不是 bug
4permit 中的零地址Decision(可选加固);前提已被修正
5, 6decrypt v2/v3 路由潜在风险,当前非活跃 bug
9–15SDK 必须只产出 v1 permit / v0–v1 extraData已确认:该不变量当前未被保证(核心问题)
7parse/serialize permit 测试增量工作——当前覆盖为零
8为 Ankur 提供 createEip712增量工作——当前不存在

以下按优先级(Workstream A → B → C → D → E)展开。

二、Workstream A:v1-only 不变量(最高优先级,覆盖点 6/9/10/11/12/13/14/15)

2.1 核心发现:SDK 可能产出 v2 permit 与 v2 extraData

审查的关键结论是:当前 SDK 不存在任何experimental标志src中唯一的experimental命中是 isomorphicWorker.ts 里一处无关的 worker 注释),因此存在两条相互独立的违规路径:

  • Path A(v2 permit):审查时点的signDecryptionPermit路由器在解析协议 ≥ 0.14.0 时派发到signDecryptionPermitV2,而后者是公开可达的。
  • Path B(v1 permit 内嵌入 v2 extraData)signDecryptionPermitV1通过createUnsignedDecryptionPermitEip712V1调用的是通用readCurrentKmsSignersContext(readKmsSignersContext-p.ts),当链上 KMSVerifier ≥ 0.4.0 时,会落入无门禁_readCurrentKmsSignersContext_ProtocolApi_14_or_higher:177),构造出带非零epochId的 v2 extraData;随后kmsSignersContextToExtraData(KmsSignersContext-p.ts)会根据epochId自动选择版本——不会封顶在 v1

也就是说,一个"v1 形状的 permit"内部可能携带 v2 编码的 KMS 上下文,链上/网关按 v1 解析时会产生语义漂移。

2.2 当前仓库代码状态核对

对照当前源码,上述发现的部分落点已经演进,需要在阅读时区分"审查快照"与"当前实现":

  1. 路由器已固定 V1:当前 SignedDecryptionPermit-p.ts 的signDecryptionPermit:153)被刻意 pin 到 V1,注释说明该公开 action 已废弃,建议迁移到signLegacyDecryptionPermit/signUnifiedDecryptionPermit。而 signUnifiedDecryptionPermit.ts 直接调用SignedDecryptionPermitV2-p.tssignDecryptionPermitV2——v2 签名能力仍然公开可达。
  2. Path B 的结构未变:SignedDecryptionPermitV1-p.ts 的createUnsignedDecryptionPermitEip712V1:152)仍在:167调用通用readCurrentKmsSignersContext,再经kmsSignersContextToExtraData:173)编码。只要链上 KMSVerifier 已升级到 ≥ 0.4.0,v1 构建路径就可能产出 v2 extraData。
  3. extraData 版本自选择逻辑确认存在:KmsSignersContext-p.ts 的kmsSignersContextToExtraData:153)与 kmsExtraData-p.ts 的createKmsExtraData:286)依据epochId是否为 0 自动选择 v0/v1/v2。

2.3 三种 extraData 编码(源码实证)

从 kmsExtraData-p.ts 可精确还原三种编码(EXTRA_DATA_V0/V1/V2定义于:10-12):

版本字节结构十六进制长度约束
v00x00(单字节哨兵)2contextId、epochId 必须为 0
v11 字节版本 + 32 字节大端 contextId68contextId 非 0,epochId 必须为 0
v21 字节版本 + 32 字节 contextId + 32 字节 epochId132contextId 与 epochId 均非 0

另有一个易被忽略的细节:toKmsSignedExtraDataBytesHex:228)规定 KMS 签名时 v0 以空字节0x表示,而非 SDK 内部的0x00——任何按内部编码重建签名消息的代码(per-share 解密、公开解密证明等)若漏掉这一转换,v0 签名将无法验证。

2.4 修复计划(对应点 10–15)

  1. 在 runtime/client 上引入显式experimental模式标志(当前不存在),并贯穿到 KMS context 读取器。
  2. _readCurrentKmsSignersContext_ProtocolApi_14_or_higher置于 experimental 门禁之后(对应点 15)。
  3. signDecryptionPermitV1提供仅限 v1 的签名者上下文——专用的readCurrentKmsSignersContextV1maxVersion=1约束,保证其永远拿不到 v2 上下文/非零 epoch(对应点 10、14)。
  4. kmsSignersContextToExtraData封顶在 v1,并在signDecryptionPermitV1内增加assert(extraData.version <= EXTRA_DATA_V1),让 Path B响亮地失败而不是静默产出 v2(对应点 9、12)。V2 签名路径已有镜像断言(SignedDecryptionPermitV2-p.ts 的createUnsignedDecryptionPermitEip712V2assert(kmsContextExtraData.ge(EXTRA_DATA_V2)):316-319)。
  5. 普通模式下禁止signDecryptionPermitV2——路由器在 ≥ 0.14.0 时抛错,除非 experimental 开启(对应点 11);同时决策parse/serialize是否仍应接受v2 输入。
  6. 加固路由(对应点 5、6):在解密路由处交叉校验signedPermit.version与协议推导的分支,不一致即抛错,而不是盲目转换。
  7. 复查reconcileKmsSignersContext(可被 relayer 输入的 v2 extraData 污染),一并加门禁。

待决策:门禁模型——客户端单一布尔experimental,还是完全以解析出的协议版本为键。点 14("epochId 0")与点 15("experimental only")暗示:普通模式 = 永远 v1/epoch-0;experimental = 允许 0.14.0 的 v2/v3 路径。

2.5 路由现状与潜在风险(点 5、6)

审查指出:路由并不以permit.version分支,而是在单一0.14.0边界上按解析出的协议版本分支;"v2 路由"/"v3 路由"对应 HTTP 端点v2/user-decrypt(经fetchUserDecryptV1)与v3/user-decrypt(经fetchUserDecryptV2)。点 5 的前提成立:v1 抓取路由读取contractAddresses,而 v2 permit 消息中缺少该字段(v2 使用userAddress+allowedContracts)。

当前源码的演进:从当前 decryptValuesFromPairs.ts 看,路由已改为parameters.signedPermit.version分支:58-65),注释明确写道"permit 是自描述工件,其 version 固定了 EIP-712 消息形状,只有匹配的路由能读取它并命中对应 relayer 端点"——这正对应审查建议 6 的硬化方向。同理 fetchUserDecrypt.ts 也按parameters.version === 1分支。这说明审查建议的部分内容已在后续提交中落地,但 Path B 的"v1 permit 内嵌 v2 extraData"与 experimental 门禁仍属开放项。

三、Workstream B:threshold 类型缺陷(点 1、2,已确认、自包含)

3.1 缺陷本质:链上 uint256 与 TS Uint8Number 不一致

链上与 SDK 自己的 ABI 片段都将 threshold 声明为uint256(fragments.ts 以及 KMSVerifier.sol 的结构体uint256 threshold),但 TS 类型层将其标注为Uint8Number并用isUint8校验(uint.ts 的isUint8:143)。于是readContract返回的bigint只要> 255就会抛"Invalid threshold."

涉及面包括:

  • KMS:kmsSignersContext.ts(threshold: Uint8Number:56)、KmsSignersContext-p.ts、getKmsContextSignersAndThresholdFromExtraData-p.ts、getKmsContextSignersAndThreshold-p.ts;
  • Coprocessor:coprocessor.ts、coprocessorSignersContext.ts、CoprocessorSignersContext-p.ts、getCoprocessorContextSignersAndThreshold-p.ts。

3.2 修复方案

  1. 将两类 threshold 统一重类型为 uint256/bigint,用isUint256(uint.ts 的isUint256:163)校验;
  2. 去掉Number(...)收窄(在 2^53 以上存在精度损失);
  3. recoveredAddresses.length < threshold之类的比较改为BigInt(length) < threshold

审查同时给出风险定性:实践中阈值等于节点数量、通常远小于 256,属于低频但真实的正确性上限(例如 KmsSignersContext-p.ts 的createKmsSignersContextkmsSignerThreshold: Number(kmsSignerThreshold) as Uint8Number的收窄)。

四、Workstream C:permit 中的零地址(点 4,需决策,且前提被修正)

4.1 前提修正

  • 仓库中不存在 RFC 12,只有 notes/rfc/DRAFT_RFC_016.md(RFC 16)。该草案将contractAddresses类型化为readonly string[],但规定零地址、去重与最小数量。
  • permit 的合约列表校验发生在gateway-contracts/contracts/Decryption.sol,而非 host 侧KMSVerifier.sol(后者只校验 KMS 响应签名)。

4.2 现状:SDK 与合约都不拒绝零地址/重复项

  • SDK 侧:fetchKmsSigncryptedSharesV1-p.ts仅做成员资格校验;空列表与最大 10 个的限制在MAX_USER_DECRYPT_CONTRACT_ADDRESSES = 10;SignedDecryptionPermitV1-p.ts 的_validateDecryptionPermitEip712V1:266)只检查非空、≤10、duration 合法,同样不检查零地址。
  • 合约侧:Decryption.sol检查空列表、最大 10、user/delegator 不在列表中、ct-pair 在列表中,同样没有零地址/重复项检查。

SDK 与链上行为一致,因此不存在"SDK 与链分叉"问题;零地址在语义上只是"无用的地址"(下游 ACLisAllowed会失败),并非安全漏洞。

4.3 决策

(a) 保持现状(与链一致);或 (b) 在assertPermitV1IncludesContractAddresses与 permit 构建器中提前拒绝零地址与重复项。审查倾向 (b)——成本低、fail-fast——但属可选加固。

五、Workstream D:测试补齐与 createEip712(点 7、8,增量工作)

5.1 点 7:permit 的 serialize/parse 当前零覆盖

审查时点,test/fheTest下唯一的 parse/serialize 相关代码是index.hello.test.ts中一段被注释掉的测试块。涉及函数:

  • serializeSignedDecryptionPermitToJSON(SignedDecryptionPermit-p.ts:55):内部有instanceof守卫(:69),并特意不在类上实现toJSON(),防止JSON.stringify(permit)意外序列化敏感数据;domain 的 bigintchainId会以十进制字符串输出,保证JSON.stringify后仍可还原(_toJsonSafeEip712:27)。
  • parseSignedDecryptionPermit:217):无 version 时默认按 v1(:232),未知版本抛错(:235-237),v1/v2 分别派发(:242-246);并先经_normalizeSerializedPermitDomainChainId:179)把字符串形式的chainId转回 bigint。
  • 公开包装:actions/chain/serializeSignedDecryptionPermit.tsactions/chain/parseSignedDecryptionPermit.ts

审查建议补充三类测试:

  1. 单元测试(如src/core/kms/SignedDecryptionPermit-p.test.ts):serialize 拒绝非 Impl 输入;parse 的版本派发(未知→抛错、缺省→v1);畸形输入拒绝(SignedDecryptionPermitV1-p.ts:277-306的校验);publicKey 不匹配(:289-294,V1 的_validateDecryptionPermitEip712V1与 parse 路径)。
  2. 往返测试(需链,放在test/fheTest/**):parse(serialize(permit))深比较,覆盖 v1 自解密 + v1 委托(若保留 experimental,再加 v2)。
  3. 接口一致性ParseSignedDecryptionPermitParametersactions/chain/parseSignedDecryptionPermit.ts)字段名为serializedPermit只接受对象,违反命名规范(应支持serialized: string | Record<...>)——JSON 字符串目前会直接失败,需要锁定或修复。

5.2 点 8:公开的 createEip712 不存在

内部 builder 存在但未导出:createKmsUserDecryptEip712V1(createKmsUserDecryptEip712V1.ts)、createKmsUserDecryptEip712V2、委托变体与 domain builder。而 notes/rfc/RFC003.md(:256-257)规定了公开 APIcreateUserDecryptEIP712/ `createDelegatedUserDecryptEIP712",语义是"构造 EIP-712 typed data 但不签名"。

计划落点:新增一个decrypt 层公开 actioncreateUserDecryptEip712(fhevm, params)(含委托变体),内部完成:解析协议版本与链字段(verifyingContractAddressDecryptionchainId)、解析extraData(链上抓取或接受入参)、派发到内部 builder、返回未签名的 typed data。按 Workstream A,普通模式下应构建V1typed data。命名须遵循createEip712/createUserDecryptEip712的大小写规范。

值得注意的现状:当前仓库已存在两个"未签名 EIP-712"公开 action——createUnsignedLegacyDecryptionPermitEip712.ts(V1)与createUnsignedUnifiedDecryptionPermitEip712.ts(V2),它们与 RFC003 的createUserDecryptEIP712理念一致,但最终命名形态与委托变体仍需与 Ankur 确认。

待决策:RFC003 的签名形态是否满足需求;extraData由调用方提供还是链上获取。

六、Workstream E:已验证、无需处理(点 3)

getResolvedProtocolVersion(CoreFhevm-p.ts 中声明为protocolVersion: ProtocolVersionResolution)的类型是ProtocolVersionResolution | undefined全部 7 个调用点都处理了 undefined(要么抛出明确错误,要么在 ProtocolVersionResolver-p.ts 中回退);测试也覆盖了 undefined 路径(ProtocolVersionResolver-p.test.ts)。结论:无修复项。

七、建议的实施顺序

  1. A 优先——正确性/安全性不变量(若在 v0.13.0 版本中发出 v2 permit/extraData 会破坏解密),且改动面最大;其 experimental 标志决策会解锁 C/D 中的 v2 问题。
  2. B 并行——隔离、机械式的阈值类型修复。
  3. D 在 A 之后——测试需要编码 v1-only 不变量;createEip712也要遵守它。
  4. C 最后——可选的零地址加固。

八、实施前待决的两个开放问题

  1. Workstream A 的experimental 门禁模型:单一布尔标志 vs. 完全依赖解析出的协议版本。
  2. createEip712是否遵循 RFC003 的签名形态、extraData由调用方提供还是链上获取。

九、继续深入:相关文件索引

  • 审查计划原文:sdk/js-sdk/notes/SDK_REVIEW_PLAN.md
  • Permit 实现:SignedDecryptionPermit-p.ts、SignedDecryptionPermitV1-p.ts、SignedDecryptionPermitV2-p.ts
  • extraData 编码:kmsExtraData-p.ts、KmsSignersContext-p.ts
  • 链上上下文读取:readKmsSignersContext-p.ts、kmsSignersContext.ts
  • 数字校验基元:uint.ts
  • 解密路由:decryptValuesFromPairs.ts、fetchUserDecrypt.ts
  • 链上契约侧:KMSVerifier.sol、Decryption.sol
  • 协议规范:notes/rfc/RFC003.md、notes/rfc/DRAFT_RFC_016.md

这份审查计划的真正价值在于它的方法论:先把"SDK 必须只产出 v1 permit"这类不变量显式写出来,再用源码证据逐条验证、按风险排序实施,最后用测试把不变量固化下来——这正是多版本协议 SDK 演进时值得复用的工程范式。

【免费下载链接】fhevmFHEVM, a full-stack framework for integrating Fully Homomorphic Encryption (FHE) with blockchain applications项目地址: https://gitcode.com/GitHub_Trending/fh/fhevm

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/13 23:28:33

SAP HANA Cloud 迁移真正要搬什么,从 BTP 账户到数据库对象与业务数据的完整资产地图

很多 SAP HANA 迁移项目刚启动时,团队脑海里出现的第一幅画面往往是数据库。 源端有一套本地部署的 SAP HANA,目标端准备了一套 SAP HANA Cloud,于是很自然地开始盘点 schema、table、view、procedure,再讨论数据量、停机窗口和数据传输速度。数据库当然是核心,但如果整个…

作者头像 李华
网站建设 2026/9/13 23:25:51

【AgentScope 2.0】02-五分钟跑通 loser-agent:从 MySQL 到第一条 SSE 消息

源码地址:后端地址 前端地址 上一篇我们把 loser-agent 的全链路地图铺开了——一次聊天请求从入口到持久化要穿过灰度、装配、模型路由、工具、ReAct 循环七大段。但地图不是地形,看源码之前,得先把平台跑起来,亲手发出第一条消息。 问题在于,loser-agent 不是那种 mvn…

作者头像 李华
网站建设 2026/9/13 23:25:07

ICA盲源分离实战:从FastICA到双麦克风语音分离

简介&#xff1a;面向信号处理与机器学习初学者的 ICA 盲源分离 MATLAB 实现包&#xff0c;以 5 个 .m 源文件&#xff08;约 5KB&#xff09;浓缩了经典 Bell-Sejnowski 独立成分分析算法核心流程&#xff0c;适合用来理解盲分离从混合信号中恢复独立源的基本原理。包内代码覆…

作者头像 李华
网站建设 2026/9/13 23:24:29

小米 Home Assistant 集成浴霸模式控制丢失完整修复指南

小米 Home Assistant 集成浴霸模式控制丢失完整修复指南 【免费下载链接】ha_xiaomi_home Xiaomi Home Integration for Home Assistant 项目地址: https://gitcode.com/GitHub_Trending/ha/ha_xiaomi_home 小米 Home Assistant 集成 ha_xiaomi_home 让你在 Home Assist…

作者头像 李华