摘要

  • RFC 9596 注册了受保护的 COSE typ 参数,标签值为 16,用来声明完整 COSE 对象的类型;它与只描述 payload 或 ciphertext 的 content type 不同。
  • COSE 库只把 typ 交给应用,不替应用解释;只有应用把类型绑定到互斥的校验、密钥用途和授权规则时,标签才成为有效控制。

一份 COSE 对象到达服务入口。签名验证成功,受保护标头中的 typ 也正好等于路由所期待的值。系统已经知道该把它送到哪个处理器,却仍不知道是否应该让处理器改变现实。

RFC 9596 刻意把这两个问题分开。它补上 COSE 相对于 JOSE 的一个能力:当同一数据结构可能容纳多类 COSE 对象时,应用可以显式声明完整对象属于哪一类,从而减少类型混淆。但规范不把通用 COSE 实现变成业务语义裁判。库只传递参数,应用自己决定何时要求它、接受哪些值、何种不一致必须拒绝。

标签解决的是分流问题,不是主权问题。写在受保护标头里的名称可以指向一套规则,却不能自行取得执行这些规则后的权力。

外层对象与内层载荷是两种事实

RFC 9052 早已定义 content type,用来说明 payload 或 ciphertext 中数据的类型。RFC 9596 新增的 typ 说明完整 COSE 对象的类型。两者可使用相同的值语法,但语义层级不同。

例如,一份签名回执和一份签名鉴证结果都可能携带 CBOR 载荷。内层 content type 告诉解析器如何理解数据,外层 typ 告诉应用该选择哪一类验证合同。仅仅知道“这是 CBOR”无法回答它是回执、令牌、事件还是控制请求。

typ 可以是 CoAP Content-Formats 注册表中的无符号整数,也可以是媒体类型字符串,并可携带媒体类型参数。紧凑数字与可读字符串都有互操作价值,但接收方不能过早把它们归一化成一个模糊标签。原始表示、所在层级和受保护字节都是证据的一部分。

若审计只存“对象类型”一列,日后便无法判断发送方声明的是整个包络还是载荷,也无法还原接收方当时究竟比较了什么。

防篡改不等于声明正确

RFC 9596 禁止把 typ 放在未受保护标头中。在相关 COSE 构造里,受保护标头参与密码学验证,中间人若改写类型,验证就会失败。这使类型声明与签名对象绑定。

绑定并不会自动让声明为真。签名者可以忠实地签署一个错误类型,使用旧别名,选择只适用于另一入口的类型,或者在正确的外层类型里放入不符合规范的载荷。密码学能证明字节未被悄悄改动,不能证明发起者拥有正确判断。

规范因此明确说,COSE 实现除了把 typ 传给应用外会忽略它。显式类型应用应拒绝预期集合以外的值;在本应有类型时,也应拒绝缺失。

真正的安全控制是拒绝路径。如果系统只是解析、记录并展示 typ,却在错误或缺失时回退到默认处理器,那么它部署的是经过签名的装饰,而不是类型隔离。

类型分开后,校验规则也必须分开

RFC 9596 借用了 RFC 8725 关于 JWT 显式类型的安全理由。不同用途的令牌可能互相混淆,用不同 typ 能减少这种替换。但 RFC 8725 随即提出更强要求:不同令牌类别的校验规则必须互斥。

同样的逻辑适用于 COSE。若两个对象类型共享不受用途限制的密钥、宽松的 audience 默认值、同一组可选声明以及同一授权入口,标签不同也未必改变接受面。攻击者只需找到忽略类型的旧路径,或让通用校验器同时满足两个合同。

每条分支应把类型与允许的 COSE 结构、必需受保护字段、载荷类型、必需声明、签发者、受众、密钥用途、时效、重放规则、入口和可执行操作一起绑定。错误类型必须在公共业务逻辑抹平来源之前失败。

RFC 8725 还警告,旧类型的校验器往往不查看 typ。因此,统计“生产方已加标签”不能证明迁移完成。必须同时记录接收方规则版本,并用缺失、互换和错配样本验证真实拒绝。

注册表提供共同名称,不提供共同信任

IANA 在 COSE Header Parameters 注册表中把 typ 分配为标签 16,并让取值引用媒体类型或 CoAP Content-Formats 注册表。这个公共命名空间能防止冲突,让独立实现对同一数值取得一致含义。

注册表并不知道某个服务是否启用该类型,也不知道哪把钥匙有权签署它、哪些参数有效、载荷是否符合类型或本地政策是否允许行动。“已注册”是协调事实,不是权限事实。

健康的架构保留三套不同记录:公开的名称注册、每个入口版本化的接受策略,以及具体对象的处理证据。把三者合成一个全局“可信类型表”,就会让目录管理员获得并非由运行代码授予的权力。

这与《heng-lu-note》的最小初始规范相吻合:公共层只定义必须共同的语法和位置,未来决定留给运行校验器的参与者;自愿采用要由真实执行和拒绝来证明,而不是由中央表格宣布。

证据链应跟随一次实际决定

接收对象时,应保存原始字节摘要、COSE 结构种类、受保护标头字节、typ 的精确数值与表示、载荷类型、入口、操作、预期类型集合、校验规则版本、签名或 MAC 或解密结果、密钥标识与授权用途、载荷解析、必需声明、签发者、受众、时效、重放检查、授权决定、选中的处理器与最终效果。

这些字段不是重复日志。签名证明一把钥匙与一组字节的关系;匹配类型证明声明落入该入口的预期集合;解析证明格式;配置文件校验证明语义约束;密钥政策证明签名者是否有资格发送这类对象;本地授权决定是否允许当前操作;应用结果证明实际发生了什么。

不能让 COSE 库替业务行动发言,也不能让媒体类型注册表替密钥用途发言。每一层只出具自己能够观察的收据,才不会在故障时把一个绿色勾号扩张成整条链的真相。

现实分层的意义并不是贬低符号,而是限制符号的证明范围。RFC 9596 给了运行系统一个准确的分流信号;只有部署代码真的执行互斥规则时,这个信号才抵达现实。

来源