BLS 签名无处不在,从以太坊的共识到 EigenLayer。但使用它们是很容易出错的。 什么是 BLS 签名?让我们谈谈使用它们的正确方式和错误方式:
但首先,什么是BLS签名? BLS签名是一种用于签署消息的密码学原语,类似于ECDSA。 数学是这样运作的。它建立在椭圆曲线配对之上。 但是什么让它们如此特别?为什么要使用这些复杂的配对?
BLS 的杀手级功能:签名聚合。 您可以将多个 BLS 签名组合成一个签名。这使您能够一次性传输和检查 N 个签名,更加节省空间和时间!而且在链上,优化对于节省燃气费用至关重要。
要深入了解BLS签名的工作原理,以及构建聚合和多重签名的过程,请查看本线程末尾链接的完整博客文章! 现在,让我们看看BLS签名可能出现的问题,以及EigenLayer如何正确使用它们(避免这些陷阱)!
EigenLayer 是以太坊的再质押层。在 EigenLayer AVS 中,验证者对其验证计算的结果进行签名。 聚合器收集所有这些签名并将其推送到链上。聚合的签名在链上进行验证。
该任务包含任务创建时的区块号和一个阈值,指示操作员验证所需的百分比,以验证该任务。 选择加入 AVS 的操作员可以获取这些任务来计算任务答案,然后操作员可以将答案与其任务的 BLS 签名发送给聚合器。 一旦达到相同答案的阈值,聚合器将所有 BLS 签名合并为一个唯一的聚合签名,并将其发送回 AVS 合约。 合约验证签名是否正确,并开启一个挑战期,在此期间挑战者可以提供证据证明验证不正确,如果是这样,违规的操作员将被削减。
在合同中,验证发生在 `trySignatureAndApkVerification` 函数中:
然而,如果使用不当,多重签名会带来一个严重的问题,称为恶意密钥攻击。 假设一个诚实的用户有一个公钥 `pk_0`。一个之前见过 `pk_0` 的攻击者可以选择他们的公钥为 pk_1 = sk_1⋅G_1—pk_0。 攻击者并不知道与公钥相关的私钥。然而,多重签名验证将给出以下结果:
只需要 `sk_1` 就可以签署一条消息,从而生成有效的多重签名,即使第一个用户可能没有签署它。 这很容易推广到任何数量 `r` 的诚实用户,只需选择恶意密钥,即:
这是一个危险的威胁,因为在我们之前的 AVS 示例中,一个恶意的聚合器如果之前注册了一个恶意密钥,就可以发送未由验证者签名的聚合签名,但仍然会被合约接受。 这将导致验证者被削减,即使他们没有不当行为。
因此,为了防止恶意密钥攻击,常见的方法是要求用户证明他们知道私钥与其公钥匹配,这被称为持有证明。 因此,在第一次注册步骤中,用户被要求注册他们的公钥以及持有证明 π,条件如下:
基本上,用户被要求签署他们的公钥或任何其他身份识别消息。在 AVS 中,消息是操作员地址。 在 EigenLayer 合约中,持有证明通过 `registerBLSPublicKey` 函数进行验证:
函数 `pubkeyRegistrationMessageHash` 用于对自定义域分隔符 `PUBKEY_REGISTRATION_TYPEHASH` 和操作员地址进行哈希处理。
注册后,公钥会被添加到合约中。我们可以通过调用 `getRegisteredPubkey` 函数来验证其值。 以下是为 EigenDA AVS 注册的 BLS 公钥的示例:
持有证明基本上是一个BLS签名。然而,在持有证明步骤中使用多重签名也是不明智的,例如,为单个参与者注册多个公钥。 如果这样,参与者将会实现分裂零攻击。在这种情况下,参与者可以注册在相加时会相互抵消的密钥,从而绕过持有证明。
我们已经看到,BLS 多重签名提供了显著的优化机会。 EigenLayer 的实现展示了 BLS 签名的强大,同时也突显了其实际部署中涉及的复杂性。然而,多重签名引入了安全风险,例如恶意密钥攻击,这需要像持有证明这样的保护措施。 但随着 Pectra 升级支持 BLS12-381,我们可能会看到在 Solidity 中进一步的实现和改进,因此我们希望这篇文章能帮助避免已知的实现错误和漏洞。
要深入了解BLS签名、构建聚合和多重签名,请查看我们最近发布的博客文章:
64.18K