Search code examples
mongodbgobsonmgo

Is mgo bson marshalling guaranteed to preserve order of struct components?


I am saving go structs in mongo using mgo. I wish to save them with a hash of that struct (and a secret) to determine whether they have been tampered with (and I do not wish the mongo db itself to have the secret).

Currently I am hashing the structs by serializing them using gob whose ordering of struct components is well-defined. This works great, save when I go to reread the struct from mango, things have changed - to be precise the time values in mongo have truncated accuracy compared to go - therefore the hashes do not match up.

My planned work around for this is simply to marshall and unmarshall the struct from BSON before calculating the hash, i.e.:

  • Marshal struct to BSON
  • Unmarshal struct from BSON (thereby losing precision on time)
  • Marshall struct to gob and hash resultant []byte
  • Put hash in struct
  • Save struct to mongo

Now, that's more than a little circuitous.

If I could guaranteed that the BSON itself always preserved order of components in structs, I could:

  • Marshal struct to BSON
  • hash resultant byte[]
  • Put hash in struct
  • Save struct to mongo

Which would be less nasty (albeit that it would still require converting to BSON twice).

Any ideas?


Solution

  • Answering your actual question, yes, you can trust mgo/bson to always marshal struct fields in the order they are observed in the code. Although not yet documented (issue), this is very much intentional behavior, and even mgo itself depends on it internally.

    Now, responding to your intended usage: don't do that. The fact fields are in a guaranteed order does not mean the binary format is stable as a whole. Even now there are known ways the output can change without breaking even the existing clients, but that would break a hash of its output.

    Here is some suggested reading to understand a bit better the ins and outs of the problem you are trying to solve (I'm the author):

    This is precisely addressing the issue of marshaling an arbitrary or map with arbitrary fields and keys in a stable way, to obtain a stable hash out of it for signatures. The reference implementation is in Go.