11

I am attempting to generate "raw", unencoded ECDSA signatures for use with a cryptographic chip. The goal is to sign something on the host pc, then send it to the chip to be validated. However, I am running into a little problem. My understanding is that the ECDSA signature should be 64 bytes (for secp256v1). And, when I use the chip to generate a signature, it is indeed 64 bytes in length. However, when I use openssl, the signature is 71 bytes in length. The beginning of the signature seems to be some kind of prefix, but I can't find any data about what that is.

Here is how I am trying to do everything:

Generate the key:

openssl ecparam -genkey -name secp256r1 -noout -out privkeyv1.pem

Generate the "message" to be signed:

echo -n "Hello World" > test.txt

I have tried two methods for signing the message. Both lead to the same, unexpected output.

First method - generate sha256 hash of test file, then sign it:

sha256sum test.txt | cut -f 1 -d " " > hash

Sign with pkutil

openssl pkeyutl -sign -in hash -inkey privkeyv1.pem -out test_sig_meth1

Method 2: Sign with openssl dgst

openssl dgst -sha256 -binary -sign privkeyv1.pem -out test_sig_meth2 test.txt The issue: Here is the output of xxd -p -c 256 test_sig_meth1: 3045022000a86fb146d5f8f6c15b962640bc2d1d928f5e0f96a5924e4db2853ec8b66fb002210085431613d0a235db1adabc090cc1062a246a78941972e298423f4b3d081b48c8

And the output of xxd -p -c 256 test_sig_meth2: 30450220693732cd53d9f2ba3deae213d74cdf69a00e7325a10ddc6a4445ff2b33f95e62022100b6d2561e3afba10f95247ed05f0c59620dc0913f0d798b4148e05c4116b6384e

As you can see, both of these methods generate some bytes at the beginning that look like header bytes (the 30450220, maybe longer), but I am not sure what they are for or how to remove them. For reference, here is a a signature of the same method generated on the crypto chip. If you remove the null byte padding at the end, it's 64 bytes. 4677AD09F2AF49D7445ED5D6AC7253ADC863EC6D5DB6D3CFBF9C6D3E221D0A7BA2561942524F46B590AEE749D827FBF80A961E884E3A7D85EC75FE48ADBC0BD00000000000000000000000

The question: How can I use openssl to generate a 64 byte raw (unencoded, with no header) ECDSA signature I can use with this scheme?

jww
  • 97,681
  • 90
  • 411
  • 885
f41lurizer
  • 133
  • 1
  • 6
  • 1
    I have discovered that the output from both commands is der-encoded. By using asn1parse -inform der, I was able to find the integers that make up the signature (r and s) – f41lurizer Jun 28 '17 at 16:25
  • 1
    How is this related to C, please? – alk Jun 28 '17 at 16:27
  • 1
    Stack Overflow is a site for programming and development questions. This question appears to be off-topic because it is not about programming or development. See [What topics can I ask about here](http://stackoverflow.com/help/on-topic) in the Help Center. Perhaps [Super User](http://superuser.com/) or [Unix & Linux Stack Exchange](http://unix.stackexchange.com/) would be a better place to ask. – jww Jun 28 '17 at 21:43

1 Answers1

2

Most chips will, for efficiency reasons, just output the r and s as a byte array or octet string, where each r and s is the same as the field size (i.e. key size) in octets. Another approach is to output r and s as a sequence of numbers, because in the end, that is what r and s are. Using ASN.1 this becomes a SEQUENCE of INTEGER values.

To convert from such a sequence you can first BER decode using a BER parser to retrieve the integer. Then implement an I2OSP algorithm (integer to octet stream primitive) which requires the value and the key size in bytes/octets as arguments. The number should be in big integer form, but that's fine as ASN.1 BER encoded integers are also big integers. Basically you must left pad with zero bytes if the number is too small. Then you concatenate the number.

I won't go into OS2IP which converts a byte array to an integer. Note thought that if you encode it in BER form then the integers should not be left padded with zero bytes. So some trickery is required still.

So although the signature changes form, the signature still stays valid; you can simply convert between one form and the other and the signature will still verify - as long as you use the right library for the job, obviously.

Maarten Bodewes
  • 90,524
  • 13
  • 150
  • 263
  • I was able to convert the format of the keys with openssl asn1parse -inform der -in test_sig_meth2, which outputs the r and s values. Thanks! – f41lurizer Jun 29 '17 at 13:36
  • @f41lurizer OK, but don't forget the conversion to octet string. This will usually be just a concatenation...until a shorter r or s value will bite you. – Maarten Bodewes Jun 29 '17 at 16:50
  • I am not sure what you mean by converting to an octet string. Is this basically just hex encoding the string, or am I missing something? – f41lurizer Jun 29 '17 at 19:25
  • An octet string is just an array of bytes. Octet = byte, string = array. Hex encoding is to make these bytes human readable. Don't confuse the representation of bytes with the bytes themselves. So if you have a 256 bit curve you'd encode to 32 bytes and if you have a 521 (not a misprint) then you'd encode to 66 bytes by left padding the big endian encoded number. – Maarten Bodewes Jun 29 '17 at 22:35
  • *"So although the signature changes form, the signature still stays valid; you can simply convert between one form and the other and the signature will still verify - as long as you use the right library for the job"* - Related, see [ECDSA sign with OpenSSL, verify with Crypto++](https://stackoverflow.com/q/17316178/608639). It converts between ASN.1/DER (OpenSSL, Java and and several others) and P1363 formats (Crypto++ and several others). .Net is odd man out; it uses an XML encoding format. – jww Feb 16 '18 at 07:25