micro-ecc: Failure to verify against openssl

I built the library and am running under osx. I am using the secp256r1 curve.

If I run the example to sign / verify using uECC it will verify.

What fails is when I try to use uECC to verify a signature generated by openssl, or use openssl to verify using a signature generated by uECC.

I have generated a private key with openssl and verified that uECC_make_public_key() will generate the same public key that openssl is getting so I am confident the curve is matching.

My key generation, signing and verification script:

openssl ec -in private.pem -pubout -out public.pem  #-conv_form uncompressed
openssl ec -in private.pem -text
touch msg.txt | echo -n "aaa" > msg.txt
openssl dgst -sha256 -out msg.digest.txt msg.txt
cat msg.digest.txt
openssl dgst -sha256 -sign private.pem -out msg.signature.txt msg.digest.txt
openssl asn1parse -inform DER -in msg.signature.txt
python3 dumpsig.py <msg.signature.txt
openssl dgst -sha256 -hex -verify public.pem -signature msg.signature.txt msg.digest.txt

And output of script:

writing EC key
read EC key
Private-Key: (256 bit)
priv:
    5f:ef:5f:e5:62:7c:ff:60:56:87:3e:64:87:32:72:
    c3:ed:8c:08:c4:b6:01:b1:3c:36:c0:db:c2:bb:8d:
    24:fc
pub: 
    04:f1:bf:ca:c9:3c:ff:50:71:4f:7d:cc:1c:23:e7:
    03:02:d1:81:8e:88:52:20:43:01:8a:3e:68:98:78:
    9b:8a:9f:48:a2:a8:43:f4:2a:e3:3c:5a:da:d8:90:
    bc:5d:f3:87:98:64:fb:b8:ea:e7:ea:95:5e:39:84:
    3a:85:44:a3:49
ASN1 OID: prime256v1
NIST CURVE: P-256
writing EC key
-----BEGIN EC PRIVATE KEY-----
MHcCAQEEIF/vX+VifP9gVoc+ZIcycsPtjAjEtgGxPDbA28K7jST8oAoGCCqGSM49
AwEHoUQDQgAE8b/KyTz/UHFPfcwcI+cDAtGBjohSIEMBij5omHibip9IoqhD9Crj
PFra2JC8XfOHmGT7uOrn6pVeOYQ6hUSjSQ==
-----END EC PRIVATE KEY-----
SHA256(msg.txt)= 9834876dcfb05cb167a5c24953eba58c4ac89b1adf57f28f2f9d09af107ee8f0
    0:d=0  hl=2 l=  68 cons: SEQUENCE          
    2:d=1  hl=2 l=  32 prim: INTEGER           :58CF628E0597B8AE32BD8199C6E7DF0192A50CF3AAC07F66769286FB00F45815
   36:d=1  hl=2 l=  32 prim: INTEGER           :78235F960F70E5641B6F1CCE080F897F20722541BDD6A81FFA5DF3C2B130FAEB
signature(70) = 3044022058cf628e0597b8ae32bd8199c6e7df0192a50cf3aac07f66769286fb00f45815022078235f960f70e5641b6f1cce080f897f20722541bdd6a81ffa5df3c2b130faeb
Verified OK

My uECC test code is simply:

char sig[] = "58CF628E0597B8AE32BD8199C6E7DF0192A50CF3AAC07F66769286FB00F4581578235F960F70E5641B6F1CCE080F897F20722541BDD6A81FFA5DF3C2B130FAEB";
char priv[] = "5fef5fe5627cff6056873e64873272c3ed8c08c4b601b13c36c0dbc2bb8d24fc";

void uECC_test(void)
{
  int r;
  const struct uECC_Curve_t * curve = uECC_secp256r1();

  uint8_t private1[32];
  uint8_t public1[64];

 uint8_t signature[64];

  uint8_t hash[32] = {0x98, 0x34, 0x87, 0x6D, 0xCF, 0xB0, 0x5C, 0xB1, 0x67, 0xA5, 0xC2, 0x49, 0x53, 0xEB, 0xA5, 0x8C, 0x4A, 0xC8, 0x9B, 0x1A, 0xDF, 0x57, 0xF2, 0x8F, 0x2F, 0x9D, 0x09, 0xAF, 0x10, 0x7E, 0xE8, 0xF0};
  dump("hash", hash, sizeof(hash));

  strtobytes(priv, private1);
  dump("priv", private1, sizeof(private1));
  uECC_compute_public_key(private1, public1, curve);
  dump("pub ", public1, sizeof(public1));

  memset(signature, 0xDB, sizeof(signature));
  r =  uECC_sign(private1, hash, sizeof(hash), signature, curve);

//  strtobytes(sig, signature);  // if to override uECC signature with openssl signature
  dump("sig ", signature, sizeof(signature));

  r = uECC_verify(public1, hash, sizeof(hash), signature, curve);
  printf("verify result = %d\n", r);
}

When I run that using the signature generated by uECC, it works and I get:

priv (32): 5fef5fe5627cff6056873e64873272c3ed8c08c4b601b13c36c0dbc2bb8d24fc
pub  (64): f1bfcac93cff50714f7dcc1c23e70302d1818e88522043018a3e6898789b8a9f48a2a843f42ae33c5adad890bc5df3879864fbb8eae7ea955e39843a8544a349
sig  (64): 9fff2fceeb0015300030b78b5c930340408c2145390980e3b4bdff613eb4658e9690d1c9c970bdc1f16fee457d550ede4758df8ea3e24dfb3dee2d6481f1cf98
verify result = 1

If I override the signature generated by uECC (uncomment the strtobytes call), I get:

priv (32): 5fef5fe5627cff6056873e64873272c3ed8c08c4b601b13c36c0dbc2bb8d24fc
pub  (64): f1bfcac93cff50714f7dcc1c23e70302d1818e88522043018a3e6898789b8a9f48a2a843f42ae33c5adad890bc5df3879864fbb8eae7ea955e39843a8544a349
sig  (64): 58cf628e0597b8ae32bd8199c6e7df0192a50cf3aac07f66769286fb00f4581578235f960f70e5641b6f1cce080f897f20722541bdd6a81ffa5df3c2b130faeb
verify result = 0

I have the same issue if I take the 64-byte signature generated by uECC above, converting it to asn1/DER format and trying to get openssl to verify the signature.


$ python3 mksig.py 9fff2fceeb0015300030b78b5c930340408c2145390980e3b4bdff613eb4658e9690d1c9c970bdc1f16fee457d550ede4758df8ea3e24dfb3dee2d6481f1cf98 >junk
$ openssl asn1parse -inform DER -in junk
    0:d=0  hl=2 l=  70 cons: SEQUENCE          
    2:d=1  hl=2 l=  33 prim: INTEGER           :9FFF2FCEEB0015300030B78B5C930340408C2145390980E3B4BDFF613EB4658E
   37:d=1  hl=2 l=  33 prim: INTEGER           :9690D1C9C970BDC1F16FEE457D550EDE4758DF8EA3E24DFB3DEE2D6481F1CF98
 $ openssl dgst -sha256 -hex -verify public.pem -signature junk msg.digest.txt
Verification Failure

Is there a reason that this is not working for me? I understand that this is for embedded systems and that is where I plan to use it, but I wanted to at least sanity check it on a more friendly platform before investing more time with it.

Thank you.

About this issue

  • Original URL
  • State: closed
  • Created 4 years ago
  • Comments: 18 (1 by maintainers)

Most upvoted comments

The issue is that when you run ‘openssl dgst -sha256 -sign private.pem -out msg.signature.txt msg.digest.txt’, it hashes whatever the input is (msg.digest.txt) in this case, and signs the result of that. If you instead run ‘openssl dgst -sha256 -sign private.pem -out msg.signature.txt msg.txt’, everything works as expected.

> openssl ec -in key.pem -pubout -out public.pem -conv_form uncompressed
read EC key
writing EC key
> openssl ec -in key.pem -text
read EC key
Private-Key: (256 bit)
priv:
    00:ca:8f:15:46:3c:ed:57:44:69:16:37:6e:7f:32:
    ea:82:e9:52:66:47:97:cf:52:25:9e:cf:d3:ec:cb:
    9c:32
pub: 
    04:ef:54:c3:1f:04:1e:8c:d4:e7:a6:a7:2e:49:eb:
    b1:f5:8b:bb:60:01:ea:50:8d:3d:f0:3a:7e:b9:43:
    b8:63:85:75:3a:6c:48:a5:36:e7:1e:6f:dd:54:43:
    96:8d:3e:df:cb:df:15:2a:ca:be:71:8c:a4:c4:39:
    19:2f:8e:10:df
ASN1 OID: prime256v1
writing EC key
-----BEGIN EC PRIVATE KEY-----
MHYCAQEEH8qPFUY87VdEaRY3bn8y6oLpUmZHl89SJZ7P0+zLnDKgCgYIKoZIzj0D
AQehRANCAATvVMMfBB6M1Oempy5J67H1i7tgAepQjT3wOn65Q7hjhXU6bEilNuce
b91UQ5aNPt/L3xUqyr5xjKTEORkvjhDf
-----END EC PRIVATE KEY-----
> touch msg.txt | echo -n "aaa" > msg.txt
> openssl dgst -sha256 -out msg.digest.txt msg.txt
> cat msg.digest.txt
SHA256(msg.txt)= 9834876dcfb05cb167a5c24953eba58c4ac89b1adf57f28f2f9d09af107ee8f0
> openssl dgst -sha256 -sign key.pem -out msg.signature.txt msg.txt
> openssl asn1parse -inform DER -in msg.signature.txt
    0:d=0  hl=2 l=  70 cons: SEQUENCE          
    2:d=1  hl=2 l=  33 prim: INTEGER           :A0CC5254B415668A7CFE1FFCD671A72EB463656EC8060CE702459BA2BDCBD260
   37:d=1  hl=2 l=  33 prim: INTEGER           :FE086A1B54B8761DD66C1180AC94A00427451173E45F47079462F1B4FA1EBA5C
> openssl dgst -sha256 -hex -verify public.pem -signature msg.signature.txt msg.txt
Verified OK

and then the C code using uECC to verify:

#include "uECC.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

char priv[] = "00ca8f15463ced57446916376e7f32ea82e952664797cf52259ecfd3eccb9c32";
char pub[] = "ef54c31f041e8cd4e7a6a72e49ebb1f58bbb6001ea508d3df03a7eb943b86385753a6c48a536e71e6fdd5443968d3edfcbdf152acabe718ca4c439192f8e10df";
char sig[] = "A0CC5254B415668A7CFE1FFCD671A72EB463656EC8060CE702459BA2BDCBD260FE086A1B54B8761DD66C1180AC94A00427451173E45F47079462F1B4FA1EBA5C";
char hash[] = "9834876dcfb05cb167a5c24953eba58c4ac89b1adf57f28f2f9d09af107ee8f0";

void dump(const char* tag, uint8_t *vli, unsigned int size) {
  printf("%s: ", tag);
  for(unsigned i=0; i<size; ++i) {
      printf("%02X ", (unsigned)vli[i]);
  }
  printf("\n");
}

void strtobytes(const char* str, uint8_t* bytes, int count) {
  for (int c = 0; c < count; ++c) {
    if (sscanf(str, "%2hhx", &bytes[c]) != 1) {
      printf("Failed to read string to bytes");
      exit(1);
    }
    str += 2;
  }
}

int main() {
  int r;
  const struct uECC_Curve_t * curve = uECC_secp256r1();

  uint8_t private[32];
  uint8_t public[64];
  uint8_t signature[64];
  uint8_t hash_bin[32];

  uint8_t public2[64];

  strtobytes(priv, private, 32);
  strtobytes(pub, public, 64);
  strtobytes(sig, signature, 64);
  strtobytes(hash, hash_bin, 32);

  dump("private", private, sizeof(private));
  dump("public", public, sizeof(public));
  dump("signature", signature, sizeof(signature));
  dump("hash_bin", hash_bin, sizeof(hash_bin));

  uECC_compute_public_key(private, public2, curve);
  if (memcmp(public, public2, sizeof(public)) != 0) {
    printf("Public key doesn't match\n");
    return 1;
  }

  r = uECC_verify(public, hash_bin, sizeof(hash_bin), signature, curve);
  printf("verify result = %d\n", r);

  return 0;
}