mbedtls: How to transfer and load public key as raw bytes

I want to use a public/private key pair (ECDSA using secp256r1 curve) to sign commands sent to an embedded device via BLE, and verify them on the device. This means sending the public key to the embedded device (during registration) and storing the public key on the device. When a command is received by the device, the public key is used to verify the signature with mbedtls.

To keep things simple and efficient, I thought it would be best to send and store the uncompressed 64 byte data from the public key (i.e. x and y co-ordinates).

I can generate a key pair and extract the 64 bytes of the public key in my Android app, and send these to the device using BLE.

However, I haven't found a good way to use this public key data to verify signatures with embedtls - i.e. to load the raw 64 bytes into a suitable mbedtls_ecp_keypair struct.

I have been able to do it with "mbedtls_ecp_point_read_string" as follows (error checking removed for brevity):

// public_key_data is buffer containing 64 bytes (32 bytes X, 32 bytes Y).

mbedtls_ecdsa_context ecdsa;
mbedtls_ecdsa_init(&ecdsa);

mbedtls_ecp_keypair public_key;
mbedtls_ecp_keypair_init(&public_key);

mbedtls_ecp_point_init(&public_key.Q);

mbedtls_ecp_group_init(&public_key.grp);
mbedtls_ecp_group_load(&public_key.grp, MBEDTLS_ECP_DP_SECP256R1);

// Convert raw bytes of public key (public_key_data) to ASCII string (hex format).
// BytesToHexStr is C function to convert bytes to string:
char x_str[65]; // 2 x 32 bytes + 1 byte for terminating NUL
char y_str[65];
BytesToHexStr(public_key_data, x_str, ...);
BytesToHexStr(public_key_data + 32, y_str, ...);

// Get ECP point from string...
mbedtls_ecp_point_read_string(
    &public_key.Q,   // mbedtls_ecp_point *P,
    16,                  // int radix,
    x_str,               // const char *x,
    y_str                // const char *y 
);

mbedtls_ecdsa_from_keypair(&ecdsa, &public_key);

// Generate hash of message:
mbedtls_sha256_ret(message, message_size, hash, SHA_256);

// Verify signature:
int verify_result = mbedtls_ecdsa_read_signature(&ecdsa, hash, sizeof(hash), signature, signature_size);

// Check verify_result, clean up, etc.

However, this seems cumbersome.

I tried using the "mbedtls_ecp_point_read_binary" function with my raw public key, but it returned -20096 (MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE). The mbedtls documentation says "This function imports a point from unsigned binary data", but it doesn't give any details about what format the data is expected to be in.

Is there a better way to transfer / store / load ECDSA public key data with mbedtls?


Solution 1:

I faced the exact same problem today. Eventually figured out that mbedtls_ecp_point_read_binary expects binary data in uncompressed public key format, i.e 0x04 followed by X followed by Y.