Nymchat 技术白皮书
后量子密钥协议在Nymchat
ML-KEM-768公钥在Nostr上分发,没有目录或注册表,并将其种植在一个没有公有价值暴露的秘密中。
将后量子密钥交换添加到信使中,大多不是一个加密问题,原始人是标准化的,图书馆是存在的。 二、 这篇论文描述了Nymchat如何回答这个问题 - 第二个密钥来自哪里,它如何到达需要它的人,以及接口可以对结果声称什么 - 在最后一节中,该结果不会保护什么。
为了方便起见,此页面是机器翻译的。英文原件为适用版本。
1问题
我们的私人信息被加密 NIP-44,其中有两个可分离的半数. 扭曲文本的一半 — ChaCha20 具有 HMAC-SHA256 标签,键入 香港 (RFC 5869 使用) - 不是由量子计算机有意义地威胁的;格罗弗的算法对对称密钥的平方根加速,256位吸收了这半数。 同意 在键上是圆曲线Diffie-Hellman 第256章 K1,和Shor的算法完全解决了分散的logarithm. 从其公共对手中恢复一个私钥反过来暴露了每个共享的秘密,该密钥曾经产生。
这种威胁不会被推迟,直到这样的机器存在。具有存储的对手今天可以记录加密文本,并在能力到达时解密它。现在发送的任何东西仍然重要,现在已经受到威胁。
1.1 本文回答的问题
这种缓解措施已得到充分理解:将后量子密钥封装机制与经典交换机制并行运行,这样攻击者必须同时破解两者才能读取任何内容。Nymchat 使用 ML-KEM-768 (第203章)这立即引发了一个分布问题:
添加后量子交换,你需要第二个 - 她的ML-KEM公共密钥。
一个NPUB是自存的,你可以把它写在纸上,大声阅读,或者从屏幕上扫描,这就是任何人需要向你加密的全部。一个ML-KEM-768公钥是184字节。它不能大声阅读,它不会适合用户名,它不属于QR代码,除了一个只有32字节的身份。
更困难的部分是,第二个密钥带来了三个不同的问题,这篇论文的其余部分主要是答案:
- 它可以被取代。 一个没有人能一眼就能读到的密钥,正是攻击者用自己的东西交换的类型。
- 它必须在用户的设备上一致。 手机和笔记本电脑的同一个帐户必须呈现相同的密钥,否则在另一端无法打开密封的消息。
- 它可以失去。 公钥是从一个秘密中重新发布的,所以真正必须存活的是这个秘密 - 通过建造,没有其他东西可以重建它。
2设计限制
四个限制塑造了答案,它们在任何代码被写之前排除了大多数显而易见的设计。
- 尽可能少的秘密。 每一个额外的秘密都是失去你的历史的另一种方式,谁知道备份一个 nsec 将不会知道如何备份其他任何东西。第 3.1 节显示,这个秘密不能完全满足 - 来自 nsec 的后量子密钥根本没有提供后量子保护 - 因此,设计花费了一个秘密,而不是更多的:一个单一的关键材料,每一个身份产生一次,以相同的形式呈现为 nsec 和在同一个地方,所以谁知道如何保留一个知道如何保留另一个。
- 没有权威。 没有一个可以信任的服务器可以说哪个密钥属于谁,任何这样的服务器都成为可以重定向消息的点。
- 多个设备,一个身份。 Nostr 身份通常由多个客户端同时使用,无论存在什么关键材料,都必须在所有客户端上相同,并且携带它的路径本身都不能被该功能所防御的对手读取。
- 没有谈判。 任何“你支持哪个数字”的频段交换是攻击者可以剥离的表面,以强迫更弱的选项。
3独立的秘密根
负载的决定是,ML-KEM解密密钥是从没有公开价值暴露的关键材料中种植出来的。
pqRoot = 32 bytes from a CSPRNG, generated ONCE per identity
seed = HKDF-Expand(
HKDF-Extract(salt = "nym-pq-root-v2", IKM = pqRoot),
info = "mlkem768/epoch/" || epoch,
64 bytes)
(ek, dk) = ML-KEM-768.KeyGen(seed)
根是以 nsec 的方式呈现给用户: 贝克32 以人类可读的前缀
nympq因此,它阅读为 nympq1…,在相同的透露交互背后的身份屏幕中显示在 nsec 旁边,用相同的控制器复制,从未登录,从未在清晰的任何地方发送。
盐是故意分离的领域,所以没有其他秘密可以从同一个密钥对中获取。 epoch 轮回驱动计量(第9节)。
3.1 为什么钥匙不能从身份钥匙中提取
显而易见的设计是从用户已经拥有的秘密中种下键:
seed = HKDF(salt = "…", IKM = nsec) // do not do this
这是有吸引力的四个原因,它们都是真实的:没有什么新的备份,因为 nsec已经是备份;每个设备通过构建同意,没有同步协议会出错;每个身份的一个可替代的公告显然是正确的,因为设备不能不同意密钥;并且在发布之前已经存在的密钥,因此客户端可以首次封锁自己。
所有四个好处都是毫无价值的,出于一个原因。Shor的算法与发布的npub进行对抗,结果是 nsec. 种子衍生是对 nsec的公开算法. 所以打破经典的一半的对手通过运行相同的HKDF来重建后量子的一半。
ML-KEM 解密密钥必须来自于从 nsec 既不能衍生,也从未在经典加密中传输过的。
独立生成的秘密,然后在普通NIP-44消息中的用户设备之间同步,是同样的失败,额外的步骤:一个对手今天记录了该消息,后来恢复了它的经典密钥,然后根掉了,这就是为什么下一节中的路径是它们的路径,为什么没有人会把它发送到你的其他设备。
3.2 将根到用户的其他设备
第2节的限制3 - 一个身份,多个设备 - 在这里不能通过算法满足,因为整个点是,钥匙不是设备已经共享的任何功能。 nympq1… 代码自己
根被显示为 nympq1… 在 nsec 旁邊,第二個裝置接受它插入到同一面板上. 這就是整個機制. 未給予代碼的裝置無法參與,這是在第 4.2 節中描述的。
手动传输是故意的,而不是第一个步骤。第 3.1 节的规则说,根可能永远不会在经典的仅加密下旅行,并且任何能够使此过程自动化的机制 - 通过传递器同步,将其包裹到身份密钥 - 完全违反了这一点。
格式为包装的路径留下了空间:一个记录可能携带一份包装列表,每一个在用户可以在另一台设备上复制的键下包含一个AEAD blob - 例如,一个passkey PRF输出。 nympq1… 代码是唯一的路径,第 10.1 节说明了成本。
记录本身生活在自己的设置类别中, nymchat-pq-root即使没有包裹,它也做出了必要的工作:它的存在是如何让第二个设备了解到这个身份已经有一个根,这就是它阻止了竞争对手的存在(3.3节)。
该 nymchat-pq-root 分类必须 不是 该行携带了根的唯一副本,所以在根源的钥匙下密封它是一个锁,其钥匙在盒子里:没有任何设备可以打开它,包括写它的人。
任何其他设置类别都可以使用并应该使用根源衍生密钥,这是唯一的例外,它是关于圆形性而不是强度的例外。
3.3 世代与收养
在启动时,持有持久的身份,客户端以以下顺序工作:
- 寻找现有
nymchat-pq-root纪录。 - 记录被发现,这个设备可以解开它 接受它并宣布这个身份为后量子能力。
- 记录被发现,此设备无法解开它 不要生成新的根,也不要发布任何公告. 请用户通过输入
nympq1…从已经有它设备的代码。 - 没有记录 生成根,发布记录,宣布并显示
nympq1…将代码发送给用户一次,以便他们可以保存。
第三步是容易犯错的步骤,这就是为什么订单被写下来而不是留给每个实现的原因。
4能力公告
衍生密钥对的公共一半被发布为可地址的
尼泊尔-01
事件 — kind 30078, 标签 nym-pq:
{
"kind": 30078,
"tags": [
["d", "nym-pq"],
["t", "nym-pq"],
["expiration", "<unix seconds>"]
],
"content": {
"v": 2,
"alg": "mlkem768",
"nym": 1,
"epoch": 0,
"pk2": "<base64url ML-KEM-768 encapsulation key>",
"exp": <unix seconds>,
"devices": [ ... ]
}
}
Addressable 意味着传递器保持每一个事件(kind, pubkey,d-tag),所以一个共和替代了以前的公告。因此,每个身份都有一个精确的当前记录,这就是为什么“Alice’s key”是一个单一的单一收集而不是一个清单来调和。
签名是具有约束力的。 事件是由身份密钥签署的,所以声称这个 ML-KEM 密钥属于这个 npub” 与 npub 本身完全一样强大。 替换不同的封装密钥需要铸造 secp256k1 签名。
公告截止。 七日 NIP-40 expiration,每24小时重新发布,记录一个关于客户端的声明,而不是曾经的客户端。
错过的公告就像从未携带过密钥一样被阅读:同行会发送普通的NIP-44,每个登录都可以读取,客户端在下一个连接时恢复后量子交换,当它重新发布时。
没有一个,一个记录超越了它命名的密钥:一个被删除,重置,或其根被更换的设备留下一个站着的指令,以封装一个密钥没有人再持有,并在其下发送的消息在任何一方都没有错误丢失。
关键字段命名其格式。 该领域是 pk2,而且数字是合同的一部分,而不是装饰:它命名了可以使用的密钥的负载格式。不识别字段的读者结束了“Nymchat客户端”,没有后量子密钥,并发送了普通的NIP-44,每一个登录都可以读到。这就是正确的失败方向,而且值得指出作为一个规则,格式编号存在于执行:一个未识别的能力声称必须支付保护费用,永远不会交付。
4.1 缺席是有意义的,是三重的
一个微妙但重要的细节:该公告由每个Nymchat客户端发布,不仅仅是后量子能力的客户端,而关键字段是可选的。
| 观察 | 方法 | 发送行为 |
|---|---|---|
| 与钥匙的公告 | Nymchat,后量子能力 | 混合物 |
| 公告,根本没有钥匙 | Nymchat,仅经典 - 后量子关闭,或尚未链接到身份根的设备 | 经典 NIP-17 |
| 无公告 | 未知客户端. 可能是任何Nostr或Bitchat用户 | 经典,加上兼容性包装 |
一个无钥匙的公告是一个签署的声明,即发送者运行Nymchat,这允许发送路径跳过一种投机性的跨协议包装,否则它将不得不包括给任何它无法识别的人。
4.2 无法打开根的设备保持沉默
该公告是可替代的:每个身份的一个事件,最后的写作赢得了。这就是让单个记录设计工作的原因,这也是如果它发布,如果一个未链接的设备变得危险的原因。
因此,一台知道根的设备存在,但无法打开它,它不会发布任何公告,它不会被打破,也不会被锁定出应用程序:它仍然会读取它有密钥的每个消息,仍然会以经典的方式发送,同时促使用户链接它。
5发现和发送的决定
客户会以两种方式学习同行的密钥。一个站式订阅覆盖了用户实际相应的人 - 开放的对话和群组成员 - 因此他们的公告作为普通事件到达。
一个用户连接一个新的设备,或谁从一个浏览器扩展的登录移动到一个本地密钥,成为后量子能力的中间对话,一个永久的缓存 “no”将保持他们在经典加密的广告的生命。
5.1 为什么没有下级攻击
路由决定减少到一个问题:
pq = (we hold a signed, unexpired ML-KEM key for this recipient)
没有能力谈判,没有支持的算法列表,没有攻击者可以清晰地强迫更弱的路径。 是 被剥夺或被阻止的公告的失败模式是,消息是经典的 - 此功能之前的现状 - 而不是混合消息被降级为可欺骗的东西。
反向也持有,而且更重要:客户发送混合动力 只有 当它持有密钥时,并持有密钥是收件人可以解锁的证据. 没有状态,在该状态下,一个消息被发送后量子到一个不能读取它的人。
6混合建筑
未经修改的 NIP-44 加密文本是内部层,而 ML-KEM 则在其周围键入外部 AEAD:
inner = nip44_encrypt(plaintext, conversation_key(sender, recipient))
info = "nymchat-pq2" || sender_secp_pk || recip_secp_pk || kem_ct || recip_kem_pk
prk = HKDF-Extract(salt = "nymchat-pq2-v1", IKM = kem_ss)
key = HKDF-Expand(prk, info || "key", 32)
nonce = HKDF-Expand(prk, info || "nonce", 12)
outer = ChaCha20-Poly1305(key, nonce, plaintext = inner, aad = info)
payload = "pq2." || base64url(kem_ct) || "." || base64url(outer)
两种秘密仍然需要被恢复才能读取信息:外层只能产生 NIP-44 加密文本,而打开需要经典的 ECDH。打破 secp256k1 的量子对手得到内钥,仍然面对 ML-KEM;一个打破的 ML-KEM 切断了外层,让 NIP-44 站立。
kem_ss没有任何东西在这个衍生中触摸到原始 ECDH 输出,这就是第 6.1 节发生的事情。kem_ct,recip_kem_pk两个身份密钥都被绑定为相关的数据,所以外层是致力于产生它精确的转录。
接收者的 ML-KEM 密钥是持久的,但每个消息携带独立的加密文本,因此独立的加密文本。
kem_ss这就是为什么衍生非词而不是随机化它听起来像:
1305
它是通过重复使用一个(钥匙,非钥匙)对而被打破的,在这里,每个消息的钥匙本身都是新的,所以没有一对可以重复。
6.1 为什么层保持分开
另一种选择是将这两个秘密混合成一个单一的对话密钥,并将其交给NIP44:
ck = HKDF-Extract(salt = "…",
IKM = ecdh_x || kem_ss || …) // do not do this
这种结构听起来像加密,它有一个结构性问题:它需要
ecdh_x,原始 x 坐标的 ECDH 输出,作为关键材料 - 和浏览器扩展(NIP-07)或远程签名者(NIP-46它代表呼叫者执行NIP-44并返回加密文本,这是把密钥放在应用程序无法到达的地方的整个点。
混合秘密因此排除了将身份密钥保存在签名器中的每一个登录,也就是说,最谨慎的用户,而对密钥衍生的任何工作都无法改变它。Layering消除了依赖性:NIP-44保持完整,由持有身份密钥的任何东西生产,包括签名器,而KEM的一半是从客户端直接持有的恢复代码计算出来的。
成本是带宽,它并不小. ML-KEM 加密文本是 1,088 字节,并在每个消息上运行, base64url 编码为 1,451 个字符;外部 AEAD 添加了一个 16 字节的 Poly1305 标签,并将它包装的 NIP-44 有效载荷扩展到三分之一。 50 字符的消息从 176 字节增长到 1,712 个字符,而 2,000 字符的消息从 2,820 到 5,238 个字符。
6.2 自我描述 payloads
该 pq2. 序列使部署增量:它是自我描述的,所以客户端通过检查有效负载而不是信任标签或记住同行支持的东西来选择解密路径。
因此,错误的密钥在KEM层上根本不会出现 - 它在NIP-44内部出现HMAC故障,这就像错误的经典密钥表面一样。呼叫者对待两者都是一样的,所以失败没有任何区别信号。这也是第 9.1 节的候选人列表可行的原因:一个客户端反过来尝试每个密钥,并让NIP-44说出一个是正确的。
6.3 礼品包装的两层
A NIP-17 个人信息是 a NIP-59 礼物包装:一个未签名的谣言,在发件人的身份密钥下封闭(13类),然后在每条消息(1059类)生成的投放密钥下封装。
一个签名者登录只获得外层。该密封由签名者作为普通的NIP-44生产 - 应用程序永远不会看到创建密钥 - 因此它不能在场合混合化。 这对所涉及的攻击没有成本:密封只能通过包裹到达,包裹是录音机存储的东西。
7群组消息和部分覆盖
一个群组消息不是一个加密文本. 它是给每个成员发出的相同的单文本,每个副本都封装在该成员的ML-KEM密钥中。
这会造成一个会计问题,而一个天真的执行错了。如果十个成员中有八个收到混合副本,则消息是 不是 一个对手需要一个经典的原文复印件,在所有十个都相同,所以信息只有在保护时才能得到保护。 每一个 副本是。
因此,Nymchat在粉丝发出期间追踪每条消息的覆盖范围 - 只有在包裹正在建造时才可知 - 标志报告“10个成员中有8个是量子抵抗的”,而不是声称消息是受保护的。 接收 群组消息(只有发件人计算了粉丝),界面报告部分而不是完整的保护。
7.1 什么是盾牌报告
盾牌说出关于真相的 消息,不是关于发送它的软件:
- 完整的保护:这个文本的每个副本都是混合的。
- 部分:一组消息的一些副本是经典的,而不是完整的,因为一个经典的原文副本在所有这些都是对手的需求。
- 经典表示是直接的,而不是显示为没有标志,因为一个缺少的指标在 “不受保护”, “破坏”和 “这个构建缺乏功能”之间是模糊的。
当消息被封闭而不是从同行广告中重新计算时,判决被记录下来. 已经存在的数字文本不能比以前更受保护,并且重塑旧消息的新公告的界面将声称在传递上的字节上有一些错误。
上面的组规则堆积在上面,而不是取代它:一个组消息只有在每个成员的副本被完全保护时,而没有覆盖数的收到组消息显示为部分。
8写给自己写的副本
客户端存储的几件东西被加密到用户的身份:同步设置、对话列表、组密钥和消息档案. 这些内容比大多数单一消息的内容都多,所以将它们保留在经典中会使它们成为最弱的存储文物,无论这些消息自己被密封多么仔细。 nymchat-pq-root 类别本身,它不能被密封在只有它才能生产的钥匙之下。
这些文物是设计最重要的地方。设置blob或档案行坐在一个地方多年,这正是收获现在解密后对手收集的东西的形状 - 远比任何单一消息,这在用户自己的头脑中至少是短暂的。
一个约束性规定了此处的格式,而不是密钥。 每一个 每个设备在帐户上的设备,所以每个设备在其公告所载的列表中宣传它可以打开的内容,帐户只写出他们所有人都能读到的内容。
一个持有身份但不是根的设备不能打开对根衍生的密钥密封的东西,包括其自己的设置。这是一种故意的后果,而不是监督,这就是为什么第3.3 节有这样一个设备提示来链接而不是引用新根:第二根不会使blob可读,它只会把身份的关键材料分成两部分。
驱动浏览器扩展或远程签名器(NIP-46)的设备没有 nsec 可以从中获取,但它确实拥有恢复代码,并且在第 6.1 节的层结构下,这是所有后量子的一半需要的:签名器会像往常一样产生 NIP-44 层,而客户端则会键入外层本身。
9旋转
该 epoch 在衍生中的计数是使旋转无需新密钥材料成为可能的。增加它会产生来自同一个根的新密钥对和重新发布的公告;同行会从可更换的记录中获取新密钥。
9.1 旧时代被保留,没有任何东西被重新加密
当一个密钥旋转时,没有任何东西被重写。客户端从当前的时代创建了解密候选人,直到“3”时代,所以在旋转前不久封闭的消息仍然打开了发送时当前的密钥对。
这个窗口是使旋转完全安全的:没有它,每一次旋转都会破坏飞行中的任何东西。
10这不保护什么
只列出一个设计所取得的成就的论文不是描述一个系统,在接口中夸大一个安全属性比忽略它更糟糕。
根是第二个秘密,失去它是不可挽回的。
这是设计的真实价格。第 2 节的限制,即用户应该有完全一件事要保留,不能满足:单靠 nsec 无法重建后量子密钥,因为整个问题是没有任何公共价值和其他秘密暴露它。如果没有设备持有根,并且没有任何第 3.2 节的包裹可以打开,将密封到根源密钥的材料无法恢复。
使用手动传输的唯一路径,这比它可能首先读到的更清晰。 nympq1… 任何地方的代码都有一个副本,在一个设备上,失去该设备会失去每个后量子消息,设置blob和存档行。
nsec 它没有帮助;这就是整个设计所依赖的财产。
攻击者攻击可用的最便宜的路径,所以一个方案的价值是其最弱的恢复路径的价值 - 例如,一个难忘的密码短语会把整个东西放在任何密码短语的价值上,而被包裹的行是收获后被解密的对手收集的精品,并在休闲时脱机磨炼。
10.2 身份验证,不同于保密性
在Nostr中,每个签名都是Schnorr在secp256k1上,这在这里没有变化。使用量子计算机的对手可以伪造签名,并在实时假装用户。混合密钥交换的失败是收获现在解密后:今天记录流量的攻击者无法稍后读取它。它不会使对已经拥有机器的对手不可原谅的消息。这种区别被刻意带入应用程序 - 板块指示器报告身份验证,盾牌报告保密性,它们是单独的格利夫,因为一个消息可以没有另一个。
它还限制了第 4 节中的公告所能承诺的内容。NPUB 和 ML-KEM 密钥之间的绑定是 secp256k1 签名,因此可以伪造这些密钥的对手可以取代自己的密钥。
10.3 元数据
礼物包装隐藏了发送者,接收者超越一个单一的 p 标签,内部消息的类型和时间标签. 它不隐藏事件的存在,其大小,或转发器接收它时。
10.4 离线网格
Nymchat的Bluetooth网格传输是一个独立的协议,有自己的握手,它不被这项工作所涵盖。
10.5 已发送的消息
數字文本記錄,而雙方仍然是古典仍然是古典永久性. 它已經存在並不能重新密封. 保護始於訊息,其中雙方持有後量子鍵,而不是在功能被啟用時。
11考虑的替代方案
| 接近 | 为什么不 |
|---|---|
| 从身份密钥中提取后量子密钥 | 衍生是对 nsec 的公开算法,一个量子对手从出版的 npub 中恢复了 nsec,因此打破了经典的一半手在后量子的一半上。 |
| 通过 NIP-44 将 root 发送到用户的其他设备 | 由于相同的原因在不同的服装中被拒绝. 在经典的加密下传输的根可以被任何记录了该消息的人恢复,并在以后破坏它的钥匙,这是根存在的对手来阻止。 |
| 每个设备上单独生成的 ML-KEM 键盘对 | 被拒绝. 设备将持有不同的解密密钥,每个身份的一个可替代的公告不能携带所有密钥。 同行将加密到最后发布的密钥,其他任何设备都无法读取结果。 |
| 将根包裹在一个PIN下 | 被拒绝. 四位数的PIN大约为13位,而非线攻击者则持有被包裹的行。 |
| 扩展 npub 以携带两个密钥 | 1,184 字节不是一个可共享的标识符,它会破坏每个现有 Nostr 客户端对定义为 32 字节的地址的分析。 |
| 关键目录服务 | 重新引入网络存在的权威,以避免,谁回答搜索决定谁可以阅读消息。 |
| 将密钥附加到每个消息 | 解决不了问题:发送者需要 接收者 关键在第一条消息之前,这恰恰是没有先前的消息传递它的情况。 |
| 乐队内谈判能力 | 创建一个降级表面. 一个可以剥夺能力旗帜的攻击者强迫经典路径。 |
| 只有后量子,没有经典的腿 | 抛弃了数十年的secp256k1分析,以换取一个更年轻的原始生物。 两个都 失败。 |
12实施平等
Nymchat提供两种独立的构建实现 - 一个用于Web应用程序的JavaScript,一个用于移动应用程序的Dart,包括一个从头开始的ML-KEM-768端口。
Dart ML-KEM 实施对官方进行验证
NIST ACVP
(ML-KEM-*-FIPS203) — 25 个密钥生成, 25 个封装和 10 个封装案例,作为自己的套件运行。 这些是 NIST 发布的验证实现的矢量,因此通过它们是对端口的正确证据,而不仅仅是证明两个客户端彼此同意的证据。 此外,测试矢量的共享固定 - 种子衍生,封装,负载格式和完整礼品包装 - 来自 JavaScript 参考并由两个测试套件检查。
nympq1… 任何一个实现中的差异都会导致构建失败,而不是产生另一个客户端无法打开的消息。
这是一个设备告诉我,这是我持有的根源,从 “这是一种不同的根源,并且一个客户端无法复制另一个客户端的指纹会读到一个完全好的记录,因为没有任何记录 - 然后,按照3.3 节,挖掘第二根和分割身份。
Nymchat 是 开源 AGPL-3.0. 这里描述的加密核心是
js/nym-crypto.js 和 js/modules/pq.js 在网络客户端,以及
lib/core/crypto/ 与 lib/features/identity/pq_registry.dart 在移动客户端。
对于更简短的非技术解释,请参见 关于量子抵抗加密的知识库页面.