[gnome-keyring/wip/dueno/ecdsa-support: 1/2] Support ECDSA in ssh-agent interface



commit a788bd4f34370d6a6d56bef5572697b972cc8edf
Author: Jakub Jelen <jjelen redhat com>
Date:   Tue Aug 8 19:06:58 2017 +0200

    Support ECDSA in ssh-agent interface
    
    https://bugzilla.gnome.org/show_bug.cgi?id=641082

 daemon/ssh-agent/Makefile.am             |    1 +
 daemon/ssh-agent/gkd-ssh-agent-ops.c     |   41 +++-
 daemon/ssh-agent/gkd-ssh-agent-private.h |   41 +++-
 daemon/ssh-agent/gkd-ssh-agent-proto.c   |  380 +++++++++++++++++++++++++++++-
 4 files changed, 452 insertions(+), 11 deletions(-)
---
diff --git a/daemon/ssh-agent/Makefile.am b/daemon/ssh-agent/Makefile.am
index 01d0907..be0b470 100644
--- a/daemon/ssh-agent/Makefile.am
+++ b/daemon/ssh-agent/Makefile.am
@@ -28,4 +28,5 @@ gkd_ssh_agent_standalone_LDADD = \
        libgkd-ssh-agent.la \
        libegg-buffer.la \
        libegg-secure.la \
+       libgkm.la \
        $(DAEMON_LIBS)
diff --git a/daemon/ssh-agent/gkd-ssh-agent-ops.c b/daemon/ssh-agent/gkd-ssh-agent-ops.c
index ced7877..ab4beca 100644
--- a/daemon/ssh-agent/gkd-ssh-agent-ops.c
+++ b/daemon/ssh-agent/gkd-ssh-agent-ops.c
@@ -112,6 +112,11 @@ build_like_attributes (GckAttributes *attrs, CK_OBJECT_CLASS klass)
                copy_attribute (attrs, CKA_VALUE, &builder);
                break;
 
+       case CKK_EC:
+               copy_attribute (attrs, CKA_EC_PARAMS, &builder);
+               copy_attribute (attrs, CKA_EC_POINT, &builder);
+               break;
+
        default:
                g_return_val_if_reached (NULL);
                break;
@@ -306,7 +311,8 @@ load_identity_v2_attributes (GckObject *object, gpointer user_data)
 
        attrs = gck_object_get (object, NULL, &error, CKA_ID, CKA_LABEL, CKA_KEY_TYPE, CKA_MODULUS,
                                CKA_PUBLIC_EXPONENT, CKA_PRIME, CKA_SUBPRIME, CKA_BASE,
-                               CKA_VALUE, CKA_CLASS, CKA_MODULUS_BITS, CKA_TOKEN, GCK_INVALID);
+                               CKA_VALUE, CKA_CLASS, CKA_MODULUS_BITS, CKA_TOKEN,
+                               CKA_EC_POINT, CKA_EC_PARAMS, GCK_INVALID);
        if (error) {
                g_warning ("error retrieving attributes for public key: %s", egg_error_message (error));
                g_clear_error (&error);
@@ -643,6 +649,9 @@ op_add_identity (GkdSshAgentCall *call)
        case CKK_DSA:
                ret = gkd_ssh_agent_proto_read_pair_dsa (call->req, &offset, &priv, &pub);
                break;
+       case CKK_EC:
+               ret = gkd_ssh_agent_proto_read_pair_ecdsa (call->req, &offset, &priv, &pub);
+               break;
        default:
                g_assert_not_reached ();
                return FALSE;
@@ -982,6 +991,7 @@ unlock_and_sign (GckSession *session, GckObject *key, gulong mech_type, const gu
        return gck_session_sign (session, key, mech_type, input, n_input, n_result, NULL, err);
 }
 
+
 static gboolean
 op_sign_request (GkdSshAgentCall *call)
 {
@@ -1002,6 +1012,7 @@ op_sign_request (GkdSshAgentCall *call)
        gulong algo, mech;
        GChecksumType halgo;
        gsize n_hash = 0;
+       GQuark oid = 0;
 
        offset = 5;
 
@@ -1022,7 +1033,12 @@ op_sign_request (GkdSshAgentCall *call)
                mech = CKM_RSA_PKCS;
        else if (algo == CKK_DSA)
                mech = CKM_DSA;
-       else
+       else if (algo == CKK_EC) {
+               mech = CKM_ECDSA;
+               oid = gkd_ssh_agent_proto_find_curve_oid (attrs);
+               if (!oid)
+                       return FALSE;
+       } else
                g_return_val_if_reached (FALSE);
 
        if (!egg_buffer_get_byte_array (call->req, offset, &offset, &data, &n_data) ||
@@ -1040,11 +1056,20 @@ op_sign_request (GkdSshAgentCall *call)
                return TRUE;
        }
 
-       /* Usually we hash the data with SHA1 */
-       if (flags & GKD_SSH_FLAG_OLD_SIGNATURE)
+       if (mech == CKM_ECDSA) {
+               /* ECDSA is using SHA-2 hash algorithms based on key size */
+               gint ret = gkd_ssh_agent_proto_curve_oid_to_hash_algo (oid);
+               if (ret == -1) {
+                       egg_buffer_add_byte (call->resp, GKD_SSH_RES_FAILURE);
+                       return FALSE;
+               }
+               halgo = (GChecksumType) ret;
+       } else if (flags & GKD_SSH_FLAG_OLD_SIGNATURE) {
                halgo = G_CHECKSUM_MD5;
-       else
+       } else {
+               /* Usually we hash the data with SHA1 */
                halgo = G_CHECKSUM_SHA1;
+       }
 
        /* Build the hash */
        if (mech == CKM_RSA_PKCS)
@@ -1076,7 +1101,7 @@ op_sign_request (GkdSshAgentCall *call)
        blobpos = call->resp->len;
        egg_buffer_add_uint32 (call->resp, 0);
 
-       salgo = gkd_ssh_agent_proto_algo_to_keytype (algo);
+       salgo = gkd_ssh_agent_proto_algo_to_keytype (algo, oid);
        g_assert (salgo);
        egg_buffer_add_string (call->resp, salgo);
 
@@ -1089,6 +1114,10 @@ op_sign_request (GkdSshAgentCall *call)
                ret = gkd_ssh_agent_proto_write_signature_dsa (call->resp, result, n_result);
                break;
 
+       case CKK_EC:
+               ret = gkd_ssh_agent_proto_write_signature_ecdsa (call->resp, result, n_result);
+               break;
+
        default:
                g_assert_not_reached ();
        }
diff --git a/daemon/ssh-agent/gkd-ssh-agent-private.h b/daemon/ssh-agent/gkd-ssh-agent-private.h
index efdc969..28a1faf 100644
--- a/daemon/ssh-agent/gkd-ssh-agent-private.h
+++ b/daemon/ssh-agent/gkd-ssh-agent-private.h
@@ -103,7 +103,18 @@ void                  gkd_ssh_agent_checkin_main_session            (GckSession*
 
 gulong                gkd_ssh_agent_proto_keytype_to_algo           (const gchar *salgo);
 
-const gchar*          gkd_ssh_agent_proto_algo_to_keytype           (gulong algo);
+const gchar*          gkd_ssh_agent_proto_algo_to_keytype           (gulong algo,
+                                                                     GQuark oid);
+
+GQuark                gkd_ssh_agent_proto_curve_to_oid              (const gchar *salgo);
+
+const gchar*          gkd_ssh_agent_proto_oid_to_curve              (GQuark oid);
+
+gint                  gkd_ssh_agent_proto_curve_oid_to_hash_algo    (GQuark oid);
+
+const gchar*          gkd_ssh_agent_proto_curve_oid_to_keytype      (GQuark oid);
+
+GQuark                gkd_ssh_agent_proto_find_curve_oid            (GckAttributes *attrs);
 
 gboolean              gkd_ssh_agent_proto_read_mpi                  (EggBuffer *req,
                                                                      gsize *offset,
@@ -119,12 +130,24 @@ const guchar*         gkd_ssh_agent_proto_read_challenge_v1         (EggBuffer *
                                                                      gsize *offset,
                                                                      gsize *n_challenge);
 
+gboolean              gkd_ssh_agent_proto_read_string_to_der        (EggBuffer *req,
+                                                                     gsize *offset,
+                                                                     GckBuilder *attrs,
+                                                                     CK_ATTRIBUTE_TYPE type);
+
+gboolean              gkd_ssh_agent_proto_read_ecdsa_curve          (EggBuffer *req,
+                                                                     gsize *offset,
+                                                                     GckBuilder *attrs);
+
 gboolean              gkd_ssh_agent_proto_write_mpi                 (EggBuffer *resp,
                                                                      const GckAttribute *attr);
 
 gboolean              gkd_ssh_agent_proto_write_mpi_v1              (EggBuffer *resp,
                                                                      const GckAttribute *attr);
 
+gboolean              gkd_ssh_agent_proto_write_string              (EggBuffer *resp,
+                                                                     const GckAttribute *attr);
+
 gboolean              gkd_ssh_agent_proto_read_public               (EggBuffer *req,
                                                                      gsize *offset,
                                                                      GckBuilder *attrs,
@@ -138,6 +161,10 @@ gboolean              gkd_ssh_agent_proto_read_public_dsa           (EggBuffer *
                                                                      gsize *offset,
                                                                      GckBuilder *attrs);
 
+gboolean              gkd_ssh_agent_proto_read_public_ecdsa         (EggBuffer *req,
+                                                                     gsize *offset,
+                                                                     GckBuilder *attrs);
+
 gboolean              gkd_ssh_agent_proto_read_public_v1            (EggBuffer *req,
                                                                      gsize *offset,
                                                                      GckBuilder *attrs);
@@ -152,6 +179,11 @@ gboolean              gkd_ssh_agent_proto_read_pair_dsa             (EggBuffer *
                                                                      GckBuilder *priv,
                                                                      GckBuilder *pub);
 
+gboolean              gkd_ssh_agent_proto_read_pair_ecdsa           (EggBuffer *req,
+                                                                     gsize *offset,
+                                                                     GckBuilder *priv,
+                                                                     GckBuilder *pub);
+
 gboolean              gkd_ssh_agent_proto_read_pair_v1              (EggBuffer *req,
                                                                      gsize *offset,
                                                                      GckBuilder *priv,
@@ -166,6 +198,9 @@ gboolean              gkd_ssh_agent_proto_write_public_rsa          (EggBuffer *
 gboolean              gkd_ssh_agent_proto_write_public_dsa          (EggBuffer *resp,
                                                                      GckAttributes *attrs);
 
+gboolean              gkd_ssh_agent_proto_write_public_ecdsa        (EggBuffer *resp,
+                                                                     GckAttributes *attrs);
+
 gboolean              gkd_ssh_agent_proto_write_public_v1           (EggBuffer *resp,
                                                                      GckAttributes *attrs);
 
@@ -177,4 +212,8 @@ gboolean              gkd_ssh_agent_proto_write_signature_dsa       (EggBuffer *
                                                                      CK_BYTE_PTR signature,
                                                                      CK_ULONG n_signature);
 
+gboolean              gkd_ssh_agent_proto_write_signature_ecdsa     (EggBuffer *resp,
+                                                                     CK_BYTE_PTR signature,
+                                                                     CK_ULONG n_signature);
+
 #endif /*GKDSSHPRIVATE_H_*/
diff --git a/daemon/ssh-agent/gkd-ssh-agent-proto.c b/daemon/ssh-agent/gkd-ssh-agent-proto.c
index a020dd3..034bfb9 100644
--- a/daemon/ssh-agent/gkd-ssh-agent-proto.c
+++ b/daemon/ssh-agent/gkd-ssh-agent-proto.c
@@ -24,6 +24,8 @@
 
 #include "gkd-ssh-agent-private.h"
 
+#include "gkm/gkm-data-der.h"
+
 #include "egg/egg-buffer.h"
 
 #include <gck/gck.h>
@@ -32,6 +34,34 @@
 
 #include <string.h>
 
+/* -----------------------------------------------------------------------------
+ * QUARKS
+ */
+
+static GQuark OID_ANSI_SECP256R1;
+static GQuark OID_ANSI_SECP384R1;
+static GQuark OID_ANSI_SECP521R1;
+
+static void
+init_quarks (void)
+{
+       static volatile gsize quarks_inited = 0;
+
+       if (g_once_init_enter (&quarks_inited)) {
+
+               #define QUARK(name, value) \
+                       name = g_quark_from_static_string(value)
+
+               QUARK (OID_ANSI_SECP256R1, "1.2.840.10045.3.1.7");
+               QUARK (OID_ANSI_SECP384R1, "1.3.132.0.34");
+               QUARK (OID_ANSI_SECP521R1, "1.3.132.0.35");
+
+               #undef QUARK
+
+               g_once_init_leave (&quarks_inited, 1);
+       }
+}
+
 gulong
 gkd_ssh_agent_proto_keytype_to_algo (const gchar *salgo)
 {
@@ -40,19 +70,122 @@ gkd_ssh_agent_proto_keytype_to_algo (const gchar *salgo)
                return CKK_RSA;
        else if (strcmp (salgo, "ssh-dss") == 0)
                return CKK_DSA;
+       else if (strcmp (salgo, "ecdsa-sha2-nistp256") == 0 ||
+                strcmp (salgo, "ecdsa-sha2-nistp384") == 0 ||
+                strcmp (salgo, "ecdsa-sha2-nistp521") == 0)
+               return CKK_EC;
        return G_MAXULONG;
 }
 
+GQuark
+gkd_ssh_agent_proto_curve_to_oid (const gchar *salgo)
+{
+       g_return_val_if_fail (salgo, 0);
+
+       init_quarks ();
+
+       if (g_str_equal (salgo, "nistp256"))
+               return OID_ANSI_SECP256R1;
+       else if (g_str_equal (salgo, "nistp384"))
+               return OID_ANSI_SECP384R1;
+       else if (g_str_equal (salgo, "nistp521"))
+               return OID_ANSI_SECP521R1;
+
+       return 0;
+}
+
 const gchar*
-gkd_ssh_agent_proto_algo_to_keytype (gulong algo)
+gkd_ssh_agent_proto_oid_to_curve (GQuark oid)
 {
-       if (algo == CKK_RSA)
+       g_return_val_if_fail (oid, NULL);
+
+       init_quarks ();
+
+       if (oid == OID_ANSI_SECP256R1)
+               return "nistp256";
+       else if (oid == OID_ANSI_SECP384R1)
+               return "nistp384";
+       else if (oid == OID_ANSI_SECP521R1)
+               return "nistp521";
+
+       return NULL;
+}
+
+gint
+gkd_ssh_agent_proto_curve_oid_to_hash_algo (GQuark oid)
+{
+       g_return_val_if_fail (oid, -1);
+
+       init_quarks ();
+
+       /* from rfc5656 */
+       if (oid == OID_ANSI_SECP256R1)
+               return G_CHECKSUM_SHA256;
+       else if (oid == OID_ANSI_SECP384R1)
+               return G_CHECKSUM_SHA384;
+       else if (oid == OID_ANSI_SECP521R1)
+               return G_CHECKSUM_SHA512;
+
+       return -1;
+}
+
+const gchar*
+gkd_ssh_agent_proto_curve_oid_to_keytype (GQuark oid)
+{
+       g_return_val_if_fail (oid, NULL);
+
+       init_quarks ();
+
+       if (oid == OID_ANSI_SECP256R1)
+               return "ecdsa-sha2-nistp256";
+       else if (oid == OID_ANSI_SECP384R1)
+               return "ecdsa-sha2-nistp384";
+       else if (oid == OID_ANSI_SECP521R1)
+               return "ecdsa-sha2-nistp521";
+
+       return NULL;
+}
+
+const gchar*
+gkd_ssh_agent_proto_algo_to_keytype (gulong algo, GQuark curve_oid)
+{
+       if (algo == CKK_RSA) {
+               g_return_val_if_fail (curve_oid == 0, NULL);
                return "ssh-rsa";
-       else if (algo == CKK_DSA)
+       } else if (algo == CKK_DSA) {
+               g_return_val_if_fail (curve_oid == 0, NULL);
                return "ssh-dss";
+       } else if (algo == CKK_EC) {
+               g_return_val_if_fail (curve_oid != 0, NULL);
+               return gkd_ssh_agent_proto_curve_oid_to_keytype (curve_oid);
+       }
+
        return NULL;
 }
 
+
+GQuark
+gkd_ssh_agent_proto_find_curve_oid (GckAttributes *attrs)
+{
+       GBytes *bytes;
+       const GckAttribute *attr;
+       GQuark oid;
+
+       g_assert (attrs);
+
+       attr = gck_attributes_find (attrs, CKA_EC_PARAMS);
+       if (attr == NULL)
+               g_return_val_if_reached (0);
+
+       bytes = g_bytes_new (attr->value, attr->length);
+
+       oid = gkm_data_der_oid_from_ec_params (bytes);
+
+       g_bytes_unref (bytes);
+
+       return oid;
+}
+
 gboolean
 gkd_ssh_agent_proto_read_mpi (EggBuffer *req, gsize *offset,
                               GckBuilder *builder,
@@ -104,6 +237,27 @@ gkd_ssh_agent_proto_read_mpi_v1 (EggBuffer *req,
 }
 
 gboolean
+gkd_ssh_agent_proto_read_string_to_der (EggBuffer *req,
+                                        gsize *offset,
+                                        GckBuilder *attrs,
+                                        CK_ATTRIBUTE_TYPE type)
+{
+       const guchar *data, *q_data;
+       gsize len, q_len;
+       GBytes *bytes;
+
+       if (!egg_buffer_get_byte_array (req, *offset, offset, &data, &len))
+               return FALSE;
+
+       bytes = gkm_data_der_encode_ecdsa_q_str (data, len);
+
+       q_data = g_bytes_get_data (bytes, &q_len);
+
+       gck_builder_add_data (attrs, type, q_data, q_len);
+       return TRUE;
+}
+
+gboolean
 gkd_ssh_agent_proto_write_mpi (EggBuffer *resp,
                                const GckAttribute *attr)
 {
@@ -147,6 +301,23 @@ gkd_ssh_agent_proto_write_mpi_v1 (EggBuffer *resp,
        return TRUE;
 }
 
+gboolean
+gkd_ssh_agent_proto_write_string (EggBuffer *resp,
+                                  const GckAttribute *attr)
+{
+       guchar *data;
+
+       g_assert (resp);
+       g_assert (attr);
+
+       data = egg_buffer_add_byte_array_empty (resp, attr->length);
+       if (data == NULL)
+               return FALSE;
+
+       memcpy (data, attr->value, attr->length);
+       return TRUE;
+}
+
 const guchar*
 gkd_ssh_agent_proto_read_challenge_v1 (EggBuffer *req, gsize *offset, gsize *n_challenge)
 {
@@ -204,6 +375,9 @@ gkd_ssh_agent_proto_read_public (EggBuffer *req,
        case CKK_DSA:
                ret = gkd_ssh_agent_proto_read_public_dsa (req, offset, attrs);
                break;
+       case CKK_EC:
+               ret = gkd_ssh_agent_proto_read_public_ecdsa (req, offset, attrs);
+               break;
        default:
                g_assert_not_reached ();
                return FALSE;
@@ -396,10 +570,98 @@ gkd_ssh_agent_proto_read_public_dsa (EggBuffer *req,
 }
 
 gboolean
+gkd_ssh_agent_proto_read_ecdsa_curve (EggBuffer *req,
+                                     gsize *offset,
+                                     GckBuilder *attrs)
+{
+       GBytes *params;
+       gchar *curve_name;
+       const guchar *params_data;
+       GQuark oid;
+       gsize params_len;
+
+       g_assert (req);
+       g_assert (offset);
+       g_assert (attrs);
+
+       /* first part is the curve name (nistp* part of key name) and needs
+        * to be converted to CKA_EC_PARAMS
+        */
+       if (!egg_buffer_get_string (req, *offset, offset, &curve_name,
+                                    (EggBufferAllocator)g_realloc))
+               return FALSE;
+
+       oid = gkd_ssh_agent_proto_curve_to_oid (curve_name);
+       g_return_val_if_fail (oid, FALSE);
+
+       params = gkm_data_der_get_ec_params (oid);
+       g_return_val_if_fail (params != NULL, FALSE);
+
+       params_data = g_bytes_get_data (params, &params_len);
+       gck_builder_add_data (attrs, CKA_EC_PARAMS, params_data, params_len);
+
+       return TRUE;
+}
+
+gboolean
+gkd_ssh_agent_proto_read_pair_ecdsa (EggBuffer *req,
+                                     gsize *offset,
+                                     GckBuilder *priv_attrs,
+                                     GckBuilder *pub_attrs)
+{
+       const GckAttribute *attr;
+
+       g_assert (req);
+       g_assert (offset);
+       g_assert (priv_attrs);
+       g_assert (pub_attrs);
+
+       if (!gkd_ssh_agent_proto_read_ecdsa_curve (req, offset, priv_attrs) ||
+           !gkd_ssh_agent_proto_read_string_to_der (req, offset, priv_attrs, CKA_EC_POINT) ||
+           !gkd_ssh_agent_proto_read_mpi (req, offset, priv_attrs, CKA_VALUE))
+               return FALSE;
+
+       /* Copy attributes to the public key */
+       attr = gck_builder_find (priv_attrs, CKA_EC_POINT);
+       gck_builder_add_attribute (pub_attrs, attr);
+       attr = gck_builder_find (priv_attrs, CKA_EC_PARAMS);
+       gck_builder_add_attribute (pub_attrs, attr);
+
+       /* Add in your basic other required attributes */
+       gck_builder_add_ulong (priv_attrs, CKA_CLASS, CKO_PRIVATE_KEY);
+       gck_builder_add_ulong (priv_attrs, CKA_KEY_TYPE, CKK_EC);
+       gck_builder_add_ulong (pub_attrs, CKA_CLASS, CKO_PUBLIC_KEY);
+       gck_builder_add_ulong (pub_attrs, CKA_KEY_TYPE, CKK_EC);
+
+       return TRUE;
+}
+
+gboolean
+gkd_ssh_agent_proto_read_public_ecdsa (EggBuffer *req,
+                                       gsize *offset,
+                                       GckBuilder *attrs)
+{
+       g_assert (req);
+       g_assert (offset);
+       g_assert (attrs);
+
+       if (!gkd_ssh_agent_proto_read_ecdsa_curve (req, offset, attrs) ||
+           !gkd_ssh_agent_proto_read_string_to_der (req, offset, attrs, CKA_EC_POINT))
+               return FALSE;
+
+       /* Add in your basic other required attributes */
+       gck_builder_add_ulong (attrs, CKA_CLASS, CKO_PUBLIC_KEY);
+       gck_builder_add_ulong (attrs, CKA_KEY_TYPE, CKK_EC);
+
+       return TRUE;
+}
+
+gboolean
 gkd_ssh_agent_proto_write_public (EggBuffer *resp, GckAttributes *attrs)
 {
        gboolean ret = FALSE;
        const gchar *salgo;
+       GQuark oid = 0;
        gulong algo;
 
        g_assert (resp);
@@ -407,8 +669,13 @@ gkd_ssh_agent_proto_write_public (EggBuffer *resp, GckAttributes *attrs)
 
        if (!gck_attributes_find_ulong (attrs, CKA_KEY_TYPE, &algo))
                g_return_val_if_reached (FALSE);
+       if (algo == CKK_EC) {
+               oid = gkd_ssh_agent_proto_find_curve_oid (attrs);
+               if (!oid)
+                       return FALSE;
+       }
 
-       salgo = gkd_ssh_agent_proto_algo_to_keytype (algo);
+       salgo = gkd_ssh_agent_proto_algo_to_keytype (algo, oid);
        g_assert (salgo);
        egg_buffer_add_string (resp, salgo);
 
@@ -421,6 +688,10 @@ gkd_ssh_agent_proto_write_public (EggBuffer *resp, GckAttributes *attrs)
                ret = gkd_ssh_agent_proto_write_public_dsa (resp, attrs);
                break;
 
+       case CKK_EC:
+               ret = gkd_ssh_agent_proto_write_public_ecdsa (resp, attrs);
+               break;
+
        default:
                g_return_val_if_reached (FALSE);
                break;
@@ -488,6 +759,55 @@ gkd_ssh_agent_proto_write_public_dsa (EggBuffer *resp, GckAttributes *attrs)
 }
 
 gboolean
+gkd_ssh_agent_proto_write_public_ecdsa (EggBuffer *resp, GckAttributes *attrs)
+{
+       const GckAttribute *attr;
+       GQuark oid;
+       const gchar *curve;
+       guchar *data;
+       const guchar *q_data;
+       GBytes *bytes, *q;
+       gboolean rv;
+       gsize q_len;
+
+       g_assert (resp);
+       g_assert (attrs);
+
+       /* decode curve name from EC_PARAMS */
+       oid = gkd_ssh_agent_proto_find_curve_oid (attrs);
+       g_return_val_if_fail (oid, FALSE);
+
+       curve = gkd_ssh_agent_proto_oid_to_curve (oid);
+       g_return_val_if_fail (curve != NULL, FALSE);
+
+       data = egg_buffer_add_byte_array_empty (resp, strlen (curve));
+       if (data == NULL)
+               return FALSE;
+
+       memcpy (data, curve, strlen(curve));
+
+       /* decode DER-encoded value Q */
+       attr = gck_attributes_find (attrs, CKA_EC_POINT);
+       g_return_val_if_fail (attr, FALSE);
+
+       bytes = g_bytes_new_static (attr->value, attr->length);
+       rv = gkm_data_der_decode_ecdsa_q (bytes, &q);
+       g_return_val_if_fail (rv, FALSE);
+       g_bytes_unref (bytes);
+
+       q_data = g_bytes_get_data (q, &q_len);
+
+       data = egg_buffer_add_byte_array_empty (resp, q_len);
+       if (data == NULL)
+               return FALSE;
+
+       memcpy (data, q_data, q_len);
+       g_bytes_unref (q);
+
+       return TRUE;
+}
+
+gboolean
 gkd_ssh_agent_proto_write_public_v1 (EggBuffer *resp, GckAttributes *attrs)
 {
        const GckAttribute *attr;
@@ -532,3 +852,55 @@ gkd_ssh_agent_proto_write_signature_dsa (EggBuffer *resp, CK_BYTE_PTR signature,
        g_return_val_if_fail (n_signature == 40, FALSE);
        return egg_buffer_add_byte_array (resp, signature, n_signature);
 }
+
+static gboolean
+gkd_ssh_agent_buffer_put_rfc_mpi (EggBuffer *buffer, const guchar *val,
+                                  gsize len)
+{
+       gsize pad = 0;
+
+       /*
+        * From RFC 4251:
+        * If the most significant bit would be set for a positive number,
+        * the number MUST be preceded by a zero byte.
+        */
+       if ((val[0] & 0x80))
+               pad = 1;
+
+       if (!egg_buffer_add_uint32 (buffer, len + pad))
+               return 0;
+       if (pad && !egg_buffer_add_byte (buffer, 0x00))
+               return 0;
+       return egg_buffer_append (buffer, val, len);
+}
+
+gboolean
+gkd_ssh_agent_proto_write_signature_ecdsa (EggBuffer *resp, CK_BYTE_PTR signature, CK_ULONG n_signature)
+{
+       gboolean rv;
+       gsize mpi_size;
+       gsize pads = 0;
+
+       g_return_val_if_fail ((n_signature % 2) == 0, FALSE);
+
+       /* PKCS#11 lists the MPIs concatenated, SSH-agent expects the size headers */
+       mpi_size = n_signature/2;
+
+       /*
+        * From RFC 4251, Section 5:
+        * If the most significant bit would be set for a positive number,
+        * the number MUST be preceded by a zero byte.
+        */
+       pads = ((signature[0] & 0x80) == 0x80) + ((signature[mpi_size] & 0x80) == 0x80);
+
+       /* First we need header for the whole signature blob
+        * (including 2 length headers and potential "padding")
+        */
+       egg_buffer_add_uint32 (resp, n_signature + 8 + pads);
+
+       rv = gkd_ssh_agent_buffer_put_rfc_mpi (resp, signature, mpi_size);
+       g_return_val_if_fail (rv, FALSE);
+
+       rv = gkd_ssh_agent_buffer_put_rfc_mpi (resp, signature + mpi_size, mpi_size);
+       return rv;
+}


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