ssh: Accept OpenSSH *cert-v01 key variants.

* agent/command-ssh.c (SPEC_FLAG_WITH_CERT): New.
(ssh_key_types): Add OpenSSH cert types.
(stream_read_string): Allow a dummy read.
(ssh_receive_mpint_list): Pass SPEC by reference.
(ssh_receive_mpint_list): New arg CERT and use it.
(ssh_receive_key): Read certificate into an estream object and modify
parser to make use of that object.
--

This is a first step to support certificate via the agent.  The only
effect of this change is the removal of an error message parsing the
certificate.  Note that ssh-add sends the private key anyway first and
only then follows with the certificate+private key.

What we need to implement next is a way to store the certificate in
the agent and return it on request.

Signed-off-by: Werner Koch <wk@gnupg.org>
This commit is contained in:
Werner Koch 2016-01-12 19:12:02 +01:00
parent 4619ea8e51
commit e2f984b4af
No known key found for this signature in database
GPG Key ID: E3FDFF218E45B72B
1 changed files with 136 additions and 38 deletions

View File

@ -75,6 +75,7 @@
#define SPEC_FLAG_USE_PKCS1V2 (1 << 0) #define SPEC_FLAG_USE_PKCS1V2 (1 << 0)
#define SPEC_FLAG_IS_ECDSA (1 << 1) #define SPEC_FLAG_IS_ECDSA (1 << 1)
#define SPEC_FLAG_IS_EdDSA (1 << 2) /*(lowercase 'd' on purpose.)*/ #define SPEC_FLAG_IS_EdDSA (1 << 2) /*(lowercase 'd' on purpose.)*/
#define SPEC_FLAG_WITH_CERT (1 << 7)
/* The name of the control file. */ /* The name of the control file. */
#define SSH_CONTROL_FILE_NAME "sshcontrol" #define SSH_CONTROL_FILE_NAME "sshcontrol"
@ -302,6 +303,42 @@ static ssh_key_type_spec_t ssh_key_types[] =
"ecdsa-sha2-nistp521", "ECDSA", "ecdsa", "qd", "q", "rs", "qd", "ecdsa-sha2-nistp521", "ECDSA", "ecdsa", "qd", "q", "rs", "qd",
NULL, ssh_signature_encoder_ecdsa, NULL, ssh_signature_encoder_ecdsa,
"nistp521", GCRY_MD_SHA512, SPEC_FLAG_IS_ECDSA "nistp521", GCRY_MD_SHA512, SPEC_FLAG_IS_ECDSA
},
{
"ssh-ed25519-cert-v01@openssh.com", "Ed25519",
"ecc", "qd", "q", "rs", "qd",
NULL, ssh_signature_encoder_eddsa,
"Ed25519", 0, SPEC_FLAG_IS_EdDSA | SPEC_FLAG_WITH_CERT
},
{
"ssh-rsa-cert-v01@openssh.com", "RSA",
"rsa", "nedupq", "en", "s", "nedpqu",
ssh_key_modifier_rsa, ssh_signature_encoder_rsa,
NULL, 0, SPEC_FLAG_USE_PKCS1V2 | SPEC_FLAG_WITH_CERT
},
{
"ssh-dss-cert-v01@openssh.com", "DSA",
"dsa", "pqgyx", "pqgy", "rs", "pqgyx",
NULL, ssh_signature_encoder_dsa,
NULL, 0, SPEC_FLAG_WITH_CERT | SPEC_FLAG_WITH_CERT
},
{
"ecdsa-sha2-nistp256-cert-v01@openssh.com", "ECDSA",
"ecdsa", "qd", "q", "rs", "qd",
NULL, ssh_signature_encoder_ecdsa,
"nistp256", GCRY_MD_SHA256, SPEC_FLAG_IS_ECDSA | SPEC_FLAG_WITH_CERT
},
{
"ecdsa-sha2-nistp384-cert-v01@openssh.com", "ECDSA",
"ecdsa", "qd", "q", "rs", "qd",
NULL, ssh_signature_encoder_ecdsa,
"nistp384", GCRY_MD_SHA384, SPEC_FLAG_IS_ECDSA | SPEC_FLAG_WITH_CERT
},
{
"ecdsa-sha2-nistp521-cert-v01@openssh.com", "ECDSA",
"ecdsa", "qd", "q", "rs", "qd",
NULL, ssh_signature_encoder_ecdsa,
"nistp521", GCRY_MD_SHA512, SPEC_FLAG_IS_ECDSA | SPEC_FLAG_WITH_CERT
} }
}; };
@ -531,7 +568,7 @@ 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 /* Read a binary string from STREAM into STRING, store size of string
in STRING_SIZE. Append a hidden nul so that the result may in STRING_SIZE. Append a hidden nul so that the result may
directly be used as a C string. Depending on SECURE use secure directly be used as a C string. Depending on SECURE use secure
memory for STRING. */ memory for STRING. If STRING is NULL do only a dummy read. */
static gpg_error_t static gpg_error_t
stream_read_string (estream_t stream, unsigned int secure, stream_read_string (estream_t stream, unsigned int secure,
unsigned char **string, u32 *string_size) unsigned char **string, u32 *string_size)
@ -548,25 +585,35 @@ stream_read_string (estream_t stream, unsigned int secure,
if (err) if (err)
goto out; goto out;
/* Allocate space. */ if (string)
if (secure)
buffer = xtrymalloc_secure (length + 1);
else
buffer = xtrymalloc (length + 1);
if (! buffer)
{ {
err = gpg_error_from_syserror (); /* Allocate space. */
goto out; if (secure)
buffer = xtrymalloc_secure (length + 1);
else
buffer = xtrymalloc (length + 1);
if (! buffer)
{
err = gpg_error_from_syserror ();
goto out;
}
/* Read data. */
err = stream_read_data (stream, buffer, length);
if (err)
goto out;
/* Finalize string object. */
buffer[length] = 0;
*string = buffer;
}
else /* Dummy read requested. */
{
err = stream_read_skip (stream, length);
if (err)
goto out;
} }
/* Read data. */
err = stream_read_data (stream, buffer, length);
if (err)
goto out;
/* Finalize string object. */
buffer[length] = 0;
*string = buffer;
if (string_size) if (string_size)
*string_size = length; *string_size = length;
@ -1258,31 +1305,38 @@ mpint_list_free (gcry_mpi_t *mpi_list)
} }
/* Receive key material MPIs from STREAM according to KEY_SPEC; /* Receive key material MPIs from STREAM according to KEY_SPEC;
depending on SECRET expect a public key or secret key. The newly depending on SECRET expect a public key or secret key. CERT is the
certificate blob used if KEY_SPEC indicates the certificate format;
it needs to be positioned to the end of the nonce. The newly
allocated list of MPIs is stored in MPI_LIST. Returns usual error allocated list of MPIs is stored in MPI_LIST. Returns usual error
code. */ code. */
static gpg_error_t static gpg_error_t
ssh_receive_mpint_list (estream_t stream, int secret, ssh_receive_mpint_list (estream_t stream, int secret,
ssh_key_type_spec_t key_spec, gcry_mpi_t **mpi_list) ssh_key_type_spec_t *spec, estream_t cert,
gcry_mpi_t **mpi_list)
{ {
const char *elems_public; const char *elems_public;
unsigned int elems_n; unsigned int elems_n;
const char *elems; const char *elems;
int elem_is_secret; int elem_is_secret;
gcry_mpi_t *mpis; gcry_mpi_t *mpis = NULL;
gpg_error_t err; gpg_error_t err = 0;
unsigned int i; unsigned int i;
mpis = NULL;
err = 0;
if (secret) if (secret)
elems = key_spec.elems_key_secret; elems = spec->elems_key_secret;
else else
elems = key_spec.elems_key_public; elems = spec->elems_key_public;
elems_n = strlen (elems); elems_n = strlen (elems);
elems_public = spec->elems_key_public;
elems_public = key_spec.elems_key_public; /* Check that either noth, CERT and the WITH_CERT flag, are given or
none of them. */
if (!(!!(spec->flags & SPEC_FLAG_WITH_CERT) ^ !cert))
{
err = gpg_error (GPG_ERR_INV_CERT_OBJ);
goto out;
}
mpis = xtrycalloc (elems_n + 1, sizeof *mpis ); mpis = xtrycalloc (elems_n + 1, sizeof *mpis );
if (!mpis) if (!mpis)
@ -1295,18 +1349,20 @@ ssh_receive_mpint_list (estream_t stream, int secret,
for (i = 0; i < elems_n; i++) for (i = 0; i < elems_n; i++)
{ {
if (secret) if (secret)
elem_is_secret = ! strchr (elems_public, elems[i]); elem_is_secret = !strchr (elems_public, elems[i]);
err = stream_read_mpi (stream, elem_is_secret, &mpis[i]);
if (cert && !elem_is_secret)
err = stream_read_mpi (cert, elem_is_secret, &mpis[i]);
else
err = stream_read_mpi (stream, elem_is_secret, &mpis[i]);
if (err) if (err)
break; goto out;
} }
if (err)
goto out;
*mpi_list = mpis; *mpi_list = mpis;
mpis = NULL;
out: out:
if (err) if (err)
mpint_list_free (mpis); mpint_list_free (mpis);
@ -2112,6 +2168,7 @@ ssh_receive_key (estream_t stream, gcry_sexp_t *key_new, int secret,
gpg_error_t err; gpg_error_t err;
char *key_type = NULL; char *key_type = NULL;
char *comment = NULL; char *comment = NULL;
estream_t cert = NULL;
gcry_sexp_t key = NULL; gcry_sexp_t key = NULL;
ssh_key_type_spec_t spec; ssh_key_type_spec_t spec;
gcry_mpi_t *mpi_list = NULL; gcry_mpi_t *mpi_list = NULL;
@ -2127,6 +2184,44 @@ ssh_receive_key (estream_t stream, gcry_sexp_t *key_new, int secret,
if (err) if (err)
goto out; goto out;
if ((spec.flags & SPEC_FLAG_WITH_CERT))
{
/* This is an OpenSSH certificate+private key. The certificate
is an SSH string and which we store in an estream object. */
unsigned char *buffer;
u32 buflen;
char *cert_key_type;
err = stream_read_string (stream, 0, &buffer, &buflen);
if (err)
goto out;
cert = es_fopenmem_init (0, "rb", buffer, buflen);
xfree (buffer);
if (!cert)
{
err = gpg_error_from_syserror ();
goto out;
}
/* Check that the key type matches. */
err = stream_read_cstring (cert, &cert_key_type);
if (err)
goto out;
if (strcmp (cert_key_type, key_type) )
{
xfree (cert_key_type);
log_error ("key types in received ssh certificate do not match\n");
err = gpg_error (GPG_ERR_INV_CERT_OBJ);
goto out;
}
xfree (cert_key_type);
/* Skip the nonce. */
err = stream_read_string (cert, 0, NULL, NULL);
if (err)
goto out;
}
if ((spec.flags & SPEC_FLAG_IS_EdDSA)) if ((spec.flags & SPEC_FLAG_IS_EdDSA))
{ {
/* The format of an EdDSA key is: /* The format of an EdDSA key is:
@ -2146,7 +2241,7 @@ ssh_receive_key (estream_t stream, gcry_sexp_t *key_new, int secret,
goto out; goto out;
} }
err = stream_read_blob (stream, 0, &mpi_list[0]); err = stream_read_blob (cert? cert : stream, 0, &mpi_list[0]);
if (err) if (err)
goto out; goto out;
if (secret) if (secret)
@ -2196,12 +2291,14 @@ ssh_receive_key (estream_t stream, gcry_sexp_t *key_new, int secret,
* mpint ecdsa_private * mpint ecdsa_private
* *
* Note that we use the mpint reader instead of the string * Note that we use the mpint reader instead of the string
* reader for ecsa_public_key. * reader for ecsa_public_key. For the certificate variante
* ecdsa_curve_name+ecdsa_public_key are replaced by the
* certificate.
*/ */
unsigned char *buffer; unsigned char *buffer;
const char *mapped; const char *mapped;
err = stream_read_string (stream, 0, &buffer, NULL); err = stream_read_string (cert? cert : stream, 0, &buffer, NULL);
if (err) if (err)
goto out; goto out;
curve_name = buffer; curve_name = buffer;
@ -2228,13 +2325,13 @@ ssh_receive_key (estream_t stream, gcry_sexp_t *key_new, int secret,
} }
} }
err = ssh_receive_mpint_list (stream, secret, spec, &mpi_list); err = ssh_receive_mpint_list (stream, secret, &spec, cert, &mpi_list);
if (err) if (err)
goto out; goto out;
} }
else else
{ {
err = ssh_receive_mpint_list (stream, secret, spec, &mpi_list); err = ssh_receive_mpint_list (stream, secret, &spec, cert, &mpi_list);
if (err) if (err)
goto out; goto out;
} }
@ -2292,6 +2389,7 @@ ssh_receive_key (estream_t stream, gcry_sexp_t *key_new, int secret,
*key_new = key; *key_new = key;
out: out:
es_fclose (cert);
mpint_list_free (mpi_list); mpint_list_free (mpi_list);
xfree (curve_name); xfree (curve_name);
xfree (key_type); xfree (key_type);