aboutsummaryrefslogtreecommitdiffstats
path: root/agent/command-ssh.c
diff options
context:
space:
mode:
authorWerner Koch <[email protected]>2014-03-22 20:12:46 +0000
committerWerner Koch <[email protected]>2014-03-22 20:12:46 +0000
commit072432883ededa15bf35f80102e0572746ba4af1 (patch)
treeaf6189516aba961d7343bc92f6256c0fef3344c4 /agent/command-ssh.c
parentagent: Cleanups to prepare implementation of Ed25519. (diff)
downloadgnupg-072432883ededa15bf35f80102e0572746ba4af1.tar.gz
gnupg-072432883ededa15bf35f80102e0572746ba4af1.zip
agent: Support the Ed25519 signature algorithm for ssh.
* agent/command-ssh.c (SPEC_FLAG_IS_EdDSA): New. (ssh_key_types): Add entry for ssh-ed25519. (ssh_identifier_from_curve_name): Move to the top. (stream_read_skip): New. (stream_read_blob): New. (ssh_signature_encoder_rsa): Replace MPIS array by an s-exp and move the s-exp parsing to here. (ssh_signature_encoder_dsa): Ditto. (ssh_signature_encoder_ecdsa): Ditto. (ssh_signature_encoder_eddsa): New. (sexp_key_construct): Rewrite. (ssh_key_extract): Rename to ... (ssh_key_to_blob): .. this and rewrite most of it. (ssh_receive_key): Add case for EdDSA. (ssh_convert_key_to_blob, key_secret_to_public): Remove. (ssh_send_key_public): Rewrite. (ssh_handler_request_identities): Simplify. (data_sign): Add rename args. Add new args HASH and HASHLEN. Make use of es_fopenmen and es_fclose_snatch. Remove parsing into MPIs which is now doe in the sgnature encoder functions. (ssh_handler_sign_request): Take care of Ed25519. (ssh_key_extract_comment): Rewrite using gcry_sexp_nth_string. -- To make the code easier readable most of the Ed25591 work has been done using a new explicit code path. Warning: Libgcrypt 1.6.1 uses a non optimized implementation for Ed25519 and timing attacks might be possible. While working on the code I realized that it could need more rework; it is at some places quite baroque and more complicated than needed. Given that we require Libgcrypt 1.6 anyway, we should make more use of modern Libgcrypt functions.
Diffstat (limited to '')
-rw-r--r--agent/command-ssh.c1282
1 files changed, 761 insertions, 521 deletions
diff --git a/agent/command-ssh.c b/agent/command-ssh.c
index fb3b29a7f..be2ab3b2c 100644
--- a/agent/command-ssh.c
+++ b/agent/command-ssh.c
@@ -1,6 +1,6 @@
/* command-ssh.c - gpg-agent's ssh-agent emulation layer
* Copyright (C) 2004, 2005, 2006, 2009, 2012 Free Software Foundation, Inc.
- * Copyright (C) 2013 Werner Koch
+ * Copyright (C) 2013, 2014 Werner Koch
*
* This file is part of GnuPG.
*
@@ -74,6 +74,7 @@
#define SSH_DSA_SIGNATURE_ELEMS 2
#define SPEC_FLAG_USE_PKCS1V2 (1 << 0)
#define SPEC_FLAG_IS_ECDSA (1 << 1)
+#define SPEC_FLAG_IS_EdDSA (1 << 2) /*(lowercase 'd' on purpose.)*/
/* The name of the control file. */
#define SSH_CONTROL_FILE_NAME "sshcontrol"
@@ -138,7 +139,7 @@ typedef gpg_error_t (*ssh_key_modifier_t) (const char *elems,
functions are necessary. */
typedef gpg_error_t (*ssh_signature_encoder_t) (ssh_key_type_spec_t *spec,
estream_t signature_blob,
- gcry_mpi_t *mpis);
+ gcry_sexp_t sig);
/* Type, which is used for boundling all the algorithm specific
information together in a single object. */
@@ -229,13 +230,17 @@ static gpg_error_t ssh_handler_unlock (ctrl_t ctrl,
static gpg_error_t ssh_key_modifier_rsa (const char *elems, gcry_mpi_t *mpis);
static gpg_error_t ssh_signature_encoder_rsa (ssh_key_type_spec_t *spec,
estream_t signature_blob,
- gcry_mpi_t *mpis);
+ gcry_sexp_t signature);
static gpg_error_t ssh_signature_encoder_dsa (ssh_key_type_spec_t *spec,
estream_t signature_blob,
- gcry_mpi_t *mpis);
+ gcry_sexp_t signature);
static gpg_error_t ssh_signature_encoder_ecdsa (ssh_key_type_spec_t *spec,
estream_t signature_blob,
- gcry_mpi_t *mpis);
+ gcry_sexp_t signature);
+static gpg_error_t ssh_signature_encoder_eddsa (ssh_key_type_spec_t *spec,
+ estream_t signature_blob,
+ gcry_sexp_t signature);
+static gpg_error_t ssh_key_extract_comment (gcry_sexp_t key, char **comment);
@@ -266,9 +271,14 @@ static ssh_request_spec_t request_specs[] =
static ssh_key_type_spec_t ssh_key_types[] =
{
{
+ "ssh-ed25519", "ecc", "qd", "q", "rs", "qd",
+ NULL, ssh_signature_encoder_eddsa,
+ "Ed25519", 0, SPEC_FLAG_IS_EdDSA
+ },
+ {
"ssh-rsa", "rsa", "nedupq", "en", "s", "nedpqu",
ssh_key_modifier_rsa, ssh_signature_encoder_rsa,
- NULL, 0, SPEC_FLAG_USE_PKCS1V2
+ NULL, 0, SPEC_FLAG_USE_PKCS1V2
},
{
"ssh-dss", "dsa", "pqgyx", "pqgy", "rs", "pqgyx",
@@ -290,7 +300,6 @@ static ssh_key_type_spec_t ssh_key_types[] =
NULL, ssh_signature_encoder_ecdsa,
"nistp521", GCRY_MD_SHA512, SPEC_FLAG_IS_ECDSA
}
-
};
@@ -336,7 +345,20 @@ make_cstring (const char *data, size_t data_n)
return s;
}
+/* Lookup the ssh-identifier for the ECC curve CURVE_NAME. Returns
+ NULL if not found. */
+static const char *
+ssh_identifier_from_curve_name (const char *curve_name)
+{
+ int i;
+
+ for (i = 0; i < DIM (ssh_key_types); i++)
+ if (ssh_key_types[i].curve_name
+ && !strcmp (ssh_key_types[i].curve_name, curve_name))
+ return ssh_key_types[i].ssh_identifier;
+ return NULL;
+}
/*
@@ -459,6 +481,34 @@ stream_read_data (estream_t stream, unsigned char *buffer, size_t size)
return err;
}
+/* Skip over SIZE bytes from STREAM. */
+static gpg_error_t
+stream_read_skip (estream_t stream, size_t size)
+{
+ char buffer[128];
+ size_t bytes_to_read, bytes_read;
+ int ret;
+
+ do
+ {
+ bytes_to_read = size;
+ if (bytes_to_read > sizeof buffer)
+ bytes_to_read = sizeof buffer;
+
+ ret = es_read (stream, buffer, bytes_to_read, &bytes_read);
+ if (ret)
+ return gpg_error_from_syserror ();
+ else if (bytes_read != bytes_to_read)
+ return gpg_error (GPG_ERR_EOF);
+ else
+ size -= bytes_to_read;
+ }
+ while (size);
+
+ return 0;
+}
+
+
/* Write SIZE bytes from BUFFER to STREAM. */
static gpg_error_t
stream_write_data (estream_t stream, const unsigned char *buffer, size_t size)
@@ -525,21 +575,68 @@ stream_read_string (estream_t stream, unsigned int secure,
return err;
}
-/* Read a C-string from STREAM, store copy in STRING. */
+
+/* Read a binary string from STREAM and store it as an opaque MPI at
+ R_MPI. Depending on SECURE use secure memory. If the string is
+ too large for key material return an error. */
static gpg_error_t
-stream_read_cstring (estream_t stream, char **string)
+stream_read_blob (estream_t stream, unsigned int secure, gcry_mpi_t *r_mpi)
{
- unsigned char *buffer;
gpg_error_t err;
+ unsigned char *buffer = NULL;
+ u32 length = 0;
- err = stream_read_string (stream, 0, &buffer, NULL);
+ *r_mpi = NULL;
+
+ /* Read string length. */
+ err = stream_read_uint32 (stream, &length);
if (err)
- goto out;
+ goto leave;
+
+ /* To avoid excessive use of secure memory we check that an MPI is
+ not too large. */
+ if (length > (4096/8) + 8)
+ {
+ log_error (_("ssh keys greater than %d bits are not supported\n"), 4096);
+ err = GPG_ERR_TOO_LARGE;
+ goto leave;
+ }
- *string = (char *) buffer;
+ /* Allocate space. */
+ if (secure)
+ buffer = xtrymalloc_secure (length? length:1);
+ else
+ buffer = xtrymalloc (length?length:1);
+ if (!buffer)
+ {
+ err = gpg_error_from_syserror ();
+ goto leave;
+ }
- out:
+ /* Read data. */
+ err = stream_read_data (stream, buffer, length);
+ if (err)
+ goto leave;
+
+ *r_mpi = gcry_mpi_set_opaque (NULL, buffer, 8*length);
+ buffer = NULL;
+
+ leave:
+ xfree (buffer);
+ return err;
+}
+
+
+/* Read a C-string from STREAM, store copy in STRING. */
+static gpg_error_t
+stream_read_cstring (estream_t stream, char **string)
+{
+ gpg_error_t err;
+ unsigned char *buffer;
+ err = stream_read_string (stream, 0, &buffer, NULL);
+ if (!err)
+ *string = (char *)buffer;
return err;
}
@@ -635,6 +732,7 @@ stream_write_mpi (estream_t stream, gcry_mpi_t mpint)
return err;
}
+
/* Copy data from SRC to DST until EOF is reached. */
static gpg_error_t
stream_copy (estream_t dst, estream_t src)
@@ -753,7 +851,7 @@ open_control_file (ssh_control_file_t *r_cf, int append)
err = gpg_error_from_syserror ();
goto leave;
}
- /* FIXME: With "a+" we are not able to check whether this will will
+ /* FIXME: With "a+" we are not able to check whether this will
be created and thus the blurb needs to be written first. */
cf->fp = fopen (cf->fname, append? "a+":"r");
if (!cf->fp && errno == ENOENT)
@@ -1245,15 +1343,63 @@ ssh_key_modifier_rsa (const char *elems, gcry_mpi_t *mpis)
/* Signature encoder function for RSA. */
static gpg_error_t
ssh_signature_encoder_rsa (ssh_key_type_spec_t *spec,
- estream_t signature_blob, gcry_mpi_t *mpis)
+ estream_t signature_blob,
+ gcry_sexp_t s_signature)
{
+ gpg_error_t err = 0;
+ gcry_sexp_t valuelist = NULL;
+ gcry_sexp_t sublist = NULL;
+ gcry_mpi_t sig_value = NULL;
+ gcry_mpi_t *mpis = NULL;
+ const char *elems;
+ size_t elems_n;
+ int i;
+
unsigned char *data;
size_t data_n;
- gpg_error_t err;
gcry_mpi_t s;
- (void)spec;
+ valuelist = gcry_sexp_nth (s_signature, 1);
+ if (!valuelist)
+ {
+ err = gpg_error (GPG_ERR_INV_SEXP);
+ goto out;
+ }
+
+ elems = spec->elems_signature;
+ elems_n = strlen (elems);
+
+ mpis = xtrycalloc (elems_n + 1, sizeof *mpis);
+ if (!mpis)
+ {
+ err = gpg_error_from_syserror ();
+ goto out;
+ }
+
+ for (i = 0; i < elems_n; i++)
+ {
+ sublist = gcry_sexp_find_token (valuelist, spec->elems_signature + i, 1);
+ if (!sublist)
+ {
+ err = gpg_error (GPG_ERR_INV_SEXP);
+ break;
+ }
+
+ sig_value = gcry_sexp_nth_mpi (sublist, 1, GCRYMPI_FMT_USG);
+ if (!sig_value)
+ {
+ err = gpg_error (GPG_ERR_INTERNAL); /* FIXME? */
+ break;
+ }
+ gcry_sexp_release (sublist);
+ sublist = NULL;
+
+ mpis[i] = sig_value;
+ }
+ if (err)
+ goto out;
+ /* RSA specific */
s = mpis[0];
err = gcry_mpi_aprint (GCRYMPI_FMT_USG, &data, &data_n, s);
@@ -1264,7 +1410,9 @@ ssh_signature_encoder_rsa (ssh_key_type_spec_t *spec,
xfree (data);
out:
-
+ gcry_sexp_release (valuelist);
+ gcry_sexp_release (sublist);
+ mpint_list_free (mpis);
return err;
}
@@ -1272,17 +1420,63 @@ ssh_signature_encoder_rsa (ssh_key_type_spec_t *spec,
/* Signature encoder function for DSA. */
static gpg_error_t
ssh_signature_encoder_dsa (ssh_key_type_spec_t *spec,
- estream_t signature_blob, gcry_mpi_t *mpis)
+ estream_t signature_blob,
+ gcry_sexp_t s_signature)
{
+ gpg_error_t err = 0;
+ gcry_sexp_t valuelist = NULL;
+ gcry_sexp_t sublist = NULL;
+ gcry_mpi_t sig_value = NULL;
+ gcry_mpi_t *mpis = NULL;
+ const char *elems;
+ size_t elems_n;
+ int i;
+
unsigned char buffer[SSH_DSA_SIGNATURE_PADDING * SSH_DSA_SIGNATURE_ELEMS];
- unsigned char *data;
+ unsigned char *data = NULL;
size_t data_n;
- gpg_error_t err;
- int i;
- (void)spec;
+ valuelist = gcry_sexp_nth (s_signature, 1);
+ if (!valuelist)
+ {
+ err = gpg_error (GPG_ERR_INV_SEXP);
+ goto out;
+ }
+
+ elems = spec->elems_signature;
+ elems_n = strlen (elems);
+
+ mpis = xtrycalloc (elems_n + 1, sizeof *mpis);
+ if (!mpis)
+ {
+ err = gpg_error_from_syserror ();
+ goto out;
+ }
+
+ for (i = 0; i < elems_n; i++)
+ {
+ sublist = gcry_sexp_find_token (valuelist, spec->elems_signature + i, 1);
+ if (!sublist)
+ {
+ err = gpg_error (GPG_ERR_INV_SEXP);
+ break;
+ }
+
+ sig_value = gcry_sexp_nth_mpi (sublist, 1, GCRYMPI_FMT_USG);
+ if (!sig_value)
+ {
+ err = gpg_error (GPG_ERR_INTERNAL); /* FIXME? */
+ break;
+ }
+ gcry_sexp_release (sublist);
+ sublist = NULL;
+
+ mpis[i] = sig_value;
+ }
+ if (err)
+ goto out;
- data = NULL;
+ /* DSA specific code. */
/* FIXME: Why this complicated code? Why collecting boths mpis in a
buffer instead of writing them out one after the other? */
@@ -1312,9 +1506,10 @@ ssh_signature_encoder_dsa (ssh_key_type_spec_t *spec,
err = stream_write_string (signature_blob, buffer, sizeof (buffer));
out:
-
xfree (data);
-
+ gcry_sexp_release (valuelist);
+ gcry_sexp_release (sublist);
+ mpint_list_free (mpis);
return err;
}
@@ -1322,15 +1517,62 @@ ssh_signature_encoder_dsa (ssh_key_type_spec_t *spec,
/* Signature encoder function for ECDSA. */
static gpg_error_t
ssh_signature_encoder_ecdsa (ssh_key_type_spec_t *spec,
- estream_t stream, gcry_mpi_t *mpis)
+ estream_t stream, gcry_sexp_t s_signature)
{
+ gpg_error_t err = 0;
+ gcry_sexp_t valuelist = NULL;
+ gcry_sexp_t sublist = NULL;
+ gcry_mpi_t sig_value = NULL;
+ gcry_mpi_t *mpis = NULL;
+ const char *elems;
+ size_t elems_n;
+ int i;
+
unsigned char *data[2] = {NULL, NULL};
size_t data_n[2];
size_t innerlen;
- gpg_error_t err;
- int i;
- (void)spec;
+ valuelist = gcry_sexp_nth (s_signature, 1);
+ if (!valuelist)
+ {
+ err = gpg_error (GPG_ERR_INV_SEXP);
+ goto out;
+ }
+
+ elems = spec->elems_signature;
+ elems_n = strlen (elems);
+
+ mpis = xtrycalloc (elems_n + 1, sizeof *mpis);
+ if (!mpis)
+ {
+ err = gpg_error_from_syserror ();
+ goto out;
+ }
+
+ for (i = 0; i < elems_n; i++)
+ {
+ sublist = gcry_sexp_find_token (valuelist, spec->elems_signature + i, 1);
+ if (!sublist)
+ {
+ err = gpg_error (GPG_ERR_INV_SEXP);
+ break;
+ }
+
+ sig_value = gcry_sexp_nth_mpi (sublist, 1, GCRYMPI_FMT_USG);
+ if (!sig_value)
+ {
+ err = gpg_error (GPG_ERR_INTERNAL); /* FIXME? */
+ break;
+ }
+ gcry_sexp_release (sublist);
+ sublist = NULL;
+
+ mpis[i] = sig_value;
+ }
+ if (err)
+ goto out;
+
+ /* ECDSA specific */
innerlen = 0;
for (i = 0; i < DIM(data); i++)
@@ -1355,6 +1597,101 @@ ssh_signature_encoder_ecdsa (ssh_key_type_spec_t *spec,
out:
for (i = 0; i < DIM(data); i++)
xfree (data[i]);
+ gcry_sexp_release (valuelist);
+ gcry_sexp_release (sublist);
+ mpint_list_free (mpis);
+ return err;
+}
+
+
+/* Signature encoder function for EdDSA. */
+static gpg_error_t
+ssh_signature_encoder_eddsa (ssh_key_type_spec_t *spec,
+ estream_t stream, gcry_sexp_t s_signature)
+{
+ gpg_error_t err = 0;
+ gcry_sexp_t valuelist = NULL;
+ gcry_sexp_t sublist = NULL;
+ gcry_mpi_t sig_value = NULL;
+ gcry_mpi_t *mpis = NULL;
+ const char *elems;
+ size_t elems_n;
+ int i;
+
+ unsigned char *data[2] = {NULL, NULL};
+ size_t data_n[2];
+ size_t totallen;
+
+ valuelist = gcry_sexp_nth (s_signature, 1);
+ if (!valuelist)
+ {
+ err = gpg_error (GPG_ERR_INV_SEXP);
+ goto out;
+ }
+
+ elems = spec->elems_signature;
+ elems_n = strlen (elems);
+
+ mpis = xtrycalloc (elems_n + 1, sizeof *mpis);
+ if (!mpis)
+ {
+ err = gpg_error_from_syserror ();
+ goto out;
+ }
+
+ for (i = 0; i < elems_n; i++)
+ {
+ sublist = gcry_sexp_find_token (valuelist, spec->elems_signature + i, 1);
+ if (!sublist)
+ {
+ err = gpg_error (GPG_ERR_INV_SEXP);
+ break;
+ }
+
+ sig_value = gcry_sexp_nth_mpi (sublist, 1, GCRYMPI_FMT_USG);
+ if (!sig_value)
+ {
+ err = gpg_error (GPG_ERR_INTERNAL); /* FIXME? */
+ break;
+ }
+ gcry_sexp_release (sublist);
+ sublist = NULL;
+
+ mpis[i] = sig_value;
+ }
+ if (err)
+ goto out;
+
+ /* EdDSA specific. Actually TOTALLEN will always be 64. */
+
+ totallen = 0;
+ for (i = 0; i < DIM(data); i++)
+ {
+ err = gcry_mpi_aprint (GCRYMPI_FMT_USG, &data[i], &data_n[i], mpis[i]);
+ if (err)
+ goto out;
+ totallen += data_n[i];
+ }
+
+ gcry_log_debug (" out: len=%zu\n", totallen);
+ err = stream_write_uint32 (stream, totallen);
+ if (err)
+ goto out;
+
+ for (i = 0; i < DIM(data); i++)
+ {
+ gcry_log_debughex (" out", data[i], data_n[i]);
+ err = stream_write_data (stream, data[i], data_n[i]);
+ if (err)
+ goto out;
+ }
+
+ out:
+ for (i = 0; i < DIM(data); i++)
+ xfree (data[i]);
+ gcry_sexp_release (valuelist);
+ gcry_sexp_release (sublist);
+ mpint_list_free (mpis);
return err;
}
@@ -1373,85 +1710,117 @@ sexp_key_construct (gcry_sexp_t *r_sexp,
const char *curve_name, gcry_mpi_t *mpis,
const char *comment)
{
- const char *key_identifier[] = { "public-key", "private-key" };
gpg_error_t err;
gcry_sexp_t sexp_new = NULL;
void *formatbuf = NULL;
void **arg_list = NULL;
- int arg_idx;
- estream_t format;
- const char *elems;
- size_t elems_n;
- unsigned int i, j;
-
- if (secret)
- elems = key_spec.elems_sexp_order;
- else
- elems = key_spec.elems_key_public;
- elems_n = strlen (elems);
+ estream_t format = NULL;
- format = es_fopenmem (0, "a+b");
- if (!format)
- {
- err = gpg_error_from_syserror ();
- goto out;
- }
- /* Key identifier, algorithm identifier, mpis, comment, and a NULL
- as a safeguard. */
- arg_list = xtrymalloc (sizeof (*arg_list) * (2 + 1 + elems_n + 1 + 1));
- if (!arg_list)
+ if ((key_spec.flags & SPEC_FLAG_IS_EdDSA))
{
- err = gpg_error_from_syserror ();
- goto out;
+ /* It is much easier and more readable to use a separate code
+ path for EdDSA. */
+ if (!curve_name)
+ err = gpg_error (GPG_ERR_INV_CURVE);
+ else if (!mpis[0] || !gcry_mpi_get_flag (mpis[0], GCRYMPI_FLAG_OPAQUE))
+ err = gpg_error (GPG_ERR_BAD_PUBKEY);
+ else if (secret
+ && (!mpis[1]
+ || !gcry_mpi_get_flag (mpis[1], GCRYMPI_FLAG_OPAQUE)))
+ err = gpg_error (GPG_ERR_BAD_SECKEY);
+ else if (secret)
+ err = gcry_sexp_build (&sexp_new, NULL,
+ "(private-key(ecc(curve %s)"
+ "(flags eddsa)(q %m)(d %m))"
+ "(comment%s))",
+ curve_name,
+ mpis[0], mpis[1],
+ comment? comment:"");
+ else
+ err = gcry_sexp_build (&sexp_new, NULL,
+ "(public-key(ecc(curve %s)"
+ "(flags eddsa)(q %m))"
+ "(comment%s))",
+ curve_name,
+ mpis[0],
+ comment? comment:"");
}
- arg_idx = 0;
-
- es_fputs ("(%s(%s", format);
- arg_list[arg_idx++] = &key_identifier[secret];
- arg_list[arg_idx++] = &key_spec.identifier;
- if (curve_name)
+ else
{
- es_fputs ("(curve%s)", format);
- arg_list[arg_idx++] = &curve_name;
- }
+ const char *key_identifier[] = { "public-key", "private-key" };
+ int arg_idx;
+ const char *elems;
+ size_t elems_n;
+ unsigned int i, j;
- for (i = 0; i < elems_n; i++)
- {
- es_fprintf (format, "(%c%%m)", elems[i]);
if (secret)
- {
- for (j = 0; j < elems_n; j++)
- if (key_spec.elems_key_secret[j] == elems[i])
- break;
- }
+ elems = key_spec.elems_sexp_order;
else
- j = i;
- arg_list[arg_idx++] = &mpis[j];
- }
- es_fputs (")(comment%s))", format);
- arg_list[arg_idx++] = &comment;
- arg_list[arg_idx] = NULL;
+ elems = key_spec.elems_key_public;
+ elems_n = strlen (elems);
- es_putc (0, format);
- if (es_ferror (format))
- {
- err = gpg_error_from_syserror ();
- goto out;
- }
- if (es_fclose_snatch (format, &formatbuf, NULL))
- {
- err = gpg_error_from_syserror ();
- goto out;
- }
- format = NULL;
+ format = es_fopenmem (0, "a+b");
+ if (!format)
+ {
+ err = gpg_error_from_syserror ();
+ goto out;
+ }
- err = gcry_sexp_build_array (&sexp_new, NULL, formatbuf, arg_list);
- if (err)
- goto out;
+ /* Key identifier, algorithm identifier, mpis, comment, and a NULL
+ as a safeguard. */
+ arg_list = xtrymalloc (sizeof (*arg_list) * (2 + 1 + elems_n + 1 + 1));
+ if (!arg_list)
+ {
+ err = gpg_error_from_syserror ();
+ goto out;
+ }
+ arg_idx = 0;
- *r_sexp = sexp_new;
- err = 0;
+ es_fputs ("(%s(%s", format);
+ arg_list[arg_idx++] = &key_identifier[secret];
+ arg_list[arg_idx++] = &key_spec.identifier;
+ if (curve_name)
+ {
+ es_fputs ("(curve%s)", format);
+ arg_list[arg_idx++] = &curve_name;
+ }
+
+ for (i = 0; i < elems_n; i++)
+ {
+ es_fprintf (format, "(%c%%m)", elems[i]);
+ if (secret)
+ {
+ for (j = 0; j < elems_n; j++)
+ if (key_spec.elems_key_secret[j] == elems[i])
+ break;
+ }
+ else
+ j = i;
+ arg_list[arg_idx++] = &mpis[j];
+ }
+ es_fputs (")(comment%s))", format);
+ arg_list[arg_idx++] = &comment;
+ arg_list[arg_idx] = NULL;
+
+ es_putc (0, format);
+ if (es_ferror (format))
+ {
+ err = gpg_error_from_syserror ();
+ goto out;
+ }
+ if (es_fclose_snatch (format, &formatbuf, NULL))
+ {
+ err = gpg_error_from_syserror ();
+ goto out;
+ }
+ format = NULL;
+
+ err = gcry_sexp_build_array (&sexp_new, NULL, formatbuf, arg_list);
+ }
+
+ if (!err)
+ *r_sexp = sexp_new;
out:
es_fclose (format);
@@ -1462,96 +1831,66 @@ sexp_key_construct (gcry_sexp_t *r_sexp,
}
-/* This functions breaks up the key contained in the S-Expression SEXP
- according to KEY_SPEC. The MPIs are bundled in a newly create
- list, which is to be stored in MPIS; a newly allocated string
- holding the curve name may be stored at RCURVE, and a comment will
- be stored at COMMENT; SECRET will be filled with a boolean flag
- specifying what kind of key it is. Returns an error code. */
+/* This function extracts the key from the s-expression SEXP according
+ to KEY_SPEC and stores it in ssh format at (R_BLOB, R_BLOBLEN). If
+ WITH_SECRET is true, the secret key parts are also extracted if
+ possible. Returns 0 on success or an error code. Note that data
+ stored at R_BLOB must be freed using es_free! */
static gpg_error_t
-sexp_key_extract (gcry_sexp_t sexp,
- ssh_key_type_spec_t key_spec, int *secret,
- gcry_mpi_t **mpis, char **r_curve, char **comment)
+ssh_key_to_blob (gcry_sexp_t sexp, int with_secret,
+ ssh_key_type_spec_t key_spec,
+ void **r_blob, size_t *r_blob_size)
{
gpg_error_t err = 0;
gcry_sexp_t value_list = NULL;
gcry_sexp_t value_pair = NULL;
- gcry_sexp_t comment_list = NULL;
- unsigned int i;
- char *comment_new = NULL;
- const char *data;
- size_t data_n;
- int is_secret;
- size_t elems_n;
- const char *elems;
- gcry_mpi_t *mpis_new = NULL;
- gcry_mpi_t mpi;
char *curve_name = NULL;
+ estream_t stream = NULL;
+ void *blob = NULL;
+ size_t blob_size;
+ const char *elems, *p_elems;
+ const char *data;
+ size_t datalen;
- data = gcry_sexp_nth_data (sexp, 0, &data_n);
- if (! data)
+ *r_blob = NULL;
+ *r_blob_size = 0;
+
+ stream = es_fopenmem (0, "r+b");
+ if (!stream)
{
- err = gpg_error (GPG_ERR_INV_SEXP);
+ err = gpg_error_from_syserror ();
goto out;
}
- if ((data_n == 10 && !strncmp (data, "public-key", 10))
- || (data_n == 21 && !strncmp (data, "protected-private-key", 21))
- || (data_n == 20 && !strncmp (data, "shadowed-private-key", 20)))
- {
- is_secret = 0;
- elems = key_spec.elems_key_public;
- }
- else if (data_n == 11 && !strncmp (data, "private-key", 11))
- {
- is_secret = 1;
- elems = key_spec.elems_key_secret;
- }
- else
+ /* Get the type of the key extpression. */
+ data = gcry_sexp_nth_data (sexp, 0, &datalen);
+ if (!data)
{
err = gpg_error (GPG_ERR_INV_SEXP);
goto out;
}
- elems_n = strlen (elems);
- mpis_new = xtrycalloc (elems_n + 1, sizeof *mpis_new );
- if (!mpis_new)
+ if ((datalen == 10 && !strncmp (data, "public-key", 10))
+ || (datalen == 21 && !strncmp (data, "protected-private-key", 21))
+ || (datalen == 20 && !strncmp (data, "shadowed-private-key", 20)))
+ elems = key_spec.elems_key_public;
+ else if (datalen == 11 && !strncmp (data, "private-key", 11))
+ elems = with_secret? key_spec.elems_key_secret : key_spec.elems_key_public;
+ else
{
- err = gpg_error_from_syserror ();
+ err = gpg_error (GPG_ERR_INV_SEXP);
goto out;
}
+ /* Get the algorithm identifier. */
value_list = gcry_sexp_find_token (sexp, key_spec.identifier, 0);
- if (! value_list)
+ if (!value_list)
{
err = gpg_error (GPG_ERR_INV_SEXP);
goto out;
}
- for (i = 0; i < elems_n; i++)
- {
- value_pair = gcry_sexp_find_token (value_list, elems + i, 1);
- if (! value_pair)
- {
- err = gpg_error (GPG_ERR_INV_SEXP);
- break;
- }
-
- /* Note that we need to use STD format; i.e. prepend a 0x00 to
- indicate a positive number if the high bit is set. */
- mpi = gcry_sexp_nth_mpi (value_pair, 1, GCRYMPI_FMT_STD);
- if (! mpi)
- {
- err = gpg_error (GPG_ERR_INV_SEXP);
- break;
- }
- mpis_new[i] = mpi;
- gcry_sexp_release (value_pair);
- value_pair = NULL;
- }
- if (err)
- goto out;
-
+ /* Write the ssh algorithm identifier. */
if ((key_spec.flags & SPEC_FLAG_IS_ECDSA))
{
/* Parse the "curve" parameter. We currently expect the curve
@@ -1559,7 +1898,9 @@ sexp_key_extract (gcry_sexp_t sexp,
easily be changed but then we need to find the curve name
from the parameters using gcry_pk_get_curve. */
const char *mapped;
+ const char *sshname;
+ gcry_sexp_release (value_pair);
value_pair = gcry_sexp_find_token (value_list, "curve", 5);
if (!value_pair)
{
@@ -1593,48 +1934,87 @@ sexp_key_extract (gcry_sexp_t sexp,
goto out;
}
}
- gcry_sexp_release (value_pair);
- value_pair = NULL;
- }
- /* We do not require a comment sublist to be present here. */
- data = NULL;
- data_n = 0;
+ sshname = ssh_identifier_from_curve_name (curve_name);
+ if (!sshname)
+ {
+ err = gpg_error (GPG_ERR_UNKNOWN_CURVE);
+ goto out;
+ }
+ err = stream_write_cstring (stream, sshname);
+ if (err)
+ goto out;
+ err = stream_write_cstring (stream, curve_name);
+ if (err)
+ goto out;
+ }
+ else
+ {
+ /* Note: This is also used for EdDSA. */
+ err = stream_write_cstring (stream, key_spec.ssh_identifier);
+ if (err)
+ goto out;
+ }
- comment_list = gcry_sexp_find_token (sexp, "comment", 0);
- if (comment_list)
- data = gcry_sexp_nth_data (comment_list, 1, &data_n);
- if (! data)
+ /* Write the parameters. */
+ for (p_elems = elems; *p_elems; p_elems++)
{
- data = "(none)";
- data_n = 6;
+ gcry_sexp_release (value_pair);
+ value_pair = gcry_sexp_find_token (value_list, p_elems, 1);
+ if (!value_pair)
+ {
+ err = gpg_error (GPG_ERR_INV_SEXP);
+ goto out;
+ }
+ if ((key_spec.flags & SPEC_FLAG_IS_EdDSA))
+ {
+
+ data = gcry_sexp_nth_data (value_pair, 1, &datalen);
+ if (!data)
+ {
+ err = gpg_error (GPG_ERR_INV_SEXP);
+ goto out;
+ }
+ err = stream_write_string (stream, data, datalen);
+ if (err)
+ goto out;
+ }
+ else
+ {
+ gcry_mpi_t mpi;
+
+ /* Note that we need to use STD format; i.e. prepend a 0x00
+ to indicate a positive number if the high bit is set. */
+ mpi = gcry_sexp_nth_mpi (value_pair, 1, GCRYMPI_FMT_STD);
+ if (!mpi)
+ {
+ err = gpg_error (GPG_ERR_INV_SEXP);
+ goto out;
+ }
+ err = stream_write_mpi (stream, mpi);
+ gcry_mpi_release (mpi);
+ if (err)
+ goto out;
+ }
}
- comment_new = make_cstring (data, data_n);
- if (! comment_new)
+ if (es_fclose_snatch (stream, &blob, &blob_size))
{
err = gpg_error_from_syserror ();
goto out;
}
+ stream = NULL;
- if (secret)
- *secret = is_secret;
- *mpis = mpis_new;
- *comment = comment_new;
- *r_curve = curve_name;
+ *r_blob = blob;
+ blob = NULL;
+ *r_blob_size = blob_size;
out:
-
gcry_sexp_release (value_list);
gcry_sexp_release (value_pair);
- gcry_sexp_release (comment_list);
-
- if (err)
- {
- xfree (curve_name);
- xfree (comment_new);
- mpint_list_free (mpis_new);
- }
+ xfree (curve_name);
+ es_fclose (stream);
+ es_free (blob);
return err;
}
@@ -1702,6 +2082,11 @@ ssh_key_type_lookup (const char *ssh_name, const char *name,
gpg_error_t err;
unsigned int i;
+ /* FIXME: Although this sees to work, it not be correct if the
+ lookup is done via name which might be "ecc" but actually it need
+ to check the flags to see whether it is eddsa or ecdsa. Maybe
+ the entire parameter controlled logic is too complicated and we
+ would do better by just switching on the ssh_name. */
for (i = 0; i < DIM (ssh_key_types); i++)
if ((ssh_name && (! strcmp (ssh_name, ssh_key_types[i].ssh_identifier)))
|| (name && (! strcmp (name, ssh_key_types[i].identifier))))
@@ -1719,23 +2104,6 @@ ssh_key_type_lookup (const char *ssh_name, const char *name,
}
-/* Lookup the ssh-identifier for the ECC curve CURVE_NAME. Returns
- NULL if not found. */
-static const char *
-ssh_identifier_from_curve_name (const char *curve_name)
-{
- int i;
-
- for (i = 0; i < DIM (ssh_key_types); i++)
- if (ssh_key_types[i].curve_name
- && !strcmp (ssh_key_types[i].curve_name, curve_name))
- return ssh_key_types[i].ssh_identifier;
-
- return NULL;
-}
-
-
-
/* Receive a key from STREAM, according to the key specification given
as KEY_SPEC. Depending on SECRET, receive a secret or a public
key. If READ_COMMENT is true, receive a comment string as well.
@@ -1763,7 +2131,65 @@ ssh_receive_key (estream_t stream, gcry_sexp_t *key_new, int secret,
if (err)
goto out;
- if ((spec.flags & SPEC_FLAG_IS_ECDSA))
+ if ((spec.flags & SPEC_FLAG_IS_EdDSA))
+ {
+ /* The format of an EdDSA key is:
+ * string key_type ("ssh-ed25519")
+ * string public_key
+ * string private_key
+ *
+ * Note that the private key is the concatenation of the private
+ * key with the public key. Thus theres are 64 bytes; however
+ * we only want the real 32 byte private key - Libgcrypt expects
+ * this.
+ */
+ mpi_list = xtrycalloc (3, sizeof *mpi_list);
+ if (!mpi_list)
+ {
+ err = gpg_error_from_syserror ();
+ goto out;
+ }
+
+ err = stream_read_blob (stream, 0, &mpi_list[0]);
+ if (err)
+ goto out;
+ if (secret)
+ {
+ u32 len = 0;
+ unsigned char *buffer;
+
+ /* Read string length. */
+ err = stream_read_uint32 (stream, &len);
+ if (err)
+ goto out;
+ if (len != 32 && len != 64)
+ {
+ err = gpg_error (GPG_ERR_BAD_SECKEY);
+ goto out;
+ }
+ buffer = xtrymalloc_secure (32);
+ if (!buffer)
+ {
+ err = gpg_error_from_syserror ();
+ goto out;
+ }
+ err = stream_read_data (stream, buffer, 32);
+ if (err)
+ {
+ xfree (buffer);
+ goto out;
+ }
+ mpi_list[1] = gcry_mpi_set_opaque (NULL, buffer, 8*32);
+ buffer = NULL;
+ if (len == 64)
+ {
+ err = stream_read_skip (stream, 32);
+ if (err)
+ goto out;
+ }
+ }
+ }
+ else if ((spec.flags & SPEC_FLAG_IS_ECDSA))
{
/* The format of an ECDSA key is:
* string key_type ("ecdsa-sha2-nistp256" |
@@ -1805,11 +2231,17 @@ ssh_receive_key (estream_t stream, gcry_sexp_t *key_new, int secret,
goto out;
}
}
- }
- err = ssh_receive_mpint_list (stream, secret, spec, &mpi_list);
- if (err)
- goto out;
+ err = ssh_receive_mpint_list (stream, secret, spec, &mpi_list);
+ if (err)
+ goto out;
+ }
+ else
+ {
+ err = ssh_receive_mpint_list (stream, secret, spec, &mpi_list);
+ if (err)
+ goto out;
+ }
if (read_comment)
{
@@ -1830,132 +2262,64 @@ ssh_receive_key (estream_t stream, gcry_sexp_t *key_new, int secret,
goto out;
}
- err = sexp_key_construct (&key, spec, secret, curve_name, mpi_list,
- comment? comment:"");
- if (err)
- goto out;
-
- if (key_spec)
- *key_spec = spec;
- *key_new = key;
-
- out:
- mpint_list_free (mpi_list);
- xfree (curve_name);
- xfree (key_type);
- xfree (comment);
-
- return err;
-}
-
-/* Converts a key of type TYPE, whose key material is given in MPIS,
- into a newly created binary blob, which is to be stored in
- BLOB/BLOB_SIZE. Returns zero on success or an error code. */
-static gpg_error_t
-ssh_convert_key_to_blob (unsigned char **blob, size_t *blob_size,
- ssh_key_type_spec_t *spec,
- const char *curve_name, gcry_mpi_t *mpis)
-{
- unsigned char *blob_new;
- long int blob_size_new;
- estream_t stream;
- gpg_error_t err;
- unsigned int i;
-
- *blob = NULL;
- *blob_size = 0;
-
- blob_new = NULL;
- stream = NULL;
- err = 0;
-
- stream = es_mopen (NULL, 0, 0, 1, NULL, NULL, "r+");
- if (! stream)
- {
- err = gpg_error_from_syserror ();
- goto out;
- }
-
- if ((spec->flags & SPEC_FLAG_IS_ECDSA) && curve_name)
+ if ((spec.flags & SPEC_FLAG_IS_EdDSA))
{
- const char *sshname = ssh_identifier_from_curve_name (curve_name);
- if (!curve_name)
+ if (secret)
{
- err = gpg_error (GPG_ERR_UNKNOWN_CURVE);
- goto out;
+ err = gcry_sexp_build (&key, NULL,
+ "(private-key(ecc(curve \"Ed25519\")"
+ "(flags eddsa)(q %m)(d %m))"
+ "(comment%s))",
+ mpi_list[0], mpi_list[1],
+ comment? comment:"");
+ }
+ else
+ {
+ err = gcry_sexp_build (&key, NULL,
+ "(public-key(ecc(curve \"Ed25519\")"
+ "(flags eddsa)(q %m))"
+ "(comment%s))",
+ mpi_list[0],
+ comment? comment:"");
}
- err = stream_write_cstring (stream, sshname);
- if (err)
- goto out;
- err = stream_write_cstring (stream, curve_name);
- if (err)
- goto out;
}
else
{
- err = stream_write_cstring (stream, spec->ssh_identifier);
+ err = sexp_key_construct (&key, spec, secret, curve_name, mpi_list,
+ comment? comment:"");
if (err)
goto out;
}
- for (i = 0; mpis[i]; i++)
- if ((err = stream_write_mpi (stream, mpis[i])))
- goto out;
-
- blob_size_new = es_ftell (stream);
- if (blob_size_new == -1)
- {
- err = gpg_error_from_syserror ();
- goto out;
- }
-
- err = es_fseek (stream, 0, SEEK_SET);
- if (err)
- goto out;
-
- blob_new = xtrymalloc (blob_size_new);
- if (! blob_new)
- {
- err = gpg_error_from_syserror ();
- goto out;
- }
-
- err = stream_read_data (stream, blob_new, blob_size_new);
- if (err)
- goto out;
-
- *blob = blob_new;
- *blob_size = blob_size_new;
+ if (key_spec)
+ *key_spec = spec;
+ *key_new = key;
out:
-
- if (stream)
- es_fclose (stream);
- if (err)
- xfree (blob_new);
+ mpint_list_free (mpi_list);
+ xfree (curve_name);
+ xfree (key_type);
+ xfree (comment);
return err;
}
-/* Write the public key KEY_PUBLIC to STREAM in SSH key format. If
+/* Write the public key from KEY to STREAM in SSH key format. If
OVERRIDE_COMMENT is not NULL, it will be used instead of the
comment stored in the key. */
static gpg_error_t
-ssh_send_key_public (estream_t stream,
- gcry_sexp_t key_public,
+ssh_send_key_public (estream_t stream, gcry_sexp_t key,
const char *override_comment)
{
ssh_key_type_spec_t spec;
- gcry_mpi_t *mpi_list = NULL;
char *key_type = NULL;
- char *curve;
char *comment = NULL;
- unsigned char *blob = NULL;
- size_t blob_n;
+ void *blob = NULL;
+ size_t bloblen;
gpg_error_t err;
- err = sexp_extract_identifier (key_public, &key_type);
+ err = sexp_extract_identifier (key, &key_type);
if (err)
goto out;
@@ -1963,32 +2327,34 @@ ssh_send_key_public (estream_t stream,
if (err)
goto out;
- err = sexp_key_extract (key_public, spec, NULL, &mpi_list, &curve, &comment);
+ err = ssh_key_to_blob (key, 0, spec, &blob, &bloblen);
if (err)
goto out;
- err = ssh_convert_key_to_blob (&blob, &blob_n, &spec, curve, mpi_list);
+ err = stream_write_string (stream, blob, bloblen);
if (err)
goto out;
- err = stream_write_string (stream, blob, blob_n);
+ if (override_comment)
+ err = stream_write_cstring (stream, override_comment);
+ else
+ {
+ err = ssh_key_extract_comment (key, &comment);
+ if (!err)
+ err = stream_write_cstring (stream, comment);
+ }
if (err)
goto out;
- err = stream_write_cstring (stream,
- override_comment? override_comment : comment);
-
out:
-
- mpint_list_free (mpi_list);
- xfree (curve);
- xfree (comment);
xfree (key_type);
- xfree (blob);
+ xfree (comment);
+ es_free (blob);
return err;
}
+
/* Read a public key out of BLOB/BLOB_SIZE according to the key
specification given as KEY_SPEC, storing the new key in KEY_PUBLIC.
Returns zero on success or an error code. */
@@ -2001,7 +2367,7 @@ ssh_read_key_public_from_blob (unsigned char *blob, size_t blob_size,
gpg_error_t err;
err = 0;
-
+ /* FIXME: Use fopenmem_init */
blob_stream = es_mopen (NULL, 0, 0, 1, NULL, NULL, "r+");
if (! blob_stream)
{
@@ -2045,39 +2411,6 @@ ssh_key_grip (gcry_sexp_t key, unsigned char *buffer)
}
-/* Converts the secret key KEY_SECRET into a public key, storing it in
- KEY_PUBLIC. SPEC is the according key specification. Returns zero
- on success or an error code. */
-static gpg_error_t
-key_secret_to_public (gcry_sexp_t *key_public,
- ssh_key_type_spec_t spec, gcry_sexp_t key_secret)
-{
- char *curve;
- char *comment;
- gcry_mpi_t *mpis;
- gpg_error_t err;
- int is_secret;
-
- comment = NULL;
- mpis = NULL;
-
- err = sexp_key_extract (key_secret, spec, &is_secret, &mpis,
- &curve, &comment);
- if (err)
- goto out;
-
- err = sexp_key_construct (key_public, spec, 0, curve, mpis, comment);
-
- out:
-
- mpint_list_free (mpis);
- xfree (comment);
- xfree (curve);
-
- return err;
-}
-
-
/* Check whether a smartcard is available and whether it has a usable
key. Store a copy of that key at R_PK and return 0. If no key is
available store NULL at R_PK and return an error code. If CARDSN
@@ -2376,20 +2709,12 @@ ssh_handler_request_identities (ctrl_t ctrl,
goto out;
}
- err = key_secret_to_public (&key_public, spec, key_secret);
+ err = ssh_send_key_public (key_blobs, key_secret, NULL);
if (err)
goto out;
-
gcry_sexp_release (key_secret);
key_secret = NULL;
- err = ssh_send_key_public (key_blobs, key_public, NULL);
- if (err)
- goto out;
-
- gcry_sexp_release (key_public);
- key_public = NULL;
-
key_counter++;
}
err = 0;
@@ -2441,30 +2766,27 @@ data_hash (unsigned char *data, size_t data_n,
return 0;
}
-/* This function signs the data contained in CTRL, stores the created
- signature in newly allocated memory in SIG and it's size in SIG_N;
- SIG_ENCODER is the signature encoder to use. */
+
+/* This function signs the data described by CTRL. If HASH is is not
+ NULL, (HASH,HASHLEN) overrides the hash stored in CTRL. This is to
+ allow the use of signature algorithms that implement the hashing
+ internally (e.g. Ed25519). On success the created signature is
+ stored in ssh format at R_SIG and it's size at R_SIGLEN; the caller
+ must use es_free to releaase this memory. */
static gpg_error_t
data_sign (ctrl_t ctrl, ssh_key_type_spec_t *spec,
- unsigned char **sig, size_t *sig_n)
+ const void *hash, size_t hashlen,
+ unsigned char **r_sig, size_t *r_siglen)
{
gpg_error_t err;
gcry_sexp_t signature_sexp = NULL;
estream_t stream = NULL;
- gcry_sexp_t valuelist = NULL;
- gcry_sexp_t sublist = NULL;
- gcry_mpi_t sig_value = NULL;
- unsigned char *sig_blob = NULL;
- size_t sig_blob_n = 0;
- int ret;
- unsigned int i;
- const char *elems;
- size_t elems_n;
- gcry_mpi_t *mpis = NULL;
+ void *blob = NULL;
+ size_t bloblen;
char hexgrip[40+1];
- *sig = NULL;
- *sig_n = 0;
+ *r_sig = NULL;
+ *r_siglen = 0;
/* Quick check to see whether we have a valid keygrip and convert it
to hex. */
@@ -2516,20 +2838,13 @@ data_sign (ctrl_t ctrl, ssh_key_type_spec_t *spec,
"for the ssh key%%0A %F%%0A (%c)"),
&signature_sexp,
CACHE_MODE_SSH, ttl_from_sshcontrol,
- NULL, 0);
+ hash, hashlen);
ctrl->use_auth_call = 0;
if (err)
goto out;
- valuelist = gcry_sexp_nth (signature_sexp, 1);
- if (! valuelist)
- {
- err = gpg_error (GPG_ERR_INV_SEXP);
- goto out;
- }
-
- stream = es_mopen (NULL, 0, 0, 1, NULL, NULL, "r+");
- if (! stream)
+ stream = es_fopenmem (0, "r+b");
+ if (!stream)
{
err = gpg_error_from_syserror ();
goto out;
@@ -2539,99 +2854,40 @@ data_sign (ctrl_t ctrl, ssh_key_type_spec_t *spec,
if (err)
goto out;
- elems = spec->elems_signature;
- elems_n = strlen (elems);
-
- mpis = xtrycalloc (elems_n + 1, sizeof *mpis);
- if (!mpis)
- {
- err = gpg_error_from_syserror ();
- goto out;
- }
-
- for (i = 0; i < elems_n; i++)
- {
- sublist = gcry_sexp_find_token (valuelist, spec->elems_signature + i, 1);
- if (! sublist)
- {
- err = gpg_error (GPG_ERR_INV_SEXP);
- break;
- }
-
- sig_value = gcry_sexp_nth_mpi (sublist, 1, GCRYMPI_FMT_USG);
- if (! sig_value)
- {
- err = gpg_error (GPG_ERR_INTERNAL); /* FIXME? */
- break;
- }
- gcry_sexp_release (sublist);
- sublist = NULL;
-
- mpis[i] = sig_value;
- }
+ err = spec->signature_encoder (spec, stream, signature_sexp);
if (err)
goto out;
- err = spec->signature_encoder (spec, stream, mpis);
- if (err)
- goto out;
-
- sig_blob_n = es_ftell (stream);
- if (sig_blob_n == -1)
- {
- err = gpg_error_from_syserror ();
- goto out;
- }
-
- sig_blob = xtrymalloc (sig_blob_n);
- if (! sig_blob)
- {
- err = gpg_error_from_syserror ();
- goto out;
- }
-
- ret = es_fseek (stream, 0, SEEK_SET);
- if (ret)
- {
- err = gpg_error_from_syserror ();
- goto out;
- }
-
- err = stream_read_data (stream, sig_blob, sig_blob_n);
+ err = es_fclose_snatch (stream, &blob, &bloblen);
if (err)
goto out;
+ stream = NULL;
- *sig = sig_blob;
- *sig_n = sig_blob_n;
+ *r_sig = blob; blob = NULL;
+ *r_siglen = bloblen;
out:
-
- if (err)
- xfree (sig_blob);
-
- if (stream)
- es_fclose (stream);
- gcry_sexp_release (valuelist);
+ xfree (blob);
+ es_fclose (stream);
gcry_sexp_release (signature_sexp);
- gcry_sexp_release (sublist);
- mpint_list_free (mpis);
return err;
}
+
/* Handler for the "sign_request" command. */
static gpg_error_t
ssh_handler_sign_request (ctrl_t ctrl, estream_t request, estream_t response)
{
- gcry_sexp_t key;
+ gcry_sexp_t key = NULL;
ssh_key_type_spec_t spec;
unsigned char hash[MAX_DIGEST_LEN];
unsigned int hash_n;
unsigned char key_grip[20];
- unsigned char *key_blob;
+ unsigned char *key_blob = NULL;
u32 key_blob_size;
- unsigned char *data;
- unsigned char *sig;
+ unsigned char *data = NULL;
+ unsigned char *sig = NULL;
size_t sig_n;
u32 data_size;
u32 flags;
@@ -2639,11 +2895,6 @@ ssh_handler_sign_request (ctrl_t ctrl, estream_t request, estream_t response)
gpg_error_t ret_err;
int hash_algo;
- key_blob = NULL;
- data = NULL;
- sig = NULL;
- key = NULL;
-
/* Receive key. */
err = stream_read_string (request, 0, &key_blob, &key_blob_size);
@@ -2667,42 +2918,48 @@ ssh_handler_sign_request (ctrl_t ctrl, estream_t request, estream_t response)
hash_algo = spec.hash_algo;
if (!hash_algo)
hash_algo = GCRY_MD_SHA1; /* Use the default. */
-
- /* Hash data. */
- hash_n = gcry_md_get_algo_dlen (hash_algo);
- if (! hash_n)
- {
- err = gpg_error (GPG_ERR_INTERNAL);
- goto out;
- }
- err = data_hash (data, data_size, hash_algo, hash);
- if (err)
- goto out;
-
- /* Calculate key grip. */
- err = ssh_key_grip (key, key_grip);
- if (err)
- goto out;
-
- /* Sign data. */
-
ctrl->digest.algo = hash_algo;
- memcpy (ctrl->digest.value, hash, hash_n);
- ctrl->digest.valuelen = hash_n;
if ((spec.flags & SPEC_FLAG_USE_PKCS1V2))
ctrl->digest.raw_value = 0;
else
ctrl->digest.raw_value = 1;
+
+ /* Calculate key grip. */
+ err = ssh_key_grip (key, key_grip);
+ if (err)
+ goto out;
ctrl->have_keygrip = 1;
memcpy (ctrl->keygrip, key_grip, 20);
- err = data_sign (ctrl, &spec, &sig, &sig_n);
+ /* Hash data unless we use EdDSA. */
+ if ((spec.flags & SPEC_FLAG_IS_EdDSA))
+ {
+ ctrl->digest.valuelen = 0;
+ }
+ else
+ {
+ hash_n = gcry_md_get_algo_dlen (hash_algo);
+ if (!hash_n)
+ {
+ err = gpg_error (GPG_ERR_INTERNAL);
+ goto out;
+ }
+ err = data_hash (data, data_size, hash_algo, hash);
+ if (err)
+ goto out;
+ memcpy (ctrl->digest.value, hash, hash_n);
+ ctrl->digest.valuelen = hash_n;
+ }
+
+ /* Sign data. */
+ if ((spec.flags & SPEC_FLAG_IS_EdDSA))
+ err = data_sign (ctrl, &spec, data, data_size, &sig, &sig_n);
+ else
+ err = data_sign (ctrl, &spec, NULL, 0, &sig, &sig_n);
out:
-
/* Done. */
-
- if (! err)
+ if (!err)
{
ret_err = stream_write_byte (response, SSH_RESPONSE_SIGN_RESPONSE);
if (ret_err)
@@ -2713,6 +2970,8 @@ ssh_handler_sign_request (ctrl_t ctrl, estream_t request, estream_t response)
}
else
{
+ log_error ("ssh sign request failed: %s <%s>\n",
+ gpg_strerror (err), gpg_strsource (err));
ret_err = stream_write_byte (response, SSH_RESPONSE_FAILURE);
if (ret_err)
goto leave;
@@ -2723,54 +2982,35 @@ ssh_handler_sign_request (ctrl_t ctrl, estream_t request, estream_t response)
gcry_sexp_release (key);
xfree (key_blob);
xfree (data);
- xfree (sig);
+ es_free (sig);
return ret_err;
}
+
/* This function extracts the comment contained in the key
- S-Expression KEY and stores a copy in COMMENT. Returns usual error
+ s-expression KEY and stores a copy in COMMENT. Returns usual error
code. */
static gpg_error_t
-ssh_key_extract_comment (gcry_sexp_t key, char **comment)
+ssh_key_extract_comment (gcry_sexp_t key, char **r_comment)
{
gcry_sexp_t comment_list;
- char *comment_new;
- const char *data;
- size_t data_n;
- gpg_error_t err;
- comment_list = gcry_sexp_find_token (key, "comment", 0);
- if (! comment_list)
- {
- err = gpg_error (GPG_ERR_INV_SEXP);
- goto out;
- }
-
- data = gcry_sexp_nth_data (comment_list, 1, &data_n);
- if (! data)
- {
- err = gpg_error (GPG_ERR_INV_SEXP);
- goto out;
- }
+ *r_comment = NULL;
- comment_new = make_cstring (data, data_n);
- if (! comment_new)
- {
- err = gpg_error_from_syserror ();
- goto out;
- }
-
- *comment = comment_new;
- err = 0;
-
- out:
+ comment_list = gcry_sexp_find_token (key, "comment", 0);
+ if (!comment_list)
+ return gpg_error (GPG_ERR_INV_SEXP);
+ *r_comment = gcry_sexp_nth_string (comment_list, 1);
gcry_sexp_release (comment_list);
+ if (!*r_comment)
+ return gpg_error (GPG_ERR_INV_SEXP);
- return err;
+ return 0;
}
+
/* This function converts the key contained in the S-Expression KEY
into a buffer, which is protected by the passphrase PASSPHRASE.
Returns usual error code. */