mirror of
https://github.com/librekeys/pico-openpgp.git
synced 2026-07-28 08:01:54 -07:00
piv: implement ECDH key agreement (tag 0x85)
Add tag 0x85 parsing and ECDH shared secret computation for P-256 and P-384 curves using mbedtls_ecdh APIs. The peer's uncompressed public key is read from tag 0x85, the card's private key is loaded, and the shared secret is returned in tag 0x82 within a 0x7C dynamic authentication template, per NIST SP 800-73-4. Unsupported algorithms (e.g. X25519, RSA) return SW_INCORRECT_P1P2."
This commit is contained in:
+61
-1
@@ -31,6 +31,7 @@
|
||||
#include "asn1.h"
|
||||
#include "mbedtls/aes.h"
|
||||
#include "mbedtls/des.h"
|
||||
#include "mbedtls/ecdh.h"
|
||||
#include "mbedtls/x509_crt.h"
|
||||
#include "openpgp.h"
|
||||
|
||||
@@ -606,10 +607,11 @@ static int cmd_authenticate() {
|
||||
if (!asn1_find_tag(&ctxi, 0x7C, &a7c) || asn1_len(&ctxi) == 0) {
|
||||
return SW_WRONG_DATA();
|
||||
}
|
||||
asn1_ctx_t a80 = { 0 }, a81 = { 0 }, a82 = { 0 };
|
||||
asn1_ctx_t a80 = { 0 }, a81 = { 0 }, a82 = { 0 }, a85 = { 0 };
|
||||
asn1_find_tag(&a7c, 0x80, &a80);
|
||||
asn1_find_tag(&a7c, 0x81, &a81);
|
||||
asn1_find_tag(&a7c, 0x82, &a82);
|
||||
asn1_find_tag(&a7c, 0x85, &a85);
|
||||
if (a80.data) {
|
||||
if (a80.len == 0) {
|
||||
memcpy(challenge, random_bytes_get(sizeof(challenge)), sizeof(challenge));
|
||||
@@ -834,6 +836,64 @@ static int cmd_authenticate() {
|
||||
}
|
||||
}
|
||||
}
|
||||
if (a85.data) {
|
||||
if (algo == PIV_ALGO_ECCP256 || algo == PIV_ALGO_ECCP384) {
|
||||
file_t *ef_key = search_by_fid(key_ref, NULL, SPECIFY_EF);
|
||||
if (!file_has_data(ef_key)) {
|
||||
return SW_MEMORY_FAILURE();
|
||||
}
|
||||
mbedtls_ecdsa_context ecdsa_ctx;
|
||||
mbedtls_ecdsa_init(&ecdsa_ctx);
|
||||
int r = load_private_key_ecdsa(&ecdsa_ctx, ef_key, false);
|
||||
if (r != PICOKEY_OK) {
|
||||
mbedtls_ecdsa_free(&ecdsa_ctx);
|
||||
return SW_EXEC_ERROR();
|
||||
}
|
||||
mbedtls_ecdh_context ecdh_ctx;
|
||||
mbedtls_ecdh_init(&ecdh_ctx);
|
||||
mbedtls_ecp_group_id gid = ecdsa_ctx.grp.id;
|
||||
r = mbedtls_ecdh_setup(&ecdh_ctx, gid);
|
||||
if (r != 0) {
|
||||
mbedtls_ecdsa_free(&ecdsa_ctx);
|
||||
mbedtls_ecdh_free(&ecdh_ctx);
|
||||
return SW_EXEC_ERROR();
|
||||
}
|
||||
r = mbedtls_mpi_copy(&ecdh_ctx.ctx.mbed_ecdh.d, &ecdsa_ctx.d);
|
||||
mbedtls_ecdsa_free(&ecdsa_ctx);
|
||||
if (r != 0) {
|
||||
mbedtls_ecdh_free(&ecdh_ctx);
|
||||
return SW_EXEC_ERROR();
|
||||
}
|
||||
// mbedtls_ecdh_read_public expects TLS format: [length][point_data]
|
||||
// PIV tag 0x85 contains raw uncompressed point: [04][X][Y]
|
||||
uint8_t pub_buf[1 + MBEDTLS_ECP_MAX_PT_LEN];
|
||||
pub_buf[0] = (uint8_t)a85.len;
|
||||
memcpy(pub_buf + 1, a85.data, a85.len);
|
||||
r = mbedtls_ecdh_read_public(&ecdh_ctx, pub_buf, a85.len + 1);
|
||||
if (r != 0) {
|
||||
mbedtls_ecdh_free(&ecdh_ctx);
|
||||
return SW_DATA_INVALID();
|
||||
}
|
||||
size_t olen = 0;
|
||||
uint8_t shared_secret[MBEDTLS_ECP_MAX_BYTES];
|
||||
r = mbedtls_ecdh_calc_secret(&ecdh_ctx, &olen, shared_secret,
|
||||
sizeof(shared_secret), random_gen, NULL);
|
||||
mbedtls_ecdh_free(&ecdh_ctx);
|
||||
if (r != 0) {
|
||||
return SW_EXEC_ERROR();
|
||||
}
|
||||
res_APDU[res_APDU_size++] = 0x7C;
|
||||
res_APDU[res_APDU_size++] = olen + 2;
|
||||
res_APDU[res_APDU_size++] = 0x82;
|
||||
res_APDU[res_APDU_size++] = olen;
|
||||
memcpy(res_APDU + res_APDU_size, shared_secret, olen);
|
||||
res_APDU_size += olen;
|
||||
mbedtls_platform_zeroize(shared_secret, sizeof(shared_secret));
|
||||
}
|
||||
else {
|
||||
return SW_INCORRECT_P1P2();
|
||||
}
|
||||
}
|
||||
if (meta[1] == PINPOLICY_ALWAYS) {
|
||||
has_pwpiv = false;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user