揭示zkTLS的现实:基准测试与密码分析报告

本文介绍了一种开源的基准测试框架,用于比较现有开源zkTLS库的性能。研究表明,Primus提出的garble-then-prove系统在速度上领先于其他MPC-TLS解决方案,同时发现一些Proxy-TLS实现存在安全缺陷。此外,文章详细探讨了zkTLS的TLS协议概述、两种主要方法(MPC-TLS和Proxy-TLS)的实现细节及其性能测试。

作者: 谢翔, 小王

精华 本文提供了一个开源基准框架,用于跨各种平台和网络条件对现有开源 zkTLS 库进行基准测试。我们发现,Primus 提出的 garble-then-prove 系统比其他 MPC-TLS 解决方案快 一个数量级。此外,基于 Primus 的 QuickSilver 的 Proxy-TLS 协议比替代方案快 30倍145倍。我们欢迎其他团队提交 PR 加入 开源基准 努力。此外,我们还指出一些 Proxy-TLS 实现中的安全缺陷,这可能使恶意客户端在某些情况下证明虚假陈述。我们已对相关团队进行了必要的负责任披露,并想澄清我们并没有对他们的代码进行实际攻击。

zkTLS,也称为 Web 证明,是一种加密协议,确保通过传输层安全(TLS)协议传输的数据的真实性和隐私,例如在所有基于 HTTPS 的 Web 应用程序中。zkTLS 为可验证地将 Web2 数据迁移到 Web3 开辟了可能性,同时保持隐私。

TLS 概述

在深入 zkTLS 的详细协议之前,首先回顾 TLS 的高层流程是有帮助的,因为这将帮助我们理解 zkTLS 方法的不同之处。

TLS 协议涉及两个参与方:客户端和数据源(我们将其称为服务器,以便与 zkTLS 一致)。TLS 在两个主要阶段中操作:握手阶段和记录阶段。

握手阶段

在握手阶段,客户端和数据源协商一个共享的秘密密钥,称为预主密钥(pms)。该预主密钥随后用于生成会话密钥,这些密钥将在通信过程中用于加密数据。

为了生成这些会话密钥,使用了密钥派生函数(KDF)。KDF 取输入预主密钥(pms)和其他公共信息(例如客户端和服务器随机值)来推导会话密钥。这个过程通常通过一系列 HMAC 函数完成。

正式而言,会话密钥可以如下推导:

keys←KDF(pms,public_info)

我们参考 RFC 5246RFC 8446

记录阶段

在记录阶段,客户端和数据源使用会话密钥来加密和解密消息。大多数 zkTLS 协议使用 AES-GCM 进行数据传输。以下是 AES-GCM 的高层概述。

给定一个密钥

K 和消息

(m0,...mℓ),其中每个

mi 为 16 字节(假设使用 AES-128),密文为以下形式

AES(K,ctr0)⊕m0,...,AES(K,ctrℓ)⊕mℓ,τ

这里

ctri 为计数器,且

τ 是根据前面的密文和

K 计算的标签。使用密钥

K 解密密文时,系统首先检查

τ,然后解密以恢复

(m0,...,mℓ)。我们参考 RFC 5288

zkTLS 的两种主要方法:MPC-TLS 和 Proxy-TLS

在 zkTLS 协议中,引入了一个名为 attestornotary 的第三方,用于验证客户端从数据源接收的数据,同时确保没有私密信息(例如密码)泄露给验证者。

业界中 zkTLS 的两种主要方法是 MPC-TLSProxy-TLS,这是本文的主要焦点。

MPC-TLS

在 MPC-TLS 方法中,验证者和客户端运行一个二方计算(2PC)协议来模拟 TLS 握手的客户端部分。这意味着客户端在握手阶段后并不会接收到完整的会话密钥。只有在验证者收到响应密文后,验证者才会将秘密密钥份额发送给客户端,从而使客户端能够解密所有密文。

MPC-TLS 的高层流程如下。

  1. 客户端和验证者在握手阶段运行 2PC 协议。在此步骤结束时,客户端和验证者持有会话密钥的密钥份额。此步骤的主要部分是为 KDF 函数运行 2PC 协议。
  2. 客户端和验证者运行另一个 2PC 协议以计算请求的密文,包括计算 AES 函数和标签。
  3. 客户端从数据源接收响应密文并将其转发给验证者。
  4. 验证者将密钥份额发送给客户端,客户端在获得完整的会话密钥之后,向验证者证明密文有效,并满足潜在消息的其他属性。

请注意,客户端和验证者不运行 2PC 协议来解密响应密文。

image

DECO 解决方案在 MPC-TLS 方法中使用了经过认证的混淆电路和 zkSNARKs。然而,这种方法在通信大小和计算时间上都很耗资源,难以在实际产品中部署。

TLS Notary 通过使用半诚实的混淆电路和基于混淆电路的交互式零知识证明来优化此方法。虽然这减少了一些开销,但成本仍然很大。

Primus 在 MPC-TLS 设置中引入了 garble-then-prove 协议,将半诚实的混淆电路与高效的交互式零知识证明系统 QuickSilver 相结合,并进行了其他优化。这导致了一种在计算和通信上都高效的解决方案。

Proxy-TLS

在 Proxy-TLS 方法中,验证者作为客户端与数据源之间的代理,转发所有 TLS 记录。验证者可以记录客户端与数据源之间交换的握手记录和密文。在协议结束时,客户端在零知识中向验证者证明密文的有效性。

Proxy-TLS 方法的动机是消除在 MPC-TLS 中使用的 2PC 协议,这是计算上最密集的部分。然而,这引入了更强的网络假设:验证者必须确保与真实数据源之间的直接连接。如果不满足此条件,恶意客户端可能会在验证者和数据源之间进行劫持,从而允许其伪造数据并证明任意主张。虽然许多人认为这一假设合理,但仍然很重要要强调。

根据客户端向验证者证明的陈述,不同项目采用了两个选项。

选项 1: 证明 KDF 和 AES。

在这种情况下,客户端证明 AES 加密中的密钥是由 pms 派生的,并且消息是在验证者记录的密文下用相同的密钥加密的。有关更多详细信息,请参见 这篇论文

选项 2: 证明公共填充和 AES。

此选项适用于 HTTPS,其中响应中有固定的公共填充,例如数据源返回的 200 OK 状态。客户端证明某些密文加密公共填充,而其他密文则加密消息,所有这些均使用相同的密钥。更多细节见 此处

这两个选项旨在解决 AES-GCM 密码套件的非承诺问题。此外,证实 AES 密钥在不同块之间的一致性,并与 KDF 函数的输出相匹配至关重要。我们发现一些实现未能证明这一属性,允许恶意客户端提出虚假主张。我们将在接下来的部分中对此进行详细讨论。

image

PrimusPluto 在其 Proxy-TLS 方法中利用选项 1,Primus 使用 QuickSilver,而 Pluto 则利用增量可验证计算(IVC)系统。

另一方面,Reclaim 采用选项 2,使用 Groth16 作为基础证明系统。

性能

我们对现有的开源 zkTLS 核心库进行了基准测试,包括 PrimusTLS NotaryReclaimPluto(Origo)。为了进行基准测试,我们构建了一个开源框架,测试在不同平台上的性能,包括 x86、ARM 和浏览器(WASM)。该框架还允许自定义各种网络环境。我们参考此 GitHub 仓库

为了模拟现实场景——因为大多数应用程序通过浏览器访问网站,需要通过 TLS 发送 cookies——我们将请求大小设置为 1KB 或 2KB。响应大小从 16 字节到 2KB 不等。值得注意的是,实际性能可能因网络和平台配置而异。

我们提供了几个具有代表性的环境的性能指标。

  • 带宽:200 Mbps
  • 延迟:10 ms
  • 请求大小:2 KBytes
  • 操作系统:Ubuntu-22.04
  • CPU:8 vCPUs,3.10GHz
  • 内存:32GB

MPC-TLS

Lib: Primus otls (TLS 1.2)

响应大小(字节) 上传大小(K字节) 下载大小(K字节) 原生(x86)运行时间(秒) 原生(arm)运行时间(秒) 浏览器运行时间(秒) 内存(M字节)
16 34,888 1,141 2.56 2.64 5.05 146
256 34,901 1,141 2.57 2.62 5.09 146
1024 34,930 1,141 2.57 2.64 5.04 146
2048 34,971 1,141 2.58 2.65 5.11 146

Lib: TLS Notary (TLS 1.2)

响应大小(字节) 上传大小(K字节) 下载大小(K字节) 原生(x86)运行时间(秒) 原生(arm)运行时间(秒) 浏览器运行时间(秒) 内存(M字节)
16 61,089 62,144 20 32 47 147
256 61,121 65,309 20 33 48 148
1024 61,222 75,437 21 35 49 148
2048 61,356 88,940 23 39 53 148
  1. Primus 和 TLS Notary 均已为 TLS 1.2 实施了 MPC-TLS 协议,并且在不同平台上都是内存高效的。在这两个实现中,浏览器运行时间大约比原生执行慢 2倍
  2. 在这些指标下,Primus 在不同平台上的总运行时间大约比 TLS Notary 快 10倍。Primus 无论响应大小如何都保持稳定的运行时间,而 TLS Notary 随着响应大小的增加下载大小和运行时间均有所增加——尽管变化相对较小。
  3. 基准测试结果的主要原因可能是 Primus 使用了 QuickSilver、优化电路和高效的混淆电路(GC)实现。值得注意的是,TLS Notary 团队也正在将 QuickSilver 与 Primus 团队的支持进行集成。粗略估计表明,采用 QuickSilver 将显著减少下载大小,并将计算开销从大约 3GC(运行 GC 三次)降低到 2GC。

Proxy-TLS

在 Proxy-TLS 解决方案中,为了简化基准测试并尽可能确保公平,我们将请求大小设置为零。这意味着没有实现证明请求数据,重点仅集中在响应数据的大小上。

Lib: Primus otls (TLS 1.2)

响应大小(字节) 上传大小(K字节) 下载大小(K字节) 原生(x86)运行时间(秒) 原生(arm)运行时间(秒) 浏览器运行时间(秒) 内存(M字节)
16 375 788 0.45 0.48 2.31 71
256 383 788 0.46 0.47 2.34 75
1024 416 788 0.47 0.49 2.48 91
2048 461 788 0.48 0.52 2.66 111

Lib: Reclaim (TLS 1.2/1.3)

响应大小(字节) 上传大小(1.2/1.3 K字节) 下载大小(1.2/1.3 K字节) 原生(x86)运行时间(1.2/1.3 秒) 原生(arm)运行时间(1.2/1.3 秒) 浏览器运行时间(1.2/1.3 秒) 内存(M字节)
16 32/23 31/22 4.09/3.62 8.79/8.16 13.88/13.05 356
256 34/24 32/23 5.09/4.76 11.06/10.64 18.76/21.43 313
1024 38/29 38/29 8.53/8.43 18.71/19.10 36.63/49.21 298
2048 44/35 44/36 12.92/13.38 28.61/29.83 60.22/86.21 306

Lib: Pluto origo (TLS 1.3)

响应大小(字节) 上传大小(K字节) 下载大小(K字节) 原生(x86)运行时间(秒) 原生(arm)运行时间(秒) 浏览器运行时间(秒) 内存(M字节)
16 62 14 41 56 193 1,395
256 62 14 41 57 206 1,395
1024 63 15 58 89 293 1,391
2048 65 16 76 119 390 1,412
  1. Primus 在 Proxy-TLS 中实现了选项 1,用于 TLS 1.2,而 Pluto 基于 ChaCha20-Poly1305 密码套件采用了选项 1 用于 TLS 1.3,主要原因是 ChaCha20 与他们的 IVC 系统更兼容。另一方面,Reclaim 对于 TLS 1.2 和 TLS 1.3 都实现了选项 2。
  2. Reclaim 和 Pluto 的总通信大小几乎完全相同,且均比 Primus 小 18倍,这与它们使用 zkSNARKs 是相符的。
  3. 在不同平台上,Primus 的总运行时间比 Reclaim 快 5倍30倍。此外,Primus 消耗的内存比 Reclaim 少 3倍5倍,尽管两者的内存消耗仍然轻量且足够用于大多数应用。但 Pluto 显著比 Primus 和 Reclaim 慢——比 Primus 慢超过 90倍,并且内存消耗显著更高。这是由于它使用的 IVC 非常计算密集。
  4. 以上基准测试结果的主要原因可能是 Primus 使用 QuickSilver,而 Reclaim 依赖于 Groth16,Pluto 则采用 Nova,这两者都是计算密集型解决方案。这也解释了为什么 Primus 的运行时间在响应大小变化时仍然保持相对稳定,而 Reclaim 和 Pluto 的运行时间对响应长度高度敏感。请注意,随着一些仓库的持续更新,性能指标可能会随时间而变化。

安全问题

在对 Reclaim 的实现进行基准测试时,我们发现了一些反直觉的事情。Groth16 是一个依赖电路的证明系统,意味着每个独特电路都需要一个单独的可信设置。然而,在 Proxy-TLS 设置中,电路取决于响应长度,即 AES 块的数量。然而,我们发现,在 Reclaim 的系统中,用户并不需要每次响应长度变化时都重新运行设置,这与预期行为相悖。

在与团队确认后,我们发现 Reclaim 实际上将每个 AES 块分别进行证明,而不确保加密密钥在所有块之间保持一致。因此,对于一个 AES 块的单一设置过程就足够。然而,这种实现可能允许恶意客户端提出虚假主张。以下是对此潜在问题的简单解释。也就是说,我们想强调,我们并没有对他们的代码进行实际攻击,而只是指出这个可能的安全缺陷。

可能的攻击

假设验证者/公证人获得两个 AES 密文,如下所示。

c0=AES(K,ctr0)⊕m0,c1=AES(K,ctr1)⊕m

其中

m0 是一些公共填充信息,且

m 是客户端希望证明的数据。

在 Reclaim 的实现中,客户端为

c0 和

c1 生成两个单独的 Groth16 证明,而不确保加密密钥

K 在这两个密文之间保持一致。这允许恶意客户端实施以下攻击。

客户端选择某些

K′,并重写

c1 为

c1=AES(K′,ctr1)⊕AES(K′,ctr1)⊕AES(K,ctr1)⊕m

因此,

c1 的密文为

m′=AES(K′,ctr1)⊕AES(K,ctr1)⊕m,使用密钥

K′ 和

ctr1。然后客户端可以声称来自数据源的消息是

m′。

在这种情况下,如果

m 是公共数据,如 Reclaim 团队所解释的那样,验证者将对消息执行格式检查——例如,确保

m 仅由数字字符组成。由于随机选择的

m′ 极不可能是有效的数字字符串,因此验证者最终会拒绝证明。

然而,如果

m 代表私有数据——例如银行账户余额——并且目标是证明关于其的陈述(例如,余额大于 100),潜在攻击可能会出现。这是因为验证者无法直接访问

m,因此无法验证其格式。在这种情况下,恶意选择的

m′ 可能表示一个非常大的数字,导致无效证明被接受。

结论

总之,我们发现,在 zkTLS 的背景下,零知识证明对于 1) 确保参与方的诚实行为和 2) 证明 TLS 负载中的内容至关重要。然而,我们观察到像 QuickSilver 这样交互式协议由于其可扩展性总是输出替代解决方案。

根据开放的基准测试结果,我们相信像 QuickSilver 这样的交互式 ZK 目前是 zkTLS 最适合的方法,尤其是在我们将技术推向浏览器时。

  • 原文链接: hackmd.io/@-fI_Eu_rR8qs0...
  • 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
点赞 0
收藏 0
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
-fI_Eu_rR8qs02aOhOPWNg
-fI_Eu_rR8qs02aOhOPWNg
江湖只有他的大名,没有他的介绍。