Blame SOURCES/0070-p11_child-openssl-add-support-for-EC-keys.patch

71e593
From d64d9cfbe9dc44db04b253aa08c05e645e10708a Mon Sep 17 00:00:00 2001
71e593
From: Sumit Bose <sbose@redhat.com>
71e593
Date: Fri, 9 Nov 2018 14:01:46 +0100
71e593
Subject: [PATCH 70/74] p11_child(openssl): add support for EC keys
71e593
71e593
Add support for EC keys to the OpenSSL version of p11_child. Please see
71e593
comments in the code for some technical details.
71e593
71e593
Related to https://pagure.io/SSSD/sssd/issue/3887
71e593
71e593
Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
71e593
---
71e593
 src/p11_child/p11_child_openssl.c | 319 +++++++++++++++++++++++++++++-
71e593
 1 file changed, 309 insertions(+), 10 deletions(-)
71e593
71e593
diff --git a/src/p11_child/p11_child_openssl.c b/src/p11_child/p11_child_openssl.c
71e593
index af55523a7..0f8ba3d3c 100644
71e593
--- a/src/p11_child/p11_child_openssl.c
71e593
+++ b/src/p11_child/p11_child_openssl.c
71e593
@@ -137,6 +137,7 @@ static OCSP_RESPONSE *query_responder(BIO *cbio, const char *host,
71e593
 #define X509_STORE_get0_objects(store) (store->objs)
71e593
 #define X509_OBJECT_get_type(object) (object->type)
71e593
 #define X509_OBJECT_get0_X509(object) (object->data.x509)
71e593
+#define EVP_MD_CTX_free EVP_MD_CTX_destroy
71e593
 #endif
71e593
 
71e593
 OCSP_RESPONSE *process_responder(OCSP_REQUEST *req,
71e593
@@ -860,6 +861,243 @@ done:
71e593
     return ret;
71e593
 }
71e593
 
71e593
+/* Currently this funtion is only used the print the curve type in the debug
71e593
+ * messages. */
71e593
+static void get_ec_curve_type(CK_FUNCTION_LIST *module,
71e593
+                              CK_SESSION_HANDLE session,
71e593
+                              CK_OBJECT_HANDLE key_handle)
71e593
+{
71e593
+    CK_ATTRIBUTE attribute;
71e593
+    CK_RV rv;
71e593
+    EC_GROUP *ec_group;
71e593
+    const unsigned char *p;
71e593
+    int len;
71e593
+    char der_buf[128]; /* FIXME: any other size ?? */
71e593
+    char oid_buf[128]; /* FIXME: any other size ?? */
71e593
+
71e593
+    attribute.type = CKA_ECDSA_PARAMS;
71e593
+    attribute.pValue = &der_buf;
71e593
+    attribute.ulValueLen = sizeof(der_buf);
71e593
+
71e593
+    rv = module->C_GetAttributeValue(session, key_handle, &attribute, 1);
71e593
+    if (rv != CKR_OK) {
71e593
+        free(attribute.pValue);
71e593
+        DEBUG(SSSDBG_OP_FAILURE,
71e593
+              "C_GetAttributeValue failed [%lu][%s].\n",
71e593
+              rv, p11_kit_strerror(rv));
71e593
+        return;
71e593
+    }
71e593
+
71e593
+    p = (const unsigned char *) attribute.pValue;
71e593
+    ec_group = d2i_ECPKParameters(NULL, &p, attribute.ulValueLen);
71e593
+    len = OBJ_obj2txt(oid_buf, sizeof(oid_buf),
71e593
+                      OBJ_nid2obj(EC_GROUP_get_curve_name(ec_group)), 1);
71e593
+    DEBUG(SSSDBG_TRACE_ALL, "Curve name [%s][%s][%.*s].\n",
71e593
+                            OBJ_nid2sn(EC_GROUP_get_curve_name(ec_group)),
71e593
+                            OBJ_nid2ln(EC_GROUP_get_curve_name(ec_group)),
71e593
+                            len, oid_buf);
71e593
+
71e593
+    return;
71e593
+}
71e593
+
71e593
+static CK_KEY_TYPE get_key_type(CK_FUNCTION_LIST *module,
71e593
+                                CK_SESSION_HANDLE session,
71e593
+                                CK_OBJECT_HANDLE key_handle)
71e593
+{
71e593
+    CK_ATTRIBUTE attribute;
71e593
+    CK_RV rv;
71e593
+    CK_KEY_TYPE type;
71e593
+
71e593
+    attribute.type = CKA_KEY_TYPE;
71e593
+    attribute.pValue = &type;
71e593
+    attribute.ulValueLen = sizeof(CK_KEY_TYPE);
71e593
+
71e593
+    rv = module->C_GetAttributeValue(session, key_handle, &attribute, 1);
71e593
+    if (rv != CKR_OK) {
71e593
+        DEBUG(SSSDBG_OP_FAILURE,
71e593
+              "C_GetAttributeValue failed [%lu][%s].\n",
71e593
+              rv, p11_kit_strerror(rv));
71e593
+        return CK_UNAVAILABLE_INFORMATION;
71e593
+    }
71e593
+
71e593
+    if (attribute.ulValueLen == -1) {
71e593
+        DEBUG(SSSDBG_OP_FAILURE,
71e593
+              "Key type attribute cannot be read.\n");
71e593
+        return CK_UNAVAILABLE_INFORMATION;
71e593
+    }
71e593
+
71e593
+    if (type == CKK_EC && DEBUG_IS_SET(SSSDBG_TRACE_ALL)) {
71e593
+        get_ec_curve_type(module, session, key_handle);
71e593
+    }
71e593
+
71e593
+    return type;
71e593
+}
71e593
+
71e593
+static int do_hash(TALLOC_CTX *mem_ctx, const EVP_MD *evp_md,
71e593
+                   CK_BYTE *in, size_t in_len,
71e593
+                   CK_BYTE **hash, size_t *hash_len)
71e593
+{
71e593
+    EVP_MD_CTX *md_ctx = NULL;
71e593
+    int ret;
71e593
+    unsigned char md_value[EVP_MAX_MD_SIZE];
71e593
+    unsigned int md_len;
71e593
+    CK_BYTE *out = NULL;
71e593
+
71e593
+    md_ctx = EVP_MD_CTX_create();
71e593
+    if (md_ctx == NULL) {
71e593
+        DEBUG(SSSDBG_OP_FAILURE, "EVP_MD_CTX_create failed.\n");
71e593
+        ret = ENOMEM;
71e593
+        goto done;
71e593
+    }
71e593
+
71e593
+    ret = EVP_DigestInit(md_ctx, evp_md);
71e593
+    if (ret != 1) {
71e593
+        DEBUG(SSSDBG_OP_FAILURE, "EVP_DigestInit failed.\n");
71e593
+        ret = EINVAL;
71e593
+        goto done;
71e593
+    }
71e593
+
71e593
+    ret = EVP_DigestUpdate(md_ctx, in, in_len);
71e593
+    if (ret != 1) {
71e593
+        DEBUG(SSSDBG_OP_FAILURE, "EVP_DigestUpdate failed.\n");
71e593
+        ret = EINVAL;
71e593
+        goto done;
71e593
+    }
71e593
+
71e593
+    ret = EVP_DigestFinal_ex(md_ctx, md_value, &md_len);
71e593
+    if (ret != 1) {
71e593
+        DEBUG(SSSDBG_OP_FAILURE, "EVP_DigestFinal failed.\n");
71e593
+        ret = EINVAL;
71e593
+        goto done;
71e593
+    }
71e593
+
71e593
+    out = talloc_size(mem_ctx, md_len * sizeof(CK_BYTE));
71e593
+    if (out == NULL) {
71e593
+        DEBUG(SSSDBG_OP_FAILURE, "talloc_size failed.\n");
71e593
+        ret = ENOMEM;
71e593
+        goto done;
71e593
+    }
71e593
+
71e593
+    memcpy(out, md_value, md_len);
71e593
+
71e593
+    *hash = out;
71e593
+    *hash_len = md_len;
71e593
+
71e593
+    ret = EOK;
71e593
+
71e593
+done:
71e593
+
71e593
+    if (ret != EOK) {
71e593
+        free(out);
71e593
+        EVP_MD_CTX_free(md_ctx);
71e593
+    }
71e593
+
71e593
+    return ret;
71e593
+}
71e593
+
71e593
+/* A ECDSA signature consists of 2 integer values r and s. According to the
71e593
+ * "PKCS #11 Cryptographic Token Interface Current Mechanisms Specification":
71e593
+ *
71e593
+ * """
71e593
+ * For the purposes of these mechanisms, an ECDSA signature is an octet string
71e593
+ * of even length which is at most two times nLen octets, where nLen is the
71e593
+ * length in octets of the base point order n. The signature octets correspond
71e593
+ * to the concatenation of the ECDSA values r and s, both represented as an
71e593
+ * octet string of equal length of at most nLen with the most significant byte
71e593
+ * first. If r and s have different octet length, the shorter of both must be
71e593
+ * padded with leading zero octets such that both have the same octet length.
71e593
+ * Loosely spoken, the first half of the signature is r and the second half is
71e593
+ * s. For signatures created by a token, the resulting signature is always of
71e593
+ * length 2nLen.
71e593
+ * """
71e593
+ *
71e593
+ * Unfortunately OpenSSL expects the 2 integer values r and s DER encoded as
71e593
+ * specified in X9.62 "Public Key Cryptography For The Financial Services
71e593
+ * Industry: The Elliptic Curve Digital Signature Algorithm (ECDSA)":
71e593
+ *
71e593
+ * """
71e593
+ * When a digital signature is identified by the OID ecdsa-with-SHA1 , the
71e593
+ * digital signature shall be ASN.1 encoded using the following syntax:
71e593
+ *   ECDSA-Sig-Value ::= SEQUENCE {
71e593
+ *     r  INTEGER,
71e593
+ *     s  INTEGER
71e593
+ *   }
71e593
+ *  """
71e593
+ *
71e593
+ *  The following function translates from the PKCS#11 to the X9.62 format by
71e593
+ *  manually creating the DER sequence after splitting the PKCS#11 signature.
71e593
+ *  Since r and s are positive values we have to make sure that the leading
71e593
+ *  bit is not set in the DER encoding by prepending a 0-byte if needed.
71e593
+ */
71e593
+static int rs_to_seq(TALLOC_CTX *mem_ctx, CK_BYTE *rs_sig, CK_ULONG rs_sig_len,
71e593
+                     CK_BYTE **seq_sig, CK_ULONG *seq_sig_len)
71e593
+{
71e593
+    CK_BYTE *r;
71e593
+    size_t r_len;
71e593
+    CK_BYTE *s;
71e593
+    size_t s_len;
71e593
+    size_t r_add = 0;
71e593
+    size_t s_add = 0;
71e593
+    CK_BYTE *out;
71e593
+    size_t out_len;
71e593
+
71e593
+    if (rs_sig_len % 2 != 0) {
71e593
+        DEBUG(SSSDBG_CRIT_FAILURE, "Unexpected signature size [%lu].\n",
71e593
+                                   rs_sig_len);
71e593
+        return EINVAL;
71e593
+    }
71e593
+
71e593
+    r_len = s_len = rs_sig_len / 2;
71e593
+    r = rs_sig;
71e593
+    s = rs_sig + r_len;
71e593
+
71e593
+    /* Remove padding */
71e593
+    while(r_len > 1 && *r == 0x00) {
71e593
+            r++;
71e593
+            r_len--;
71e593
+    }
71e593
+    while(s_len > 1 && *s == 0x00) {
71e593
+            s++;
71e593
+            s_len--;
71e593
+    }
71e593
+
71e593
+    /* r and s are positive, check if the highest bit is set which would
71e593
+     * indicate a negative value. In this case a 0x00 must be added. */
71e593
+    if ( *r & 0x80 ) {
71e593
+        r_add = 1;
71e593
+    }
71e593
+    if ( *s & 0x80 ) {
71e593
+        s_add = 1;
71e593
+    }
71e593
+
71e593
+    out_len = r_len + r_add + s_len + s_add + 6;
71e593
+    out = talloc_size(mem_ctx, out_len * sizeof(CK_BYTE));
71e593
+    if (out == NULL) {
71e593
+        DEBUG(SSSDBG_OP_FAILURE, "talloc_size failed.\n");
71e593
+        return ENOMEM;
71e593
+    }
71e593
+
71e593
+    out[0] = 0x30;
71e593
+    out[1] = (CK_BYTE) (out_len - 2);
71e593
+    out[2] = 0x02;
71e593
+    out[3] = (CK_BYTE) (r_len + r_add);
71e593
+    if (r_add == 1) {
71e593
+        out[4] = 0x00;
71e593
+    }
71e593
+    memcpy(&out[4 + r_add], r, r_len);
71e593
+    out[4 + r_add + r_len] = 0x02;
71e593
+    out[5 + r_add + r_len] = (CK_BYTE) (s_len + s_add);
71e593
+    if (s_add == 1)  {
71e593
+        out[6 + r_add + r_len] = 0x00;
71e593
+    }
71e593
+    memcpy(&out[6 + r_add + r_len + s_add], s, s_len);
71e593
+
71e593
+    *seq_sig = out;
71e593
+    *seq_sig_len = out_len;
71e593
+
71e593
+    return EOK;
71e593
+}
71e593
+
71e593
 static int sign_data(CK_FUNCTION_LIST *module, CK_SESSION_HANDLE session,
71e593
                      struct cert_list *cert)
71e593
 {
71e593
@@ -870,17 +1108,25 @@ static int sign_data(CK_FUNCTION_LIST *module, CK_SESSION_HANDLE session,
71e593
       {CKA_SIGN, &key_sign, sizeof(key_sign)},
71e593
       {CKA_ID, NULL, 0}
71e593
     };
71e593
-    CK_MECHANISM mechanism = { CKM_SHA1_RSA_PKCS, NULL, 0 };
71e593
+    CK_MECHANISM mechanism = { CK_UNAVAILABLE_INFORMATION, NULL, 0 };
71e593
     CK_OBJECT_HANDLE priv_key_object;
71e593
     CK_ULONG object_count;
71e593
     CK_BYTE random_value[128];
71e593
     CK_BYTE *signature = NULL;
71e593
     CK_ULONG signature_size = 0;
71e593
+    CK_BYTE *seq_sig = NULL;
71e593
+    CK_ULONG seq_sig_size = 0;
71e593
     CK_RV rv;
71e593
     CK_RV rv_f;
71e593
     EVP_PKEY *cert_pub_key = NULL;
71e593
     EVP_MD_CTX *md_ctx;
71e593
     int ret;
71e593
+    const EVP_MD *evp_md = NULL;
71e593
+    CK_BYTE *hash_val = NULL;
71e593
+    size_t hash_len = 0;
71e593
+    CK_BYTE *val_to_sign = NULL;
71e593
+    size_t val_to_sign_len = 0;
71e593
+    bool card_does_hash = false;
71e593
 
71e593
     key_template[2].pValue = cert->attributes[ATTR_ID].pValue;
71e593
     key_template[2].ulValueLen = cert->attributes[ATTR_ID].ulValueLen;
71e593
@@ -910,9 +1156,31 @@ static int sign_data(CK_FUNCTION_LIST *module, CK_SESSION_HANDLE session,
71e593
         return EINVAL;
71e593
     }
71e593
 
71e593
+    switch (get_key_type(module, session, priv_key_object)) {
71e593
+    case CKK_RSA:
71e593
+        DEBUG(SSSDBG_TRACE_ALL, "Found RSA key using CKM_SHA1_RSA_PKCS.\n");
71e593
+        mechanism.mechanism = CKM_SHA1_RSA_PKCS;
71e593
+        evp_md = EVP_sha1();
71e593
+        card_does_hash = true;
71e593
+        break;
71e593
+    case CKK_EC:
71e593
+        DEBUG(SSSDBG_TRACE_ALL, "Found ECC key using CKM_ECDSA.\n");
71e593
+        mechanism.mechanism = CKM_ECDSA;
71e593
+        evp_md = EVP_sha1();
71e593
+        card_does_hash = false;
71e593
+        break;
71e593
+    case CK_UNAVAILABLE_INFORMATION:
71e593
+        DEBUG(SSSDBG_CRIT_FAILURE, "get_key_type failed.\n");
71e593
+        return EIO;
71e593
+        break;
71e593
+    default:
71e593
+        DEBUG(SSSDBG_CRIT_FAILURE, "Unsupported key type.\n");
71e593
+        return EIO;
71e593
+    }
71e593
+
71e593
     rv = module->C_SignInit(session, &mechanism, priv_key_object);
71e593
     if (rv != CKR_OK) {
71e593
-        DEBUG(SSSDBG_OP_FAILURE, "C_SignInit failed [%lu][%s].",
71e593
+        DEBUG(SSSDBG_OP_FAILURE, "C_SignInit failed [%lu][%s].\n",
71e593
                                  rv, p11_kit_strerror(rv));
71e593
         return EIO;
71e593
     }
71e593
@@ -923,7 +1191,22 @@ static int sign_data(CK_FUNCTION_LIST *module, CK_SESSION_HANDLE session,
71e593
         return EINVAL;
71e593
     }
71e593
 
71e593
-    rv = module->C_Sign(session, random_value, sizeof(random_value), NULL,
71e593
+    if (card_does_hash) {
71e593
+        val_to_sign = random_value;
71e593
+        val_to_sign_len = sizeof(random_value);
71e593
+    } else {
71e593
+        ret = do_hash(cert, evp_md, random_value, sizeof(random_value),
71e593
+                      &hash_val, &hash_len);
71e593
+        if (ret != EOK) {
71e593
+            DEBUG(SSSDBG_CRIT_FAILURE, "do_hash failed.\n");
71e593
+            return ret;
71e593
+        }
71e593
+
71e593
+        val_to_sign = hash_val;
71e593
+        val_to_sign_len = hash_len;
71e593
+    }
71e593
+
71e593
+    rv = module->C_Sign(session, val_to_sign, val_to_sign_len, NULL,
71e593
                         &signature_size);
71e593
     if (rv != CKR_OK || signature_size == 0) {
71e593
         DEBUG(SSSDBG_OP_FAILURE, "C_Sign failed [%lu][%s].\n",
71e593
@@ -937,7 +1220,7 @@ static int sign_data(CK_FUNCTION_LIST *module, CK_SESSION_HANDLE session,
71e593
         return ENOMEM;
71e593
     }
71e593
 
71e593
-    rv = module->C_Sign(session, random_value, sizeof(random_value), signature,
71e593
+    rv = module->C_Sign(session, val_to_sign, val_to_sign_len, signature,
71e593
                         &signature_size);
71e593
     if (rv != CKR_OK) {
71e593
         DEBUG(SSSDBG_OP_FAILURE, "C_Sign failed [%lu][%s].\n",
71e593
@@ -958,7 +1241,7 @@ static int sign_data(CK_FUNCTION_LIST *module, CK_SESSION_HANDLE session,
71e593
         ret = ENOMEM;
71e593
         goto done;
71e593
     }
71e593
-    ret = EVP_VerifyInit(md_ctx, EVP_sha1());
71e593
+    ret = EVP_VerifyInit(md_ctx, evp_md);
71e593
     if (ret != 1) {
71e593
         DEBUG(SSSDBG_OP_FAILURE, "EVP_VerifyInit failed.\n");
71e593
         ret = EINVAL;
71e593
@@ -972,11 +1255,27 @@ static int sign_data(CK_FUNCTION_LIST *module, CK_SESSION_HANDLE session,
71e593
         goto done;
71e593
     }
71e593
 
71e593
-    ret = EVP_VerifyFinal(md_ctx, signature, signature_size, cert_pub_key);
71e593
-    if (ret != 1) {
71e593
-        DEBUG(SSSDBG_OP_FAILURE, "EVP_VerifyFinal failed.\n");
71e593
-        ret = EINVAL;
71e593
-        goto done;
71e593
+    if (mechanism.mechanism == CKM_ECDSA) {
71e593
+        ret = rs_to_seq(signature, signature, signature_size,
71e593
+                        &seq_sig, &seq_sig_size);
71e593
+        if (ret != EOK) {
71e593
+            DEBUG(SSSDBG_CRIT_FAILURE, "rs_to_seq failed.\n");
71e593
+            goto done;
71e593
+        }
71e593
+
71e593
+        ret = EVP_VerifyFinal(md_ctx, seq_sig, seq_sig_size, cert_pub_key);
71e593
+        if (ret != 1) {
71e593
+            DEBUG(SSSDBG_OP_FAILURE, "EVP_VerifyFinal failed.\n");
71e593
+            ret = EINVAL;
71e593
+            goto done;
71e593
+        }
71e593
+    } else {
71e593
+        ret = EVP_VerifyFinal(md_ctx, signature, signature_size, cert_pub_key);
71e593
+        if (ret != 1) {
71e593
+            DEBUG(SSSDBG_OP_FAILURE, "EVP_VerifyFinal failed.\n");
71e593
+            ret = EINVAL;
71e593
+            goto done;
71e593
+        }
71e593
     }
71e593
 
71e593
     ret = EOK;
71e593
-- 
71e593
2.19.1
71e593