4

I'm using python and cryptography.io to sign and verify messages. I can get a DER-encoded bytes representation of a signature with:

cryptography_priv_key.sign(message, hash_function)

...per this document: https://cryptography.io/en/latest/hazmat/primitives/asymmetric/ec/

A DER-encoded ECDSA Signature from a 256-bit curve is, at most, 72 bytes; see: ECDSA signature length

However, depending on the values of r and s, it can also be 70 or 71 bytes. Indeed, if I examine length of the output of this function, it varies from 70-72. Do I have that right so far?

I can decode the signature to ints r and s. These are both apparently 32 bytes, but it's not clear to me whether that will always be so.

Is it safe to cast these two ints to bytes and send them over the wire, with the intention of encoding them again on the other side?

jMyles
  • 11,772
  • 6
  • 42
  • 56
  • For ASN.1/DER encoding the `r` and `s` are `INTEGER`, which is a two's compliment integer. If the high bit is set, then a leading 0 byte is prepended to keep the integer positive. The only way to know if you need the extra octet for `r` or `s` is to sign a message. The proper way to handle it is have the signing function return a properly sized buffer. – jww Feb 11 '18 at 04:07
  • 1
    Why not just send the properly DER-encoded sequence that `sign()` gives you? – President James K. Polk Feb 11 '18 at 04:23
  • @JamesKPolk: The DER-encoded sequence is not fixed-length. – jMyles Feb 11 '18 at 18:09
  • Sending a variable number of bytes is not exactly a difficult problem. Parsing the ASN.1 `INTEGER` components out of the middle of the DER-encoding is best avoided if possible. I'd much rather have the problem of sending and receiving a variable number of bytes than the ASN.1 parsing problem. – President James K. Polk Feb 11 '18 at 18:17
  • @JamesKPolk: Why best avoided? Do you have a link that describes "the ASN.1 parsing problem"? – jMyles Feb 11 '18 at 19:55
  • It's best avoided because it's deceptively complicated and fiddly, and historically the source of many security bugs. You can use your favorite search engine to find more info. The point is, if the other side's verifier can consume the output of `sign(...)` then you shouldn't mess with it. – President James K. Polk Feb 11 '18 at 20:28
  • @JamesKPolk: I believe this is possible - I had this same initial instinct. However, even after extensive use of my favorite search engine(s), I can't find a good rundown. Perhaps it's fatigue. :-) If you happen to have a link that you like, it will be very helpful. Also, in my case, the output of `sign(...)` is indeed der_encoded. I'm using cryptography.io tooling to decode it, then concat'ing r and s. It's hard for me to immediately imagine how this can be used by an attacker, but I'm definitely interested in learning if that's so. – jMyles Feb 14 '18 at 16:18

1 Answers1

3

The simple answer is, yes, they will always be 32 bytes.

The more complete answer is that it depends on the curve. For example, a 256-bit curve has an order of 256-bits. Similarly, a 128-bit curve only has an order of 128-bits.

You can divide this number by eight to find the size of r and s.

It gets more complicated when curves aren't divisible by eight, like secp521r1 where the order is a 521-bit number.

In this case, we round up. 521 / 8 is 65.125, thus it requires that we free 66 bytes of memory to fit this number.

It is safe to send them over the wire and encode them again as long as you keep track of which is r and s.

Tux
  • 1,896
  • 3
  • 19
  • 27
  • I'm still skeptical, man (note: @Tux and I have been discussing this matter in person). This may show the limits of my understanding of some cryptography functions, but in my head: if the integer is 32-bytes, it can still possibly represent by two different points on the curve - one above the X-axis, one below. If I assume it's a positive value, I'll fail about half the time, won't I? – jMyles Feb 11 '18 at 18:36
  • OK, so two things: 1) How do you respond to @JamesKPolk's comments above? And 2) Do I then understand correctly that you're just saying that DER encoding is essentially wasteful here? (Ie, that there's no *good* reason that DER can't be fixed length, and that its reason for being variable length are unconvincing?) – jMyles Feb 14 '18 at 16:04
  • 2
    @jMyles: an EC _public key_ is a point, with X and sometimes Y coordinates, but an ECDSA signature is two integers, NOT a point. One number (r) is _derived_ from an X coordinate but modified, and the other (s) is not related to any point. Note NIST/SECG Fp curves (not F2m) have cofactor 1 so the curve order is close to, but not the same as, the underlying field modulus p; google Hasse's theorem. – dave_thompson_085 Feb 15 '18 at 22:44