Hacker News 中文摘要

RSS订阅

错误的数据结构签名方式 -- Signing data structures the wrong way

文章摘要

文章探讨了在密码学算法中如何正确编码数据结构的问题,指出需要解决两个关键问题:一是确保编码输出具有规范性,避免不同编码解码成相同内存数据;二是实现域分离,防止不同语义的数据结构因字段相同而被误用。作者通过protobuf示例展示了字段相同但含义不同的数据结构可能带来的安全隐患。

文章总结

如何正确签名数据结构 | FOKS博客

核心问题:数据结构签名中的领域分离

在将数据输入加密算法(如签名、加密、MAC或哈希)之前,如何正确打包数据?这个存在数十年的问题至今缺乏完善解决方案。其中最关键的是两个问题: 1. 编码需要产生规范化输出(避免像比特币曾遭遇的那样,不同编码解码成相同内存数据) 2. 编码系统必须解决领域分离问题

典型案例分析

假设分布式系统中有两种消息类型: protoc message 树根 { int64 时间戳 = 1; bytes 哈希值 = 2; } message 密钥撤销 { int64 时间戳 = 1; publicKeyFingerprint 哈希值 = 2; } 虽然字段结构相同,但语义完全不同。攻击者可将已签名的树根数据伪造成密钥撤销消息,造成验证者误判。这种攻击在比特币以太坊DEX等场景已多次得逞。

解决方案:Snowpack协议

核心创新:IDL内置领域分隔符

FOKS团队开发的Snowpack方案提出: struct 树根 @0x92880d38b74de9fb { 时间戳 @0 : 整型; 哈希值 @1 : 二进制; } 关键特性: 1. 64位随机不可变分隔符直接嵌入接口定义语言(IDL) 2. 编译器生成包含GetUniqueTypeID()方法的目标代码 3. 签名/验证时自动拼接分隔符与序列化数据 4. 类型系统强制保障(未标记结构体无法通过验证)

技术实现

  • 编码流程:结构化数据→位置数组→Msgpack字节流(强制最小整数编码)
  • 版本兼容:通过nil占位实现字段增删的向前/向后兼容
  • 扩展支持:列表(List)、可选(Option)和变体(variant)等复杂类型

安全优势

  1. 防碰撞:随机生成分隔符(类似Rabin指纹)确保全局唯一性
  2. 防误用:IDE/CLI工具强制生成随机值,禁止重复使用
  3. 协议稳定:结构体名称可修改,但分隔符必须终身固定

对比现有方案

传统方法如以太坊EIP-712TLS上下文字符串存在以下不足: - 临时性解决方案 - 依赖人工添加前缀 - 缺乏系统化保障

Snowpack已开源支持Go/TypeScript,其设计理念适用于任何序列化方案。开发者可通过GitHub仓库获取实现。

作者:Max
特别致谢:Jack O'Connor的技术反馈
讨论请移步Hacker News

评论总结

以下是评论内容的总结:

  1. 关于域分隔符的放置方式

    • 有建议将域分隔符放在IDL中(如protobuf的message option),也有建议放在数据流中(如类型字段)。
    • 引用:
      • "add a message option" (tantalor)
      • "putting the domain separators in-band" (Retr0id)
  2. 签名与上下文的绑定

    • 签名应与上下文绑定,而不是仅依赖协议头中的魔法数字,否则可能导致安全问题。
    • 引用:
      • "Signatures are bound to a context... Horton principle" (formerly_proven)
      • "The signature needs to included the necessary context" (lukev)
  3. 规范化的性能问题

    • 规范化(如排序)可能带来O(nlogn)的时间开销,但可以通过其他方式(如多重集合哈希)避免。
    • 引用:
      • "canonicalisation takes O(nlogn) time" (Retr0id)
      • "digest the type as part of the hash" (cogman10)
  4. 数据格式的选择

    • 对二进制格式(如protobuf)和文本格式(如JSON)的优劣有争议,部分认为二进制格式可能增加复杂性。
    • 引用:
      • "Protobuf wasn't canonical... inferior to JSON" (socketcluster)
      • "always start with msgId and msgLen" (efitz)
  5. 密码学实现的复杂性

    • 密码学实现应简单易懂,过于复杂的库可能增加安全风险。
    • 引用:
      • "Simplicity of implementation... critical feature" (socketcluster)
      • "Crypto is hard. Do it right." (jeffrallen)
  6. 其他建议

    • 使用DSSE或in-toto等工具解决模式问题。
    • 引用:
      • "DSSE is great... use in-toto" (colek42)
      • "add a 1 byte msg field" (erpellan)

总结:评论围绕域分隔符的放置、签名上下文绑定、数据格式选择和密码学实现的简洁性展开,观点多样,但普遍强调安全性和可验证性的重要性。