diff --git a/agent/agent.h b/agent/agent.h index eac7ba548..58e584132 100644 --- a/agent/agent.h +++ b/agent/agent.h @@ -368,7 +368,8 @@ char *agent_get_cache (const char *key, cache_mode_t cache_mode); int agent_pksign_do (ctrl_t ctrl, const char *cache_nonce, const char *desc_text, gcry_sexp_t *signature_sexp, - cache_mode_t cache_mode, lookup_ttl_t lookup_ttl); + cache_mode_t cache_mode, lookup_ttl_t lookup_ttl, + const void *overridedata, size_t overridedatalen); int agent_pksign (ctrl_t ctrl, const char *cache_nonce, const char *desc_text, membuf_t *outbuf, cache_mode_t cache_mode); diff --git a/agent/command-ssh.c b/agent/command-ssh.c index 4191d6f0c..fb3b29a7f 100644 --- a/agent/command-ssh.c +++ b/agent/command-ssh.c @@ -2515,7 +2515,8 @@ data_sign (ctrl_t ctrl, ssh_key_type_spec_t *spec, _("Please enter the passphrase " "for the ssh key%%0A %F%%0A (%c)"), &signature_sexp, - CACHE_MODE_SSH, ttl_from_sshcontrol); + CACHE_MODE_SSH, ttl_from_sshcontrol, + NULL, 0); ctrl->use_auth_call = 0; if (err) goto out; diff --git a/agent/command.c b/agent/command.c index d1e53cd44..fab27f09d 100644 --- a/agent/command.c +++ b/agent/command.c @@ -2147,7 +2147,7 @@ cmd_export_key (assuan_context_t ctx, char *line) if (!ctrl->server_local->export_key) { - err = gpg_error (GPG_ERR_MISSING_KEY); + err = set_error (GPG_ERR_MISSING_KEY, "did you run KEYWRAP_KEY"); goto leave; } diff --git a/agent/cvt-openpgp.c b/agent/cvt-openpgp.c index 205b9533a..5718bd904 100644 --- a/agent/cvt-openpgp.c +++ b/agent/cvt-openpgp.c @@ -1030,46 +1030,6 @@ convert_from_openpgp_native (ctrl_t ctrl, } - -static gpg_error_t -key_from_sexp (gcry_sexp_t sexp, const char *elems, gcry_mpi_t *array) -{ - gpg_error_t err = 0; - gcry_sexp_t l2; - int idx; - - for (idx=0; *elems; elems++, idx++) - { - l2 = gcry_sexp_find_token (sexp, elems, 1); - if (!l2) - { - err = gpg_error (GPG_ERR_NO_OBJ); /* Required parameter not found. */ - goto leave; - } - array[idx] = gcry_sexp_nth_mpi (l2, 1, GCRYMPI_FMT_USG); - gcry_sexp_release (l2); - if (!array[idx]) - { - err = gpg_error (GPG_ERR_INV_OBJ); /* Required parameter invalid. */ - goto leave; - } - } - - leave: - if (err) - { - int i; - - for (i=0; i < idx; i++) - { - gcry_mpi_release (array[i]); - array[i] = NULL; - } - } - return err; -} - - /* Given an ARRAY of mpis with the key parameters, protect the secret parameters in that array and replace them by one opaque encoded mpi. NPKEY is the number of public key parameters and NSKEY is @@ -1173,7 +1133,6 @@ convert_to_openpgp (ctrl_t ctrl, gcry_sexp_t s_key, const char *passphrase, gpg_error_t err; gcry_sexp_t list, l2; char *name; - int algo; const char *algoname; const char *elems; int npkey, nskey; @@ -1203,26 +1162,63 @@ convert_to_openpgp (ctrl_t ctrl, gcry_sexp_t s_key, const char *passphrase, return gpg_error (GPG_ERR_INV_OBJ); /* Invalid structure of object. */ } - algo = gcry_pk_map_name (name); - xfree (name); - - switch (algo) + /* Map NAME to a name as used by Libgcrypt. We do not use the + Libgcrypt function here because we need a lowercase name and + require special treatment for some algorithms. */ + strlwr (name); + if (!strcmp (name, "rsa")) { - case GCRY_PK_RSA: algoname = "rsa"; npkey = 2; elems = "nedpqu"; break; - case GCRY_PK_ELG: algoname = "elg"; npkey = 3; elems = "pgyx"; break; - case GCRY_PK_ELG_E: algoname = "elg"; npkey = 3; elems = "pgyx"; break; - case GCRY_PK_DSA: algoname = "dsa"; npkey = 4; elems = "pqgyx"; break; - case GCRY_PK_ECDSA: algoname = "ecdsa"; npkey = 6; elems = "pabgnqd"; break; - case GCRY_PK_ECDH: algoname = "ecdh"; npkey = 6; elems = "pabgnqd"; break; - default: algoname = ""; npkey = 0; elems = NULL; break; + algoname = "rsa"; + npkey = 2; + elems = "nedpqu"; } + else if (!strcmp (name, "elg")) + { + algoname = "elg"; + npkey = 3; + elems = "pgyx"; + } + else if (!strcmp (name, "dsa")) + { + algoname = "dsa"; + npkey = 4; + elems = "pqgyx"; + } + else if (!strcmp (name, "ecc")) + { + algoname = "?"; /* Decide later by checking the usage. */ + npkey = 6; + elems = "pabgnqd"; + } + else if (!strcmp (name, "ecdsa")) + { + algoname = "ecdsa"; + npkey = 6; + elems = "pabgnqd"; + } + else if (!strcmp (name, "ecdh")) + { + algoname = "ecdh"; + npkey = 6; + elems = "pabgnqd"; + } + else + { + algoname = ""; + npkey = 0; + elems = NULL; + } + xfree (name); assert (!elems || strlen (elems) < DIM (array) ); nskey = elems? strlen (elems) : 0; + /* Extract the parameters and put them into an array. */ if (!elems) err = gpg_error (GPG_ERR_PUBKEY_ALGO); else - err = key_from_sexp (list, elems, array); + err = gcry_sexp_extract_param (list, NULL, elems, + array+0, array+1, array+2, array+3, array+4, + array+5, array+6, NULL); gcry_sexp_release (list); if (err) return err; diff --git a/agent/findkey.c b/agent/findkey.c index 7b24c55ed..84d2cfdc6 100644 --- a/agent/findkey.c +++ b/agent/findkey.c @@ -688,7 +688,7 @@ agent_key_from_file (ctrl_t ctrl, const char *cache_nonce, string describing the names of the parameters. ALGONAMESIZE and ELEMSSIZE give the allocated size of the provided buffers. The buffers may be NULL if not required. If R_LIST is not NULL the top - level list will be stored tehre; the caller needs to release it in + level list will be stored there; the caller needs to release it in this case. */ static gpg_error_t key_parms_from_sexp (gcry_sexp_t s_key, gcry_sexp_t *r_list, @@ -776,27 +776,65 @@ key_parms_from_sexp (gcry_sexp_t s_key, gcry_sexp_t *r_list, } +/* Return true if KEYPARMS holds an EdDSA key. */ +static int +is_eddsa (gcry_sexp_t keyparms) +{ + int result = 0; + gcry_sexp_t list; + const char *s; + size_t n; + int i; + + list = gcry_sexp_find_token (keyparms, "flags", 0); + for (i = list ? gcry_sexp_length (list)-1 : 0; i > 0; i--) + { + s = gcry_sexp_nth_data (list, i, &n); + if (!s) + continue; /* Not a data element. */ + + if (n == 5 && !memcmp (s, "eddsa", 5)) + { + result = 1; + break; + } + } + gcry_sexp_release (list); + return result; +} + + /* Return the public key algorithm number if S_KEY is a DSA style key. If it is not a DSA style key, return 0. */ int agent_is_dsa_key (gcry_sexp_t s_key) { + int result; + gcry_sexp_t list; char algoname[6]; if (!s_key) return 0; - if (key_parms_from_sexp (s_key, NULL, algoname, sizeof algoname, NULL, 0)) + if (key_parms_from_sexp (s_key, &list, algoname, sizeof algoname, NULL, 0)) return 0; /* Error - assume it is not an DSA key. */ if (!strcmp (algoname, "dsa")) - return GCRY_PK_DSA; + result = GCRY_PK_DSA; else if (!strcmp (algoname, "ecc")) - return GCRY_PK_ECDSA; /* FIXME: Check for the EdDSA flag. */ + { + if (is_eddsa (list)) + result = 0; + else + result = GCRY_PK_ECDSA; + } else if (!strcmp (algoname, "ecdsa")) - return GCRY_PK_ECDSA; + result = GCRY_PK_ECDSA; else - return 0; + result = 0; + + gcry_sexp_release (list); + return result; } @@ -804,18 +842,25 @@ agent_is_dsa_key (gcry_sexp_t s_key) int agent_is_eddsa_key (gcry_sexp_t s_key) { + int result; + gcry_sexp_t list; char algoname[6]; if (!s_key) return 0; - if (key_parms_from_sexp (s_key, NULL, algoname, sizeof algoname, NULL, 0)) + if (key_parms_from_sexp (s_key, &list, algoname, sizeof algoname, NULL, 0)) return 0; /* Error - assume it is not an EdDSA key. */ - if (!strcmp (algoname, "eddsa")) - return 1; + if (!strcmp (algoname, "ecc") && is_eddsa (list)) + result = 1; + else if (!strcmp (algoname, "eddsa")) /* backward compatibility. */ + result = 1; else - return 0; + result = 0; + + gcry_sexp_release (list); + return result; } diff --git a/agent/pksign.c b/agent/pksign.c index 088615011..fb593a6e2 100644 --- a/agent/pksign.c +++ b/agent/pksign.c @@ -276,18 +276,35 @@ do_encode_raw_pkcs1 (const byte *md, size_t mdlen, unsigned int nbits, the signature S-expression. LOOKUP is an optional function to provide a way for lower layers to ask for the caching TTL. If a CACHE_NONCE is given that cache item is first tried to get a - passphrase. */ + passphrase. If OVERRIDEDATA is not NULL, OVERRIDEDATALEN bytes + from this buffer are used instead of the data in CTRL. The + override feature is required to allow the use of Ed25519 with ssh + because Ed25519 dies the hashing itself. */ int agent_pksign_do (ctrl_t ctrl, const char *cache_nonce, const char *desc_text, gcry_sexp_t *signature_sexp, - cache_mode_t cache_mode, lookup_ttl_t lookup_ttl) + cache_mode_t cache_mode, lookup_ttl_t lookup_ttl, + const void *overridedata, size_t overridedatalen) { gcry_sexp_t s_skey = NULL, s_sig = NULL; unsigned char *shadow_info = NULL; unsigned int rc = 0; /* FIXME: gpg-error? */ + const unsigned char *data; + int datalen; - if (! ctrl->have_keygrip) + if (overridedata) + { + data = overridedata; + datalen = overridedatalen; + } + else + { + data = ctrl->digest.value; + datalen = ctrl->digest.valuelen; + } + + if (!ctrl->have_keygrip) return gpg_error (GPG_ERR_NO_SECKEY); rc = agent_key_from_file (ctrl, cache_nonce, desc_text, ctrl->keygrip, @@ -315,8 +332,7 @@ agent_pksign_do (ctrl_t ctrl, const char *cache_nonce, is_ECDSA = 1; rc = divert_pksign (ctrl, - ctrl->digest.value, - ctrl->digest.valuelen, + data, datalen, ctrl->digest.algo, shadow_info, &buf, &len); if (rc) @@ -405,22 +421,18 @@ agent_pksign_do (ctrl_t ctrl, const char *cache_nonce, /* Put the hash into a sexp */ if (agent_is_eddsa_key (s_skey)) - rc = do_encode_eddsa (ctrl->digest.value, - ctrl->digest.valuelen, + rc = do_encode_eddsa (data, datalen, &s_hash); else if (ctrl->digest.algo == MD_USER_TLS_MD5SHA1) - rc = do_encode_raw_pkcs1 (ctrl->digest.value, - ctrl->digest.valuelen, + rc = do_encode_raw_pkcs1 (data, datalen, gcry_pk_get_nbits (s_skey), &s_hash); else if ( (dsaalgo = agent_is_dsa_key (s_skey)) ) - rc = do_encode_dsa (ctrl->digest.value, - ctrl->digest.valuelen, + rc = do_encode_dsa (data, datalen, dsaalgo, s_skey, &s_hash); else - rc = do_encode_md (ctrl->digest.value, - ctrl->digest.valuelen, + rc = do_encode_md (data, datalen, ctrl->digest.algo, &s_hash, ctrl->digest.raw_value); @@ -468,7 +480,8 @@ agent_pksign (ctrl_t ctrl, const char *cache_nonce, const char *desc_text, size_t len = 0; int rc = 0; - rc = agent_pksign_do (ctrl, cache_nonce, desc_text, &s_sig, cache_mode, NULL); + rc = agent_pksign_do (ctrl, cache_nonce, desc_text, &s_sig, cache_mode, NULL, + NULL, 0); if (rc) goto leave; diff --git a/common/ssh-utils.c b/common/ssh-utils.c index 0c7156746..a75b3c06d 100644 --- a/common/ssh-utils.c +++ b/common/ssh-utils.c @@ -37,6 +37,33 @@ #include "ssh-utils.h" +/* Return true if KEYPARMS holds an EdDSA key. */ +static int +is_eddsa (gcry_sexp_t keyparms) +{ + int result = 0; + gcry_sexp_t list; + const char *s; + size_t n; + int i; + + list = gcry_sexp_find_token (keyparms, "flags", 0); + for (i = list ? gcry_sexp_length (list)-1 : 0; i > 0; i--) + { + s = gcry_sexp_nth_data (list, i, &n); + if (!s) + continue; /* Not a data element. */ + + if (n == 5 && !memcmp (s, "eddsa", 5)) + { + result = 1; + break; + } + } + gcry_sexp_release (list); + return result; +} + /* Return the Secure Shell type fingerprint for KEY. The length of the fingerprint is returned at R_LEN and the fingerprint itself at @@ -53,6 +80,7 @@ get_fingerprint (gcry_sexp_t key, void **r_fpr, size_t *r_len, int as_string) int idx; const char *elems; gcry_md_hd_t md = NULL; + int blobmode = 0; *r_fpr = NULL; *r_len = 0; @@ -93,38 +121,52 @@ get_fingerprint (gcry_sexp_t key, void **r_fpr, size_t *r_len, int as_string) elems = "en"; gcry_md_write (md, "\0\0\0\x07ssh-rsa", 11); break; + case GCRY_PK_DSA: 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 = ""; + + case GCRY_PK_ECC: + if (is_eddsa (list)) + { + elems = "q"; + blobmode = 1; + /* For now there is just one curve, thus no need to switch + on it. */ + gcry_md_write (md, "\0\0\0\x0b" "ssh-ed25519", 15); + } else { - gcry_free (name); - name = gcry_sexp_nth_string (l2, 1); - gcry_sexp_release (l2); - l2 = NULL; - if (!name) + /* 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 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 = ""; + { + 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); } - 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); @@ -133,33 +175,56 @@ get_fingerprint (gcry_sexp_t key, void **r_fpr, size_t *r_len, int as_string) if (err) goto leave; + for (idx = 0, s = elems; *s; s++, idx++) { - gcry_mpi_t a; - unsigned char *buf; - size_t buflen; - l2 = gcry_sexp_find_token (list, s, 1); if (!l2) { err = gpg_err_make (default_errsource, GPG_ERR_INV_SEXP); goto leave; } - a = gcry_sexp_nth_mpi (l2, 1, GCRYMPI_FMT_USG); - gcry_sexp_release (l2); - l2 = NULL; - if (!a) + if (blobmode) { - err = gpg_err_make (default_errsource, GPG_ERR_INV_SEXP); - goto leave; - } + const char *blob; + size_t bloblen; + unsigned char lenbuf[4]; - err = gcry_mpi_aprint (GCRYMPI_FMT_SSH, &buf, &buflen, a); - gcry_mpi_release (a); - if (err) - goto leave; - gcry_md_write (md, buf, buflen); - gcry_free (buf); + blob = gcry_sexp_nth_data (l2, 1, &bloblen); + if (!blob) + { + err = gpg_err_make (default_errsource, GPG_ERR_INV_SEXP); + goto leave; + } + lenbuf[0] = bloblen >> 24; + lenbuf[1] = bloblen >> 16; + lenbuf[2] = bloblen >> 8; + lenbuf[3] = bloblen; + gcry_md_write (md, lenbuf, 4); + gcry_md_write (md, blob, bloblen); + } + else + { + gcry_mpi_t a; + unsigned char *buf; + size_t buflen; + + a = gcry_sexp_nth_mpi (l2, 1, GCRYMPI_FMT_USG); + gcry_sexp_release (l2); + l2 = NULL; + if (!a) + { + err = gpg_err_make (default_errsource, GPG_ERR_INV_SEXP); + goto leave; + } + + err = gcry_mpi_aprint (GCRYMPI_FMT_SSH, &buf, &buflen, a); + gcry_mpi_release (a); + if (err) + goto leave; + gcry_md_write (md, buf, buflen); + gcry_free (buf); + } } *r_fpr = gcry_malloc (as_string? 61:20);