[gnome-keyring/wip/dueno/ecdsa-support: 1/2] Support ECDSA in ssh-agent interface
- From: Daiki Ueno <dueno src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-keyring/wip/dueno/ecdsa-support: 1/2] Support ECDSA in ssh-agent interface
- Date: Mon, 16 Oct 2017 05:52:32 +0000 (UTC)
commit 6c8e87ced1f9db5e960c5108a5b669a4bbb50171
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 | 51 ++++-
daemon/ssh-agent/gkd-ssh-agent-private.h | 43 ++++-
daemon/ssh-agent/gkd-ssh-agent-proto.c | 348 +++++++++++++++++++++++++++++-
4 files changed, 431 insertions(+), 12 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..e8a2ac5 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,30 @@ 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 */
+ const gchar *str = g_quark_to_string (oid);
+ if (str == NULL) {
+ egg_buffer_add_byte (call->resp, GKD_SSH_RES_FAILURE);
+ return FALSE;
+ }
+ /* 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 {
+ egg_buffer_add_byte (call->resp, GKD_SSH_RES_FAILURE);
+ return FALSE;
+ }
+ } 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 +1111,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 +1124,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, ¶ms_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]