本文深入探讨了Signal应用使用Ristretto哈希和零知识证明的隐私保护机制,介绍了Ristretto哈希的实现原理及其在用户名生成中的应用。通过对源码的分析,本文展示了如何保护用户隐私,并确保服务器无法推导用户的真实用户名。
Signal 推出了用户名,这意味着用户现在可以在保持手机号码私密的情况下使用该应用程序。通过使用 Ristretto 哈希和零知识证明,实现了这种增强隐私级别。
我们想更深入地了解这两个加密原语如何为 Signal 的用户提供另一种隐私保护;幸运的是,源代码是开源的,所以让我们深入研究一下。
这篇博客文章是根据原始 Twitter 线程改编的,可以在 这里↗ 找到。
Ristretto 是一个椭圆曲线群(与 Curve25519 相关但不相同),相应的哈希是一个哈希到曲线的算法,这意味着它是一个哈希算法,接收标量输入并输出 Ristretto 群上的一个曲线点。
源代码在 Signal 的 GitHub 上是公开的,可以在 这里↗ 查看,以便我们自由地了解他们是如何实现用户名功能的。
我们可以看到 Username
结构体包含一个昵称(字符串),一个整数 discriminator
,和一些神秘的标量。
pub struct Username {
nickname: String,
discriminator: u64,
scalars: Vec<Scalar>,
}
查看反序列化代码,我们可以推断出,discriminator 只是有效 Signal 用户名所需包含的两位数字。
pub fn new(s: &str) -> Result<Self, UsernameError> {
let (nickname, discriminator) =
s.rsplit_once('.').ok_or(UsernameError::MissingSeparator)?;
Self::from_parts_without_soft_limit(nickname, discriminator)
}
标量包含以下三种数值(为简单起见,已省略一些编码细节):
sha512 (nickname, discriminator)
nickname
(压缩)discriminator
现在我们了解了 Username
结构体的内容,哈希就很简单。这三个标量都属于曲线的标量域(即可以与曲线上的点相乘)。哈希算法有三个常量生成点作为其参数。我们将它们称为 $G_1$、$G_2$ 和 $G_3$。哈希的计算方式如下:
$$ H = sha512(nickname, discriminator)*G_1 + nickname*G_2 + discriminator*G_3 $$
用户生成用户名后,可以计算哈希并将其发送到服务器进行存储。请注意,由于只发送哈希到服务器,因此服务器无法推断出与手机号码相关的用户名。
之后用户应该能够限制谁可以给他们发送消息。例如,为了邀请 Bob 给她发送消息,Alice 向他提供了她用户名的明文(昵称 + discriminator)。
为了与 Alice 建立联系,Bob 必须证明他知道 Alice 的用户名。换句话说,Bob 必须证明他知道与服务器存储的用户名哈希的原像而不会透露明文。为此,Bob 生成一个零知识证明,证明以下陈述:
我知道一个昵称、discriminant 和一些值 H,使得 username_hash == H*G_1 + nickname*G_2 + discriminator*G_3
。在 Bob 证明他知道用户名哈希的原像后,他才能成功地向 Alice 发送消息。
Bob 希望证明的陈述是对哈希原像的知识。因为哈希是基于椭圆曲线标量乘法,所以一种高效的方法是采用证明离散对数知识的方案。这里使用的精确方案是 sigma protocol↗ 的一种变体,这种变体使其可以用于多个基点。
Zellic 专注于保护新兴技术。我们的安全研究人员在最有价值的目标中发现了漏洞,从财富 500 强企业到 DeFi 巨头。
开发者、创始人和投资者信任我们的安全评估,以便快速、自信且没有关键漏洞地发布。凭借我们在实际进攻性安全研究方面的背景,我们能找到其他人未发现的漏洞。
联系我们↗,获得比其他公司更好的审核。真实的审核,而不是走过场。
- 原文链接: zellic.io/blog/signal-us...
- 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!