Open MaJerle opened 1 year ago
New findings - actually it is possible to extract 3 out of 4 options in relatively clean way, but one remains a mistery. How to NOT use private variables if public key is in binary format (65-bytes) and signature is in DEM format.
This is the only example that is accessing private components. mbedtls_ecdsa_context
and mbedtls_ecp_keypair
are same type. Keypair utilizes group
and Q
values, that are accessed in private mode.
How to fix that? Which API functions do allow us to do it properly?
/*
* PublicKey format: binary (65-bytes long, 0x04 as first byte)
* Signature format: DER (70, 71 or 72 bytes long)
*/
{
mbedtls_ecdsa_context ecdsa_ctx;
printf("Public key - ECDSA verification:\r\nPublicKey format: Binary\r\nSignature format: DER\r\n\r\n");
/* Parse public key */
mbedtls_ecdsa_init(&ecdsa_ctx);
mbedtls_ecp_group_load(&ecdsa_ctx.private_grp, MBEDTLS_ECP_DP_SECP256R1);
res = mbedtls_ecp_point_read_binary(&ecdsa_ctx.private_grp, &ecdsa_ctx.private_Q,
ecc_public_key_uncompressed_bin, sizeof(ecc_public_key_uncompressed_bin));
printf("mbedtls_ecp_point_read_binary: %d\r\n", res);
/* Verify with DER native format support */
res = mbedtls_ecdsa_read_signature(&ecdsa_ctx, data_raw_hash_digest, sizeof(data_raw_hash_digest),
signature_der, sizeof(signature_der));
printf("mbedtls_ecdsa_read_signature: %d\r\n", res);
/* Free objects */
mbedtls_ecdsa_free(&ecdsa_ctx);
/* Done */
printf("-----\r\n");
}
volatile int res;
/*
* PublicKey format: DER/PEM (String based public key, --- BEGIN PUBLIC KEY --- type of message)
* Signature format: DER (70, 71 or 72 bytes long)
*/
{
mbedtls_pk_context pubkey_ctx;
printf("Public key - ECDSA verification:\r\nPublicKey format: DER/PEM\r\nSignature format: DER\r\n\r\n");
/* Parse public key */
mbedtls_pk_init(&pubkey_ctx);
res = mbedtls_pk_parse_public_key(&pubkey_ctx, ecc_public_key_text, sizeof(ecc_public_key_text));
printf("mbedtls_pk_parse_public_key: %d\r\n", res);
/* Verify with DER native format support */
res = mbedtls_pk_verify(&pubkey_ctx, MBEDTLS_MD_SHA256, data_raw_hash_digest, sizeof(data_raw_hash_digest),
signature_der, sizeof(signature_der));
printf("mbedtls_pk_verify: %d\r\n", res);
/* Free objects */
mbedtls_pk_free(&pubkey_ctx);
/* Done */
printf("-----\r\n");
}
/*
* PublicKey format: binary (65-bytes long, 0x04 as first byte)
* Signature format: P1363 (64-bytes long, r|s)
*/
{
#define SIGNATURE_LEN (sizeof(signature_p1363))
mbedtls_mpi r, s;
mbedtls_ecp_group group;
mbedtls_ecp_point q;
printf("Public key - ECDSA verification:\r\nPublicKey format: Binary\r\nSignature format: P1363\r\n\r\n");
/* Initialize all modules */
mbedtls_mpi_init(&r);
mbedtls_mpi_init(&s);
mbedtls_ecp_point_init(&q);
mbedtls_ecp_group_init(&group);
/* Parse public key in binary format */
mbedtls_ecp_group_load(&group, MBEDTLS_ECP_DP_SECP256R1);
res = mbedtls_ecp_point_read_binary(&group, &q, ecc_public_key_uncompressed_bin,
sizeof(ecc_public_key_uncompressed_bin));
printf("mbedtls_ecp_point_read_binary: %d\r\n", res);
/* Parse signature in P1363 format to r and s big numbers */
res = mbedtls_mpi_read_binary(&r, signature_p1363, SIGNATURE_LEN / 2);
printf("mbedtls_mpi_read_binary: %d\r\n", res);
res = mbedtls_mpi_read_binary(&s, signature_p1363 + SIGNATURE_LEN / 2, SIGNATURE_LEN / 2);
printf("mbedtls_mpi_read_binary: %d\r\n", res);
/* Run verify with ecdsa */
res = mbedtls_ecdsa_verify(&group, data_raw_hash_digest, sizeof(data_raw_hash_digest), &q, &r, &s);
printf("mbedtls_ecdsa_verify: %d\r\n", res);
/* Free objects */
mbedtls_mpi_free(&r);
mbedtls_mpi_free(&s);
mbedtls_ecp_point_free(&q);
mbedtls_ecp_group_free(&group);
/* Done */
printf("-----\r\n");
#undef SIGNATURE_LEN
}
/*
* PublicKey format: DER/PEM (String based public key, --- BEGIN PUBLIC KEY --- type of message)
* Signature format: P1363 (64-bytes long, r|s)
*
* Steps to follow:
*
* - Parse public key with PK module
* - Extract EC parameters -> generate key pair
* - Extract group, D and Q values from the pair
* - Parse P1363 format into 2 big numbers R and S
* - Call ecdsa verification function
*/
{
#define SIGNATURE_LEN (sizeof(signature_p1363))
mbedtls_pk_context pubkey_ctx;
mbedtls_ecp_keypair* pair;
mbedtls_ecp_group group;
mbedtls_mpi d;
mbedtls_ecp_point q;
mbedtls_mpi r, s;
printf("Public key - ECDSA verification:\r\nPublicKey format: DER/PEM\r\nSignature format: P1363\r\n\r\n");
/* Initialize all values to its default state - avoid any segmentation faults */
mbedtls_pk_init(&pubkey_ctx);
mbedtls_mpi_init(&r);
mbedtls_mpi_init(&s);
mbedtls_ecp_group_init(&group);
mbedtls_mpi_init(&d);
mbedtls_ecp_point_init(&q);
/* Parse public key */
res = mbedtls_pk_parse_public_key(&pubkey_ctx, ecc_public_key_text, sizeof(ecc_public_key_text));
printf("mbedtls_pk_parse_public_key: %d\r\n", res);
/* Get EC pair from parsed public key */
pair = mbedtls_pk_ec(pubkey_ctx);
printf("mbedtls_pk_ec: %p\r\n", (void*)pair);
/* Export data from the pair - required for ECDSA verification purpose */
res = mbedtls_ecp_export(pair, &group, &d, &q);
printf("mbedtls_ecp_export: %d\r\n", res);
/* Parse P1363 signature to 2 big nums */
mbedtls_mpi_read_binary(&r, signature_p1363, SIGNATURE_LEN / 2);
mbedtls_mpi_read_binary(&s, signature_p1363 + SIGNATURE_LEN / 2, SIGNATURE_LEN / 2);
/* Get ECDSA verify context from parsed pk structure */
res = mbedtls_ecdsa_verify(&group, data_raw_hash_digest, sizeof(data_raw_hash_digest), &q, &r, &s);
printf("mbedtls_ecdsa_verify: %d\r\n", res);
/* Free objects */
mbedtls_pk_free(&pubkey_ctx);
mbedtls_mpi_free(&r);
mbedtls_mpi_free(&s);
mbedtls_ecp_group_free(&group);
mbedtls_mpi_free(&d);
mbedtls_ecp_point_free(&q);
/* Done */
printf("-----\r\n");
#undef SIGNATURE_LEN
}
Suggested enhancement
I was researching migration to mbedTLS and got stuck with ECDSA verification API, simply because it is not really clear how to handle things between PK, ECDSA and group modules. To make things working, access to private components was often required - unless I misused the API, which then it means that documentation is to be improved.
As you will see in the code example (that may be wrong, indeed - but result of verification was always OK), public key in PEM/DER format and signature in DER format work very well together - without accessing private components of the structure.
The rest - it is not the case.
Justification
I was struggling to get signature working, then I realized that ANS.1 type is a must in mbedTLS for ECDSA secp256r1 signature. To use the
s|r
type of signature (P1363), a pretty dirty way of working is required. Then, there is a public key, that could be in compressed mode (65-bytes for SECP256R1, with0x04
as start byte) or in PEM, string-based mode.Discussion started here: https://stackoverflow.com/questions/75635019/mbedtls-ecdsa-verification-fails/75641568?noredirect=1#comment133450813_75641568
Mbed TLS needs this because user should avoid accessing private structure members, that may change over the time.
Example
This will describe 2 types of public key storage and 2 types of signature combinations -> in total there must be way to do any combination, in my opinion.
Private key:
Public key in DER format
Public key in binary format - uncompressed,
65
bytesInput data - raw bytes
SHA256 of input data
Signature in DER format (size can vary depends on the BIGINT signed bit)
Signature in P1363 format
MbedTLS tests under Windows environment
Script to generate the keys and files