Subject: [Cbor] Signing and canonicalization

From: Laurence Lundblade <[email-masked]>
Date: Tue, 19 Jun 2018 16:31:13 -0700

The reason I took a careful look at CBOR canonicalization was to understand if it was important in a protocol design (EAT <https://tools.ietf.org/html/draft-mandyam-eat-00>) that was very focused on signed data. It took me a while and a mistake or two to understand the best way to format the TBS bytes in CBOR.  Here’s the conclusions in the form of some text that would have helped me a lot which I hope can be added to one of the CBOR documents.

How to design signing in CBOR protocols

Any protocol that involves signing needs to define exactly what the TBS (to-be-signed) bytes are, the exact bytes that are input the signing and verification operations. There are two main approaches .

In the first approach, the data is CBOR encoded as it normally would be. Then the binary bytes that are the encoded CBOR is wrapped in a bstr.  The bstr is inserted into the message and transmitted along with the signature. The value of the bstr is the TBS bytes. 

For example if the encoded CBOR was an integer with the value 1, that encodes to one byte, 0x01, then the bstr wrapping of that would be two bytes, 0x41, 0x01.  

In theory the bstr wrapping could be skipped, but this makes it very difficult to implement with generic CBOR parsers because they do not give access to encoded sub-parts of a CBOR message. 

Canonicalization of the transmitted data is NOT required. From the signature generation and verification view, it is even OK if there is variance from message to message. For example it is OK if integers are encoded in the shortest form sometimes and sometimes not. It is OK to use definite and indefinite lengths.

The second less obvious approach is for the TBS bytes to be constructed by some precisely defined process. Since CBOR has several different ways of representing the same data, some form of canonicalization is required because the sender and the receiver must come up with exactly the same TBS bytes.  Canonical Encoded CBOR should be used in this case, but protocols can also define their own rules for canonicalization if they wish.

The main reason for choosing this second approach is that it can reduce the size of the messages transmitted and/or provide flexibility in the way the data is structured.

In the end, a good practice is just to use the COSE format for signing as it takes care of these details. In COSE the payload does NOT need to be canonicalized.

It seems there is some consensus that canonicalization should not be in the main CBOR RFC, but I haven’t seen anything in GitHub for that yet. When that is figured out, I can make a pull request out of the above text.

I’m definitely in favor of reducing variability in protocols for better interop, like what CTAP did and Postel Was Wrong makes sense, but the sorting requirement in canonicalization would be disruptive to more complex EAT implementations since they stitch together chunks of encoded CBOR from several sub systems on a device. 

An implementation could try to sort encoded CBOR, but the variable lengths and such make that seem like a rather bad idea.

LL

P.S.  I am no longer working for Qualcomm (now that I have them hooked on CBOR for a few projects), but want to participate here as a private network citizen.