本文为参考译文。规范以英文版为准,如有出入,以英文版为准。 阅读英文版

安全模型

一个 Label 309 验证器信任什么、不信任什么——可独立验证(standalone-verifiable)这一不变量、密封 PoE 的隐私保证、每个实现都必须遵守的规范性密码学规则,以及这套线格式(wire format)已知的边界。

Label 309 的安全性建立在一个理念之上:证明是依赖方自己就能核查的东西。验证器信任公共的 Cardano 链,对于内容相关的主张,还信任别人交给它的那串字节。除此之外它什么都不信任:不信任发布者,不信任域名,不信任任何服务器,也不信任它恰好通过哪个网关读到这笔交易的运营方。本页上的每一项保证,要么由这一立场推导而来,要么标明它的边界,要么点明落在这一立场之外的某项残余风险。

本页是标准本身的威胁模型:一个合规验证器所处的信任模型、一个未签名密封 PoE 的隐私属性、每个实现都MUST遵守的密码学义务,以及线格式无法、也不可能克服的边界。它描述的不是某个具体部署的运营安全,而是这套格式向每一个正确实现它的人所作出的保证。

信任模型

核查一份 Label 309 证明的依赖方,问的是一个很窄的问题:这串字节是否在这个区块的时间或更早之前就已经存在,而这个结论除了 Cardano 之外,是否还要仰仗任何人的诚实行为? 标准的设计让后半个问题的答案永远是「不」。任何一步都不存在受签发者控制的中间人:验证器去取数据的那个链网关和存储网关,都是不受信任的数据来源,能用密码学核验的地方就用密码学核验,并就包含性与最终性做交叉比对。

验证器所信任的:

  • 公共的 Cardano 链。 这笔交易、它在 label 309 下的元数据,以及它的区块时间,都是在每一个 Cardano 节点上复制保存的事实。验证器通过自己选定的网关来读取它们,还可以交叉比对多个网关。
  • 内容字节,在核查内容相关主张时。 记录中的摘要承诺了一段特定的字节序列。持有这些字节的验证器会重新计算哈希并加以比对,而不会凭别人一句「字节没问题」就采信。

验证器信任的:

  • 发布者。 Label 309 没有 issuer 字段。任何钱包都可以发布任何记录;记录是否有效与是谁提交的无关。验证器从不去问「这个发布者靠谱吗?」——这个问题在模型中根本没有立足之地。参见导言中的不依赖签发者(issuer-agnostic)不变量。
  • 服务器或域名。 验证过程中没有任何一步会联系发布者运营的服务。没有可供查询的权威注册表,没有可供轮询的状态端点,也没有可供据以解析某个密钥的身份机构。
  • 网关运营方。 验证器借以读取的 Cardano、Arweave 和 IPFS 网关都是可互换、且经过内容核验的(见下文)。网关只是传输通道,而非信任根。

可独立验证是核心安全属性

即便创建一份 Label 309 证明的每一方都消失了——发布者、他们的服务器、他们的公司——这份证明也必须仍然可核查。只要给定一个交易引用、一个公共 Cardano 网关,以及(对内容相关主张而言)那串字节,任何合规实现仅凭公共链就能得出相同结论。如果某条验证路径在不声不响中需要某个特定运营方的配合,那么这条路径就是不合规的。

为什么网关不是信任根

验证器可以通过任意 Cardano 网关读取同一笔交易,也可以通过任意 Arweave 或 IPFS 网关读取同一份内容。这些网关没有一个被信任会返回诚实的数据,因为它们返回的东西本身是可以独立核查的:

  • 网关无法伪造一份带有有效签名的记录——它没有签名方的私钥,而验证器会自己重新计算被签名的字节并加以核查。参见签名
  • 网关无法在不被察觉的情况下替换内容寻址 URI 下的内容:一个 ipfs:// CID 是一个 multihash,验证器会根据取回的字节重新计算它;一个 ar:// 交易 id 则在 Arweave 共识下承诺了对应的数据。对于一份合规的条目,链上的 hashes 映射是对明文的第二道、相互独立的绑定,会在取回时重新计算。
  • 网关如果扣留一笔交易,或者谎报它被埋得有多深,那是一种拒绝服务的形态,而非伪造的形态。验证器只要通过不止一个网关来读取、并在出现分歧时中止,就把「一个不诚实的网关」变成了「它所选的每一个网关串通作恶」——而这一点掌握在它自己手里。

区块深度——验证器在把一份记录视为已定案之前要求多少个确认——属于验证器的策略,而非 Label 309 强制规定的取值。标准不固定任何具体的数值深度;依赖方按自己的风险承受度来设定阈值。参见验证

密封 PoE 的隐私属性

密封 PoE 在锚定时间戳的同时,让明文保持机密。在机密性之外,这套构造的设计还要做到:链上尽可能少地泄露关于消息的信息,并且对于它的受众一点都不泄露。这些都是线格式层面的保证:它们对任何正确的实现都成立,因为它们是字节的属性,而非任何部署的属性。

未签名密封 PoE 的发送方匿名性

一份未签名的密封 PoE——即带有 enc没有 sigs 的记录——MUST NOT在链上披露发送方的身份。它的线上字节MUST与发送方的长期密钥无关。

这一点在结构上就成立:

  • 线上没有签名方密钥。 发送方的身份密钥只有通过 sigs 条目才会上到链上。没有 sigs 的记录里根本不携带任何来自签名方的字节。作者身份是可选加入的;匿名是默认状态。发送方的长期 X25519 和 Ed25519 密钥,绝不出现在一份未签名记录里。
  • 每个槽位都用全新的封装。 每个接收方槽位都携带逐记录、逐槽位的 KEM 材料,在密封时新鲜生成——x25519 路径上是 slot.epk 里的 X25519 临时公钥,mlkem768x25519 路径上则是 slot.kem_ct 里的 X-Wing 密文。它与发送方的长期密钥无关,也不会泄露任何能把多份记录关联到同一作者的信息。
  • 打乱的槽位。 槽位数组在发布前会用 CSPRNG 打乱,因此位置顺序(「主接收方排在最前」)不携带任何信号。

之后给某份记录签名的生产方,只在那一份记录里披露自己的身份密钥;它不会出现在更早的任何未签名记录中,也没有任何操作能把更早记录中的逐槽位 KEM 材料反向映射回作者。身份 X25519 与 Ed25519 密钥是用不同的 HKDF 上下文字符串从主种子派生出来的,而一个 Ed25519 公钥既推不出、也匹配不上某个 X25519 公钥,因此给某一份记录签名,绝不会把那些未签名记录关联起来。选择加入作者身份是一个向前的动作,而非对过往的去匿名化。

这一不变量只覆盖 label-309 记录的字节。交易输入中支付手续费的 Cardano 地址、生产方在网关眼中的网络层身份,以及链下密文 blob 里的任何元数据,都在线格式之外——Label 309 存在于元数据之中,而非交易输入之中,因此无法抵御针对手续费支付方的关联。需要手续费支付方匿名性的生产方,必须在交易构造层去解决这个问题。

接收方不可关联

一份密封记录从不点名它的接收方。接收方只能通过成功试解一个槽位来识别出某条消息是发给自己的;没有可读取的收件人字段。由此引出两条彼此不同的保证,二者绝不可混为一谈。

第一条对两种 KEM、对任何观察者都成立:

  • 接收方彼此看不见对方。 每个槽位都是一把独立封装的密钥。打开了自己槽位的接收方,从中推不出关于任何其他槽位的信息,也无从得知还有谁被列为收件方。
  • 线上没有接收方公钥。 出现在线上的只有逐槽位的封装(epkkem_ct)和那把被封装的密钥。一个手里没有任何候选密钥的观察者,无法枚举出接收方——它只能获知槽位数量、KEM 族系(enc.kem),以及密封–非密封之分。

第二条——面对一个已经握有一组候选接收方公钥、并想检验某个槽位是否发给其中某人的对手,所提供的接收方匿名性——是一条更强、且因 KEM 而异的属性,标准只为经典路径声称它:

  • x25519 是密钥私密的。 逐槽位的 epk 是一个全新的临时公钥,与接收方密钥在统计上相互独立。仅凭 epkwrap,一个握有候选密钥的对手在没有配对私钥的情况下,无法判定某个槽位(若有的话)发给的是哪一个候选。
  • mlkem768x25519 不声称这一点。 面对一个握有候选密钥的对手所提供的接收方匿名性,是一条并不由混合 KEM 的 IND-CCA 安全性所蕴含的独立属性;在它针对 X-Wing 得到独立论证之前,不为 X-Wing 路径声称它。威胁模型要求面对握有候选密钥的对手仍保有接收方匿名性的部署,MUST NOT在这一属性上依赖混合路径。上面那些诚实泄露——槽位数量、KEM 族系、密封–非密封之分——对两种 KEM 完全一致;关于接收方,任何一条路径都不会再多暴露任何东西。

明文绑定

链上的摘要承诺的是明文,而非密文。解密一份密封 PoE 的接收方会重新计算明文的哈希,并将其与链上的 hashes 映射比对。这意味着:如果某个存储层提供的是被篡改过的密文,会在解密之后被抓出来,与该 URI 自身的完整性模型无关——时间戳主张是关于发送方所承诺的那段明文的主张,网关无论做什么,都无法用另一串字节来满足它。

重播相互独立

一份记录的签名在它的交易被提交那一刻就已固定。链上没有「不断扩充的签名方集合」横跨多笔交易这种概念。

把一份完全相同的记录体放进另一笔交易、配上一组不同的 sigs 重新广播,会在它自己的区块时间上创建一份独立、互不相干的记录——绝不会成为原记录的延伸。

想要为一份已发布记录背书的一方,发布的是他们自己的记录——引用同一份内容,或通过 supersedes 指向先前那笔交易——并用自己的密钥签名。他们不会往别人的记录上追加见证。因此,把一份已签名记录的字节复制进一笔新交易的攻击者,并不会因此成为原记录的共同签名人:他们产出的是一份更晚的、重复的主张,配着他们自选的 sigs,而原记录更早的区块时间仍然是存在性的权威凭据。验证器和索引器MUST NOT把记录体相同的两笔交易合并成单一的「合并签名方」视图;这么做会篡改逐记录的区块时间语义,而下游消费方正是依赖这套语义的。

实现须遵守的规范性规则

以下各项对任何合规实现都是安全规范性的,与语言或平台无关。文中的 RFC 2119 / RFC 8174 关键词均按其规范性含义使用。

规则义务
不自创密码学每一个原语都是有文档记载的 IETF / NIST / W3C 标准,且来自经过审计的库。不得自行拼凑密码、KDF、签名方案或 stanza。
仅使用认证加密所有对称加密都是 AEAD。标签校验失败MUST抛出有类型的错误;它MUST NOT在不声不响中返回部分明文。
对秘密做恒定时间比较对 MAC 标签、AEAD 标签,以及任何由秘密派生出的字节,都要做恒定时间比较。在这些面上做依赖数据的比较,等于开了一个认证预言机。
严格的 Ed25519 验证以规范(严格)模式验证签名。带余因子的/宽松的验证器会接受严格验证器所拒绝的边界情形签名,从而与参考实现产生分歧。
绝不记录秘密或明文密钥材料、派生密钥,以及解密后的明文MUST NOT出现在任何级别的日志中。字节类型的值一律无条件做脱敏处理。

不自创密码学

Label 309 刻意只接纳经过充分分析的原语,每一个都在算法注册表中具名,并由经过审计的库实现。实现MUST NOT引入自定义的对称密码、KDF、签名构造或密钥封装 stanza。线格式之所以是标准部件的一种组合,正是为了让它的安全性归约为这些部件的安全性。

仅使用认证加密

Label 309 中的每一次对称加密——内容层和逐槽位的密钥封装——都是 AEAD。这套格式里任何地方都不存在未认证的密码模式。AEAD 标签校验失败的解密方MUST给出一个结构化的错误,并带有稳定的代码判别值,且MUST NOT继续往下走、返回未经认证的字节。附加认证数据是标签计算的一部分,因此错误的 AAD 与错误的密钥会以完全相同的方式失败——不存在可供探测的「部分成功」状态。

对秘密做恒定时间比较

一个运行时间取决于秘密字节的比较循环,就是一个预言机。任何针对秘密、或针对 MAC/AEAD 标签的相等性检查,都MUST是恒定时间的:

  • 槽位集合的 MAC,以及任何 HMAC 标签——在一个 32 字节的 MAC 上逐字节的时间泄露,在对手反复施加的情况下可以把标签恢复出来。
  • AEAD 标签验证——底层库的 decrypt 已经会在不暴露哪个字节不匹配的情况下返回失败;实现MUST NOT自己重新实现这道检查。
  • 签名验证——使用库的 verify,绝不要自行拼凑等式。

在涉及上述任何一个面的字节数组上裸用 == / === 就是一个缺陷。把每一次这样的比较都路由到单一的有类型辅助函数中,能让这一属性可被审计。

恒定时间的纪律延伸到试解循环的形态,而不止于单次比较。为了守住隐藏接收方这一属性,接收方验证器MUST在单把私钥的那一遍里处理所有槽位——每把密钥固定数量的槽位运算,不在首次命中时提前跳出——这样一个有时延观测能力的网络层观察者,就无法推断某把密钥解开了哪个槽位下标,乃至是否解开了任何一个。候选密钥以恒定时间方式选定。X25519 份额的有效性由一个秘密无关的比特捕获,kem_ok = NOT constantTimeEqual(shared, 0^32),它用一次恒定时间比较算出,而非一次提前分支;随后 KEK 是在真实 KEK 与一个由全零共享密钥在相同 salt 和 info 下派生出的虚值 KEK 之间的一次恒定时间选取,而 kem_ok 被折进该槽位的接受判定(ok = kem_ok AND open_ok AND mac_ok),于是一个无效 ECDH 的槽位绝不会被接受、而循环仍做着同样的工作。槽位数组以 CSPRNG 打乱后的顺序发布,因此线上位置本就不携带「主接收方排在最前」的信号;与那一遍恒定时间扫描相结合,观察者最终只能获知槽位数量。一个持有多把私钥的接收方(例如身份轮换中跨越的归档密钥)会按密钥 × 槽位来迭代,并MAY在密钥之间提前短路——这只泄露「哪把密钥命中了」这一弱信号——但MUST在任何单把密钥的各槽位之间保持恒定时间,并按密钥重新派生那一半 KEK salt 的接收方密钥项,因为两种 KEM 都把该 salt 绑定到那把密钥的规范线上编码(恰好是那 32 字节的 X25519 公钥,或那串钉定的、恰好 1216 字节的 X-Wing 公钥字节——绝不用任何非规范的重新编码)。

严格的 Ed25519 验证

Label 309 的签名MUST以严格(规范)模式验证。一个宽松的、带余因子的验证器会接受某些可塑的或低阶密钥的签名,而严格验证器会拒绝它们,这会让两个合规实现对同一份记录得出不同结论——破坏整套标准所依赖的单一结论属性。跨语言一致性语料库专门钉住了一个低阶测试向量,以便抓出那些悄悄滑进宽松模式的验证器。参见签名

绝不记录秘密材料

种子字节、派生私钥、密钥加密密钥、内容加密密钥、口令派生材料,以及解密后的明文,都MUST NOT进入任何级别的日志。在日志器配置里做基于路径的脱敏是必要的,但单凭这一点还不够:嵌套层级比脱敏 glob 所能触及的更深的值有可能漏过去,因此实现MUST同时把每一个字节类型的值(Uint8Arraybytes 之类)一律当作不透明、需脱敏来对待,哪怕是像 nonce 这种协议上本就公开的值。最省心要保护的秘密,是那种压根不进入日志行的秘密。

密封 PoE 的构造保证

还有三条属性专属于 密封 PoE 这套构造。对任何生产或打开密封记录的实现,它们都是安全规范性的。

槽集 MAC 是一道约 128 位的承诺

正是槽集 MAC,使得一把恢复出的内容密钥成为对接收方所命中的那个槽位集合的一道承诺:恶意发送方无法构造出两个互不相同、却都被同一个接收方认作「自己的」槽位集合。这里所需的性质,是 RFC 9771 意义上、对信封内容密钥的限制性密钥承诺(restricted key commitment)——恢复出的密钥绑定到单一 slots 转录——而不是一个对任意输入都成立的完整承诺式 AEAD。接收方从某个槽位派生出一把密钥,随后要求同一把密钥能就着 slots 转录哈希复现出 slots_mac,才把该槽位认作自己的。具体而言,这道承诺所凭借的,是下式对对手所选的 CEK 和转录的多密钥抗碰撞性:

CEK ↦ HMAC-SHA-256( HKDF-SHA-256(CEK, info="cardano-poe-slots-mac-v1"), slots_hash )

由于对手同时掌控 CEK 和转录,相关的界是那个 256 位 HMAC 输出上的通用碰撞(生日)界:要找出两对 (CEK, transcript) 产出同一个 slots_mac,是一次约 128 位的搜索,而非 256 位的第二原像级别。这道承诺所凭借的安全级别,就是这 128 位的通用碰撞余量,对本威胁模型而言足矣。

在做 HMAC 之前把转录预先哈希成一个 32 字节的 slots_hash,并不会削弱这一点:一次 slots_mac 碰撞,要么意味着一次 slots_hash 碰撞(一次 SHA-256 碰撞),要么意味着在相等 slots_hash 之上那个加键 HMAC 的一次碰撞,二者都处于约 128 位级别。转录自身的防篡改性继承了 SHA-256 的 2^128 碰撞界:对任何一个被承诺的头部字段或槽位字节的改动都会改变 slots_hash,而要在一个不同转录上伪造出未变的 slots_hash,恰恰就是那场约 128 位的碰撞搜索。一个直接的推论是:逐槽位的包裹无需是一个承诺式(committing)AEAD——承诺由 slots_mac 提供,而非由包裹提供,因此一个非承诺式的包裹(默认的 ChaCha20-Poly1305)在这里是稳妥的。

零随机数包裹的安全性,系于逐槽位密钥唯一性

每个槽位都在一把逐槽位密钥加密密钥之下、用 ChaCha20-Poly1305 以一个零随机数来包裹内容密钥。零随机数之所以安全,仅仅因为那把密钥加密密钥是逐槽位的、且只用于恰好一次包裹:每个槽位都抽取全新的 KEM 随机性,因此包裹密钥在每个槽位上唯一,碰撞概率可忽略。安全条件恰恰就是逐槽位密钥唯一性,而生产方MUST NOT引入任何可能让某对 (key, nonce) 重复出现的因素——某次重复了 KEM 随机性的 CSPRNG 故障、确定性的或会相撞的封装、跨记录复用某个槽位、按 (recipient, ephemeral) 缓存某把派生密钥,或是把同一接收方的两次出现去重并成一个槽位。

这干净利落地分成一块验证器可检查的部分和一项纯属生产方的义务:

  • 记录内部的重复由验证器拒绝。 验证器MUST要求封装材料在单个 slots[] 内彼此互异——x25519 路径上所有 epk 值互异,mlkem768x25519 路径上所有 kem_ct 值互异。出现重复时,会在任何 KEM 或 AEAD 原语运行之前就以带类型的代码 ENC_SLOTS_DUPLICATE_KEM_MATERIAL 被拒绝。一致性语料库用一条携带两个共享同一 epk(或 kem_ct)槽位的记录来检验这一点,并要求出现这种拒绝。这项拒绝只在 epk / kem_ct 重复时触发;它并不禁止向同一个接收方密封两次,因为诚实的重复仍会为每次出现抽取全新的逐槽 KEM 随机性(参见下文的多命中规则)。
  • 跨记录与跨密钥的复用是生产方的义务。 没有任何验证器能察觉某个生产方在两份不同记录或两位接收方之间复用了 KEM 材料;那落在任何单份记录的字节之外,仍然是生产方的责任。倘若某个未来修订版允许密钥复用,就必须在同一次变更里把零随机数换成一个全新的随机数。

多个命中的密钥槽:填充是允许的,CEK 冲突不是

一把接收方私钥 MAY 合法地命中不止一个槽位。把同一把内容密钥、向同一个接收方、跨好几个槽位密封——每个配各自全新的逐槽临时密钥——是一种正当的填充与隐私技术,它在不暴露其中两个槽位共享一个接收方的前提下,撑大了可见的槽位数量。它有别于上文记录内部的重复封装拒绝:诚实的重复会抽取全新的 KEM 随机性,因此它的 epk / kem_ct 各不相同,绝不会触发 ENC_SLOTS_DUPLICATE_KEM_MATERIAL。因此验证器 MUST 选取第一个命中槽的内容密钥,且 MUST NOT 仅因不止一个槽命中就拒绝。

验证器唯一 MUST 拒绝的反常情形,是两个命中槽位恢复出不同的内容密钥(以恒定时间比较)。试解密循环跨所有槽位携带一个 cek_conflict 比特,若任何靠后的命中恢复出一把与已选密钥不同的密钥,便交出那个唯一的通用失败。这是纵深防御:在上文那道承诺属性之下,一个不同密钥的命中本就不可行——它恰好是那道属性所排除的那种多密钥碰撞——因此这项检查会对一个有缺陷的实现、或对该假设的未来削弱,安全地失败关闭。那个不同密钥的反例无法构造成一个字节向量(构造一个出来就会是那道承诺碰撞本身),所以这条性质是以实现层面的行为测试来钉住的,而非用一个固定向量,并配以一个针对合法接收方重复的正面固定向量。

在任何原语之前给解析器资源设界

验证器 MUST 在动用任何 KEM 或 AEAD 原语之前给解析器的资源消耗设界,使一个超大的信封无法在被拒绝之前就强加工作。参考上限是 MAX_SLOTS = 1024 槽,以及解码后的 enc 信封 65536 字节——两者都远高于约束任何诚实记录的那道约 16 KiB 的 Cardano 交易元数据天花板,因此一条超过任一上限的记录即为畸形。超量计数报 ENC_SLOTS_TOO_MANY,超大信封报 ENC_ENVELOPE_TOO_LARGE。这些都是由验证器强制执行、部署期钉定的常量——而非线上字段——某个部署 MAY 把它们收得更紧。口令路径上类似的防护,是在规范化和 Argon2id 之前 强制执行的一道原始口令输入最大长度(参考上限 4096 UTF-8 字节,MAX_PASSPHRASE_INPUT_BYTES),使一段病态的口令无法驱动一次 KDF 之前的拒绝服务。

X-Wing 公钥有效性给混合下限划界

混合 mlkem768x25519 KEM 绝不跌破 X25519 经典安全性这道下限——但那条下限是限定在合规生成的接收方密钥之上的。XWing.Encapsulate MUST 对接收方密钥施加钉定的 X-Wing 修订版的公钥有效性检查,并拒绝向一把通不过它的密钥封装,而不是把一把畸形密钥当作仍带着那条经典保证。一个跳过该检查的生产方,就为那个接收方放弃了这道下限;这项检查正是这道下限成立的前置条件。

受限的载荷大小

内容层是一个分段 STREAM:明文被切成 65536 字节的块,每块都在内容密钥之下、配一个各不相同的计数器 nonce 加以封装。那个 88 位的逐块计数器允许 2^88 个块,而每一块都稳稳处于 RFC 8439 单次调用上限之内,因此——不同于一个对整段明文的单次(single-shot)AEAD——这里不存在任何计数器溢出导致的密钥流碰撞需要防范,这套格式也不施加任何密码学层面的载荷天花板。于是一个部署所强制的上限,是一道拒绝服务策略,而非一道密码学边界:生产方和验证器SHOULD把载荷大小限制到适配自身资源的程度,并随着流被写入或读取而增量地强制它,在一份过大的载荷被缓冲之前就中止。截断由逐块的末块标志在结构上捕获——缺失的末块、尾随数据,或一个短的非末块都会导致解密失败——而非靠一次大小检查。这套姿态在槽位路径和口令路径上完全一致。

作为安全属性的算法可演进

Label 309 记录中的每一项密码学选择——哈希、AEAD、KEM、KDF、签名方案——都由一个来自可扩展注册表的字符串来命名。这不是一种风格上的取舍;它是让这套格式得以比任何单一算法活得更久的迁移基底。

如果某个原语被密码分析削弱:

  1. 相应的注册表会新增一个后继标识符。现有格式中的一切都不改变——新名字是叠加式的。
  2. 现有记录保持有效。 对于历史记录,验证器会继续识别旧的标识符;它们的区块时间主张不会因为存在某个更新的算法就蒸发掉。实现MAY把仅仅依赖于某个已被削弱算法的记录标示为较低保证级别,但MUST NOT仅凭这一点就抹除或拒绝它。
  3. 新记录在后继标识符下发布。 想要纵深防御的生产方MAY用来自不同设计族系的两种算法来承诺同一个内容哈希,这样单一族系被攻破也不会让记录的证据价值崩塌——尽管单算法记录仍然完全合规。

由于标识符是字符串、注册表又是开放的,迁移到后量子哈希、KEM 或签名只是一次叠加式的注册表变更,绝不是一次破坏性的格式修订。注册表中已有的混合后量子 KEM,正是这一属性在实践中运转的一个实例,而非临时加挂上去的特例。

标准的已知边界

以下这些并非有待在后续修订中修复的缺陷;它们是在一个永久公开账本上锚定证明、以及用长期密钥做公钥加密所固有的属性。一个诚实的安全模型会把它们一一点明。

链上的永久性

Cardano 的元数据是永久且全球复制的。一份已发布的 Label 309 记录无法被删除、抹去或作废——这种持久性正是存在性证明的全部意义所在。由此带来的是一份发布时刻的责任:一个内容哈希、一个与权益关联的签名,或是其他任何在 label 309 下承诺的东西,都将永远留在链上。Label 309 刻意从记录中略去了自由形式的描述性元数据,以把链上占用降到最低,但生产方在发布之前MUST清楚:自己所发布的内容是不可撤回的。

接收方数量可见

密封 PoE 从不点名它的接收方,但接收方槽位的数量就是数组的长度,在链上一目了然——KEM 族系(enc.kem)和密封–非密封之分也同样可见。接收方公钥不在线上、槽位也经过打乱,但用来隐藏数量的填充并不属于这条基线。对于本身就把接收方数量视为敏感信息的发送方,必须在运营层面顾及这一元数据泄露——例如自己给槽位集合做填充,或者改用多份单独的记录来发布。

没有逐接收方的撤销

不存在任何链上原语,能撤销某个接收方对一份已发布密封 PoE 的访问权。一旦记录被加密给某个公钥并锚定,这道绑定就是永久的。这是用长期密钥做公钥加密所固有的属性,而非格式的缺口。被认为已泄露的密钥要往前去处理——把新记录发给一个全新的密钥,必要时再发布一份取而代之的记录作为「买家自负」的信号——绝不能回头去改动链上已有的东西。

接收方无前向保密,这是有意为之

持有某个接收方私钥的人,可以永远解密曾经发给该密钥的每一份密封 PoE。链下密文是永久的,又没有撤销原语,因此一份密封记录是对世界保密、却不对它的接收方保密的。这正是用长期公钥加密所应有的行为。需要限制某个接收方未来触及范围的发送方,必须加密给一个短期的接收方密钥,而不是长期的身份密钥——这是发送方一侧的选择,格式允许,但不强制。

试解失败不泄露任何密钥材料

接收方通过试解发现自己的槽位。在内部——只为一个受信任的本地调用方做诊断——失败路径携带各不相同的带类型代码:「在我的密钥下没有打开任何槽位」(WRONG_RECIPIENT_KEY)、「打开了一个槽位但没有任何候选密钥复现出 slots_mac」(TAMPERED_HEADER),以及「密钥恢复且 MAC 校验通过之后,内容 AEAD 校验失败」(TAMPERED_CIPHERTEXT)。这些代码不携带任何预言机价值:没有匹配私钥的对手根本打不开任何槽位,而每一条错误路径都MUST把底层的秘密字节(共享密钥、密钥加密密钥、内容加密密钥)从它的消息和原因链中排除。

然而,对一个不受信任的外部调用方,无论解密失败的缘由为何,它都MUST收到恰好一种通用失败形态,而且外部响应 MUST NOT 凭形态区分这三种缘由,也不得透露是哪个槽位命中。那些内部代码只为本地调试而存在;它们 MUST NOT 通过一个可区分的响应泄露给一个外部观察者。

时延模型是刻意收窄的。验证器 MAY 在无命中检查处、在内容解密之前返回——于是一个非接收方(没有槽位打开)的时延,可以与一个槽位已打开、但内容密文打不开的接收方区分开来。那种区别只透露 接收方 vs 非接收方;它绝不透露是哪个槽位命中,也没有任何密钥材料。非接收方这一情形与密文有误这一情形之间,时延一致并非必需,且 MUST NOT 强制要求一次虚值内容打开——硬要每个非接收方都付出整段内容解密的代价,什么也买不到。确实成立的那条恒定时间保证,是那条跨槽位不变量(参见对秘密做恒定时间比较):在单把私钥的那一遍里,循环以恒定时间比较遍历所有槽位,因此一个观察者无从得知某把密钥解开了哪个槽位(如果有的话)——而在接收方-非接收方这一区分之外,它只能获知槽位数量。

相关页面

  • 导言 ——五条不变量,包括不依赖签发者和可独立验证。
  • 签名 ——严格的 Ed25519 验证,以及柔性失败的作者身份模型。
  • 密封 PoE ——加密信封,以及本页所概述的隐私属性。
  • 算法注册表 ——让算法可演进成为可能的那些具名标识符。
  • 验证 ——验证流水线、确认深度策略,以及错误目录。