kmackay / micro-ecc

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

Interoperability with python-ecdsa regarding the SECP160r1 curve #194

Open t123yh opened 2 years ago

t123yh commented 2 years ago

I'm trying to make a signature within my MCU running micro-ecc, and verify the signature with python-ecdsa library. The problem is, when using the SECP160r1 curve, python-ecdsa expects 42 bytes signature, but in micro-ecc, it produces 40 bytes signature.

You can test with the following python code:

from ecdsa import SECP160r1, SigningKey, VerifyingKey
import hashlib

def get_sha(s):
  h = hashlib.new('sha1')
  h.update(s.encode())
  return h.digest()

hash = get_sha("123")
our_sk = SigningKey.generate(curve=SECP160r1)
sign = our_sk.sign(hash)
print("sign:", sign.hex(), "len:", len(sign))

Example output:

sign: 0039090c6e893886b196c9ed3edf77f00cef9f47e70039b82ca31e51e3c2315afab4ca171835bc312faa len: 42

Is there any way to make python-ecdsa and micro-ecc compatible?

ab-tools commented 1 year ago

@t123yh, did you ever find a solution to this?

I would also like to use it together with this Python library. Any suggestions (possibly also alternatives that you end up using) would be welcome, thanks.

vruello commented 10 months ago

I had the same problem and I was able to get it to work :)

As I understand it, uECC outputs by default a 40 byte signature formatted as [r19]...[r0][s19]...[s0], while python-ecdsa expects [r20]...[r0][s20]...[s0]. According to some guy on stackoverflow (no cryptographer here), r20 and s20 are \x00, so you just have to transform the signature a bit before passing it to python-ecdsa : sig = b'\x00' + raw_sig[:20] + b'\x00' + raw_sig[20:].

Also note that VerifyingKey.verify computes a hash of the given data whereas uECC_sign doesn't. If you want to verify the output <sig> of uECC_sign(<pub_key>, DATA, sizeof(DATA), <sig>, <curve>), you need to use VerifyingKey.verify_digest(<sig>, DATA).

I've written a small Python script that verifies a signature created by uECC using secp160r1, given the public key, the signature and the digest (all hex formatted).

import ecdsa
import argparse

parser = argparse.ArgumentParser()
parser.add_argument("pub_key")
parser.add_argument("sig")
parser.add_argument("data")
args = parser.parse_args()

key = ecdsa.VerifyingKey.from_string(bytes.fromhex(args.pub_key), curve=ecdsa.SECP160r1)
raw_sig = bytes.fromhex(args.sig)
data = bytes.fromhex(args.data)

sig = b'\x00' + raw_sig[:20] + b'\x00' + raw_sig[20:]
print(key.verify_digest(sig, data))

Example:

$ python3 check.py 3801ab235453d93bfc7f75a092aed1c22c598a3a4c0419e9269035968d98443ddd4be4268770f70a 7be90347cdfbc01eb7df7a0a8b4eb1a6793e3f8b72640c36a828022effb84118e172f10007e4a333 0b9c2625dc21ef05f6ad4ddf47c5f203837aa32c
True