kmackay / micro-ecc

ECDH and ECDSA for 8-bit, 32-bit, and 64-bit processors.
BSD 2-Clause "Simplified" License
1.26k stars 460 forks source link

Does micro-ecc works with signature (secp256r1) where R or S is lower than 32 bytes? #200

Closed coran21 closed 1 year ago

coran21 commented 1 year ago

Hello,

Our firmware is checking the signature generated by the server using OpenSSL and we found out, that sometimes the R or S are lower than 32 bytes.

Here is the signature we got in DER format: 30 43 02 1F 3C F3 96 47 A9 2C 4D C4 E3 CC AC 4F AA 6D E5 72 3D 17 DA 31 20 23 D0 05 48 6A C1 BC 03 9E 38 02 20 63 D6 62 D0 63 BE 50 67 8A C2 F4 71 27 5C 94 06 1C 31 50 B2 2F EF 10 64 9D 2B E7 3E 74 85 01 FE

Based on the format, the R size is 31 and S size is 32. How to pass this signature to the uECC_verify function? Does it require some padding? If we just extract R and add S after the R, the signature length is 63 bytes and uECC_verify fails.

I would appreciate any help or comments.

Thanks,

Petr

jaylogue commented 1 year ago

(Hopefully Ken won't mind me jumping in here...)

Petr,

The data you have is an Ecdsa-Sig-Value structure, which has been encoded in ASN.1 DER format as you observe. Ecdsa-Sig-Value is defined in X.509/rfc5480 as follows:

   ECDSA-Sig-Value ::= SEQUENCE {
     r  INTEGER,
     s  INTEGER
   }

Because of the way ASN.1 DER encodes INTEGERs, the encoded values for r and s could, in fact, be smaller than 32 bytes (all the way down to 1 byte if the value happened to have 31 leading zero bytes) as well as bigger than 32 bytes (specifically, 33 bytes, if high bit in the first byte is 1). In practice, however, very small encodings are vanishingly rare because the bits in r and s are effectively pseudo-random. But 31, 32 and 33 byte encodings are fairly common to see.

Your best bet is to find some existing code to properly decode the integer values. Unfortunately I don't believe micro-ecc includes such a function.

If you want to implement it yourself, here's a quick summary of the rules (see X.690 for details): ASN.1 DER INTEGER values are always encoded in 2s-compliment, big-endian format using the minimum number of bytes necessary. Compression is achieved by suppressing redundant leading bytes in the encoding. Specifically: if the integer is positive, redundant leading 0x00 bytes are omitted. If the integer is negative, redundant leading 0xFF bytes are omitted. A leading 0x00/0xFF byte is redundant if removing it doesn't change the sign of the value. A special case exists for positive integers where the high bit in the first byte is a 1. In this case the encoding adds a leading 0x00 byte so that the receiver properly interprets the value as positive.

coran21 commented 1 year ago

Thanks Jay, that helped me very much and I was able to write my code to parse ASN.1 DER correctly to get always 64 bytes length signature - R and S padded with 0 as the integers should be always positive for ECDSA.

Should this issue be closed?

Petr