文章摘要
文章探讨了在密码学算法中如何正确编码数据结构的问题,指出需要解决两个关键问题:一是确保编码输出具有规范性,避免不同编码解码成相同内存数据;二是实现域分离,防止不同语义的数据结构因字段相同而被误用。作者通过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)等复杂类型
安全优势
- 防碰撞:随机生成分隔符(类似Rabin指纹)确保全局唯一性
- 防误用:IDE/CLI工具强制生成随机值,禁止重复使用
- 协议稳定:结构体名称可修改,但分隔符必须终身固定
对比现有方案
传统方法如以太坊EIP-712或TLS上下文字符串存在以下不足: - 临时性解决方案 - 依赖人工添加前缀 - 缺乏系统化保障
Snowpack已开源支持Go/TypeScript,其设计理念适用于任何序列化方案。开发者可通过GitHub仓库获取实现。
作者:Max
特别致谢:Jack O'Connor的技术反馈
讨论请移步Hacker News
评论总结
以下是评论内容的总结:
关于域分隔符的放置方式
- 有建议将域分隔符放在IDL中(如protobuf的message option),也有建议放在数据流中(如类型字段)。
- 引用:
- "add a message option" (tantalor)
- "putting the domain separators in-band" (Retr0id)
签名与上下文的绑定
- 签名应与上下文绑定,而不是仅依赖协议头中的魔法数字,否则可能导致安全问题。
- 引用:
- "Signatures are bound to a context... Horton principle" (formerly_proven)
- "The signature needs to included the necessary context" (lukev)
规范化的性能问题
- 规范化(如排序)可能带来O(nlogn)的时间开销,但可以通过其他方式(如多重集合哈希)避免。
- 引用:
- "canonicalisation takes O(nlogn) time" (Retr0id)
- "digest the type as part of the hash" (cogman10)
数据格式的选择
- 对二进制格式(如protobuf)和文本格式(如JSON)的优劣有争议,部分认为二进制格式可能增加复杂性。
- 引用:
- "Protobuf wasn't canonical... inferior to JSON" (socketcluster)
- "always start with msgId and msgLen" (efitz)
密码学实现的复杂性
- 密码学实现应简单易懂,过于复杂的库可能增加安全风险。
- 引用:
- "Simplicity of implementation... critical feature" (socketcluster)
- "Crypto is hard. Do it right." (jeffrallen)
其他建议
- 使用DSSE或in-toto等工具解决模式问题。
- 引用:
- "DSSE is great... use in-toto" (colek42)
- "add a 1 byte
msgfield" (erpellan)
总结:评论围绕域分隔符的放置、签名上下文绑定、数据格式选择和密码学实现的简洁性展开,观点多样,但普遍强调安全性和可验证性的重要性。