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



commit 36bb125e771a12f7f9757322eef7d4b707035cfd
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/gkd-ssh-agent-ops.c     |   51 ++++-
 daemon/ssh-agent/gkd-ssh-agent-private.h |   43 ++++-
 daemon/ssh-agent/gkd-ssh-agent-proto.c   |  348 +++++++++++++++++++++++++++++-
 3 files changed, 433 insertions(+), 9 deletions(-)
---
diff --git a/daemon/ssh-agent/gkd-ssh-agent-ops.c b/daemon/ssh-agent/gkd-ssh-agent-ops.c
index ced7877..dee654b 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,23 @@ 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 GChecksumType
+ecdsa_get_hash_algorithm (GQuark oid)
+{
+       const gchar *str = g_quark_to_string (oid);
+
+       /* from rfc5656 */
+       if (g_str_equal (str, GKD_SSH_OID_ANSI_SECP256R1))
+               return G_CHECKSUM_SHA256;
+       else if (g_str_equal (str, GKD_SSH_OID_ANSI_SECP384R1))
+               return G_CHECKSUM_SHA384;
+       else if (g_str_equal (str, GKD_SSH_OID_ANSI_SECP521R1))
+               return G_CHECKSUM_SHA512;
+       else
+               return 0;
+}
+
+
 static gboolean
 op_sign_request (GkdSshAgentCall *call)
 {
@@ -1002,6 +1028,7 @@ op_sign_request (GkdSshAgentCall *call)
        gulong algo, mech;
        GChecksumType halgo;
        gsize n_hash = 0;
+       GQuark oid = 0;
 
        offset = 5;
 
@@ -1022,7 +1049,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) ||
@@ -1046,6 +1078,15 @@ op_sign_request (GkdSshAgentCall *call)
        else
                halgo = G_CHECKSUM_SHA1;
 
+       /* ECDSA is using SHA-2 hash algorithms based on key size */
+       if (mech == CKM_ECDSA)
+               halgo = ecdsa_get_hash_algorithm (oid);
+
+       if (halgo == 0) {
+               egg_buffer_add_byte (call->resp, GKD_SSH_RES_FAILURE);
+               return FALSE;
+       }
+
        /* Build the hash */
        if (mech == CKM_RSA_PKCS)
                hash = make_pkcs1_sign_hash (halgo, data, n_data, &n_hash);
@@ -1076,7 +1117,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 +1130,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..3498f57 100644
--- a/daemon/ssh-agent/gkd-ssh-agent-private.h
+++ b/daemon/ssh-agent/gkd-ssh-agent-private.h
@@ -101,9 +101,22 @@ void                  gkd_ssh_agent_checkin_main_session            (GckSession*
  * gkd-ssh-agent-proto.c
  */
 
+#define GKD_SSH_OID_ANSI_SECP256R1 "1.2.840.10045.3.1.7"
+#define GKD_SSH_OID_ANSI_SECP384R1 "1.3.132.0.34"
+#define GKD_SSH_OID_ANSI_SECP521R1 "1.3.132.0.35"
+
 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);
+
+const gchar*          gkd_ssh_agent_proto_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 +132,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 +163,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 +181,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 +200,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 +214,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..0bdc66c 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>
@@ -40,17 +42,114 @@ 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)
+{
+       static GQuark secp256r1, secp384r1, secp521r1;
+
+       g_return_val_if_fail (salgo, 0);
+
+       if (g_str_equal (salgo, "nistp256")) {
+               if (secp256r1 == 0)
+                       secp256r1 = g_quark_from_static_string (GKD_SSH_OID_ANSI_SECP256R1);
+               return secp256r1;
+       }
+       if (g_str_equal (salgo, "nistp384")) {
+               if (secp384r1 == 0)
+                       secp384r1 = g_quark_from_static_string (GKD_SSH_OID_ANSI_SECP384R1);
+               return secp384r1;
+       }
+       if (g_str_equal (salgo, "nistp521")) {
+               if (secp521r1 == 0)
+                       secp521r1 = g_quark_from_static_string (GKD_SSH_OID_ANSI_SECP521R1);
+               return 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)
+       const gchar *str;
+
+       g_return_val_if_fail (oid, NULL);
+
+       str = g_quark_to_string (oid);
+       g_return_val_if_fail (str, NULL);
+
+       if (g_str_equal (str, GKD_SSH_OID_ANSI_SECP256R1))
+               return "nistp256";
+       else if (g_str_equal (str, GKD_SSH_OID_ANSI_SECP384R1))
+               return "nistp384";
+       else if (g_str_equal (str, GKD_SSH_OID_ANSI_SECP521R1))
+               return "nistp521";
+       return NULL;
+}
+
+const gchar*
+gkd_ssh_agent_proto_oid_to_keytype (GQuark oid)
+{
+       const gchar *str;
+
+       g_return_val_if_fail (oid, NULL);
+
+       str = g_quark_to_string (oid);
+       g_return_val_if_fail (str, NULL);
+
+       if (g_str_equal (str, GKD_SSH_OID_ANSI_SECP256R1))
+               return "ecdsa-sha2-nistp256";
+       else if (g_str_equal (str, GKD_SSH_OID_ANSI_SECP384R1))
+               return "ecdsa-sha2-nistp384";
+       else if (g_str_equal (str, GKD_SSH_OID_ANSI_SECP521R1))
+               return "ecdsa-sha2-nistp521";
+       g_return_val_if_reached (NULL);
+}
+
+const gchar*
+gkd_ssh_agent_proto_algo_to_keytype (gulong algo, GQuark oid)
+{
+       if (algo == CKK_RSA) {
+               g_return_val_if_fail (oid == 0, NULL);
                return "ssh-rsa";
-       else if (algo == CKK_DSA)
+       } else if (algo == CKK_DSA) {
+               g_return_val_if_fail (oid == 0, NULL);
                return "ssh-dss";
-       return NULL;
+       } else if (algo == CKK_EC) {
+               g_return_val_if_fail (oid != 0, NULL);
+               return gkd_ssh_agent_proto_oid_to_keytype (oid);
+       }
+       g_return_val_if_reached (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
@@ -104,6 +203,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 +267,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 +341,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 +536,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 +635,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 +654,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 +725,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 +818,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]