Hacker News new | ask | show | jobs
by cryptonector 2928 days ago
+1e6

Never design a protocol where you must re-encode (and canonicalize! ouch!) in order to verify signatures. Instead you should wrap the thing to sign (and the signature) as an octet string. E.g.,

    {"thing-to-sign":"<base64-encoded-thing>",
     "signer-info":...,
     "signature":"<base64-encoded-signature-of-the-base64-encoded-thing>"}
This basically kills any joy of using JSON...

This, for example, does not work:

    {"thing":<thing-object>,
     "signer-info":...,
     "signature":"<base64-encoded-signature-of-thing>"}
because you'd have to have a JSON text parser that lets you get at the as-originally-encoded <thing> part of the above JSON text. This is not a common JSON text parser feature! So implementors would tend to re-encode <thing-object> in order to verify the signature.

This also doesn't work, for the same reason and because it's even harder to deal with a signature that's in the middle of the <thing>:

    {"thing-field0":...,
     "thing-field1":...,
     "signer-info":...,
     "signature":"<base64-encoded-signature-of-the-base64-encoded-thing>",
     "thing-fieldN":...}
Even if you promise and keep the promise to put the signature fields first or last, it's still super difficult to make this work well for other implementors, and is difficult even for yourself: you'll probably end up writing a new JSON parser from scratch to deal with this mess without having to re-encode, and most likely you'll opt to re-encode.

Re-encoding for signature verification requires canonicalization. For JSON canonicalization means:

- you must specify object key ("name") order - you must specify what if any interstitial whitespace to have - you must specify a canonical string representation (e.g., all Unicode escaped, or all Unicode not escaped, ...) - you must specify a canonical number representation (oof!)

Numbers make canonicalization really tricky! You'd better limit yourself to 52-bit signed integers. If you use real numbers you'll quickly get into an IEE754 mess.

But again, no one will think this is useful:

    {"thing-to-sign":"<base64-encoded-thing>",
     "signer-info":...,
     "signature":"<base64-encoded-signature-of-the-base64-encoded-thing>"}
if the whole point of using JSON was to make this sort of thing close to human readable.

Now, of course, it's trivial to write some jq code to decode the <thing> and pretty-print it, so it's not the end of the world. But still, people will resist this approach and we'll be right back to defining a canonical JSON encoding.