[gnome-keyring/wip/dueno/ecdsa-support: 2/10] ECDSA support in GKM internals



commit cf34a4ed346c88938021ee5ed6e2114856e3c998
Author: Jakub Jelen <jjelen redhat com>
Date:   Tue Aug 8 19:01:55 2017 +0200

    ECDSA support in GKM internals
    
    https://bugzilla.gnome.org/show_bug.cgi?id=641082

 pkcs11/gkm/Makefile.am           |    2 +
 pkcs11/gkm/gkm-attributes.c      |   35 ++++++++++
 pkcs11/gkm/gkm-attributes.h      |    8 ++
 pkcs11/gkm/gkm-crypto.c          |   12 ++++
 pkcs11/gkm/gkm-dsa-mechanism.c   |    2 +-
 pkcs11/gkm/gkm-ecdsa-mechanism.c |  133 ++++++++++++++++++++++++++++++++++++++
 pkcs11/gkm/gkm-ecdsa-mechanism.h |   50 ++++++++++++++
 pkcs11/gkm/gkm-module.c          |    6 ++
 pkcs11/gkm/gkm-private-xsa-key.c |   99 ++++++++++++++++++++++++++++-
 pkcs11/gkm/gkm-private-xsa-key.h |    9 +++
 pkcs11/gkm/gkm-public-xsa-key.c  |   53 +++++++++++++++
 pkcs11/gkm/gkm-public-xsa-key.h  |    9 +++
 pkcs11/gkm/gkm-sexp-key.c        |   78 +++++++++++++++++++++-
 pkcs11/gkm/gkm-sexp-key.h        |    8 ++
 pkcs11/gkm/gkm-sexp.c            |   82 +++++++++++++++++++++++
 pkcs11/gkm/gkm-sexp.h            |    9 +++
 16 files changed, 589 insertions(+), 6 deletions(-)
---
diff --git a/pkcs11/gkm/Makefile.am b/pkcs11/gkm/Makefile.am
index 949aec3..d940ff2 100644
--- a/pkcs11/gkm/Makefile.am
+++ b/pkcs11/gkm/Makefile.am
@@ -44,6 +44,8 @@ libgkm_la_SOURCES = \
        pkcs11/gkm/gkm-dh-public-key.h \
        pkcs11/gkm/gkm-dsa-mechanism.c \
        pkcs11/gkm/gkm-dsa-mechanism.h \
+       pkcs11/gkm/gkm-ecdsa-mechanism.c \
+       pkcs11/gkm/gkm-ecdsa-mechanism.h \
        pkcs11/gkm/gkm-factory.c \
        pkcs11/gkm/gkm-factory.h \
        pkcs11/gkm/gkm-generic-key.c \
diff --git a/pkcs11/gkm/gkm-attributes.c b/pkcs11/gkm/gkm-attributes.c
index 996028b..fb185bc 100644
--- a/pkcs11/gkm/gkm-attributes.c
+++ b/pkcs11/gkm/gkm-attributes.c
@@ -21,6 +21,7 @@
 #include "config.h"
 
 #include "gkm-attributes.h"
+#include "gkm-data-der.h"
 #include "gkm-util.h"
 
 #include "egg/egg-timegm.h"
@@ -118,6 +119,24 @@ gkm_attribute_get_string (CK_ATTRIBUTE_PTR attr, gchar **value)
 }
 
 CK_RV
+gkm_attribute_get_bytes (CK_ATTRIBUTE_PTR attr, GBytes **value)
+{
+       g_return_val_if_fail (attr, CKR_GENERAL_ERROR);
+       g_return_val_if_fail (value, CKR_GENERAL_ERROR);
+
+       if (attr->ulValueLen == 0) {
+               *value = NULL;
+               return CKR_OK;
+       }
+
+       if (!attr->pValue)
+               return CKR_ATTRIBUTE_VALUE_INVALID;
+
+       *value = g_bytes_new (attr->pValue, attr->ulValueLen);
+       return CKR_OK;
+}
+
+CK_RV
 gkm_attribute_get_mpi (CK_ATTRIBUTE_PTR attr, gcry_mpi_t *value)
 {
        gcry_error_t gcry;
@@ -555,6 +574,22 @@ gkm_attributes_find_string (CK_ATTRIBUTE_PTR attrs, CK_ULONG n_attrs,
        return gkm_attribute_get_string (attr, value) == CKR_OK;
 }
 
+/* Need to get DER encoded EC parameters and point */
+gboolean
+gkm_attributes_find_bytes (CK_ATTRIBUTE_PTR attrs, CK_ULONG n_attrs,
+                           CK_ATTRIBUTE_TYPE type, GBytes **value)
+{
+       CK_ATTRIBUTE_PTR attr;
+
+       g_return_val_if_fail (attrs || !n_attrs, FALSE);
+
+       attr = gkm_attributes_find (attrs, n_attrs, type);
+       if (attr == NULL)
+               return FALSE;
+
+       return gkm_attribute_get_bytes (attr, value) == CKR_OK;
+}
+
 GArray*
 gkm_template_new (CK_ATTRIBUTE_PTR attrs, CK_ULONG n_attrs)
 {
diff --git a/pkcs11/gkm/gkm-attributes.h b/pkcs11/gkm/gkm-attributes.h
index 7f63928..8f2f9cb 100644
--- a/pkcs11/gkm/gkm-attributes.h
+++ b/pkcs11/gkm/gkm-attributes.h
@@ -39,6 +39,9 @@ CK_RV                 gkm_attribute_get_time                           (CK_ATTRI
 CK_RV                 gkm_attribute_get_string                         (CK_ATTRIBUTE_PTR attr,
                                                                         gchar **value);
 
+CK_RV                 gkm_attribute_get_bytes                          (CK_ATTRIBUTE_PTR attr,
+                                                                        GBytes **value);
+
 CK_RV                 gkm_attribute_get_mpi                            (CK_ATTRIBUTE_PTR attr,
                                                                         gcry_mpi_t *value);
 
@@ -122,6 +125,11 @@ gboolean              gkm_attributes_find_string                       (CK_ATTRI
                                                                         CK_ATTRIBUTE_TYPE type,
                                                                         gchar **value);
 
+gboolean              gkm_attributes_find_bytes                        (CK_ATTRIBUTE_PTR attrs,
+                                                                        CK_ULONG n_attrs,
+                                                                        CK_ATTRIBUTE_TYPE type,
+                                                                        GBytes **value);
+
 GArray*               gkm_template_new                                 (CK_ATTRIBUTE_PTR attrs,
                                                                         CK_ULONG n_attrs);
 
diff --git a/pkcs11/gkm/gkm-crypto.c b/pkcs11/gkm/gkm-crypto.c
index 0d46bbb..7195e0a 100644
--- a/pkcs11/gkm/gkm-crypto.c
+++ b/pkcs11/gkm/gkm-crypto.c
@@ -24,6 +24,7 @@
 #include "gkm-aes-mechanism.h"
 #include "gkm-dh-mechanism.h"
 #include "gkm-dsa-mechanism.h"
+#include "gkm-ecdsa-mechanism.h"
 #include "gkm-hkdf-mechanism.h"
 #include "gkm-null-mechanism.h"
 #include "gkm-rsa-mechanism.h"
@@ -262,6 +263,7 @@ gkm_crypto_sign (GkmSession *session, CK_MECHANISM_TYPE mech, CK_BYTE_PTR data,
        case CKM_RSA_PKCS:
        case CKM_RSA_X_509:
        case CKM_DSA:
+       case CKM_ECDSA:
                sexp = gkm_session_get_crypto_state (session);
                g_return_val_if_fail (sexp, CKR_GENERAL_ERROR);
                return gkm_crypto_sign_xsa (gkm_sexp_get (sexp), mech, data, n_data, signature, n_signature);
@@ -303,6 +305,10 @@ gkm_crypto_sign_xsa (gcry_sexp_t sexp, CK_MECHANISM_TYPE mech, CK_BYTE_PTR data,
                g_return_val_if_fail (algorithm == GCRY_PK_DSA, CKR_GENERAL_ERROR);
                rv = gkm_dsa_mechanism_sign (sexp, data, n_data, signature, n_signature);
                break;
+       case CKM_ECDSA:
+               g_return_val_if_fail (algorithm == GCRY_PK_ECC, CKR_GENERAL_ERROR);
+               rv = gkm_ecdsa_mechanism_sign (sexp, data, n_data, signature, n_signature);
+               break;
        default:
                /* Again shouldn't be reached */
                g_return_val_if_reached (CKR_GENERAL_ERROR);
@@ -321,6 +327,7 @@ gkm_crypto_verify (GkmSession *session, CK_MECHANISM_TYPE mech, CK_BYTE_PTR data
        case CKM_RSA_PKCS:
        case CKM_RSA_X_509:
        case CKM_DSA:
+       case CKM_ECDSA:
                sexp = gkm_session_get_crypto_state (session);
                g_return_val_if_fail (sexp, CKR_GENERAL_ERROR);
                return gkm_crypto_verify_xsa (gkm_sexp_get (sexp), mech, data, n_data, signature, 
n_signature);
@@ -362,6 +369,10 @@ gkm_crypto_verify_xsa (gcry_sexp_t sexp, CK_MECHANISM_TYPE mech, CK_BYTE_PTR dat
                g_return_val_if_fail (algorithm == GCRY_PK_DSA, CKR_GENERAL_ERROR);
                rv = gkm_dsa_mechanism_verify (sexp, data, n_data, signature, n_signature);
                break;
+       case CKM_ECDSA:
+               g_return_val_if_fail (algorithm == GCRY_PK_ECC, CKR_GENERAL_ERROR);
+               rv = gkm_ecdsa_mechanism_verify (sexp, data, n_data, signature, n_signature);
+               break;
        default:
                /* Again shouldn't be reached */
                g_return_val_if_reached (CKR_GENERAL_ERROR);
@@ -506,6 +517,7 @@ gkm_crypto_prepare (GkmSession *session, CK_MECHANISM_TYPE mech, GkmObject *key)
        switch (mech) {
        case CKM_RSA_PKCS:
        case CKM_RSA_X_509:
+       case CKM_ECDSA:
        case CKM_DSA:
                return gkm_crypto_prepare_xsa (session, mech, key);
        default:
diff --git a/pkcs11/gkm/gkm-dsa-mechanism.c b/pkcs11/gkm/gkm-dsa-mechanism.c
index f7a4f9e..ee8b025 100644
--- a/pkcs11/gkm/gkm-dsa-mechanism.c
+++ b/pkcs11/gkm/gkm-dsa-mechanism.c
@@ -90,7 +90,7 @@ gkm_dsa_mechanism_sign (gcry_sexp_t sexp, CK_BYTE_PTR data, CK_ULONG n_data,
        }
 
        gcry_sexp_release (ssig);
-       return CKR_OK;
+       return rv;
 }
 
 CK_RV
diff --git a/pkcs11/gkm/gkm-ecdsa-mechanism.c b/pkcs11/gkm/gkm-ecdsa-mechanism.c
new file mode 100644
index 0000000..2ef0c5a
--- /dev/null
+++ b/pkcs11/gkm/gkm-ecdsa-mechanism.c
@@ -0,0 +1,133 @@
+/*
+ * gnome-keyring
+ *
+ * Copyright (C) 2017 Red Hat, Inc.
+ *
+ * Author: Jakub Jelen <jjelen redhat com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, see
+ * <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include "gkm-crypto.h"
+#include "gkm-ecdsa-mechanism.h"
+#include "gkm-session.h"
+#include "gkm-sexp.h"
+#include "gkm-sexp-key.h"
+
+#include "egg/egg-libgcrypt.h"
+#include "egg/egg-secure-memory.h"
+
+/* ----------------------------------------------------------------------------
+ * PUBLIC
+ */
+
+CK_RV
+gkm_ecdsa_mechanism_sign (gcry_sexp_t sexp, CK_BYTE_PTR data, CK_ULONG n_data,
+                          CK_BYTE_PTR signature, CK_ULONG_PTR n_signature)
+{
+       gcry_sexp_t ssig, splain;
+       gcry_error_t gcry;
+       CK_ULONG size, key_bytes, key_bits;
+       CK_RV rv;
+
+       g_return_val_if_fail (sexp, CKR_GENERAL_ERROR);
+       g_return_val_if_fail (n_signature, CKR_ARGUMENTS_BAD);
+       g_return_val_if_fail (data, CKR_ARGUMENTS_BAD);
+
+       /* If no output, then don't process */
+       key_bits = gcry_pk_get_nbits(sexp);
+       key_bytes = (key_bits + 7)/8;
+       if (!signature) {
+               *n_signature = key_bytes * 2;
+               return CKR_OK;
+       } else if (*n_signature < (key_bytes * 2)) {
+               *n_signature = key_bytes * 2;
+               return CKR_BUFFER_TOO_SMALL;
+       }
+
+       /* Prepare the input s-expression */
+       gcry = gcry_sexp_build (&splain, NULL, "(data (flags raw) (value %b))",
+                                n_data, data);
+       g_return_val_if_fail (gcry == 0, CKR_GENERAL_ERROR);
+
+       /* Do the magic */
+       gcry = gcry_pk_sign (&ssig, splain, sexp);
+       gcry_sexp_release (splain);
+
+       /* TODO: Certain codes should be returned (data too big etc... ) */
+       if (gcry) {
+               g_message ("signing of the data failed: %s", gcry_strerror (gcry));
+               return CKR_FUNCTION_FAILED;
+       }
+
+       /* signature consists of two mpint values concatenated */
+       size = key_bytes;
+       rv = gkm_crypto_sexp_to_data (ssig, key_bits, signature, &size, NULL, "ecdsa", "r", NULL);
+       if (rv == CKR_OK) {
+               g_return_val_if_fail (size == key_bytes, CKR_GENERAL_ERROR);
+               rv = gkm_crypto_sexp_to_data (ssig, key_bits, signature + key_bytes, &size, NULL, "ecdsa", 
"s", NULL);
+               if (rv == CKR_OK) {
+                       g_return_val_if_fail (size == key_bytes, CKR_GENERAL_ERROR);
+                       *n_signature = key_bytes * 2;
+               }
+       }
+
+       gcry_sexp_release (ssig);
+       return rv;
+}
+
+CK_RV
+gkm_ecdsa_mechanism_verify (gcry_sexp_t sexp, CK_BYTE_PTR data, CK_ULONG n_data,
+                            CK_BYTE_PTR signature, CK_ULONG n_signature)
+{
+       gcry_sexp_t ssig, splain;
+       gcry_error_t gcry;
+       CK_ULONG key_bytes;
+
+       g_return_val_if_fail (sexp, CKR_GENERAL_ERROR);
+       g_return_val_if_fail (signature, CKR_ARGUMENTS_BAD);
+       g_return_val_if_fail (data, CKR_ARGUMENTS_BAD);
+
+       key_bytes = gcry_pk_get_nbits(sexp)/8;
+       if (n_signature != key_bytes*2)
+               return CKR_SIGNATURE_LEN_RANGE;
+
+       /* Prepare the input s-expressions */
+       gcry = gcry_sexp_build (&splain, NULL, "(data (flags raw) (value %b))",
+                                n_data, data);
+       g_return_val_if_fail (gcry == 0, CKR_GENERAL_ERROR);
+
+       gcry = gcry_sexp_build (&ssig, NULL, "(sig-val (ecdsa (r %b) (s %b)))",
+                                key_bytes, signature, key_bytes, signature + key_bytes);
+       g_return_val_if_fail (gcry == 0, CKR_GENERAL_ERROR);
+
+       /* Do the magic */
+       gcry = gcry_pk_verify (ssig, splain, sexp);
+       gcry_sexp_release (splain);
+       gcry_sexp_release (ssig);
+
+       /* TODO: See if any other codes should be mapped */
+       if (gcry_err_code (gcry) == GPG_ERR_BAD_SIGNATURE) {
+               return CKR_SIGNATURE_INVALID;
+       } else if (gcry) {
+               g_message ("verifying of the data failed: %s", gcry_strerror (gcry));
+               return CKR_FUNCTION_FAILED;
+       }
+
+       return CKR_OK;
+}
+
diff --git a/pkcs11/gkm/gkm-ecdsa-mechanism.h b/pkcs11/gkm/gkm-ecdsa-mechanism.h
new file mode 100644
index 0000000..9964034
--- /dev/null
+++ b/pkcs11/gkm/gkm-ecdsa-mechanism.h
@@ -0,0 +1,50 @@
+/*
+ * gnome-keyring
+ *
+ * Copyright (C) 2017 Red Hat, Inc.
+ *
+ * Author: Jakub Jelen <jjelen redhat com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, see
+ * <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GKM_ECDSA_MECHANISM_H_
+#define GKM_ECDSA_MECHANISM_H_
+
+#include "gkm-types.h"
+
+#include "pkcs11/pkcs11.h"
+
+#include <glib.h>
+
+#include <gcrypt.h>
+
+static const CK_MECHANISM_TYPE GKM_ECDSA_MECHANISMS[] = {
+       CKM_ECDSA
+};
+
+CK_RV                    gkm_ecdsa_mechanism_sign                      (gcry_sexp_t sexp,
+                                                                        CK_BYTE_PTR data,
+                                                                        CK_ULONG n_data,
+                                                                        CK_BYTE_PTR signature,
+                                                                        CK_ULONG_PTR n_signature);
+
+CK_RV                    gkm_ecdsa_mechanism_verify                    (gcry_sexp_t sexp,
+                                                                        CK_BYTE_PTR data,
+                                                                        CK_ULONG n_data,
+                                                                        CK_BYTE_PTR signature,
+                                                                        CK_ULONG n_signature);
+
+#endif /* GKM_ECDSA_MECHANISM_H_ */
diff --git a/pkcs11/gkm/gkm-module.c b/pkcs11/gkm/gkm-module.c
index 47477aa..f8177d2 100644
--- a/pkcs11/gkm/gkm-module.c
+++ b/pkcs11/gkm/gkm-module.c
@@ -162,6 +162,12 @@ static const MechanismAndInfo mechanism_list[] = {
        { CKM_DH_PKCS_DERIVE, { 1, 255, CKF_DERIVE } },
 
        /*
+        * CKM_ECDSA
+        * For ECDSA, min and max are the minimum and maximum modulus in bits
+        */
+       { CKM_ECDSA, { 256, 521, CKF_SIGN | CKF_VERIFY } },
+
+       /*
         * CKM_G_HKDF_DERIVE
         * For HKDF derivation the min and max are sizes of prime in bits.
         */
diff --git a/pkcs11/gkm/gkm-private-xsa-key.c b/pkcs11/gkm/gkm-private-xsa-key.c
index de8c937..3dbbd12 100644
--- a/pkcs11/gkm/gkm-private-xsa-key.c
+++ b/pkcs11/gkm/gkm-private-xsa-key.c
@@ -28,6 +28,7 @@
 #include "gkm-debug.h"
 #include "gkm-factory.h"
 #include "gkm-private-xsa-key.h"
+#include "gkm-data-der.h"
 #include "gkm-session.h"
 #include "gkm-transaction.h"
 #include "gkm-util.h"
@@ -42,6 +43,47 @@ G_DEFINE_TYPE (GkmPrivateXsaKey, gkm_private_xsa_key, GKM_TYPE_SEXP_KEY);
  * INTERNAL
  */
 
+/* Can't be defined in gkm_attributes, because it does not know anything about
+ * DER encoding nor OIDs
+ */
+gboolean
+gkm_attributes_find_ecc_oid (CK_ATTRIBUTE_PTR attrs, CK_ULONG n_attrs, GQuark *value)
+{
+       GBytes *bytes;
+       CK_ATTRIBUTE_PTR attr;
+       GQuark oid;
+
+       g_return_val_if_fail (attrs || !n_attrs, FALSE);
+
+       attr = gkm_attributes_find (attrs, n_attrs, CKA_EC_PARAMS);
+       if (attr == NULL)
+               return FALSE;
+
+       bytes = g_bytes_new (attr->pValue, attr->ulValueLen);
+       g_return_val_if_fail (bytes != NULL, FALSE);
+
+       oid = gkm_data_der_oid_from_ec_params (bytes);
+       g_return_val_if_fail (oid != 0, FALSE);
+       *value = oid;
+
+       g_bytes_unref (bytes);
+       return TRUE;
+}
+
+gboolean
+gkm_attributes_find_ecc_q (CK_ATTRIBUTE_PTR attrs, CK_ULONG n_attrs,
+                           CK_ATTRIBUTE_TYPE type, GBytes **value)
+{
+       GBytes *data;
+       gboolean rv;
+
+       rv = gkm_attributes_find_bytes (attrs, n_attrs, type, &data);
+       g_return_val_if_fail (rv, FALSE);
+
+       rv = gkm_data_der_decode_ecdsa_q (data, value);
+
+       return rv;
+}
 
 static CK_RV
 create_rsa_private (CK_ATTRIBUTE_PTR attrs, CK_ULONG n_attrs, gcry_sexp_t *skey)
@@ -144,6 +186,52 @@ done:
        return ret;
 }
 
+static CK_RV
+create_ecdsa_private (CK_ATTRIBUTE_PTR attrs, CK_ULONG n_attrs, gcry_sexp_t *skey)
+{
+       gcry_error_t gcry;
+       gcry_mpi_t d = NULL;
+       const gchar *curve_name, *q_data;
+       GBytes *q = NULL;
+       gsize q_size;
+       GQuark oid;
+       CK_RV ret;
+
+       if (!gkm_attributes_find_ecc_oid (attrs, n_attrs, &oid) ||
+           !gkm_attributes_find_ecc_q (attrs, n_attrs, CKA_EC_POINT, &q) ||
+           !gkm_attributes_find_mpi (attrs, n_attrs, CKA_VALUE, &d)) {
+               ret = CKR_TEMPLATE_INCOMPLETE;
+               goto done;
+       }
+
+       curve_name = gkm_data_der_oid_to_curve (oid);
+       if (curve_name == NULL) {
+               ret = CKR_FUNCTION_FAILED;
+               goto done;
+       }
+
+       q_data = g_bytes_get_data (q, &q_size);
+
+       gcry = gcry_sexp_build (skey, NULL,
+                               "(private-key (ecdsa (curve %s) (q %b) (d %m)))",
+                               curve_name, q_size, q_data, d);
+
+       if (gcry != 0) {
+               g_message ("couldn't create ECDSA key from passed attributes: %s", gcry_strerror (gcry));
+               ret = CKR_FUNCTION_FAILED;
+               goto done;
+       }
+
+       gkm_attributes_consume (attrs, n_attrs, CKA_EC_PARAMS,
+                               CKA_EC_POINT, CKA_VALUE, G_MAXULONG);
+       ret = CKR_OK;
+
+done:
+       g_bytes_unref (q);
+       gcry_mpi_release (d);
+       return ret;
+}
+
 static GkmObject*
 factory_create_private_xsa_key (GkmSession *session, GkmTransaction *transaction,
                             CK_ATTRIBUTE_PTR attrs, CK_ULONG n_attrs)
@@ -272,7 +360,13 @@ gkm_private_xsa_key_real_get_attribute (GkmObject *base, GkmSession *session, CK
        case CKA_BASE:
                return gkm_sexp_key_set_part (GKM_SEXP_KEY (self), GCRY_PK_DSA, "g", attr);
 
-       /* DSA private parts */
+       case CKA_EC_POINT:
+               return gkm_sexp_key_set_ec_q (GKM_SEXP_KEY (self), GCRY_PK_ECC, attr);
+
+       case CKA_EC_PARAMS:
+               return gkm_sexp_key_set_ec_params (GKM_SEXP_KEY (self), GCRY_PK_ECC, attr);
+
+       /* (EC)DSA private parts */
        case CKA_VALUE:
                return CKR_ATTRIBUTE_SENSITIVE;
        };
@@ -396,6 +490,9 @@ gkm_private_xsa_key_create_sexp (GkmSession *session, GkmTransaction *transactio
        case CKK_DSA:
                ret = create_dsa_private (attrs, n_attrs, &sexp);
                break;
+       case CKK_EC:
+               ret = create_ecdsa_private (attrs, n_attrs, &sexp);
+               break;
        default:
                ret = CKR_ATTRIBUTE_VALUE_INVALID;
                break;
diff --git a/pkcs11/gkm/gkm-private-xsa-key.h b/pkcs11/gkm/gkm-private-xsa-key.h
index af10c3d..d6af4a8 100644
--- a/pkcs11/gkm/gkm-private-xsa-key.h
+++ b/pkcs11/gkm/gkm-private-xsa-key.h
@@ -63,4 +63,13 @@ GkmSexp*               gkm_private_xsa_key_create_sexp            (GkmSession *s
                                                                    CK_ATTRIBUTE_PTR attrs,
                                                                    CK_ULONG n_attrs);
 
+gboolean               gkm_attributes_find_ecc_oid                (CK_ATTRIBUTE_PTR attrs,
+                                                                   CK_ULONG n_attrs,
+                                                                   GQuark *value);
+
+gboolean               gkm_attributes_find_ecc_q                  (CK_ATTRIBUTE_PTR attrs,
+                                                                   CK_ULONG n_attrs,
+                                                                   CK_ATTRIBUTE_TYPE type,
+                                                                   GBytes **value);
+
 #endif /* __GKM_PRIVATE_XSA_KEY_H__ */
diff --git a/pkcs11/gkm/gkm-public-xsa-key.c b/pkcs11/gkm/gkm-public-xsa-key.c
index a823e8a..5cc93f3 100644
--- a/pkcs11/gkm/gkm-public-xsa-key.c
+++ b/pkcs11/gkm/gkm-public-xsa-key.c
@@ -27,6 +27,7 @@
 #include "gkm-debug.h"
 #include "gkm-factory.h"
 #include "gkm-public-xsa-key.h"
+#include "gkm-data-der.h"
 #include "gkm-session.h"
 #include "gkm-sexp.h"
 #include "gkm-transaction.h"
@@ -140,6 +141,49 @@ done:
        return ret;
 }
 
+static CK_RV
+create_ecdsa_public (CK_ATTRIBUTE_PTR attrs, CK_ULONG n_attrs, gcry_sexp_t *skey)
+{
+       gcry_error_t gcry;
+       const gchar *curve_name, *q_data;
+       GBytes *q = NULL;
+       gsize q_size;
+       GQuark oid;
+       CK_RV ret;
+
+       if (!gkm_attributes_find_ecc_oid (attrs, n_attrs, &oid) ||
+           !gkm_attributes_find_ecc_q (attrs, n_attrs, CKA_EC_POINT, &q)) {
+               ret = CKR_TEMPLATE_INCOMPLETE;
+               goto done;
+       }
+
+       curve_name = gkm_data_der_oid_to_curve (oid);
+       if (curve_name == NULL) {
+               ret = CKR_FUNCTION_FAILED;
+               goto done;
+       }
+
+       q_data = g_bytes_get_data (q, &q_size);
+
+       gcry = gcry_sexp_build (skey, NULL,
+                               "(public-key (ecdsa (curve %s) (q %b)))",
+                               curve_name, q_size, q_data);
+
+       if (gcry != 0) {
+               g_message ("couldn't create ECDSA key from passed attributes: %s", gcry_strerror (gcry));
+               ret = CKR_FUNCTION_FAILED;
+               goto done;
+       }
+
+       gkm_attributes_consume (attrs, n_attrs, CKA_EC_POINT, CKA_EC_PARAMS,
+                               G_MAXULONG);
+       ret = CKR_OK;
+
+done:
+       g_bytes_unref (q);
+       return ret;
+}
+
 static GkmObject*
 factory_create_public_xsa_key (GkmSession *session, GkmTransaction *transaction,
                                CK_ATTRIBUTE_PTR attrs, CK_ULONG n_attrs)
@@ -221,6 +265,12 @@ gkm_public_xsa_key_real_get_attribute (GkmObject *base, GkmSession *session, CK_
        /* DSA public value */
        case CKA_VALUE:
                return gkm_sexp_key_set_part (GKM_SEXP_KEY (self), GCRY_PK_DSA, "y", attr);
+
+       case CKA_EC_POINT:
+               return gkm_sexp_key_set_ec_q (GKM_SEXP_KEY (self), GCRY_PK_ECC, attr);
+
+       case CKA_EC_PARAMS:
+               return gkm_sexp_key_set_ec_params (GKM_SEXP_KEY (self), GCRY_PK_ECC, attr);
        };
 
        return GKM_OBJECT_CLASS (gkm_public_xsa_key_parent_class)->get_attribute (base, session, attr);
@@ -286,6 +336,9 @@ gkm_public_xsa_key_create_sexp (GkmSession *session, GkmTransaction *transaction
        case CKK_DSA:
                ret = create_dsa_public (attrs, n_attrs, &sexp);
                break;
+       case CKK_EC:
+               ret = create_ecdsa_public (attrs, n_attrs, &sexp);
+               break;
        default:
                ret = CKR_ATTRIBUTE_VALUE_INVALID;
                break;
diff --git a/pkcs11/gkm/gkm-public-xsa-key.h b/pkcs11/gkm/gkm-public-xsa-key.h
index bc1bbee..f2c0f25 100644
--- a/pkcs11/gkm/gkm-public-xsa-key.h
+++ b/pkcs11/gkm/gkm-public-xsa-key.h
@@ -56,4 +56,13 @@ GkmSexp*                  gkm_public_xsa_key_create_sexp        (GkmSession *ses
                                                                  CK_ATTRIBUTE_PTR attrs,
                                                                  CK_ULONG n_attrs);
 
+extern gboolean           gkm_attributes_find_ecc_oid           (CK_ATTRIBUTE_PTR attrs,
+                                                                 CK_ULONG n_attrs,
+                                                                 GQuark *value);
+
+extern gboolean           gkm_attributes_find_ecc_q             (CK_ATTRIBUTE_PTR attrs,
+                                                                 CK_ULONG n_attrs,
+                                                                 CK_ATTRIBUTE_TYPE type,
+                                                                 GBytes **value);
+
 #endif /* __GKM_PUBLIC_XSA_KEY_H__ */
diff --git a/pkcs11/gkm/gkm-sexp-key.c b/pkcs11/gkm/gkm-sexp-key.c
index fa48b9a..8b98bdc 100644
--- a/pkcs11/gkm/gkm-sexp-key.c
+++ b/pkcs11/gkm/gkm-sexp-key.c
@@ -26,8 +26,10 @@
 #define DEBUG_FLAG GKM_DEBUG_OBJECT
 #include "gkm-debug.h"
 #include "gkm-dsa-mechanism.h"
+#include "gkm-ecdsa-mechanism.h"
 #include "gkm-rsa-mechanism.h"
 #include "gkm-sexp-key.h"
+#include "gkm-data-der.h"
 #include "gkm-util.h"
 
 enum {
@@ -63,6 +65,8 @@ gkm_sexp_key_real_get_attribute (GkmObject *base, GkmSession *session, CK_ATTRIB
                                return gkm_attribute_set_ulong (attr, CKK_RSA);
                        case GCRY_PK_DSA:
                                return gkm_attribute_set_ulong (attr, CKK_DSA);
+                       case GCRY_PK_ECC:
+                               return gkm_attribute_set_ulong (attr, CKK_ECDSA);
                        default:
                                g_return_val_if_reached (CKR_GENERAL_ERROR);
                        };
@@ -100,6 +104,9 @@ gkm_sexp_key_real_get_attribute (GkmObject *base, GkmSession *session, CK_ATTRIB
                case GCRY_PK_DSA:
                        return gkm_attribute_set_data (attr, (CK_VOID_PTR)GKM_DSA_MECHANISMS,
                                                       sizeof (GKM_DSA_MECHANISMS));
+               case GCRY_PK_ECC:
+                       return gkm_attribute_set_data (attr, (CK_VOID_PTR)GKM_ECDSA_MECHANISMS,
+                                                      sizeof (GKM_ECDSA_MECHANISMS));
                default:
                        g_return_val_if_reached (CKR_GENERAL_ERROR);
                };
@@ -224,8 +231,9 @@ gkm_sexp_key_get_algorithm (GkmSexpKey *self)
        return algorithm;
 }
 
-CK_RV
-gkm_sexp_key_set_part (GkmSexpKey *self, int algo, const char *part, CK_ATTRIBUTE_PTR attr)
+static CK_RV
+gkm_sexp_key_set_part_encode (GkmSexpKey *self, int algo, const char *part,
+                              CK_ATTRIBUTE_PTR attr, int der_encode)
 {
        gcry_sexp_t numbers;
        gcry_mpi_t mpi;
@@ -236,7 +244,7 @@ gkm_sexp_key_set_part (GkmSexpKey *self, int algo, const char *part, CK_ATTRIBUT
        g_return_val_if_fail (self->pv->base_sexp, CKR_GENERAL_ERROR);
 
        if (!gkm_sexp_parse_key (gkm_sexp_get (self->pv->base_sexp),
-                                       &algorithm, NULL, &numbers))
+                                &algorithm, NULL, &numbers))
                g_return_val_if_reached (CKR_GENERAL_ERROR);
 
        if (algorithm != algo) {
@@ -248,13 +256,75 @@ gkm_sexp_key_set_part (GkmSexpKey *self, int algo, const char *part, CK_ATTRIBUT
 
        if (!gkm_sexp_extract_mpi (numbers, &mpi, part, NULL))
                g_return_val_if_reached (CKR_GENERAL_ERROR);
-       rv = gkm_attribute_set_mpi (attr, mpi);
+
+       if (der_encode) {
+               /* convert mpi to DER encoded OCTET string */
+               GBytes *data;
+
+               rv = gkm_data_der_encode_ecdsa_q (mpi, &data);
+               g_return_val_if_fail (rv, CKR_GENERAL_ERROR);
+
+               rv = gkm_attribute_set_bytes (attr, data);
+               g_bytes_unref (data);
+       } else {
+               rv = gkm_attribute_set_mpi (attr, mpi);
+       }
+
        gcry_sexp_release (numbers);
        gcry_mpi_release (mpi);
 
        return rv;
 }
 
+CK_RV
+gkm_sexp_key_set_part (GkmSexpKey *self, int algo, const char *part, CK_ATTRIBUTE_PTR attr)
+{
+       return gkm_sexp_key_set_part_encode (self, algo, part, attr, 0);
+}
+
+CK_RV
+gkm_sexp_key_set_ec_q (GkmSexpKey *self, int algo, CK_ATTRIBUTE_PTR attr)
+{
+       return gkm_sexp_key_set_part_encode (self, algo, "q", attr, 1);
+}
+
+CK_RV
+gkm_sexp_key_set_ec_params (GkmSexpKey *self, int algo, CK_ATTRIBUTE_PTR attr)
+{
+       CK_RV rv;
+       gchar *curve_name;
+       GBytes *data;
+       int algorithm;
+       gcry_sexp_t numbers;
+
+       g_return_val_if_fail (GKM_IS_SEXP_KEY (self), CKR_GENERAL_ERROR);
+       g_return_val_if_fail (self->pv->base_sexp, CKR_GENERAL_ERROR);
+
+       if (!gkm_sexp_parse_key (gkm_sexp_get (self->pv->base_sexp),
+                                &algorithm, NULL, &numbers))
+               g_return_val_if_reached (CKR_GENERAL_ERROR);
+
+       if (algorithm != algo) {
+               gcry_sexp_release (numbers);
+               gkm_debug ("CKR_ATTRIBUTE_TYPE_INVALID: attribute %s not valid for key algorithm: %s",
+                          gkm_log_attr_type (attr->type), gcry_pk_algo_name (algo));
+               return CKR_ATTRIBUTE_TYPE_INVALID;
+       }
+
+       rv = gkm_sexp_extract_string (numbers, &curve_name, "curve", NULL);
+       g_return_val_if_fail (rv, CKR_GENERAL_ERROR);
+
+       data = gkm_data_der_curve_to_ec_params (curve_name);
+       g_return_val_if_fail (data != NULL, CKR_GENERAL_ERROR);
+
+       rv = gkm_attribute_set_bytes (attr, data);
+       g_bytes_unref (data);
+       gcry_sexp_release (numbers);
+       g_free (curve_name);
+
+       return rv;
+}
+
 GkmSexp*
 gkm_sexp_key_acquire_crypto_sexp (GkmSexpKey *self, GkmSession *session)
 {
diff --git a/pkcs11/gkm/gkm-sexp-key.h b/pkcs11/gkm/gkm-sexp-key.h
index 7400167..99e0042 100644
--- a/pkcs11/gkm/gkm-sexp-key.h
+++ b/pkcs11/gkm/gkm-sexp-key.h
@@ -64,6 +64,14 @@ CK_RV                gkm_sexp_key_set_part                (GkmSexpKey *self,
                                                            const char *part,
                                                            CK_ATTRIBUTE_PTR attr);
 
+CK_RV                gkm_sexp_key_set_ec_params           (GkmSexpKey *self,
+                                                           int algo,
+                                                           CK_ATTRIBUTE_PTR attr);
+
+CK_RV                gkm_sexp_key_set_ec_q                (GkmSexpKey *self,
+                                                           int algo,
+                                                           CK_ATTRIBUTE_PTR attr);
+
 GkmSexp*             gkm_sexp_key_acquire_crypto_sexp     (GkmSexpKey *self,
                                                            GkmSession *session);
 
diff --git a/pkcs11/gkm/gkm-sexp.c b/pkcs11/gkm/gkm-sexp.c
index 28d7ce2..287f874 100644
--- a/pkcs11/gkm/gkm-sexp.c
+++ b/pkcs11/gkm/gkm-sexp.c
@@ -194,6 +194,31 @@ done:
        return pubkey;
 }
 
+static gcry_sexp_t
+ecdsa_numbers_to_public (gcry_sexp_t ecdsa)
+{
+       gchar *curve_name = NULL, *q = NULL;
+       gsize q_len;
+       gcry_sexp_t pubkey = NULL;
+       gcry_error_t gcry;
+
+       if (!gkm_sexp_extract_string (ecdsa, &curve_name, "curve", NULL) ||
+           !gkm_sexp_extract_buffer (ecdsa, &q, &q_len, "q", NULL))
+               goto done;
+
+       gcry = gcry_sexp_build (&pubkey, NULL, "(public-key (ecdsa (curve %s) (q %b)))",
+                               curve_name, q_len, q);
+       if (gcry)
+               goto done;
+       g_assert (pubkey);
+
+done:
+       g_free (curve_name);
+       g_free (q);
+
+       return pubkey;
+}
+
 gboolean
 gkm_sexp_key_to_public (gcry_sexp_t privkey, gcry_sexp_t *pubkey)
 {
@@ -210,6 +235,9 @@ gkm_sexp_key_to_public (gcry_sexp_t privkey, gcry_sexp_t *pubkey)
        case GCRY_PK_DSA:
                *pubkey = dsa_numbers_to_public (numbers);
                break;
+       case GCRY_PK_ECC:
+               *pubkey = ecdsa_numbers_to_public (numbers);
+               break;
        default:
                g_return_val_if_reached (FALSE);
        }
@@ -240,6 +268,60 @@ gkm_sexp_extract_mpi (gcry_sexp_t sexp, gcry_mpi_t *mpi, ...)
        return (*mpi) ? TRUE : FALSE;
 }
 
+/* ECDSA s-exp lists the curve name as a string */
+gboolean
+gkm_sexp_extract_string (gcry_sexp_t sexp, gchar **buf, ...)
+{
+       gcry_sexp_t at = NULL;
+       va_list va;
+
+       g_assert (sexp);
+       g_assert (buf);
+
+       va_start (va, buf);
+       at = gkm_sexp_get_childv (sexp, va);
+       va_end (va);
+
+       *buf = NULL;
+       if (at) {
+               size_t len;
+               const char *data;
+
+               data = gcry_sexp_nth_data (at, 1, &len);
+               *buf = g_strndup (data, len);
+               gcry_sexp_release (at);
+       }
+
+       return (*buf) ? TRUE : FALSE;
+}
+
+gboolean
+gkm_sexp_extract_buffer (gcry_sexp_t sexp, gchar **buf, gsize *bufsize, ...)
+{
+       gcry_sexp_t at = NULL;
+       va_list va;
+
+       g_assert (sexp);
+       g_assert (buf);
+
+       va_start (va, bufsize);
+       at = gkm_sexp_get_childv (sexp, va);
+       va_end (va);
+
+       *buf = NULL;
+       if (at) {
+               size_t len;
+               const char *data;
+
+               data = gcry_sexp_nth_data (at, 1, &len);
+               *buf = g_memdup (data, len);
+               *bufsize = len;
+               gcry_sexp_release (at);
+       }
+
+       return (*buf) ? TRUE : FALSE;
+}
+
 gcry_sexp_t
 gkm_sexp_get_childv (gcry_sexp_t sexp, va_list va)
 {
diff --git a/pkcs11/gkm/gkm-sexp.h b/pkcs11/gkm/gkm-sexp.h
index ff1aa34..0e44d80 100644
--- a/pkcs11/gkm/gkm-sexp.h
+++ b/pkcs11/gkm/gkm-sexp.h
@@ -52,6 +52,15 @@ gboolean       gkm_sexp_extract_mpi              (gcry_sexp_t sexp,
                                                   gcry_mpi_t *mpi,
                                                   ...) G_GNUC_NULL_TERMINATED;
 
+gboolean       gkm_sexp_extract_string           (gcry_sexp_t sexp,
+                                                  gchar **buf,
+                                                  ...) G_GNUC_NULL_TERMINATED;
+
+gboolean       gkm_sexp_extract_buffer           (gcry_sexp_t sexp,
+                                                  gchar **buf,
+                                                  gsize *bufsize,
+                                                  ...) G_GNUC_NULL_TERMINATED;
+
 gcry_sexp_t    gkm_sexp_get_childv               (gcry_sexp_t sexp,
                                                   va_list va);
 


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]