I’m signing JSON in OCaml (yojson) and verifying it in Rust (serde_json::to_vec()). Even using Yojson.Safe.to_string, the outputs differ: key ordering, spacing, etc. Result: signature verification fails.
I need a way to emit deterministic/canonical JSON from Yojson.Safe.t. Anyone got a minimal serializer or working approach?
If you are in Rust (and a few other languages) you may want to use Amazon Ion Hash. Ion is a superset of JSON and has a C binding (none for OCaml) and Ion Hash is a tiny addition to that. The hashing was used in the now-defunct Quantum Ledger DB, among other things.
The Yojson.Safe.t type is fairly small and easy to work with. It wouldn’t be hard to write a custom function which outputs what you want. From a quick test a LLM seems to be able to one shot it.
always output on a the same line
do not put any spaces in between elements
sort the object keys
disallow types such as float which might have tiny differences depending on the environment?
But I don’t know if it will be as easy to write the exact same function in rust to be sure that the outputs are matching, I’ve no experience with serde_json.
I do not understand that. Why do you need to do that? I think I would solve this as @yawaramin suggests: take the values, sign them, transmit the signature. Then on the Rust side, parse the JSON and verify.
That said, Yojson should in general output object keys in the same order they are specified in the list that is passed to Assoc.
If indeed you need to make sure that the structure is unchanged, JSON is the wrong format because there is no canonical representation of data in JSON. If you want that, other formats are better options like maybe ProtoBuf.
I don’t know whether there is an implementation of canonicalization of JSON in OCaml, for JWS there’s GitHub - ulrikstrid/ocaml-jose which may be a good starting point?
Hmm. Yeah, you’re right, I wasn’t aware of it. However it is outside the JSON spec (RFC 8259) itself itself and only describes a way to do that by encoding it in a special way (a bit like @yawaramin was suggesting, but valid JSON and backed by an RFC so I’d say it’s a nicer way).
If someone wants to contribute a printer that outputs JSON in a JCS-compliant manner to Yojson I’d most definitely be interested in including such a feature! It shouldn’t be too hard, the main part is sorting and deduplicating keys in objects and printing in the exact way RFC 8785 specifies.