Arm Mbed OS support forum

How to use "mbedtls_ecdsa_sign_det()"?

Hi again,

I feel my questions is not well organized.
Previously, I completed the ecdsa verification without hash based on your advice.
From now on, I hope to add the hash such as SHA256.
Normally, verification code support hash algorithm as a argument like following.
-> mbedtls_ecdsa_sign_det( &grp, &r, &s, &d, hash, hlen, md_alg )
But, mbedtls_ecdsa_verify doesn’t support hash algorithm as an argument.
So, do I have to make digest before calling mbedtls_ecdsa_verify()?

What is the best and simple way for verification with using R,S and Hash?

Thanks,
Matthew

Hi @matthewc
Asymetric authentication does the verification process on the message hash. Mbed TLS API suggests the hash to be the input. ( You can sign the full message, however sometimes it may take long time, if the message is long).

In mbedtls_ecdsa_sign_det(), the input is expected to be hashed and the md_alg is The hash algorithm used to hash the original data.. It is used to initialize the hmac_drbg() that is used as part of the deterministic ecdsa.
Regards,
Ron

Hi Ron,

First off, I appreciate your help.
It’s very clear to me. Before calling mbedtls_ecdsa_verify(), I better to make digest with hash like md_alg in mbedtls_ecdsa_sign_det() .
Can you show the example code for SHA384 or SHA512? It seems that the code for SHA512 can be used for SHA384. I hope to know how to initialize and get the digest from SHA384 API.

Thanks,
Matthew

Hi @matthewc
SHA384 is basically truncated SHA512 ( with different initial state)
the API is same, except that in SHA 384, you need to set 1 as the is384 parameter in mbedtls_sha512_starts_ret(), or mbedtls_sha512_ret() if you want to do a one call Hash calculations in the full input.

Note that the Hash size of SHA384 is 384 bits( 48 bytes) and not 512 bits (64 bytes), so you will need to take the first 48 bytes of your 64 bytes output.
Regards

Hi Ron,

I appreciate your help. It’s very clear to me. I have more questions.
Now, I have DER public key and raw r and s bit value.

  1. As you recommendation, I can use mbedtls_ecdsa_verify() for r and s bit value.
    In this case, Do I have to extract public key from DER format?
    Which function do I have to use it to get the public key and send it as Q value?

  2. For these, can I also use below function?
    mbedtls_pk_verify_ext(MBEDTLS_PK_ECDSA,…)
    From the ECDSA point of view, I’m wondering what is difference between mbedtls_pk_verify_ext and mbedtls_ecdsa_verify.

Thank you so much.

Hi @matthewc
In order to convert the DER public key format, to mbedtls_pk_context, you will need to call mbedtls_pk_parse_public_key().
The PK module is a wrapper for the public key cryptography, and you may consider using mbedtls_pk_verify_ext(), however internally it calls mbedtls_ecdsa_read_signature() which is not what you want, AFAIK.
However, once you parsed the key to mbedtls_pk_context, you can call mbedtls_pk_ec() on this context, to get the mbedtls_ecp_keypair, and extract r,s, and group from this structure.
Regards.
This is not ideal, as it accesses the internals of a structure, and as such not future compatible.
Regards

Hi Ron,

Thanks again.

  1. mbedtls_pk_verify_ext() cannot be used with binary R,S.
    mbedtls_pk_ec() can be called. It’s not recommended.

  2. mbedtls_pk_parse_public_key(), mbedtls_ecdsa_veryfy() seems to be recommended.
    mbedtls_pk_parse_public_key() produce mbedtls_pk_context.
    mbedtls_ecdsa_veryfy() requires mbedtls_ecp_point Q as an argument.
    How to translate mbedtls_pk_context to mbedtls_ecp_point?

Thank you so much.

Hi @matthewc

How to translate mbedtls_pk_context to mbedtls_ecp_point?

Calling mbedtls_pk_ec() on the pk context will retreive you the mbedtls_ecp_keypair structure.
You can access the mbedtls_ecp_point member of this struct, however there isn’t an API that retrieves this point to your convenience. In case there will be an ABI breakage of the structure in the future, it might not work.
Regards

Hi Ron,

I hope you’re doing well. Your last explanation was very helpful for me. I appreciate it.
I have one more question.
I made binary format signature(r,s) with using python script and loaded this to the array.
And then I hope to store this array to mbedtls_mpi r, s.
This is the purpose of sending these r,s as an argument of mbedtls_ecdsa_verify().
What I want to do is to import raw r, s data to mbedtls_mpi r, s value.
How to store this array to this structure? Can you show me some example?

Thanks,

Hi @matthewc
I believe what you need is the mbedtls_mpi_read_binary() API.
This reads a binary buffer into an mbedtls_mpi struct.

For example, you can look at this example, how a binary signature is translated into r and s.
You should take into consideration the endianess of your buffer.
Regards

Hi Ron,

Thanks again. It is also very helpful. I have one more question. It would be the last question I think.
I’m using some function in memory_buffer_alloc.c which got many exit(). I’m not using std library.
Thus, It’s causing undefined reference to ‘exit’ in function ‘buffer_calloc_free’ and 'buffer_alloc_free.
I simply redefine “#define mbedtls_exit return”. It seems to work well.
Is there any good solution you have? If so, I hope to get some advice.

Thanks,
Matthew

Hi @matthewc
May I know why you are using the memory_buffer_alloc.c feature?
Is it only to replace the calloc and free functions or is it because you would like to use static memory?
If it is only to replace the calloc and free, I would suggest you use your platfomr’s implementation of calloc and free, as the alternatives. As mentioned in this article and this post.
If it is a matter of reducing heap memory usage, note that the feature is not a full Memory Management Unit implementation, and was used only for the Mbed TLS needs. Perhaps you could consider modifying the platform’s linker file in this case, to modify the heap memory on the expence of the static memory ( to keep same RAM usage.)
Regards

I would like to replace the calloc and free function.
My platform’s calloc and free have got some problem, so, it’s not useful.
That’s why I’m trying to use memory_buffer_alloc.c feature.

Could this not be a solution that “#define mbedtls_exit return” is redefined.
Otherwise, what am I supposed for exit function?

Thanks,
Matthew

Hi @matthewc
Yes, if your platform doesn’t have and exit function, and you need it, you can define mbedtls_exit as you see fit

Note that return and exit do not have same functionality, as exit aborts the application, while return simply returns to the caller. You should be careful what s the value that you return to the caller.

From a brief look, the mbedtls_calloc function may have a fatal error, so, instead of aborting the application with status 1, you return to the caller the value 1. Since this is not NULL the application will then use address 1 as a valid pointer, and will cause undefined behavior in your system.
So, replacing mbedtls_exit to simply be return will not work in cases where you have fatal errors.
If you have an abort function or an assertions, I would use these, and see how to port the (1) value to its implementation.
Regards

Hi Ron,

Thanks again. I hope this would be the last question.
Roughly, I’m following below.

mbedtls_memory_buffer_alloc_init()
ecp = mbedtls_pk_ec(key)
mbedtls_ecp_point_init(&Q)
mbedtls_mpi_init(&r)
mbedtls_mpi_init(&s)
mbedtls_ecp_group_init(&grp)
mbedtls_ecp_group_load(&grp, id)

Q = ecp->Q;
mbedtls_ecp_check_pubkey(&grp, &Q)
mbedtls_sha512();

mbedtls_mpi_read_binary(&r, sign, sign_len);
mbedtls_mpi_read_binary(&s, sign, sign_len);
mbedtls_ecdsa_verify()

As you can see, Q, r, s comes from outside. This code only needs to verify signature.
I didn’t call the “mbedtls_ecp_gen_keypair() and mbedtls_ecdsa_sign()”
In this case, I’m assuming I don’t need to free memory like below.
The reason is that above code doesn’t allocate memory for grp, Q, r, s.
Is this correct?

mbedtls_ecp_group_free(&grp);
mbedtls_ecp_point_free(&Q);
mbedtls_mpi_free(&r);
mbedtls_mpi_free(&s);

Thanks in advance.

Hi @matthewc

mbedtls_mpi_read_binary(&r, sign, sign_len);
mbedtls_mpi_read_binary(&s, sign, sign_len);

This is incorrect. The signature is consisted of r+s , so you will read half the signature into r and the other half into s.

In this case, I’m assuming I don’t need to free memory like below.

I don’t follow you. Are you thinking of not calling the functions

mbedtls_ecp_group_free(&grp);
mbedtls_ecp_point_free(&Q);
mbedtls_mpi_free(&r);
mbedtls_mpi_free(&s);

Well, you should call them. When you read into mbedtls_mpi you allocate enough memory to fit the buffer.
In addition, all the data that was initiated in the xxx_init functions should be freed.
Regards

My issue is that “mbedtls_ecp_point_free(&Q)” shows error which is “FATAL: mbedtls_free() outside of managed space”. The others doesn’t make any errors.
This is my opinion. In case of Q, keyparser_der_import() is called before “mbedtls_memory_buffer_alloc_init()” is called. Memory for Q seems to be allocated by standard library rather than “buffer_alloc_calloc()”. At this point, there is no memory to free by “mbedtls_ecp_point_free(&Q)”. That’s why it makes above error.
What do you think about it?

Hi @matthewc
If you have a standard library, why are you using the memory_buffer feature?
I wouldn’t advise that.

However, in your example, you can see that Q is initiated at your code, soit should be freed at your code. How does it come from outside? Are you using by any chance a pointer to a pointer of Q?
Regards

Hi Roneld,

First off, I’m sorry for my poor explanation.

At first, mbedtls_pk_init() and mbedtls_pk_parse_public_key() is called for extracting Q.
(This is executed with standard library.)
And then mbedtls_pk_context value for Q is delivered to another function.

And then, this new called function will run below with delivered Q value.
mbedtls_ecp_point Q;
mbedtls_memory_buffer_alloc_init()
ecp = mbedtls_pk_ec(key)
mbedtls_ecp_point_init(&Q)
Q = ecp->Q;
(Standard library cannot be run here. So, mbedtls_memory_buffer_alloc_init() is called here. )

I called “mbedtls_ecp_point_init(&Q)” after mbedtls_pk_init() and mbedtls_pk_parse_public_key() are called.
I might guess I don’t need to initiate Q value through mbedtls_ecp_point_init() again.
I also guess It might not make “FATAL: mbedtls_free() outside of managed space”, if I don’t call mbedtls_ecp_point_init(&Q).
What do you think about it?

Thank a lot,
Matthew

Hi @matthewc
If you have parsed the key, and already have the key context, then yes, you shouldn’t initiate Q with mbedtls_ecp_point_init() and you shouldn’t free it at the end of this function.
In addition, you shouldn’t assign ecp->Q by value, but Q should be a pointer to ecp->Q
In short, instead of Q define mbedtls_ecp_point* Q; and assign pQ = & ecp->Q;
After that you should use pQ instead of %Q in all the places in the function.
However, if the only place you are using Q is for mbedtls_ecp_check_pubkey(&grp, &Q), then you could remove Q and call mbedtls_ecp_check_pubkey(&grp, &ecp->Q)
Regards