From 0e6be22f1a8e7202a72cf2eeb953480ae9272072 Mon Sep 17 00:00:00 2001
From: S-P Chan <shihping.chan@gmail.com>
Date: Thu, 22 Feb 2024 23:06:13 +0800
Subject: [PATCH] Workaround for compressed EC point from OpenSSL <= 3.0.7

https://github.com/openssl/openssl/issues/16595
OpenSSL <= 3.0.7 unconditionally exports EC public keys
in compressed format - create the uncompressed
format for correct import

Addresses #348

Signed-off-by: S-P Chan <shihping.chan@gmail.com>
---
 src/objects.c | 79 +++++++++++++++++++++++++++++++++++++++++++++------
 1 file changed, 71 insertions(+), 8 deletions(-)

diff --git a/src/objects.c b/src/objects.c
index 9fe0f33b..640e1d4c 100644
--- a/src/objects.c
+++ b/src/objects.c
@@ -2502,11 +2502,21 @@ static CK_RV prep_rsa_find(P11PROV_CTX *ctx, const OSSL_PARAM params[],
     return CKR_OK;
 }
 
+/* P-521 ~ 133 bytes, this should suffice */
+#define MAX_EC_PUB_KEY_SIZE 150
 static CK_RV prep_ec_find(P11PROV_CTX *ctx, const OSSL_PARAM params[],
                           struct pool_find_ctx *findctx)
 {
     EC_GROUP *group = NULL;
+    EC_POINT *point = NULL;
+    BN_CTX *bn_ctx = NULL;
+    int ret, plen;
+
     OSSL_PARAM tmp;
+    const OSSL_PARAM *p;
+    OSSL_PARAM pub_key[2] = { 0 };
+    uint8_t pub_data[MAX_EC_PUB_KEY_SIZE];
+
     const char *curve_name = NULL;
     int curve_nid;
     unsigned char *ecparams = NULL;
@@ -2518,13 +2528,6 @@ static CK_RV prep_ec_find(P11PROV_CTX *ctx, const OSSL_PARAM params[],
     }
     findctx->numattrs = 0;
 
-    rv = param_to_attr(ctx, params, OSSL_PKEY_PARAM_PUB_KEY, &findctx->attrs[0],
-                       CKA_P11PROV_PUB_KEY, false);
-    if (rv != CKR_OK) {
-        return rv;
-    }
-    findctx->numattrs++;
-
     group = EC_GROUP_new_from_params(params, p11prov_ctx_get_libctx(ctx), NULL);
     if (!group) {
         P11PROV_raise(ctx, CKR_KEY_INDIGESTIBLE, "Unable to decode ec group");
@@ -2532,6 +2535,60 @@ static CK_RV prep_ec_find(P11PROV_CTX *ctx, const OSSL_PARAM params[],
         goto done;
     }
 
+    p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_PUB_KEY);
+    if (!p) {
+        P11PROV_raise(ctx, CKR_KEY_INDIGESTIBLE, "Missing %s",
+                      OSSL_PKEY_PARAM_PUB_KEY);
+        EC_GROUP_free(group);
+        return CKR_KEY_INDIGESTIBLE;
+    }
+
+    /* Providers may export in any format - OpenSSL < 3.0.8
+     * ignores the "point-format" OSSL_PARAM and unconditionally uses
+     * compressed format:
+     * - https://github.com/openssl/openssl/pull/16624
+     * - https://github.com/openssl/openssl/issues/16595
+     *
+     * Convert from compressed to uncompressed if necessary
+     */
+    if (((char *)p->data)[0] == '\x02' || ((char *)p->data)[0] == '\x03') {
+        P11PROV_debug("OpenSSL 3.0.7 BUG - received compressed EC public key");
+        pub_key[0].key = OSSL_PKEY_PARAM_PUB_KEY;
+        pub_key[0].data_type = p->data_type;
+        pub_key[0].data = pub_data;
+
+        point = EC_POINT_new(group);
+        bn_ctx = BN_CTX_new();
+        ret = EC_POINT_oct2point(group, point, p->data, p->data_size, bn_ctx);
+        if (!ret) {
+            goto done0;
+        }
+
+        plen = EC_POINT_point2oct(group, point, POINT_CONVERSION_UNCOMPRESSED,
+                                  pub_key[0].data, MAX_EC_PUB_KEY_SIZE, bn_ctx);
+        if (!plen) {
+            ret = CKR_KEY_INDIGESTIBLE;
+            goto done0;
+        }
+
+        pub_key[0].data_size = plen;
+        ret = param_to_attr(ctx, pub_key, OSSL_PKEY_PARAM_PUB_KEY,
+                            &findctx->attrs[0], CKA_P11PROV_PUB_KEY, false);
+        if (ret != CKR_OK) {
+            goto done0;
+        }
+        EC_POINT_free(point);
+        BN_CTX_free(bn_ctx);
+    } else {
+        rv = param_to_attr(ctx, params, OSSL_PKEY_PARAM_PUB_KEY,
+                           &findctx->attrs[0], CKA_P11PROV_PUB_KEY, false);
+        if (rv != CKR_OK) {
+            return rv;
+        }
+    }
+
+    findctx->numattrs++;
+
     curve_nid = EC_GROUP_get_curve_name(group);
     if (curve_nid != NID_undef) {
         curve_name = OSSL_EC_curve_nid2name(curve_nid);
@@ -2583,11 +2640,17 @@ static CK_RV prep_ec_find(P11PROV_CTX *ctx, const OSSL_PARAM params[],
     findctx->bit_size = EC_GROUP_order_bits(group);
     findctx->key_size = (findctx->bit_size + 7) / 8;
     rv = CKR_OK;
-
 done:
     OPENSSL_free(ecparams);
     EC_GROUP_free(group);
     return rv;
+
+done0:
+
+    EC_GROUP_free(group);
+    EC_POINT_free(point);
+    BN_CTX_free(bn_ctx);
+    return ret;
 }
 
 static CK_RV return_dup_key(P11PROV_OBJ *dst, P11PROV_OBJ *src)