mgo bson 编组是否保证保留结构组件的顺序?
Is mgo bson marshalling guaranteed to preserve order of struct components?
我正在使用 mgo 在 mongo 中保存 go 结构。我希望用该结构的散列(和一个秘密)来保存它们以确定它们是否已被篡改(并且我不希望 mongo 数据库本身拥有秘密)。
目前我正在通过使用 gob
对结构进行序列化来对结构进行哈希处理,gob
结构组件的顺序定义明确。这很好用,当我从芒果重新读取结构时,情况发生了变化——准确地说,mongo 中的时间值与 go 相比具有截断的准确性——因此哈希值不匹配。
我计划的解决方法是在计算哈希之前从 BSON 编组和解组结构,即:
- 将结构编组到 BSON
- 从 BSON 解组结构(从而失去时间精度)
- Marshall 结构到 gob 和哈希结果
[]byte
- 将哈希放入结构
- 将结构保存到 mongo
现在,这有点迂回。
如果我能保证 BSON 本身始终保留结构中组件的顺序,我可以:
- 将结构编组到 BSON
- 哈希结果
byte[]
- 将哈希放入结构
- 将结构保存到 mongo
哪个会不那么讨厌(尽管它仍然需要两次转换为 BSON)。
有什么想法吗?
BSON 保持秩序。我会在几个简单的包装函数中完成所有秘密:
type StructWrap struct{
Value bson.Raw
Hash []byte
}
func save2mgo(in interface{}){
str := StructWrap{}
orig,_err := bson.Marshal(in)
str.Value = bson.Raw{0, orig}
str.Hash = <do hash based on orig bytes>
//to save str to mgo. because Value already in bson.Raw, it will not do marshal again.
}
func unmarshal(inType interface{}){
//read from mgo
//check str.Hash here
str.Value.Unmarshal(inType)
}
希望能帮到你。
回答您的实际问题,是的,您可以相信 mgo/bson
始终按照代码中观察到的顺序编组结构字段。虽然尚未记录 (issue),但这是非常有意的行为,甚至 mgo 本身在内部也依赖于它。
现在,响应您的预期用途:不要那样做。事实字段的顺序有保证并不意味着二进制格式作为一个整体是稳定的。即使现在也有已知的方法可以在不破坏现有客户端的情况下更改输出,但这会破坏其输出的散列。
这里有一些建议阅读,以便更好地理解您要解决的问题的来龙去脉(我是作者):
- http://blog.labix.org/2013/06/25/strepr-v1
- http://blog.labix.org/2013/07/03/reference-strepr-implementation
这恰恰解决了以稳定的方式编组具有任意字段和键的任意或映射的问题,以便从中获得稳定的哈希值用于签名。参考实现在 Go 中。
我正在使用 mgo 在 mongo 中保存 go 结构。我希望用该结构的散列(和一个秘密)来保存它们以确定它们是否已被篡改(并且我不希望 mongo 数据库本身拥有秘密)。
目前我正在通过使用 gob
对结构进行序列化来对结构进行哈希处理,gob
结构组件的顺序定义明确。这很好用,当我从芒果重新读取结构时,情况发生了变化——准确地说,mongo 中的时间值与 go 相比具有截断的准确性——因此哈希值不匹配。
我计划的解决方法是在计算哈希之前从 BSON 编组和解组结构,即:
- 将结构编组到 BSON
- 从 BSON 解组结构(从而失去时间精度)
- Marshall 结构到 gob 和哈希结果
[]byte
- 将哈希放入结构
- 将结构保存到 mongo
现在,这有点迂回。
如果我能保证 BSON 本身始终保留结构中组件的顺序,我可以:
- 将结构编组到 BSON
- 哈希结果
byte[]
- 将哈希放入结构
- 将结构保存到 mongo
哪个会不那么讨厌(尽管它仍然需要两次转换为 BSON)。
有什么想法吗?
BSON 保持秩序。我会在几个简单的包装函数中完成所有秘密:
type StructWrap struct{
Value bson.Raw
Hash []byte
}
func save2mgo(in interface{}){
str := StructWrap{}
orig,_err := bson.Marshal(in)
str.Value = bson.Raw{0, orig}
str.Hash = <do hash based on orig bytes>
//to save str to mgo. because Value already in bson.Raw, it will not do marshal again.
}
func unmarshal(inType interface{}){
//read from mgo
//check str.Hash here
str.Value.Unmarshal(inType)
}
希望能帮到你。
回答您的实际问题,是的,您可以相信 mgo/bson
始终按照代码中观察到的顺序编组结构字段。虽然尚未记录 (issue),但这是非常有意的行为,甚至 mgo 本身在内部也依赖于它。
现在,响应您的预期用途:不要那样做。事实字段的顺序有保证并不意味着二进制格式作为一个整体是稳定的。即使现在也有已知的方法可以在不破坏现有客户端的情况下更改输出,但这会破坏其输出的散列。
这里有一些建议阅读,以便更好地理解您要解决的问题的来龙去脉(我是作者):
- http://blog.labix.org/2013/06/25/strepr-v1
- http://blog.labix.org/2013/07/03/reference-strepr-implementation
这恰恰解决了以稳定的方式编组具有任意字段和键的任意或映射的问题,以便从中获得稳定的哈希值用于签名。参考实现在 Go 中。