1
0
mirror of git://git.gnupg.org/gnupg.git synced 2024-12-22 10:19:57 +01:00

ssh: Support ECDSA keys.

* agent/command-ssh.c (SPEC_FLAG_IS_ECDSA): New.
(struct ssh_key_type_spec): Add fields CURVE_NAME and HASH_ALGO.
(ssh_key_types): Add types ecdsa-sha2-nistp{256,384,521}.
(ssh_signature_encoder_t): Add arg spec and adjust all callers.
(ssh_signature_encoder_ecdsa): New.
(sexp_key_construct, sexp_key_extract, ssh_receive_key)
(ssh_convert_key_to_blob): Support ecdsa.
(ssh_identifier_from_curve_name): New.
(ssh_send_key_public): Retrieve and pass the curve_name.
(key_secret_to_public): Ditto.
(data_sign): Add arg SPEC and change callers to pass it.
(ssh_handler_sign_request): Get the hash algo from SPEC.
* common/ssh-utils.c (get_fingerprint): Support ecdsa.

* agent/protect.c (protect_info): Add flag ECC_HACK.
(agent_protect): Allow the use of the "curve" parameter.
* agent/t-protect.c (test_agent_protect): Add a test case for ecdsa.

* agent/command-ssh.c (ssh_key_grip): Print a better error code.
--

The 3 standard curves are now supported in gpg-agent's ssh-agent
protocol implementation.  I tested this with all 3 curves and keys
generated by OpenSSH 5.9p1.

Using existing non-ssh generated keys will likely fail for now. To fix
this, the code should first undergo some more cleanup; then the fixes
are pretty straightforward.  And yes, the data structures are way too
complicated.
This commit is contained in:
Werner Koch 2012-12-12 18:47:21 +01:00
parent f76a0312c3
commit 649b31c663
4 changed files with 384 additions and 106 deletions

View File

@ -17,7 +17,18 @@
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
/* Only v2 of the ssh-agent protocol is implemented. */
/* Only v2 of the ssh-agent protocol is implemented. Relevant RFCs
are:
RFC-4250 - Protocol Assigned Numbers
RFC-4251 - Protocol Architecture
RFC-4252 - Authentication Protocol
RFC-4253 - Transport Layer Protocol
RFC-5656 - ECC support
The protocol for the agent is defined in OpenSSH's PROTOCL.agent
file.
*/
#include <config.h>
@ -61,6 +72,7 @@
#define SSH_DSA_SIGNATURE_PADDING 20
#define SSH_DSA_SIGNATURE_ELEMS 2
#define SPEC_FLAG_USE_PKCS1V2 (1 << 0)
#define SPEC_FLAG_IS_ECDSA (1 << 1)
/* The name of the control file. */
#define SSH_CONTROL_FILE_NAME "sshcontrol"
@ -99,6 +111,10 @@ typedef gpg_error_t (*ssh_request_handler_t) (ctrl_t ctrl,
estream_t request,
estream_t response);
struct ssh_key_type_spec;
typedef struct ssh_key_type_spec ssh_key_type_spec_t;
/* Type, which is used for associating request handlers with the
appropriate request IDs. */
typedef struct ssh_request_spec
@ -119,12 +135,13 @@ typedef gpg_error_t (*ssh_key_modifier_t) (const char *elems,
/* The encoding of a generated signature is dependent on the
algorithm; therefore algorithm specific signature encoding
functions are necessary. */
typedef gpg_error_t (*ssh_signature_encoder_t) (estream_t signature_blob,
typedef gpg_error_t (*ssh_signature_encoder_t) (ssh_key_type_spec_t *spec,
estream_t signature_blob,
gcry_mpi_t *mpis);
/* Type, which is used for boundling all the algorithm specific
information together in a single object. */
typedef struct ssh_key_type_spec
struct ssh_key_type_spec
{
/* Algorithm identifier as used by OpenSSH. */
const char *ssh_identifier;
@ -157,9 +174,16 @@ typedef struct ssh_key_type_spec
algorithm. */
ssh_signature_encoder_t signature_encoder;
/* The name of the ECC curve or NULL. */
const char *curve_name;
/* The hash algorithm to be used with this key. 0 for using the
default. */
int hash_algo;
/* Misc flags. */
unsigned int flags;
} ssh_key_type_spec_t;
};
/* An object used to access the sshcontrol file. */
@ -204,10 +228,15 @@ static gpg_error_t ssh_handler_unlock (ctrl_t ctrl,
estream_t response);
static gpg_error_t ssh_key_modifier_rsa (const char *elems, gcry_mpi_t *mpis);
static gpg_error_t ssh_signature_encoder_rsa (estream_t signature_blob,
static gpg_error_t ssh_signature_encoder_rsa (ssh_key_type_spec_t *spec,
estream_t signature_blob,
gcry_mpi_t *mpis);
static gpg_error_t ssh_signature_encoder_dsa (estream_t signature_blob,
static gpg_error_t ssh_signature_encoder_dsa (ssh_key_type_spec_t *spec,
estream_t signature_blob,
gcry_mpi_t *mpis);
static gpg_error_t ssh_signature_encoder_ecdsa (ssh_key_type_spec_t *spec,
estream_t signature_blob,
gcry_mpi_t *mpis);
@ -240,13 +269,29 @@ static ssh_key_type_spec_t ssh_key_types[] =
{
"ssh-rsa", "rsa", "nedupq", "en", "s", "nedpqu",
ssh_key_modifier_rsa, ssh_signature_encoder_rsa,
SPEC_FLAG_USE_PKCS1V2
NULL, 0, SPEC_FLAG_USE_PKCS1V2
},
{
"ssh-dss", "dsa", "pqgyx", "pqgy", "rs", "pqgyx",
NULL, ssh_signature_encoder_dsa,
0
NULL, 0, 0
},
{
"ecdsa-sha2-nistp256", "ecdsa", "qd", "q", "rs", "qd",
NULL, ssh_signature_encoder_ecdsa,
"nistp256", GCRY_MD_SHA256, SPEC_FLAG_IS_ECDSA
},
{
"ecdsa-sha2-nistp384", "ecdsa", "qd", "q", "rs", "qd",
NULL, ssh_signature_encoder_ecdsa,
"nistp384", GCRY_MD_SHA384, SPEC_FLAG_IS_ECDSA
},
{
"ecdsa-sha2-nistp521", "ecdsa", "qd", "q", "rs", "qd",
NULL, ssh_signature_encoder_ecdsa,
"nistp521", GCRY_MD_SHA512, SPEC_FLAG_IS_ECDSA
}
};
@ -341,6 +386,7 @@ stream_write_byte (estream_t stream, unsigned char b)
return err;
}
/* Read a uint32 from STREAM, store it in UINT32. */
static gpg_error_t
stream_read_uint32 (estream_t stream, u32 *uint32)
@ -431,8 +477,9 @@ stream_write_data (estream_t stream, const unsigned char *buffer, size_t size)
}
/* Read a binary string from STREAM into STRING, store size of string
in STRING_SIZE; depending on SECURE use secure memory for
string. */
in STRING_SIZE. Append a hidden nul so that the result may
directly be used as a C string. Depending on SECURE use secure
memory for STRING. */
static gpg_error_t
stream_read_string (estream_t stream, unsigned int secure,
unsigned char **string, u32 *string_size)
@ -1114,13 +1161,16 @@ ssh_key_modifier_rsa (const char *elems, gcry_mpi_t *mpis)
/* Signature encoder function for RSA. */
static gpg_error_t
ssh_signature_encoder_rsa (estream_t signature_blob, gcry_mpi_t *mpis)
ssh_signature_encoder_rsa (ssh_key_type_spec_t *spec,
estream_t signature_blob, gcry_mpi_t *mpis)
{
unsigned char *data;
size_t data_n;
gpg_error_t err;
gcry_mpi_t s;
(void)spec;
s = mpis[0];
err = gcry_mpi_aprint (GCRYMPI_FMT_USG, &data, &data_n, s);
@ -1138,7 +1188,8 @@ ssh_signature_encoder_rsa (estream_t signature_blob, gcry_mpi_t *mpis)
/* Signature encoder function for DSA. */
static gpg_error_t
ssh_signature_encoder_dsa (estream_t signature_blob, gcry_mpi_t *mpis)
ssh_signature_encoder_dsa (ssh_key_type_spec_t *spec,
estream_t signature_blob, gcry_mpi_t *mpis)
{
unsigned char buffer[SSH_DSA_SIGNATURE_PADDING * SSH_DSA_SIGNATURE_ELEMS];
unsigned char *data;
@ -1146,8 +1197,12 @@ ssh_signature_encoder_dsa (estream_t signature_blob, gcry_mpi_t *mpis)
gpg_error_t err;
int i;
(void)spec;
data = NULL;
/* FIXME: Why this complicated code? Why collecting boths mpis in a
buffer instead of writing them out one after the other? */
for (i = 0; i < 2; i++)
{
err = gcry_mpi_aprint (GCRYMPI_FMT_USG, &data, &data_n, mpis[i]);
@ -1180,23 +1235,63 @@ ssh_signature_encoder_dsa (estream_t signature_blob, gcry_mpi_t *mpis)
return err;
}
/* 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)
{
unsigned char *data[2] = {NULL, NULL};
size_t data_n[2];
size_t innerlen;
gpg_error_t err;
int i;
innerlen = 0;
for (i = 0; i < DIM(data); i++)
{
err = gcry_mpi_aprint (GCRYMPI_FMT_STD, &data[i], &data_n[i], mpis[i]);
if (err)
goto out;
innerlen += 4 + data_n[i];
}
err = stream_write_uint32 (stream, innerlen);
if (err)
goto out;
for (i = 0; i < DIM(data); i++)
{
err = stream_write_string (stream, data[i], data_n[i]);
if (err)
goto out;
}
out:
for (i = 0; i < DIM(data); i++)
xfree (data[i]);
return err;
}
/*
S-Expressions.
*/
/* This function constructs a new S-Expression for the key identified
by the KEY_SPEC, SECRET, MPIS and COMMENT, which is to be stored in
*SEXP. Returns usual error code. */
by the KEY_SPEC, SECRET, CURVE_NAME, MPIS, and COMMENT, which is to
be stored at R_SEXP. Returns an error code. */
static gpg_error_t
sexp_key_construct (gcry_sexp_t *r_sexp,
ssh_key_type_spec_t key_spec, int secret,
gcry_mpi_t *mpis, const char *comment)
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;
char *formatbuf = NULL;
void *formatbuf = NULL;
void **arg_list = NULL;
int arg_idx;
estream_t format;
@ -1219,7 +1314,7 @@ sexp_key_construct (gcry_sexp_t *r_sexp,
/* Key identifier, algorithm identifier, mpis, comment, and a NULL
as a safeguard. */
arg_list = xtrymalloc (sizeof (*arg_list) * (2 + elems_n + 1 + 1));
arg_list = xtrymalloc (sizeof (*arg_list) * (2 + 1 + elems_n + 1 + 1));
if (!arg_list)
{
err = gpg_error_from_syserror ();
@ -1230,6 +1325,11 @@ sexp_key_construct (gcry_sexp_t *r_sexp,
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++)
{
@ -1261,7 +1361,6 @@ sexp_key_construct (gcry_sexp_t *r_sexp,
}
format = NULL;
log_debug ("sexp formatbuf='%s' nargs=%d\n", formatbuf, arg_idx);
err = gcry_sexp_build_array (&sexp_new, NULL, formatbuf, arg_list);
if (err)
goto out;
@ -1281,34 +1380,28 @@ 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 comment will be stored in COMMENT; SECRET will be
filled with a boolean flag specifying what kind of key it is.
Returns usual error code. */
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. */
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 **comment)
gcry_mpi_t **mpis, char **r_curve, char **comment)
{
gpg_error_t err;
gcry_sexp_t value_list;
gcry_sexp_t value_pair;
gcry_sexp_t comment_list;
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;
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;
gcry_mpi_t *mpis_new = NULL;
gcry_mpi_t mpi;
err = 0;
value_list = NULL;
value_pair = NULL;
comment_list = NULL;
comment_new = NULL;
mpis_new = NULL;
char *curve_name = NULL;
data = gcry_sexp_nth_data (sexp, 0, &data_n);
if (! data)
@ -1374,6 +1467,51 @@ sexp_key_extract (gcry_sexp_t sexp,
if (err)
goto out;
if ((key_spec.flags & SPEC_FLAG_IS_ECDSA))
{
/* Parse the "curve" parameter. We currently expect the curve
name for ECC and not the parameters of the curve. This can
easily be changed but then we need to find the curve name
from the parameters using gcry_pk_get_curve. */
const char *mapped;
value_pair = gcry_sexp_find_token (value_list, "curve", 5);
if (!value_pair)
{
err = gpg_error (GPG_ERR_INV_CURVE);
goto out;
}
curve_name = gcry_sexp_nth_string (value_pair, 1);
if (!curve_name)
{
err = gpg_error (GPG_ERR_INV_CURVE); /* (Or out of core.) */
goto out;
}
/* Fixme: The mapping should be done by using gcry_pk_get_curve
et al to iterate over all name aliases. */
if (!strcmp (curve_name, "NIST P-256"))
mapped = "nistp256";
else if (!strcmp (curve_name, "NIST P-384"))
mapped = "nistp384";
else if (!strcmp (curve_name, "NIST P-521"))
mapped = "nistp521";
else
mapped = NULL;
if (mapped)
{
xfree (curve_name);
curve_name = xtrystrdup (mapped);
if (!curve_name)
{
err = gpg_error_from_syserror ();
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;
@ -1398,6 +1536,7 @@ sexp_key_extract (gcry_sexp_t sexp,
*secret = is_secret;
*mpis = mpis_new;
*comment = comment_new;
*r_curve = curve_name;
out:
@ -1407,6 +1546,7 @@ sexp_key_extract (gcry_sexp_t sexp,
if (err)
{
xfree (curve_name);
xfree (comment_new);
mpint_list_free (mpis_new);
}
@ -1493,6 +1633,24 @@ ssh_key_type_lookup (const char *ssh_name, const char *name,
return err;
}
/* 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.
@ -1509,6 +1667,8 @@ ssh_receive_key (estream_t stream, gcry_sexp_t *key_new, int secret,
ssh_key_type_spec_t spec;
gcry_mpi_t *mpi_list = NULL;
const char *elems;
char *curve_name = NULL;
err = stream_read_cstring (stream, &key_type);
if (err)
@ -1518,6 +1678,50 @@ ssh_receive_key (estream_t stream, gcry_sexp_t *key_new, int secret,
if (err)
goto out;
if ((spec.flags & SPEC_FLAG_IS_ECDSA))
{
/* The format of an ECDSA key is:
* string key_type ("ecdsa-sha2-nistp256" |
* "ecdsa-sha2-nistp384" |
* "ecdsa-sha2-nistp521" )
* string ecdsa_curve_name
* string ecdsa_public_key
* mpint ecdsa_private
*
* Note that we use the mpint reader instead of the string
* reader for ecsa_public_key.
*/
unsigned char *buffer;
const char *mapped;
err = stream_read_string (stream, 0, &buffer, NULL);
if (err)
goto out;
curve_name = buffer;
/* Fixme: Check that curve_name matches the keytype. */
/* Because Libgcrypt < 1.6 has no support for the "nistpNNN"
curve names, we need to translate them here to Libgcrypt's
native names. */
if (!strcmp (curve_name, "nistp256"))
mapped = "NIST P-256";
else if (!strcmp (curve_name, "nistp384"))
mapped = "NIST P-384";
else if (!strcmp (curve_name, "nistp521"))
mapped = "NIST P-521";
else
mapped = NULL;
if (mapped)
{
xfree (curve_name);
curve_name = xtrystrdup (mapped);
if (!curve_name)
{
err = gpg_error_from_syserror ();
goto out;
}
}
}
err = ssh_receive_mpint_list (stream, secret, spec, &mpi_list);
if (err)
goto out;
@ -1541,7 +1745,8 @@ ssh_receive_key (estream_t stream, gcry_sexp_t *key_new, int secret,
goto out;
}
err = sexp_key_construct (&key, spec, secret, mpi_list, comment? comment:"");
err = sexp_key_construct (&key, spec, secret, curve_name, mpi_list,
comment? comment:"");
if (err)
goto out;
@ -1550,8 +1755,8 @@ ssh_receive_key (estream_t stream, gcry_sexp_t *key_new, int secret,
*key_new = key;
out:
mpint_list_free (mpi_list);
xfree (curve_name);
xfree (key_type);
xfree (comment);
@ -1563,7 +1768,8 @@ ssh_receive_key (estream_t stream, gcry_sexp_t *key_new, int secret,
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,
const char *type, gcry_mpi_t *mpis)
ssh_key_type_spec_t *spec,
const char *curve_name, gcry_mpi_t *mpis)
{
unsigned char *blob_new;
long int blob_size_new;
@ -1585,14 +1791,31 @@ ssh_convert_key_to_blob (unsigned char **blob, size_t *blob_size,
goto out;
}
err = stream_write_cstring (stream, type);
if (err)
goto out;
if ((spec->flags & SPEC_FLAG_IS_ECDSA) && curve_name)
{
const char *sshname = ssh_identifier_from_curve_name (curve_name);
if (!curve_name)
{
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
{
err = stream_write_cstring (stream, spec->ssh_identifier);
if (err)
goto out;
}
for (i = 0; mpis[i] && (! err); i++)
err = stream_write_mpi (stream, mpis[i]);
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)
@ -1634,22 +1857,19 @@ ssh_convert_key_to_blob (unsigned char **blob, size_t *blob_size,
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_public,
const char *override_comment)
{
ssh_key_type_spec_t spec;
gcry_mpi_t *mpi_list;
char *key_type;
char *comment;
unsigned char *blob;
gcry_mpi_t *mpi_list = NULL;
char *key_type = NULL;
char *curve;
char *comment = NULL;
unsigned char *blob = NULL;
size_t blob_n;
gpg_error_t err;
key_type = NULL;
mpi_list = NULL;
comment = NULL;
blob = NULL;
err = sexp_extract_identifier (key_public, &key_type);
if (err)
goto out;
@ -1658,12 +1878,11 @@ ssh_send_key_public (estream_t stream, gcry_sexp_t key_public,
if (err)
goto out;
err = sexp_key_extract (key_public, spec, NULL, &mpi_list, &comment);
err = sexp_key_extract (key_public, spec, NULL, &mpi_list, &curve, &comment);
if (err)
goto out;
err = ssh_convert_key_to_blob (&blob, &blob_n,
spec.ssh_identifier, mpi_list);
err = ssh_convert_key_to_blob (&blob, &blob_n, &spec, curve, mpi_list);
if (err)
goto out;
@ -1677,8 +1896,9 @@ ssh_send_key_public (estream_t stream, gcry_sexp_t key_public,
out:
mpint_list_free (mpi_list);
xfree (key_type);
xfree (curve);
xfree (comment);
xfree (key_type);
xfree (blob);
return err;
@ -1731,7 +1951,10 @@ static gpg_error_t
ssh_key_grip (gcry_sexp_t key, unsigned char *buffer)
{
if (!gcry_pk_get_keygrip (key, buffer))
return gpg_error (GPG_ERR_INTERNAL);
{
gpg_error_t err = gcry_pk_testkey (key);
return err? err : gpg_error (GPG_ERR_INTERNAL);
}
return 0;
}
@ -1744,6 +1967,7 @@ 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;
@ -1752,16 +1976,18 @@ key_secret_to_public (gcry_sexp_t *key_public,
comment = NULL;
mpis = NULL;
err = sexp_key_extract (key_secret, spec, &is_secret, &mpis, &comment);
err = sexp_key_extract (key_secret, spec, &is_secret, &mpis,
&curve, &comment);
if (err)
goto out;
err = sexp_key_construct (key_public, spec, 0, mpis, comment);
err = sexp_key_construct (key_public, spec, 0, curve, mpis, comment);
out:
mpint_list_free (mpis);
xfree (comment);
xfree (curve);
return err;
}
@ -2134,7 +2360,7 @@ data_hash (unsigned char *data, size_t data_n,
signature in newly allocated memory in SIG and it's size in SIG_N;
SIG_ENCODER is the signature encoder to use. */
static gpg_error_t
data_sign (ctrl_t ctrl, ssh_signature_encoder_t sig_encoder,
data_sign (ctrl_t ctrl, ssh_key_type_spec_t *spec,
unsigned char **sig, size_t *sig_n)
{
gpg_error_t err;
@ -2145,10 +2371,6 @@ data_sign (ctrl_t ctrl, ssh_signature_encoder_t sig_encoder,
gcry_mpi_t sig_value = NULL;
unsigned char *sig_blob = NULL;
size_t sig_blob_n = 0;
char *identifier = NULL;
const char *identifier_raw;
size_t identifier_n;
ssh_key_type_spec_t spec;
int ret;
unsigned int i;
const char *elems;
@ -2227,29 +2449,11 @@ data_sign (ctrl_t ctrl, ssh_signature_encoder_t sig_encoder,
goto out;
}
identifier_raw = gcry_sexp_nth_data (valuelist, 0, &identifier_n);
if (! identifier_raw)
{
err = gpg_error (GPG_ERR_INV_SEXP);
goto out;
}
identifier = make_cstring (identifier_raw, identifier_n);
if (! identifier)
{
err = gpg_error_from_syserror ();
goto out;
}
err = ssh_key_type_lookup (NULL, identifier, &spec);
err = stream_write_cstring (stream, spec->ssh_identifier);
if (err)
goto out;
err = stream_write_cstring (stream, spec.ssh_identifier);
if (err)
goto out;
elems = spec.elems_signature;
elems = spec->elems_signature;
elems_n = strlen (elems);
mpis = xtrycalloc (elems_n + 1, sizeof *mpis);
@ -2261,7 +2465,7 @@ data_sign (ctrl_t ctrl, ssh_signature_encoder_t sig_encoder,
for (i = 0; i < elems_n; i++)
{
sublist = gcry_sexp_find_token (valuelist, spec.elems_signature + i, 1);
sublist = gcry_sexp_find_token (valuelist, spec->elems_signature + i, 1);
if (! sublist)
{
err = gpg_error (GPG_ERR_INV_SEXP);
@ -2282,7 +2486,7 @@ data_sign (ctrl_t ctrl, ssh_signature_encoder_t sig_encoder,
if (err)
goto out;
err = (*sig_encoder) (stream, mpis);
err = spec->signature_encoder (spec, stream, mpis);
if (err)
goto out;
@ -2325,7 +2529,6 @@ data_sign (ctrl_t ctrl, ssh_signature_encoder_t sig_encoder,
gcry_sexp_release (signature_sexp);
gcry_sexp_release (sublist);
mpint_list_free (mpis);
xfree (identifier);
return err;
}
@ -2348,6 +2551,7 @@ ssh_handler_sign_request (ctrl_t ctrl, estream_t request, estream_t response)
u32 flags;
gpg_error_t err;
gpg_error_t ret_err;
int hash_algo;
key_blob = NULL;
data = NULL;
@ -2374,14 +2578,18 @@ ssh_handler_sign_request (ctrl_t ctrl, estream_t request, estream_t response)
if (err)
goto out;
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 (GCRY_MD_SHA1);
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, GCRY_MD_SHA1, hash);
err = data_hash (data, data_size, hash_algo, hash);
if (err)
goto out;
@ -2392,14 +2600,17 @@ ssh_handler_sign_request (ctrl_t ctrl, estream_t request, estream_t response)
/* Sign data. */
ctrl->digest.algo = GCRY_MD_SHA1;
ctrl->digest.algo = hash_algo;
memcpy (ctrl->digest.value, hash, hash_n);
ctrl->digest.valuelen = hash_n;
ctrl->digest.raw_value = ! (spec.flags & SPEC_FLAG_USE_PKCS1V2);
if ((spec.flags & SPEC_FLAG_USE_PKCS1V2))
ctrl->digest.raw_value = 0;
else
ctrl->digest.raw_value = 1;
ctrl->have_keygrip = 1;
memcpy (ctrl->keygrip, key_grip, 20);
err = data_sign (ctrl, spec.signature_encoder, &sig, &sig_n);
err = data_sign (ctrl, &spec, &sig, &sig_n);
out:
@ -2520,6 +2731,7 @@ reenter_compare_cb (struct pin_entry_info_s *pi)
return -1;
}
/* Store the ssh KEY into our local key storage and protect it after
asking for a passphrase. Cache that passphrase. TTL is the
maximum caching time for that key. If the key already exists in
@ -2570,7 +2782,6 @@ ssh_identity_register (ctrl_t ctrl, gcry_sexp_t key, int ttl, int confirm)
goto out;
}
pi = gcry_calloc_secure (2, sizeof (*pi) + 100 + 1);
if (!pi)
{

View File

@ -51,13 +51,14 @@ static struct {
const char *algo;
const char *parmlist;
int prot_from, prot_to;
int ecc_hack;
} protect_info[] = {
{ "rsa", "nedpqu", 2, 5 },
{ "dsa", "pqgyx", 4, 4 },
{ "elg", "pgyx", 3, 3 },
{ "ecdsa","pabgnqd", 6, 6 },
{ "ecdh", "pabgnqd", 6, 6 },
{ "ecc", "pabgnqd", 6, 6 },
{ "ecdsa","pabgnqd", 6, 6, 1 },
{ "ecdh", "pabgnqd", 6, 6, 1 },
{ "ecc", "pabgnqd", 6, 6, 1 },
{ NULL }
};
@ -450,6 +451,8 @@ agent_protect (const unsigned char *plainkey, const char *passphrase,
unsigned long s2k_count)
{
int rc;
const char *parmlist;
int prot_from_idx, prot_to_idx;
const unsigned char *s;
const unsigned char *hash_begin, *hash_end;
const unsigned char *prot_begin, *prot_end, *real_end;
@ -494,10 +497,13 @@ agent_protect (const unsigned char *plainkey, const char *passphrase,
if (!protect_info[infidx].algo)
return gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM);
parmlist = protect_info[infidx].parmlist;
prot_from_idx = protect_info[infidx].prot_from;
prot_to_idx = protect_info[infidx].prot_to;
prot_begin = prot_end = NULL;
for (i=0; (c=protect_info[infidx].parmlist[i]); i++)
for (i=0; (c=parmlist[i]); i++)
{
if (i == protect_info[infidx].prot_from)
if (i == prot_from_idx)
prot_begin = s;
if (*s != '(')
return gpg_error (GPG_ERR_INV_SEXP);
@ -507,7 +513,20 @@ agent_protect (const unsigned char *plainkey, const char *passphrase,
if (!n)
return gpg_error (GPG_ERR_INV_SEXP);
if (n != 1 || c != *s)
return gpg_error (GPG_ERR_INV_SEXP);
{
if (n == 5 && !memcmp (s, "curve", 5)
&& !i && protect_info[infidx].ecc_hack)
{
/* This is a private ECC key but the first parameter is
the name of the curve. We change the parameter list
here to the one we expect in this case. */
parmlist = "?qd";
prot_from_idx = 2;
prot_to_idx = 2;
}
else
return gpg_error (GPG_ERR_INV_SEXP);
}
s += n;
n = snext (&s);
if (!n)
@ -516,7 +535,7 @@ agent_protect (const unsigned char *plainkey, const char *passphrase,
if (*s != ')')
return gpg_error (GPG_ERR_INV_SEXP);
depth--;
if (i == protect_info[infidx].prot_to)
if (i == prot_to_idx)
prot_end = s;
s++;
}
@ -533,7 +552,6 @@ agent_protect (const unsigned char *plainkey, const char *passphrase,
assert (!depth);
real_end = s-1;
/* Hash the stuff. Because the timestamp_exp won't get protected,
we can't simply hash a continuous buffer but need to use several
md_writes. */

View File

@ -138,6 +138,24 @@ test_agent_protect (void)
"\x55\x87\x11\x1C\x74\xE7\x7F\xA0\xBA\xE3\x34\x5D\x61\xBF\x29\x29\x29\x00"
};
struct key_spec key_ecdsa_valid =
{
"\x28\x31\x31\x3A\x70\x72\x69\x76\x61\x74\x65\x2D\x6B\x65\x79\x28"
"\x35\x3A\x65\x63\x64\x73\x61\x28\x35\x3A\x63\x75\x72\x76\x65\x31"
"\x30\x3A\x4E\x49\x53\x54\x20\x50\x2D\x32\x35\x36\x29\x28\x31\x3A"
"\x71\x36\x35\x3A\x04\x64\x5A\x12\x6F\x86\x7C\x43\x87\x2B\x7C\xAF"
"\x77\xFE\xD8\x22\x31\xEA\xE6\x89\x9F\xAA\xEA\x63\x26\xBC\x49\xED"
"\x85\xC6\xD2\xC9\x8B\x38\xD2\x78\x75\xE6\x1C\x27\x57\x01\xC5\xA1"
"\xE3\xF9\x1F\xBE\xCF\xC1\x72\x73\xFE\xA4\x58\xB6\x6A\x92\x7D\x33"
"\x1D\x02\xC9\xCB\x12\x29\x28\x31\x3A\x64\x33\x33\x3A\x00\x81\x2D"
"\x69\x9A\x5F\x5B\x6F\x2C\x99\x61\x36\x15\x6B\x44\xD8\x06\xC1\x54"
"\xC1\x4C\xFB\x70\x6A\xB6\x64\x81\x78\xF3\x94\x2F\x30\x5D\x29\x29"
"\x28\x37\x3A\x63\x6F\x6D\x6D\x65\x6E\x74\x32\x32\x3A\x2F\x68\x6F"
"\x6D\x65\x2F\x77\x6B\x2F\x2E\x73\x73\x68\x2F\x69\x64\x5F\x65\x63"
"\x64\x73\x61\x29\x29"
};
struct
{
const char *key;
@ -167,6 +185,9 @@ test_agent_protect (void)
{ key_rsa_bogus_1.string,
"passphrase", 0, 0, NULL, 0, GPG_ERR_INV_SEXP, NULL, 0 },
{ key_ecdsa_valid.string,
"passphrase", 0, 0, NULL, 0, 0, NULL, 0 },
/* FIXME: add more test data. */
};
@ -177,8 +198,8 @@ test_agent_protect (void)
&specs[i].result, &specs[i].resultlen, 0);
if (gpg_err_code (ret) != specs[i].ret_expected)
{
printf ("agent_protect() returned '%i/%s'; expected '%i/%s'\n",
ret, gpg_strerror (ret),
printf ("agent_protect(%d) returned '%i/%s'; expected '%i/%s'\n",
i, ret, gpg_strerror (ret),
specs[i].ret_expected, gpg_strerror (specs[i].ret_expected));
abort ();
}

View File

@ -97,6 +97,34 @@ get_fingerprint (gcry_sexp_t key, void **r_fpr, size_t *r_len, int as_string)
elems = "pqgy";
gcry_md_write (md, "\0\0\0\x07ssh-dss", 11);
break;
case GCRY_PK_ECDSA:
/* We only support the 3 standard curves for now. It is just a
quick hack. */
elems = "q";
gcry_md_write (md, "\0\0\0\x13" "ecdsa-sha2-nistp", 20);
l2 = gcry_sexp_find_token (list, "curve", 0);
if (!l2)
elems = "";
else
{
gcry_free (name);
name = gcry_sexp_nth_string (l2, 1);
gcry_sexp_release (l2);
l2 = NULL;
if (!name)
elems = "";
else if (!strcmp (name, "NIST P-256") || !strcmp (name, "nistp256"))
gcry_md_write (md, "256\0\0\0\x08nistp256", 15);
else if (!strcmp (name, "NIST P-384") || !strcmp (name, "nistp384"))
gcry_md_write (md, "384\0\0\0\x08nistp521", 15);
else if (!strcmp (name, "NIST P-521") || !strcmp (name, "nistp521"))
gcry_md_write (md, "521\0\0\0\x08nistp521", 15);
else
elems = "";
}
if (!*elems)
err = gpg_err_make (default_errsource, GPG_ERR_UNKNOWN_CURVE);
break;
default:
elems = "";
err = gpg_err_make (default_errsource, GPG_ERR_PUBKEY_ALGO);