Topic: ecda verify signature, hash msg and key help

Hello everyone,

I am trying to use the wolfcrypt functions in order to be able to verify a signature (along with public key and hash message). I am running my tests on a Cortex-m0 platform.

I am working with the following files:

signature.c
ecc.c
hash.c
sp_int.c
wolfmath.c
sha256.c
sha.c
asn.c
memory.c
integer.c
sp_cortexm.c


My configuration is the following:

#define HAVE_ECC
#define HAVE_ECC_VERIFY
#define ECC_USER_CURVES
#define HAVE_ECC_KEY_IMPORT
#define WOLFSSL_ECC_NO_SMALL_STACK
#define NO_RSA
#define SINGLE_THREADED
#define NO_FILESYSTEM
#define NO_MD5
#define NO_OLD_TLS
#define WOLFSSL_NO_PEM
#define WC_NO_RNG
#define NO_ECC_SIGN
#define NO_DSA
#define NO_ASN_TIME
#define NO_PWDBASED
#define NO_SKID
#define WOLFCRYPT_ONLY
#define NO_WOLFSSL_MEMORY
#define WOLFSSL_NO_MALLOC
#define WC_NO_HARDEN
#define SIZEOF_LONG 4
#define SIZEOF_LONG_LONG 8
#define NO_DEV_RANDOM
#define NO_MAIN_DRIVER
#define NO_WRITEV
#define WOLFSSL_USER_IO
#define NO_SESSION_CACHE
#define WOLFSSL_SP_MATH
#define WOLFSSL_HAVE_SP_DH
#define WOLFSSL_SP_ARM_CORTEX_M_ASM


The functions I am testing with are the:

int wc_ecc_rs_raw_to_sig(const byte* r, word32 rSz, const byte* s, word32 sSz,
    byte* out, word32* outlen);

int wc_ecc_import_unsigned(ecc_key* key, const byte* qx, const byte* qy,
                   const byte* d, int curve_id);

int wc_SignatureVerifyHash(
    enum wc_HashType hash_type, enum wc_SignatureType sig_type,
    const byte* hash_data, word32 hash_len,
    const byte* sig, word32 sig_len,
    const void* key, word32 key_len);

So what I am trying to do is use the test vectors in the link below to test if my code works.
I am using the following vectors (https://www.ietf.org/rfc/rfc6979.txt  A.2.5):

public key: U = xG

    Ux = 60FED4BA255A9D31C961EB74C6356D68C049B8923B61FA6CE669622E60F29FB6

    Uy = 7903FE1008B8BC99A41AE9E95628BC64F2F1B20C2D7E9F5177A3C294D4462299
   
message = 'sample'
   SHA256 hash_of_message : af2bdbe1aa9b6ec1e2ade1d694f41fc71a831d0268e9891562113d8a62add1bf

   r = EFD48B2AACB6A8FD1140DD9CD45E81D69D2C877B56AAF991C34D0EA84EAF3716
   s = F7CB1C942D657C41D436C7A1B6E29F65F3E900DBB9AFF4064DC4AB2F843ACDA8
   
   
So a bit of code now:

uint8_t r[] = {0xEF, 0xD4, 0x8B, 0x2A, 0xAC, 0xB6, 0xA8, 0xFD, 
               0x11, 0x40, 0xDD, 0x9C, 0xD4, 0x5E, 0x81, 0xD6, 
               0x9D, 0x2C, 0x87, 0x7B, 0x56, 0xAA, 0xF9, 0x91, 
               0xC3, 0x4D, 0x0E, 0xA8, 0x4E, 0xAF, 0x37, 0x16, 
              };
                
uint8_t s[] = {0xF7, 0xCB, 0x1C, 0x94, 0x2D, 0x65, 0x7C, 0x41, 
               0xD4, 0x36, 0xC7, 0xA1, 0xB6, 0xE2, 0x9F, 0x65, 
               0xF3, 0xE9, 0x00, 0xDB, 0xB9, 0xAF, 0xF4, 0x06, 
               0x4D, 0xC4, 0xAB, 0x2F, 0x84, 0x3A, 0xCD, 0xA8};
               
uint8_t pubkeyX[] ={0x60, 0xFE, 0xD4, 0xBA, 0x25, 0x5A, 0x9D, 0x31, 
                    0xC9, 0x61, 0xEB, 0x74, 0xC6, 0x35, 0x6D, 0x68, 
                    0xC0, 0x49, 0xB8, 0x92, 0x3B, 0x61, 0xFA, 0x6C, 
                    0xE6, 0x69, 0x62, 0x2E, 0x60, 0xF2, 0x9F, 0xB6};

uint8_t pubkeyY[] = {0x79, 0x03, 0xFE, 0x10, 0x08, 0xB8, 0xBC, 0x99, 
                     0xA4, 0x1A, 0xE9, 0xE9, 0x56, 0x28, 0xBC, 0x64, 
                     0xF2, 0xF1, 0xB2, 0x0C, 0x2D, 0x7E, 0x9F, 0x51, 
                     0x77, 0xA3, 0xC2, 0x94, 0xD4, 0x46, 0x22, 0x99};
                     
const uint8_t msghash[] = {0xAF, 0x2B, 0xDB, 0xE1, 0xAA, 0x9B, 0x6E, 0xC1, 
                           0xE2, 0xAD, 0xE1, 0xD6, 0x94, 0xF4, 0x1F, 0xC7, 
                           0x1A, 0x83, 0x1D, 0x02, 0x68, 0xE9, 0x89, 0x15, 
                           0x62, 0x11, 0x3D, 0x8A, 0x62, 0xAD, 0xD1, 0xBF};
                           
uint8_t signature_wc[256] = {};
uint32_t signature_wc_size = 256;

ecc_key ecc_pubkey;
int eccret;

    // RS raw to sig -  Is this correct?
    eccret = wc_ecc_rs_raw_to_sig(r, sizeof(r), s, sizeof(s), signature_wc, &signature_wc_size);
    
    // Import the key buffer to the ecc_key structure format
    eccret = wc_ecc_import_unsigned(&ecc_pubkey, pubkeyX, pubkeyY, NULL, ECC_SECP256R1);
    
    // Verify
    eccret = wc_SignatureVerifyHash(WC_HASH_TYPE_SHA256, WC_SIGNATURE_TYPE_ECC, msghash, sizeof(msghash), 
                                    signature_wc, signature_wc_size, &ecc_pubkey, sizeof(ecc_pubkey));

Verification function returns with -229 error (SIG_VERIFY_E).
I think that I am not doing something correctly with the signature or the key conversions.
Can someone provide some insights on this?
Thank you in advance,
Kind Regards,

Nikos

Share

Re: ecda verify signature, hash msg and key help

Hi Nikos,

Thanks for contacting wolfSSL Forums.

We are reviewing this and will post an update.

Thanks,
Eric @ wolfSSL Support

Re: ecda verify signature, hash msg and key help

Hello Nikos,

First of all, I think you should use the function wc_ecc_verify_hash_ex() https://www.wolfssl.com/documentation/m … fy_hash_ex. This would eliminate the need for converting the signature from raw (R,S) format to signature, and also make the code smaller. signature.c and asn.c should not be needed in your build if you stick to the APIs in ecc.h instead.

To create the "mp_int" object to import the R,S components in the format expected by this call, you could do the following:

            mp_int mp_r, mp_s;
            mp_init(&mp_r);
            mp_init(&mp_s);
            mp_read_unsigned_bin(&mp_r, r, sizeof(r));
            mp_read_unsigned_bin(&mp_s, s,  sizeof(s));

Very important: before using your ecc_pubkey structure to import the key, you must initialize it:

    eccret = wc_ecc_init(&ecc_pubkey);

Your import call then looks fine as is:

// Import the key buffer to the ecc_key structure format
    eccret = wc_ecc_import_unsigned(&ecc_pubkey, pubkeyX, pubkeyY, NULL, ECC_SECP256R1);

Then finally you can call the correct verify function as:

          eccret = wc_ecc_verify_hash_ex(&mp_r, &mp_s, hash_data, hash_len, &result, &eccpubkey);

If all goes as expected, eccret should be zero and result should be 1, indicating successful verification.

Another thing I have noticed in your configuration: the SP assembly optimizations don't match your current MCU. Cortex-M0 is ARMv6-M and we do provide thumb instructions optimization for it. I suggest you remove the option WOLFSSL_SP_ARM_CORTEX_M_ASM and use WOLFSSL_SP_ARM_THUMB_ASM instead. Also the correct assembly file for Cortex-M0 is sp_armthumb.c if you are compiling with options "-mcortex-m0 -mthumb" as expected for this MCU. The sp_cortexm.c source file contains assembly optimizations for Cortex-M3 and later (up to newer ARMv8-M).

I would also recommend to add the option WOLFSSL_HAVE_SP_ECC to ensure ecc is using those optimizations.

Furthermore, since you are defining WOLFSSL_NO_MALLOC, I expect that you don't have/don't want to use dynamic allocation. In this case, in combination with SP_MATH, I would also suggest the following:

#define WOLFSSL_SP_NO_MALLOC
#define WOLFSSL_SP_NO_DYN_STACK

I hope this helps. Please let us know if you are still experiencing issues after the suggested fixes.

Thanks,
Daniele @wolfSSL Support

Share

Re: ecda verify signature, hash msg and key help

Hello Daniele,

Thank you very much for your detailed and fast response!
I was able to build with your recommendations by only using the ecc code.
The problem was this new implementation was using too much STACK. So i reverted to using also HEAP.

Added these into my configuration:

#define WOLFSSL_SMALL_STACK
#include "stdlib.h"
#define XMALLOC_OVERRIDE
#define XMALLOC(n, h, t)        malloc(n)
#define XREALLOC(p, n, h, t)    realloc(p, n)
#define XFREE(p, h, t)          free(p)

Removed

#define WOLFSSL_NO_MALLOC
#define WOLFSSL_ECC_NO_SMALL_STACK
#define NO_WOLFSSL_MEMORY

My new configuration is:

#define HAVE_ECC
#define HAVE_ECC_VERIFY
#define ECC_USER_CURVES
#define HAVE_ECC_KEY_IMPORT

#define NO_RSA
#define SINGLE_THREADED
#define NO_FILESYSTEM
#define NO_MD5
#define NO_OLD_TLS
#define WOLFSSL_NO_PEM
#define WC_NO_RNG
#define NO_ECC_SIGN
#define NO_DSA
#define NO_ASN_TIME
#define NO_PWDBASED
#define NO_SKID
#define NO_ASN
#define WOLFCRYPT_ONLY

#define WC_NO_HARDEN
#define SIZEOF_LONG 4
#define SIZEOF_LONG_LONG 8
#define NO_DEV_RANDOM7
#define NO_MAIN_DRIVER
#define NO_WRITEV

#if 1
#define WOLFSSL_SMALL_STACK
#include "stdlib.h"
#define XMALLOC_OVERRIDE
#define XMALLOC(n, h, t)        malloc(n)
#define XREALLOC(p, n, h, t)    realloc(p, n)
#define XFREE(p, h, t)          free(p)
#else
#define WOLFSSL_NO_MALLOC
#define WOLFSSL_ECC_NO_SMALL_STACK
#define NO_WOLFSSL_MEMORY
#endif

// SP_MATH_CONFIG
#define WOLFSSL_SP_MATH
#define WOLFSSL_HAVE_SP_DH
#define WOLFSSL_SP_SMALL
#define WOLFSSL_HAVE_SP_ECC
#define WOLFSSL_SP_ARM_THUMB_ASM
//#define WOLFSSL_SP_ARM_CORTEX_M_ASM 
//#define WOLFSSL_SP_NO_MALLOC
//#define WOLFSSL_SP_NO_DYN_STACK

I have given about 2K of heap but the verify function returns -125 (out of memory error)
I will check if I can do some more optimizations so that I can be able to run this successfully.

I don't have much stack and/or heap in my usecase.
FYI what I am actually trying to do is checking if I can implement a secure bootloader with wolfssl.

Thank you again for your support.
I will come back with more information once I get it running.
Let me know if you have any additional information or configuration suggestions.

I only need to be able to verify. Do you know if there are things that I could remove and make this more light in terms of code size and stack/heap requirements?

Thank you again!
Kind Regards,
Nikos

Share

5 (edited by koutsion 2023-10-06 08:00:44)

Re: ecda verify signature, hash msg and key help

Hello again,

I managed to increase the heap needed so the verification runs correctly!
Now I am trying to optimize for code size. Thank you again for your quick and detailed support!
Do you have any tips on further optimizing for code size?
Thank you again and have a nice weekend!
Kind Regards,
Nikos

P.S my latest configuration

#define HAVE_ECC
#define HAVE_ECC_VERIFY
#define ECC_USER_CURVES

#define NO_RSA
#define SINGLE_THREADED
#define NO_MD5
#define NO_OLD_TLS
#define WOLFSSL_NO_PEM
#define WC_NO_RNG
#define NO_ECC_SIGN
#define NO_DSA
#define NO_ASN_TIME
#define NO_PWDBASED
#define NO_ASN
#define NO_ECC_KEY_EXPORT
#define WOLFCRYPT_ONLY
#define WC_NO_HARDEN
#define SIZEOF_LONG 4
#define SIZEOF_LONG_LONG 8
#define NO_FILESYSTEM

#define WOLFSSL_SMALL_STACK
#include "stdlib.h"
#define XMALLOC_OVERRIDE
#define XMALLOC(n, h, t)        malloc(n)
#define XREALLOC(p, n, h, t)    realloc(p, n)
#define XFREE(p, h, t)          free(p)

// SP_MATH_CONFIG
#define WOLFSSL_SP_MATH
#define WOLFSSL_HAVE_SP_DH
#define WOLFSSL_SP_SMALL
#define WOLFSSL_HAVE_SP_ECC
#define WOLFSSL_SP_ARM_THUMB_ASM

Share

Re: ecda verify signature, hash msg and key help

Hello Nikos,

Happy to hear that everything worked.

WOLFSSL_SMALL_STACK indeed keeps your stack usage low, but requires heap. Anyway I see you have fixed the runtime memory and it looks OK.

Regarding the footprint:
- If you are only using ECC verify, you could disable other functionality and compile them out of the ECC module with the following:

#          define NO_ECC_SIGN
#          define NO_ECC_DHE
#          define NO_ECC_EXPORT
#          define NO_ECC_KEY_EXPORT

- there is a trade-off if you are enabling WOLFSSL_SP_ARM_THUMB_ASM ECC will be faster but also larger in size. If you can afford to lose some performance you could use the same implementation in C, which is smaller (and slower). To do so, remove WOLFSSL_SP_ARM_THUMB_ASM and link the file "sp_c32.c" instead of "sp_armthumb.c". The footprint should be now significantly reduced (although taking more time to verify the signature).

Thanks,

--
Daniele @wolfSSL Support

Share

Re: ecda verify signature, hash msg and key help

> FYI what I am actually trying to do is checking if I can implement a secure bootloader with wolfssl.

We already have that! Check out wolfBoot:

https://github.com/wolfSSL/wolfBoot

It's fully featured and highly portable, you can see we already support some M0's like the STM32L073, the Atmel/Microchip SAM-R21 and Cypress PSOC6.

For your reference about the code size, wolfBoot ECC256+SHA256 on STM32L0 is ~ 18KB in size including crypto, down to ~16KB without assembly optimizations, ~14KB if using ED25519 and SHA384, and even a little smaller if using LMS (post quantum crypto algorithms).

Thanks,

--
Daniele @wolfSSL Support

Share

Re: ecda verify signature, hash msg and key help

SamR21 has an even less complicated flash driver than STM32L0 and fits below 10KB

(Ed25519/sha384):

    [SIZE]
   text       data        bss        dec        hex    filename
   9736          0         56       9792       2640    wolfboot.elf

(lms 1-10-8 + sha256)

    [SIZE]
   text       data        bss        dec        hex    filename
   8564          0         40       8604       219c    wolfboot.elf

Thanks,

--
Daniele @wolfSSL Support

Share

Re: ecda verify signature, hash msg and key help

Hello Daniele,

Thank you again for your quick and helpful suggestions!
I tried your suggestions on using the sp_32.c (instead of the assembly implementation) and I got about 2k reduction in my image size.
I am still on the limit of my application requirements. I will check what else I can do to fit everything in.
Thank you again for your valuable tips and support!
I will post again soon!
Kind Regards,
Nikos

Share